251210:1709 Frontend: reeactor organization and run build
Some checks failed
Spec Validation / validate-markdown (push) Has been cancelled
Spec Validation / validate-diagrams (push) Has been cancelled
Spec Validation / check-todos (push) Has been cancelled

This commit is contained in:
admin
2025-12-10 17:09:11 +07:00
parent aa96cd90e3
commit c8a0f281ef
140 changed files with 3780 additions and 1473 deletions

View File

@@ -3,59 +3,59 @@ import { User, CreateUserDto, Organization, AuditLog } from "@/types/admin";
// Mock Data
const mockUsers: User[] = [
{
user_id: 1,
userId: 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" }],
firstName: "System",
lastName: "Admin",
isActive: true,
roles: [{ roleId: 1, roleName: "ADMIN", description: "Administrator" }],
},
{
user_id: 2,
userId: 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" }],
firstName: "John",
lastName: "Doe",
isActive: true,
roles: [{ roleId: 2, roleName: "USER", description: "Regular User" }],
},
];
const mockOrgs: Organization[] = [
{
org_id: 1,
org_code: "PAT",
org_name: "Port Authority of Thailand",
org_name_th: "การท่าเรือแห่งประเทศไทย",
orgId: 1,
orgCode: "PAT",
orgName: "Port Authority of Thailand",
orgNameTh: "การท่าเรือแห่งประเทศไทย",
description: "Owner",
},
{
org_id: 2,
org_code: "CNPC",
org_name: "CNPC Consortium",
orgId: 2,
orgCode: "CNPC",
orgName: "CNPC Consortium",
description: "Main Contractor",
},
];
const mockLogs: AuditLog[] = [
{
audit_log_id: 1,
user_name: "admin",
auditLogId: 1,
userName: "admin",
action: "CREATE",
entity_type: "user",
entityType: "user",
description: "Created user 'jdoe'",
ip_address: "192.168.1.1",
created_at: new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString(),
ipAddress: "192.168.1.1",
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString(),
},
{
audit_log_id: 2,
user_name: "jdoe",
auditLogId: 2,
userName: "jdoe",
action: "UPDATE",
entity_type: "rfa",
entityType: "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(),
ipAddress: "192.168.1.5",
createdAt: new Date(Date.now() - 1000 * 60 * 30).toISOString(),
},
];
@@ -68,15 +68,15 @@ export const adminApi = {
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,
userId: Math.max(...mockUsers.map((u) => u.userId)) + 1,
username: data.username,
email: data.email,
first_name: data.first_name,
last_name: data.last_name,
is_active: data.is_active,
firstName: data.firstName,
lastName: data.lastName,
isActive: data.isActive,
roles: data.roles.map((id) => ({
role_id: id,
role_name: id === 1 ? "ADMIN" : "USER",
roleId: id,
roleName: id === 1 ? "ADMIN" : "USER",
description: "",
})),
};
@@ -89,9 +89,9 @@ export const adminApi = {
return [...mockOrgs];
},
createOrganization: async (data: Omit<Organization, "org_id">): Promise<Organization> => {
createOrganization: async (data: Omit<Organization, "orgId">): Promise<Organization> => {
await new Promise((resolve) => setTimeout(resolve, 600));
const newOrg = { ...data, org_id: Math.max(...mockOrgs.map((o) => o.org_id)) + 1 };
const newOrg = { ...data, orgId: Math.max(...mockOrgs.map((o) => o.orgId)) + 1 };
mockOrgs.push(newOrg);
return newOrg;
},

View File

@@ -0,0 +1,53 @@
import { Correspondence, CreateCorrespondenceDto } from "@/types/correspondence";
// Mock Data
const mockCorrespondences: Correspondence[] = [
{
correspondenceId: 1,
documentNumber: "PAT-CNPC-0001-2568",
subject: "Request for Additional Information",
description: "Please provide updated structural drawings for Phase 2",
status: "IN_REVIEW",
importance: "HIGH",
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 2).toISOString(),
updatedAt: new Date(Date.now() - 1000 * 60 * 60).toISOString(),
fromOrganizationId: 1,
toOrganizationId: 2,
documentTypeId: 1,
fromOrganization: { id: 1, orgName: "PAT", orgCode: "PAT" },
toOrganization: { id: 2, orgName: "CNPC", orgCode: "CNPC" },
attachments: [],
},
];
export const correspondenceApi = {
getAll: async (): Promise<{ data: Correspondence[]; meta: { total: number } }> => {
await new Promise((resolve) => setTimeout(resolve, 500));
return { data: mockCorrespondences, meta: { total: mockCorrespondences.length } };
},
getById: async (id: number): Promise<Correspondence | undefined> => {
await new Promise((resolve) => setTimeout(resolve, 300));
return mockCorrespondences.find((c) => c.correspondenceId === id);
},
create: async (data: CreateCorrespondenceDto): Promise<Correspondence> => {
await new Promise((resolve) => setTimeout(resolve, 800));
const newCorrespondence: Correspondence = {
correspondenceId: Math.max(...mockCorrespondences.map((c) => c.correspondenceId)) + 1,
documentNumber: `PAT-CNPC-${String(mockCorrespondences.length + 1).padStart(4, "0")}-2568`,
subject: data.subject,
description: data.description,
status: "DRAFT",
importance: data.importance,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
fromOrganizationId: data.fromOrganizationId,
toOrganizationId: data.toOrganizationId,
documentTypeId: data.documentTypeId,
attachments: [],
};
mockCorrespondences.push(newCorrespondence);
return newCorrespondence;
},
};

