251205:0000 Just start debug backend/frontend
This commit is contained in:
103
frontend/lib/api/admin.ts
Normal file
103
frontend/lib/api/admin.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { User, CreateUserDto, Organization, AuditLog } from "@/types/admin";
|
||||
|
||||
// Mock Data
|
||||
const mockUsers: User[] = [
|
||||
{
|
||||
user_id: 1,
|
||||
username: "admin",
|
||||
email: "admin@example.com",
|
||||
first_name: "System",
|
||||
last_name: "Admin",
|
||||
is_active: true,
|
||||
roles: [{ role_id: 1, role_name: "ADMIN", description: "Administrator" }],
|
||||
},
|
||||
{
|
||||
user_id: 2,
|
||||
username: "jdoe",
|
||||
email: "john.doe@example.com",
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
is_active: true,
|
||||
roles: [{ role_id: 2, role_name: "USER", description: "Regular User" }],
|
||||
},
|
||||
];
|
||||
|
||||
const mockOrgs: Organization[] = [
|
||||
{
|
||||
org_id: 1,
|
||||
org_code: "PAT",
|
||||
org_name: "Port Authority of Thailand",
|
||||
org_name_th: "การท่าเรือแห่งประเทศไทย",
|
||||
description: "Owner",
|
||||
},
|
||||
{
|
||||
org_id: 2,
|
||||
org_code: "CNPC",
|
||||
org_name: "CNPC Consortium",
|
||||
description: "Main Contractor",
|
||||
},
|
||||
];
|
||||
|
||||
const mockLogs: AuditLog[] = [
|
||||
{
|
||||
audit_log_id: 1,
|
||||
user_name: "admin",
|
||||
action: "CREATE",
|
||||
entity_type: "user",
|
||||
description: "Created user 'jdoe'",
|
||||
ip_address: "192.168.1.1",
|
||||
created_at: new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString(),
|
||||
},
|
||||
{
|
||||
audit_log_id: 2,
|
||||
user_name: "jdoe",
|
||||
action: "UPDATE",
|
||||
entity_type: "rfa",
|
||||
description: "Updated status of RFA-001 to APPROVED",
|
||||
ip_address: "192.168.1.5",
|
||||
created_at: new Date(Date.now() - 1000 * 60 * 30).toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
export const adminApi = {
|
||||
getUsers: async (): Promise<User[]> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
return [...mockUsers];
|
||||
},
|
||||
|
||||
createUser: async (data: CreateUserDto): Promise<User> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 800));
|
||||
const newUser: User = {
|
||||
user_id: Math.max(...mockUsers.map((u) => u.user_id)) + 1,
|
||||
username: data.username,
|
||||
email: data.email,
|
||||
first_name: data.first_name,
|
||||
last_name: data.last_name,
|
||||
is_active: data.is_active,
|
||||
roles: data.roles.map((id) => ({
|
||||
role_id: id,
|
||||
role_name: id === 1 ? "ADMIN" : "USER",
|
||||
description: "",
|
||||
})),
|
||||
};
|
||||
mockUsers.push(newUser);
|
||||
return newUser;
|
||||
},
|
||||
|
||||
getOrganizations: async (): Promise<Organization[]> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
return [...mockOrgs];
|
||||
},
|
||||
|
||||
createOrganization: async (data: Omit<Organization, "org_id">): Promise<Organization> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 600));
|
||||
const newOrg = { ...data, org_id: Math.max(...mockOrgs.map((o) => o.org_id)) + 1 };
|
||||
mockOrgs.push(newOrg);
|
||||
return newOrg;
|
||||
},
|
||||
|
||||
getAuditLogs: async (): Promise<AuditLog[]> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 400));
|
||||
return [...mockLogs];
|
||||
},
|
||||
};
|
||||
85
frontend/lib/api/correspondences.ts
Normal file
85
frontend/lib/api/correspondences.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { Correspondence, CreateCorrespondenceDto } from "@/types/correspondence";
|
||||
|
||||
// Mock Data
|
||||
const mockCorrespondences: Correspondence[] = [
|
||||
{
|
||||
correspondence_id: 1,
|
||||
document_number: "LCBP3-COR-001",
|
||||
subject: "Submission of Monthly Report - Jan 2025",
|
||||
description: "Please find attached the monthly progress report.",
|
||||
status: "PENDING",
|
||||
importance: "NORMAL",
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
from_organization_id: 1,
|
||||
to_organization_id: 2,
|
||||
document_type_id: 1,
|
||||
from_organization: { id: 1, org_name: "Contractor A", org_code: "CON-A" },
|
||||
to_organization: { id: 2, org_name: "Owner", org_code: "OWN" },
|
||||
},
|
||||
{
|
||||
correspondence_id: 2,
|
||||
document_number: "LCBP3-COR-002",
|
||||
subject: "Request for Information regarding Foundation",
|
||||
description: "Clarification needed on drawing A-101.",
|
||||
status: "IN_REVIEW",
|
||||
importance: "HIGH",
|
||||
created_at: new Date(Date.now() - 86400000).toISOString(),
|
||||
updated_at: new Date(Date.now() - 86400000).toISOString(),
|
||||
from_organization_id: 2,
|
||||
to_organization_id: 1,
|
||||
document_type_id: 1,
|
||||
from_organization: { id: 2, org_name: "Owner", org_code: "OWN" },
|
||||
to_organization: { id: 1, org_name: "Contractor A", org_code: "CON-A" },
|
||||
},
|
||||
];
|
||||
|
||||
export const correspondenceApi = {
|
||||
getAll: async (params?: { page?: number; status?: string; search?: string }) => {
|
||||
// Simulate API delay
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
let filtered = [...mockCorrespondences];
|
||||
if (params?.status) {
|
||||
filtered = filtered.filter((c) => c.status === params.status);
|
||||
}
|
||||
if (params?.search) {
|
||||
const lowerSearch = params.search.toLowerCase();
|
||||
filtered = filtered.filter((c) =>
|
||||
c.subject.toLowerCase().includes(lowerSearch) ||
|
||||
c.document_number.toLowerCase().includes(lowerSearch)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
page: params?.page || 1,
|
||||
totalPages: 1,
|
||||
};
|
||||
},
|
||||
|
||||
getById: async (id: number) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
return mockCorrespondences.find((c) => c.correspondence_id === id);
|
||||
},
|
||||
|
||||
create: async (data: CreateCorrespondenceDto) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
const newId = Math.max(...mockCorrespondences.map((c) => c.correspondence_id)) + 1;
|
||||
const newCorrespondence: Correspondence = {
|
||||
correspondence_id: newId,
|
||||
document_number: `LCBP3-COR-00${newId}`,
|
||||
...data,
|
||||
status: "DRAFT",
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
// Mock organizations for display
|
||||
from_organization: { id: data.from_organization_id, org_name: "Mock Org From", org_code: "MOCK" },
|
||||
to_organization: { id: data.to_organization_id, org_name: "Mock Org To", org_code: "MOCK" },
|
||||
} as Correspondence; // Casting for simplicity in mock
|
||||
|
||||
mockCorrespondences.unshift(newCorrespondence);
|
||||
return newCorrespondence;
|
||||
},
|
||||
};
|
||||
65
frontend/lib/api/dashboard.ts
Normal file
65
frontend/lib/api/dashboard.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { DashboardStats, ActivityLog, PendingTask } from "@/types/dashboard";
|
||||
|
||||
export const dashboardApi = {
|
||||
getStats: async (): Promise<DashboardStats> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
return {
|
||||
correspondences: 124,
|
||||
rfas: 45,
|
||||
approved: 89,
|
||||
pending: 12,
|
||||
};
|
||||
},
|
||||
|
||||
getRecentActivity: async (): Promise<ActivityLog[]> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 600));
|
||||
return [
|
||||
{
|
||||
id: 1,
|
||||
user: { name: "John Doe", initials: "JD" },
|
||||
action: "Created RFA",
|
||||
description: "RFA-001: Concrete Pouring Request",
|
||||
createdAt: new Date(Date.now() - 1000 * 60 * 30).toISOString(), // 30 mins ago
|
||||
targetUrl: "/rfas/1",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
user: { name: "Jane Smith", initials: "JS" },
|
||||
action: "Approved Correspondence",
|
||||
description: "COR-005: Site Safety Report",
|
||||
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 2).toISOString(), // 2 hours ago
|
||||
targetUrl: "/correspondences/5",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
user: { name: "Mike Johnson", initials: "MJ" },
|
||||
action: "Uploaded Drawing",
|
||||
description: "A-101: Ground Floor Plan Rev B",
|
||||
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 5).toISOString(), // 5 hours ago
|
||||
targetUrl: "/drawings/1",
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
getPendingTasks: async (): Promise<PendingTask[]> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 400));
|
||||
return [
|
||||
{
|
||||
id: 1,
|
||||
title: "Review RFA-002",
|
||||
description: "Approval required for steel reinforcement",
|
||||
daysOverdue: 2,
|
||||
url: "/rfas/2",
|
||||
priority: "HIGH",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Approve Monthly Report",
|
||||
description: "January 2025 Progress Report",
|
||||
daysOverdue: 0,
|
||||
url: "/correspondences/10",
|
||||
priority: "MEDIUM",
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
119
frontend/lib/api/drawings.ts
Normal file
119
frontend/lib/api/drawings.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import { Drawing, CreateDrawingDto, DrawingRevision } from "@/types/drawing";
|
||||
|
||||
// Mock Data
|
||||
const mockDrawings: Drawing[] = [
|
||||
{
|
||||
drawing_id: 1,
|
||||
drawing_number: "A-101",
|
||||
title: "Ground Floor Plan",
|
||||
type: "CONTRACT",
|
||||
discipline_id: 2,
|
||||
discipline: { id: 2, discipline_code: "ARC", discipline_name: "Architecture" },
|
||||
sheet_number: "01",
|
||||
scale: "1:100",
|
||||
current_revision: "0",
|
||||
issue_date: new Date(Date.now() - 100000000).toISOString(),
|
||||
revision_count: 1,
|
||||
revisions: [
|
||||
{
|
||||
revision_id: 1,
|
||||
revision_number: "0",
|
||||
revision_date: new Date(Date.now() - 100000000).toISOString(),
|
||||
revision_description: "Issued for Construction",
|
||||
revised_by_name: "John Doe",
|
||||
file_url: "/mock-drawing.pdf",
|
||||
is_current: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
drawing_id: 2,
|
||||
drawing_number: "S-201",
|
||||
title: "Foundation Details",
|
||||
type: "SHOP",
|
||||
discipline_id: 1,
|
||||
discipline: { id: 1, discipline_code: "STR", discipline_name: "Structure" },
|
||||
sheet_number: "05",
|
||||
scale: "1:50",
|
||||
current_revision: "B",
|
||||
issue_date: new Date().toISOString(),
|
||||
revision_count: 2,
|
||||
revisions: [
|
||||
{
|
||||
revision_id: 3,
|
||||
revision_number: "B",
|
||||
revision_date: new Date().toISOString(),
|
||||
revision_description: "Updated reinforcement",
|
||||
revised_by_name: "Jane Smith",
|
||||
file_url: "/mock-drawing-v2.pdf",
|
||||
is_current: true,
|
||||
},
|
||||
{
|
||||
revision_id: 2,
|
||||
revision_number: "A",
|
||||
revision_date: new Date(Date.now() - 50000000).toISOString(),
|
||||
revision_description: "First Submission",
|
||||
revised_by_name: "Jane Smith",
|
||||
file_url: "/mock-drawing-v1.pdf",
|
||||
is_current: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const drawingApi = {
|
||||
getAll: async (params?: { type?: "CONTRACT" | "SHOP"; search?: string }) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
let filtered = [...mockDrawings];
|
||||
if (params?.type) {
|
||||
filtered = filtered.filter((d) => d.type === params.type);
|
||||
}
|
||||
if (params?.search) {
|
||||
const lowerSearch = params.search.toLowerCase();
|
||||
filtered = filtered.filter((d) =>
|
||||
d.drawing_number.toLowerCase().includes(lowerSearch) ||
|
||||
d.title.toLowerCase().includes(lowerSearch)
|
||||
);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
},
|
||||
|
||||
getById: async (id: number) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
return mockDrawings.find((d) => d.drawing_id === id);
|
||||
},
|
||||
|
||||
create: async (data: CreateDrawingDto) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
const newId = Math.max(...mockDrawings.map((d) => d.drawing_id)) + 1;
|
||||
const newDrawing: Drawing = {
|
||||
drawing_id: newId,
|
||||
drawing_number: data.drawing_number,
|
||||
title: data.title,
|
||||
type: data.drawing_type,
|
||||
discipline_id: data.discipline_id,
|
||||
discipline: { id: data.discipline_id, discipline_code: "MOCK", discipline_name: "Mock Discipline" },
|
||||
sheet_number: data.sheet_number,
|
||||
scale: data.scale,
|
||||
current_revision: "0",
|
||||
issue_date: new Date().toISOString(),
|
||||
revision_count: 1,
|
||||
revisions: [
|
||||
{
|
||||
revision_id: newId * 10,
|
||||
revision_number: "0",
|
||||
revision_date: new Date().toISOString(),
|
||||
revision_description: "Initial Upload",
|
||||
revised_by_name: "Current User",
|
||||
file_url: "#",
|
||||
is_current: true,
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
mockDrawings.unshift(newDrawing);
|
||||
return newDrawing;
|
||||
},
|
||||
};
|
||||
50
frontend/lib/api/notifications.ts
Normal file
50
frontend/lib/api/notifications.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { NotificationResponse } from "@/types/notification";
|
||||
|
||||
// Mock Data
|
||||
let mockNotifications = [
|
||||
{
|
||||
notification_id: 1,
|
||||
title: "RFA Approved",
|
||||
message: "RFA-001 has been approved by the Project Manager.",
|
||||
type: "SUCCESS" as const,
|
||||
is_read: false,
|
||||
created_at: new Date(Date.now() - 1000 * 60 * 15).toISOString(), // 15 mins ago
|
||||
link: "/rfas/1",
|
||||
},
|
||||
{
|
||||
notification_id: 2,
|
||||
title: "New Correspondence",
|
||||
message: "You have received a new correspondence from Contractor A.",
|
||||
type: "INFO" as const,
|
||||
is_read: false,
|
||||
created_at: new Date(Date.now() - 1000 * 60 * 60).toISOString(), // 1 hour ago
|
||||
link: "/correspondences/3",
|
||||
},
|
||||
{
|
||||
notification_id: 3,
|
||||
title: "Drawing Revision Required",
|
||||
message: "Drawing S-201 requires revision based on recent comments.",
|
||||
type: "WARNING" as const,
|
||||
is_read: true,
|
||||
created_at: new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString(), // 1 day ago
|
||||
link: "/drawings/2",
|
||||
},
|
||||
];
|
||||
|
||||
export const notificationApi = {
|
||||
getUnread: async (): Promise<NotificationResponse> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
const unread = mockNotifications.filter((n) => !n.is_read);
|
||||
return {
|
||||
items: mockNotifications, // Return all for the list, but count unread
|
||||
unreadCount: unread.length,
|
||||
};
|
||||
},
|
||||
|
||||
markAsRead: async (id: number) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
mockNotifications = mockNotifications.map((n) =>
|
||||
n.notification_id === id ? { ...n, is_read: true } : n
|
||||
);
|
||||
},
|
||||
};
|
||||
111
frontend/lib/api/numbering.ts
Normal file
111
frontend/lib/api/numbering.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { NumberingTemplate, NumberingSequence, CreateTemplateDto, TestGenerationResult } from "@/types/numbering";
|
||||
|
||||
// Mock Data
|
||||
let mockTemplates: NumberingTemplate[] = [
|
||||
{
|
||||
template_id: 1,
|
||||
document_type_id: "correspondence",
|
||||
document_type_name: "Correspondence",
|
||||
discipline_code: "",
|
||||
template_format: "{ORG}-CORR-{YYYY}-{SEQ}",
|
||||
example_number: "PAT-CORR-2025-0001",
|
||||
current_number: 125,
|
||||
reset_annually: true,
|
||||
padding_length: 4,
|
||||
is_active: true,
|
||||
updated_at: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
template_id: 2,
|
||||
document_type_id: "rfa",
|
||||
document_type_name: "RFA",
|
||||
discipline_code: "STR",
|
||||
template_format: "{ORG}-RFA-STR-{YYYY}-{SEQ}",
|
||||
example_number: "ITD-RFA-STR-2025-0042",
|
||||
current_number: 42,
|
||||
reset_annually: true,
|
||||
padding_length: 4,
|
||||
is_active: true,
|
||||
updated_at: new Date(Date.now() - 86400000).toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
const mockSequences: NumberingSequence[] = [
|
||||
{
|
||||
sequence_id: 1,
|
||||
template_id: 1,
|
||||
year: 2025,
|
||||
organization_code: "PAT",
|
||||
current_number: 125,
|
||||
last_generated_number: "PAT-CORR-2025-0125",
|
||||
updated_at: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
sequence_id: 2,
|
||||
template_id: 2,
|
||||
year: 2025,
|
||||
organization_code: "ITD",
|
||||
discipline_code: "STR",
|
||||
current_number: 42,
|
||||
last_generated_number: "ITD-RFA-STR-2025-0042",
|
||||
updated_at: new Date().toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
export const numberingApi = {
|
||||
getTemplates: async (): Promise<NumberingTemplate[]> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
return [...mockTemplates];
|
||||
},
|
||||
|
||||
getTemplate: async (id: number): Promise<NumberingTemplate | undefined> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
return mockTemplates.find((t) => t.template_id === id);
|
||||
},
|
||||
|
||||
createTemplate: async (data: CreateTemplateDto): Promise<NumberingTemplate> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 800));
|
||||
const newTemplate: NumberingTemplate = {
|
||||
template_id: Math.max(...mockTemplates.map((t) => t.template_id)) + 1,
|
||||
document_type_name: data.document_type_id.toUpperCase(), // Simplified
|
||||
...data,
|
||||
example_number: "TEST-0001", // Simplified
|
||||
current_number: data.starting_number - 1,
|
||||
is_active: true,
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
mockTemplates.push(newTemplate);
|
||||
return newTemplate;
|
||||
},
|
||||
|
||||
updateTemplate: async (id: number, data: Partial<CreateTemplateDto>): Promise<NumberingTemplate> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 600));
|
||||
const index = mockTemplates.findIndex((t) => t.template_id === id);
|
||||
if (index === -1) throw new Error("Template not found");
|
||||
|
||||
const updatedTemplate = { ...mockTemplates[index], ...data, updated_at: new Date().toISOString() };
|
||||
mockTemplates[index] = updatedTemplate;
|
||||
return updatedTemplate;
|
||||
},
|
||||
|
||||
getSequences: async (templateId: number): Promise<NumberingSequence[]> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 400));
|
||||
return mockSequences.filter((s) => s.template_id === templateId);
|
||||
},
|
||||
|
||||
testTemplate: async (templateId: number, data: any): Promise<TestGenerationResult> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
const template = mockTemplates.find(t => t.template_id === templateId);
|
||||
if (!template) throw new Error("Template not found");
|
||||
|
||||
// Mock generation logic
|
||||
let number = template.template_format;
|
||||
number = number.replace("{ORG}", data.organization_id === "1" ? "PAT" : "ITD");
|
||||
number = number.replace("{DOCTYPE}", template.document_type_id.toUpperCase());
|
||||
number = number.replace("{DISC}", data.discipline_id === "1" ? "STR" : "ARC");
|
||||
number = number.replace("{YYYY}", data.year.toString());
|
||||
number = number.replace("{SEQ}", "0001");
|
||||
|
||||
return { number };
|
||||
},
|
||||
};
|
||||
98
frontend/lib/api/rfas.ts
Normal file
98
frontend/lib/api/rfas.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { RFA, CreateRFADto, RFAItem } from "@/types/rfa";
|
||||
|
||||
// Mock Data
|
||||
const mockRFAs: RFA[] = [
|
||||
{
|
||||
rfa_id: 1,
|
||||
rfa_number: "LCBP3-RFA-001",
|
||||
subject: "Approval for Concrete Mix Design",
|
||||
description: "Requesting approval for the proposed concrete mix design for foundations.",
|
||||
contract_id: 1,
|
||||
discipline_id: 1,
|
||||
status: "PENDING",
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
contract_name: "Main Construction Contract",
|
||||
discipline_name: "Civil",
|
||||
items: [
|
||||
{ id: 1, item_no: "1.1", description: "Concrete Mix Type A", quantity: 1, unit: "Lot", status: "PENDING" },
|
||||
{ id: 2, item_no: "1.2", description: "Concrete Mix Type B", quantity: 1, unit: "Lot", status: "PENDING" },
|
||||
],
|
||||
},
|
||||
{
|
||||
rfa_id: 2,
|
||||
rfa_number: "LCBP3-RFA-002",
|
||||
subject: "Approval for Steel Reinforcement Shop Drawings",
|
||||
description: "Shop drawings for Zone A foundations.",
|
||||
contract_id: 1,
|
||||
discipline_id: 2,
|
||||
status: "APPROVED",
|
||||
created_at: new Date(Date.now() - 172800000).toISOString(),
|
||||
updated_at: new Date(Date.now() - 86400000).toISOString(),
|
||||
contract_name: "Main Construction Contract",
|
||||
discipline_name: "Structural",
|
||||
items: [
|
||||
{ id: 3, item_no: "1", description: "Shop Drawing Set A", quantity: 1, unit: "Set", status: "APPROVED" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const rfaApi = {
|
||||
getAll: async (params?: { page?: number; status?: string; search?: string }) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
let filtered = [...mockRFAs];
|
||||
if (params?.status) {
|
||||
filtered = filtered.filter((r) => r.status === params.status);
|
||||
}
|
||||
if (params?.search) {
|
||||
const lowerSearch = params.search.toLowerCase();
|
||||
filtered = filtered.filter((r) =>
|
||||
r.subject.toLowerCase().includes(lowerSearch) ||
|
||||
r.rfa_number.toLowerCase().includes(lowerSearch)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
items: filtered,
|
||||
total: filtered.length,
|
||||
page: params?.page || 1,
|
||||
totalPages: 1,
|
||||
};
|
||||
},
|
||||
|
||||
getById: async (id: number) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
return mockRFAs.find((r) => r.rfa_id === id);
|
||||
},
|
||||
|
||||
create: async (data: CreateRFADto) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
const newId = Math.max(...mockRFAs.map((r) => r.rfa_id)) + 1;
|
||||
const newRFA: RFA = {
|
||||
rfa_id: newId,
|
||||
rfa_number: `LCBP3-RFA-00${newId}`,
|
||||
...data,
|
||||
status: "DRAFT",
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
contract_name: "Mock Contract",
|
||||
discipline_name: "Mock Discipline",
|
||||
items: data.items.map((item, index) => ({ ...item, id: index + 1, status: "PENDING" })),
|
||||
};
|
||||
|
||||
mockRFAs.unshift(newRFA);
|
||||
return newRFA;
|
||||
},
|
||||
|
||||
updateStatus: async (id: number, status: RFA['status'], comments?: string) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 800));
|
||||
const rfa = mockRFAs.find((r) => r.rfa_id === id);
|
||||
if (rfa) {
|
||||
rfa.status = status;
|
||||
rfa.updated_at = new Date().toISOString();
|
||||
// In a real app, we'd log the comments and history
|
||||
}
|
||||
return rfa;
|
||||
},
|
||||
};
|
||||
79
frontend/lib/api/search.ts
Normal file
79
frontend/lib/api/search.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { SearchResult, SearchFilters } from "@/types/search";
|
||||
|
||||
// Mock Data
|
||||
const mockResults: SearchResult[] = [
|
||||
{
|
||||
id: 1,
|
||||
type: "correspondence",
|
||||
title: "Submission of Monthly Report - Jan 2025",
|
||||
description: "Please find attached the monthly progress report.",
|
||||
status: "PENDING",
|
||||
documentNumber: "LCBP3-COR-001",
|
||||
createdAt: new Date().toISOString(),
|
||||
highlight: "Submission of <b>Monthly Report</b> - Jan 2025",
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
type: "rfa",
|
||||
title: "Approval for Concrete Mix Design",
|
||||
description: "Requesting approval for the proposed concrete mix design.",
|
||||
status: "PENDING",
|
||||
documentNumber: "LCBP3-RFA-001",
|
||||
createdAt: new Date().toISOString(),
|
||||
highlight: "Approval for <b>Concrete Mix</b> Design",
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
type: "drawing",
|
||||
title: "Ground Floor Plan",
|
||||
description: "Architectural ground floor plan.",
|
||||
status: "APPROVED",
|
||||
documentNumber: "A-101",
|
||||
createdAt: new Date(Date.now() - 100000000).toISOString(),
|
||||
highlight: "Ground Floor <b>Plan</b>",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: "correspondence",
|
||||
title: "Request for Information regarding Foundation",
|
||||
description: "Clarification needed on drawing A-101.",
|
||||
status: "IN_REVIEW",
|
||||
documentNumber: "LCBP3-COR-002",
|
||||
createdAt: new Date(Date.now() - 86400000).toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
export const searchApi = {
|
||||
search: async (filters: SearchFilters) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 600));
|
||||
|
||||
let results = [...mockResults];
|
||||
|
||||
if (filters.query) {
|
||||
const lowerQuery = filters.query.toLowerCase();
|
||||
results = results.filter((r) =>
|
||||
r.title.toLowerCase().includes(lowerQuery) ||
|
||||
r.documentNumber.toLowerCase().includes(lowerQuery) ||
|
||||
r.description?.toLowerCase().includes(lowerQuery)
|
||||
);
|
||||
}
|
||||
|
||||
if (filters.types && filters.types.length > 0) {
|
||||
results = results.filter((r) => filters.types?.includes(r.type));
|
||||
}
|
||||
|
||||
if (filters.statuses && filters.statuses.length > 0) {
|
||||
results = results.filter((r) => filters.statuses?.includes(r.status));
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
suggest: async (query: string) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
const lowerQuery = query.toLowerCase();
|
||||
return mockResults
|
||||
.filter((r) => r.title.toLowerCase().includes(lowerQuery))
|
||||
.slice(0, 5);
|
||||
},
|
||||
};
|
||||
84
frontend/lib/api/workflows.ts
Normal file
84
frontend/lib/api/workflows.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { Workflow, CreateWorkflowDto, ValidationResult } from "@/types/workflow";
|
||||
|
||||
// Mock Data
|
||||
let mockWorkflows: Workflow[] = [
|
||||
{
|
||||
workflow_id: 1,
|
||||
workflow_name: "Standard RFA Workflow",
|
||||
description: "Default approval process for RFAs",
|
||||
workflow_type: "RFA",
|
||||
version: 1,
|
||||
is_active: true,
|
||||
dsl_definition: `name: Standard RFA Workflow
|
||||
steps:
|
||||
- name: Review
|
||||
type: REVIEW
|
||||
role: CM
|
||||
next: Approval
|
||||
- name: Approval
|
||||
type: APPROVAL
|
||||
role: PM`,
|
||||
step_count: 2,
|
||||
updated_at: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
workflow_id: 2,
|
||||
workflow_name: "Correspondence Review",
|
||||
description: "Incoming correspondence review flow",
|
||||
workflow_type: "CORRESPONDENCE",
|
||||
version: 2,
|
||||
is_active: true,
|
||||
dsl_definition: `name: Correspondence Review
|
||||
steps:
|
||||
- name: Initial Review
|
||||
type: REVIEW
|
||||
role: DC`,
|
||||
step_count: 1,
|
||||
updated_at: new Date(Date.now() - 86400000).toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
export const workflowApi = {
|
||||
getWorkflows: async (): Promise<Workflow[]> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
return [...mockWorkflows];
|
||||
},
|
||||
|
||||
getWorkflow: async (id: number): Promise<Workflow | undefined> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
return mockWorkflows.find((w) => w.workflow_id === id);
|
||||
},
|
||||
|
||||
createWorkflow: async (data: CreateWorkflowDto): Promise<Workflow> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 800));
|
||||
const newWorkflow: Workflow = {
|
||||
workflow_id: Math.max(...mockWorkflows.map((w) => w.workflow_id)) + 1,
|
||||
...data,
|
||||
version: 1,
|
||||
is_active: true,
|
||||
step_count: 0, // Simplified for mock
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
mockWorkflows.push(newWorkflow);
|
||||
return newWorkflow;
|
||||
},
|
||||
|
||||
updateWorkflow: async (id: number, data: Partial<CreateWorkflowDto>): Promise<Workflow> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 600));
|
||||
const index = mockWorkflows.findIndex((w) => w.workflow_id === id);
|
||||
if (index === -1) throw new Error("Workflow not found");
|
||||
|
||||
const updatedWorkflow = { ...mockWorkflows[index], ...data, updated_at: new Date().toISOString() };
|
||||
mockWorkflows[index] = updatedWorkflow;
|
||||
return updatedWorkflow;
|
||||
},
|
||||
|
||||
validateDSL: async (dsl: string): Promise<ValidationResult> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 400));
|
||||
// Simple mock validation
|
||||
if (!dsl.includes("name:") || !dsl.includes("steps:")) {
|
||||
return { valid: false, errors: ["Missing 'name' or 'steps' field"] };
|
||||
}
|
||||
return { valid: true, errors: [] };
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user