View File

@@ -0,0 +1,43 @@
import { Drawing } from "@/types/drawing";
// Mock Data
const mockDrawings: Drawing[] = [
{
drawingId: 1,
drawingNumber: "S-201-A",
title: "Structural Foundation Plan",
discipline: "Structural",
status: "APPROVED",
revision: "A",
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5).toISOString(),
updatedAt: new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString(),
},
{
drawingId: 2,
drawingNumber: "A-101-B",
title: "Architectural Floor Plan - Level 1",
discipline: "Architectural",
status: "IN_REVIEW",
revision: "B",
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 3).toISOString(),
updatedAt: new Date(Date.now() - 1000 * 60 * 60 * 2).toISOString(),
},
];
export const drawingApi = {
getAll: async (): Promise<{ data: Drawing[]; meta: { total: number } }> => {
await new Promise((resolve) => setTimeout(resolve, 500));
return { data: mockDrawings, meta: { total: mockDrawings.length } };
},
getById: async (id: number): Promise<Drawing | undefined> => {
await new Promise((resolve) => setTimeout(resolve, 300));
return mockDrawings.find((d) => d.drawingId === id);
},
getByContract: async (contractId: number): Promise<{ data: Drawing[] }> => {
await new Promise((resolve) => setTimeout(resolve, 400));
// Mock: return all drawings for any contract
return { data: mockDrawings };
},
};

View File

@@ -3,30 +3,30 @@ import { NotificationResponse } from "@/types/notification";
// Mock Data
let mockNotifications = [
{
notification_id: 1,
notificationId: 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
isRead: false,
createdAt: new Date(Date.now() - 1000 * 60 * 15).toISOString(), // 15 mins ago
link: "/rfas/1",
},
{
notification_id: 2,
notificationId: 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
isRead: false,
createdAt: new Date(Date.now() - 1000 * 60 * 60).toISOString(), // 1 hour ago
link: "/correspondences/3",
},
{
notification_id: 3,
notificationId: 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
isRead: true,
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString(), // 1 day ago
link: "/drawings/2",
},
];
@@ -34,7 +34,7 @@ let mockNotifications = [
export const notificationApi = {
getUnread: async (): Promise<NotificationResponse> => {
await new Promise((resolve) => setTimeout(resolve, 300));
const unread = mockNotifications.filter((n) => !n.is_read);
const unread = mockNotifications.filter((n) => !n.isRead);
return {
items: mockNotifications, // Return all for the list, but count unread
unreadCount: unread.length,
@@ -44,7 +44,7 @@ export const notificationApi = {
markAsRead: async (id: number) => {
await new Promise((resolve) => setTimeout(resolve, 200));
mockNotifications = mockNotifications.map((n) =>
n.notification_id === id ? { ...n, is_read: true } : n
n.notificationId === id ? { ...n, isRead: true } : n
);
},
};

View File

@@ -1,83 +1,84 @@
// Types
export interface NumberingTemplate {
template_id: number;
project_id?: number; // Added optional for flexibility in mock, generally required
document_type_name: string; // e.g. Correspondence, RFA
discipline_code?: string; // e.g. STR, ARC, NULL for all
template_format: string; // e.g. {ORG}-{DOCTYPE}-{YYYY}-{SEQ}
example_number: string;
current_number: number;
reset_annually: boolean;
padding_length: number;
is_active: boolean;
templateId: number;
projectId?: number;
documentTypeId?: string;
documentTypeName: string;
disciplineCode?: string;
templateFormat: string;
exampleNumber: string;
currentNumber: number;
resetAnnually: boolean;
paddingLength: number;
isActive: boolean;
}
export interface NumberSequence {
sequence_id: number;
sequenceId: number;
year: number;
organization_code?: string;
discipline_code?: string;
current_number: number;
last_generated_number: string;
updated_at: string;
organizationCode?: string;
disciplineCode?: string;
currentNumber: number;
lastGeneratedNumber: string;
updatedAt: string;
}
// Mock Data
const mockTemplates: NumberingTemplate[] = [
{
template_id: 1,
project_id: 1, // LCBP3
document_type_name: 'Correspondence',
discipline_code: '',
template_format: '{ORIGINATOR}-{RECIPIENT}-{SEQ:4}-{YEAR:B.E.}',
example_number: 'PAT-CN-0001-2568',
current_number: 142,
reset_annually: true,
padding_length: 4,
is_active: true,
templateId: 1,
projectId: 1,
documentTypeName: 'Correspondence',
disciplineCode: '',
templateFormat: '{ORIGINATOR}-{RECIPIENT}-{SEQ:4}-{YEAR:B.E.}',
exampleNumber: 'PAT-CN-0001-2568',
currentNumber: 142,
resetAnnually: true,
paddingLength: 4,
isActive: true,
},
{
template_id: 2,
project_id: 1, // LCBP3
document_type_name: 'RFA',
discipline_code: 'STR',
template_format: '{PROJECT}-{CORR_TYPE}-{DISCIPLINE}-{RFA_TYPE}-{SEQ:4}-{REV}',
example_number: 'LCBP3-RFA-STR-SDW-0056-A',
current_number: 56,
reset_annually: true,
padding_length: 4,
is_active: true,
templateId: 2,
projectId: 1,
documentTypeName: 'RFA',
disciplineCode: 'STR',
templateFormat: '{PROJECT}-{CORR_TYPE}-{DISCIPLINE}-{RFA_TYPE}-{SEQ:4}-{REV}',
exampleNumber: 'LCBP3-RFA-STR-SDW-0056-A',
currentNumber: 56,
resetAnnually: true,
paddingLength: 4,
isActive: true,
},
{
template_id: 3,
project_id: 2, // LCBP3-Maintenance
document_type_name: 'Maintenance Request',
discipline_code: '',
template_format: 'MAINT-{SEQ:4}',
example_number: 'MAINT-0001',
current_number: 1,
reset_annually: true,
padding_length: 4,
is_active: true,
templateId: 3,
projectId: 2,
documentTypeName: 'Maintenance Request',
disciplineCode: '',
templateFormat: 'MAINT-{SEQ:4}',
exampleNumber: 'MAINT-0001',
currentNumber: 1,
resetAnnually: true,
paddingLength: 4,
isActive: true,
},
];
const mockSequences: NumberSequence[] = [
{
sequence_id: 1,
sequenceId: 1,
year: 2025,
organization_code: 'PAT',
current_number: 142,
last_generated_number: 'PAT-CORR-2025-0142',
updated_at: new Date().toISOString(),
organizationCode: 'PAT',
currentNumber: 142,
lastGeneratedNumber: 'PAT-CORR-2025-0142',
updatedAt: new Date().toISOString(),
},
{
sequence_id: 2,
sequenceId: 2,
year: 2025,
discipline_code: 'STR',
current_number: 56,
last_generated_number: 'RFA-STR-2025-0056',
updated_at: new Date().toISOString(),
disciplineCode: 'STR',
currentNumber: 56,
lastGeneratedNumber: 'RFA-STR-2025-0056',
updatedAt: new Date().toISOString(),
},
];
@@ -90,32 +91,32 @@ export const numberingApi = {
getTemplate: async (id: number): Promise<NumberingTemplate | undefined> => {
return new Promise((resolve) => {
setTimeout(() => resolve(mockTemplates.find(t => t.template_id === id)), 300);
setTimeout(() => resolve(mockTemplates.find(t => t.templateId === id)), 300);
});
},
saveTemplate: async (template: Partial<NumberingTemplate>): Promise<NumberingTemplate> => {
return new Promise((resolve) => {
setTimeout(() => {
if (template.template_id) {
if (template.templateId) {
// Update
const index = mockTemplates.findIndex(t => t.template_id === template.template_id);
const index = mockTemplates.findIndex(t => t.templateId === template.templateId);
if (index !== -1) {
mockTemplates[index] = { ...mockTemplates[index], ...template } as NumberingTemplate;
resolve(mockTemplates[index]);
}
}
} else {
// Create
const newTemplate: NumberingTemplate = {
template_id: Math.floor(Math.random() * 1000),
document_type_name: 'New Type',
is_active: true,
current_number: 0,
example_number: 'PREVIEW',
template_format: template.template_format || '',
discipline_code: template.discipline_code,
padding_length: template.padding_length ?? 4,
reset_annually: template.reset_annually ?? true,
templateId: Math.floor(Math.random() * 1000),
documentTypeName: 'New Type',
isActive: true,
currentNumber: 0,
exampleNumber: 'PREVIEW',
templateFormat: template.templateFormat || '',
disciplineCode: template.disciplineCode,
paddingLength: template.paddingLength ?? 4,
resetAnnually: template.resetAnnually ?? true,
...template
} as NumberingTemplate;
mockTemplates.push(newTemplate);
@@ -131,19 +132,19 @@ export const numberingApi = {
});
},
generateTestNumber: async (templateId: number, context: { organization_id: string, discipline_id: string }): Promise<{ number: string }> => {
generateTestNumber: async (templateId: number, context: { organizationId: string, disciplineId: string }): Promise<{ number: string }> => {
return new Promise((resolve) => {
setTimeout(() => {
const template = mockTemplates.find(t => t.template_id === templateId);
const template = mockTemplates.find(t => t.templateId === templateId);
if (!template) return resolve({ number: 'ERROR' });
let format = template.template_format;
let format = template.templateFormat;
// Mock replacement
format = format.replace('{PROJECT}', 'LCBP3');
format = format.replace('{ORIGINATOR}', context.organization_id === '1' ? 'PAT' : 'CN');
format = format.replace('{RECIPIENT}', context.organization_id === '1' ? 'CN' : 'PAT');
format = format.replace('{CORR_TYPE}', template.document_type_name === 'Correspondence' ? 'CORR' : 'RFA');
format = format.replace('{DISCIPLINE}', context.discipline_id === '1' ? 'STR' : (context.discipline_id === '2' ? 'ARC' : 'GEN'));
format = format.replace('{ORIGINATOR}', context.organizationId === '1' ? 'PAT' : 'CN');
format = format.replace('{RECIPIENT}', context.organizationId === '1' ? 'CN' : 'PAT');
format = format.replace('{CORR_TYPE}', template.documentTypeName === 'Correspondence' ? 'CORR' : 'RFA');
format = format.replace('{DISCIPLINE}', context.disciplineId === '1' ? 'STR' : (context.disciplineId === '2' ? 'ARC' : 'GEN'));
format = format.replace('{RFA_TYPE}', 'SDW'); // Mock
const year = new Date().getFullYear();

View File

@@ -3,13 +3,13 @@ import { Workflow, CreateWorkflowDto, ValidationResult } from "@/types/workflow"
// Mock Data
let mockWorkflows: Workflow[] = [
{
workflow_id: 1,
workflow_name: "Standard RFA Workflow",
workflowId: 1,
workflowName: "Standard RFA Workflow",
description: "Default approval process for RFAs",
workflow_type: "RFA",
workflowType: "RFA",
version: 1,
is_active: true,
dsl_definition: `name: Standard RFA Workflow
isActive: true,
dslDefinition: `name: Standard RFA Workflow
steps:
- name: Review
type: REVIEW
@@ -18,23 +18,23 @@ steps:
- name: Approval
type: APPROVAL
role: PM`,
step_count: 2,
updated_at: new Date().toISOString(),
stepCount: 2,
updatedAt: new Date().toISOString(),
},
{
workflow_id: 2,
workflow_name: "Correspondence Review",
workflowId: 2,
workflowName: "Correspondence Review",
description: "Incoming correspondence review flow",
workflow_type: "CORRESPONDENCE",
workflowType: "CORRESPONDENCE",
version: 2,
is_active: true,
dsl_definition: `name: Correspondence Review
isActive: true,
dslDefinition: `name: Correspondence Review
steps:
- name: Initial Review
type: REVIEW
role: DC`,
step_count: 1,
updated_at: new Date(Date.now() - 86400000).toISOString(),
stepCount: 1,
updatedAt: new Date(Date.now() - 86400000).toISOString(),
},
];
@@ -46,18 +46,18 @@ export const workflowApi = {
getWorkflow: async (id: number): Promise<Workflow | undefined> => {
await new Promise((resolve) => setTimeout(resolve, 300));
return mockWorkflows.find((w) => w.workflow_id === id);
return mockWorkflows.find((w) => w.workflowId === 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,
workflowId: Math.max(...mockWorkflows.map((w) => w.workflowId)) + 1,
...data,
version: 1,
is_active: true,
step_count: 0, // Simplified for mock
updated_at: new Date().toISOString(),
isActive: true,
stepCount: 0, // Simplified for mock
updatedAt: new Date().toISOString(),
};
mockWorkflows.push(newWorkflow);
return newWorkflow;
@@ -65,10 +65,10 @@ export const workflowApi = {
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);
const index = mockWorkflows.findIndex((w) => w.workflowId === id);
if (index === -1) throw new Error("Workflow not found");
const updatedWorkflow = { ...mockWorkflows[index], ...data, updated_at: new Date().toISOString() };
const updatedWorkflow = { ...mockWorkflows[index], ...data, updatedAt: new Date().toISOString() };
mockWorkflows[index] = updatedWorkflow;
return updatedWorkflow;
},