From 9c5ac74ce5153a1953b67d44a0c50230db234269 Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 27 Mar 2026 00:24:16 +0700 Subject: [PATCH] 690327:0024 Fixing Refactor ADR-019 Naming convention uuid #11 --- .../src/modules/user/entities/user.entity.ts | 9 +++++- backend/src/modules/user/user.service.ts | 5 +++- .../admin/doc-control/contracts/page.tsx | 10 +++---- .../drawings/contract/categories/page.tsx | 4 +-- .../drawings/contract/sub-categories/page.tsx | 4 +-- .../drawings/contract/volumes/page.tsx | 4 +-- .../drawings/shop/main-categories/page.tsx | 4 +-- .../drawings/shop/sub-categories/page.tsx | 4 +-- .../doc-control/numbering/[id]/edit/page.tsx | 9 ++++-- .../admin/doc-control/numbering/new/page.tsx | 9 ++++-- .../admin/doc-control/numbering/page.tsx | 6 ++-- .../admin/doc-control/projects/page.tsx | 6 ++-- .../admin/doc-control/reference/tags/page.tsx | 8 +++--- frontend/app/(dashboard)/drawings/page.tsx | 4 +-- frontend/components/drawings/upload-form.tsx | 28 ++++++++++++++----- 15 files changed, 72 insertions(+), 42 deletions(-) diff --git a/backend/src/modules/user/entities/user.entity.ts b/backend/src/modules/user/entities/user.entity.ts index 655dde7..6e85c4f 100644 --- a/backend/src/modules/user/entities/user.entity.ts +++ b/backend/src/modules/user/entities/user.entity.ts @@ -17,7 +17,7 @@ import { Organization } from '../../organization/entities/organization.entity'; import { UserAssignment } from './user-assignment.entity'; import { UserPreference } from './user-preference.entity'; import { UuidBaseEntity } from '../../../common/entities/uuid-base.entity'; -import { Exclude } from 'class-transformer'; +import { Exclude, Expose } from 'class-transformer'; @Entity('users') export class User extends UuidBaseEntity { @@ -57,12 +57,19 @@ export class User extends UuidBaseEntity { // Relation กับ Organization (สังกัดหลัก) @Column({ name: 'primary_organization_id', nullable: true }) + @Exclude() // INT ID - never expose, use primaryOrganizationPublicId instead (ADR-019) primaryOrganizationId?: number; @ManyToOne(() => Organization, { nullable: true, onDelete: 'SET NULL' }) @JoinColumn({ name: 'primary_organization_id' }) organization?: Organization; + // ADR-019: Expose UUID instead of INT ID + @Expose({ name: 'primaryOrganizationId' }) + get primaryOrganizationPublicId(): string | undefined { + return this.organization?.publicId; + } + // Relation กับ Assignments (RBAC) @OneToMany(() => UserAssignment, (assignment) => assignment.user) assignments?: UserAssignment[]; diff --git a/backend/src/modules/user/user.service.ts b/backend/src/modules/user/user.service.ts index 1a015a8..d20c734 100644 --- a/backend/src/modules/user/user.service.ts +++ b/backend/src/modules/user/user.service.ts @@ -84,6 +84,7 @@ export class UserService { .leftJoinAndSelect('user.preference', 'preference') // Optional .leftJoinAndSelect('user.assignments', 'assignments') .leftJoinAndSelect('assignments.role', 'role') + .leftJoinAndSelect('user.organization', 'organization') // [FIX] Required for primaryOrganizationPublicId getter (ADR-019) .select([ 'user.user_id', 'user.publicId', @@ -92,13 +93,13 @@ export class UserService { 'user.firstName', 'user.lastName', 'user.lineId', - 'user.primaryOrganizationId', 'user.isActive', 'user.createdAt', 'user.updatedAt', 'assignments.id', 'role.roleId', 'role.roleName', + 'organization.publicId', // [FIX] Expose org UUID for getter (ADR-019) ]); // Apply Filters @@ -146,6 +147,7 @@ export class UserService { 'assignments', 'assignments.role', 'assignments.role.permissions', // [FIX] Required for RBAC AbilityFactory + 'organization', // [FIX] Required for primaryOrganizationPublicId getter (ADR-019) ], }); @@ -164,6 +166,7 @@ export class UserService { 'assignments', 'assignments.role', 'assignments.role.permissions', + 'organization', // [FIX] Required for primaryOrganizationPublicId getter (ADR-019) ], }); diff --git a/frontend/app/(admin)/admin/doc-control/contracts/page.tsx b/frontend/app/(admin)/admin/doc-control/contracts/page.tsx index 88966c4..12c953e 100644 --- a/frontend/app/(admin)/admin/doc-control/contracts/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/contracts/page.tsx @@ -38,7 +38,7 @@ import { SearchContractDto, CreateContractDto, UpdateContractDto } from '@/types import { AxiosError } from 'axios'; interface _Project { - id: string; // ADR-019: uuid exposed as 'id' (string) + publicId: string; // ADR-019: uuid exposed as 'publicId' (string) projectCode: string; projectName: string; } @@ -206,8 +206,8 @@ export default function ContractsPage() { const handleEdit = (contract: Contract) => { setEditingUuid(contract.id); - // ADR-019: project.id is the project's UUID (exposed via @Expose) - const pId = contract.project?.id || ''; + // ADR-019: project.publicId is the project's UUID + const pId = contract.project?.publicId || ''; reset({ contractCode: contract.contractCode, contractName: contract.contractName, @@ -299,8 +299,8 @@ export default function ContractsPage() { {projects?.map((p) => ( - // ADR-019: Project exposes UUID as 'id' (string) - + // ADR-019: Project exposes UUID as 'publicId' + {p.projectCode} - {p.projectName} ))} diff --git a/frontend/app/(admin)/admin/doc-control/drawings/contract/categories/page.tsx b/frontend/app/(admin)/admin/doc-control/drawings/contract/categories/page.tsx index 998a75b..0ce2340 100644 --- a/frontend/app/(admin)/admin/doc-control/drawings/contract/categories/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/drawings/contract/categories/page.tsx @@ -67,8 +67,8 @@ export default function ContractCategoriesPage() { )} - {(projects as { id?: number; uuid?: string; projectCode: string; projectName: string }[]).map((project) => ( - + {(projects as { id?: number; publicId?: string; projectCode: string; projectName: string }[]).map((project) => ( + {project.projectCode} - {project.projectName} ))} diff --git a/frontend/app/(admin)/admin/doc-control/drawings/contract/sub-categories/page.tsx b/frontend/app/(admin)/admin/doc-control/drawings/contract/sub-categories/page.tsx index e5be756..b14fe8f 100644 --- a/frontend/app/(admin)/admin/doc-control/drawings/contract/sub-categories/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/drawings/contract/sub-categories/page.tsx @@ -59,8 +59,8 @@ export default function ContractSubCategoriesPage() { )} - {(projects as { id?: number; uuid?: string; projectCode: string; projectName: string }[]).map((project) => ( - + {(projects as { id?: number; publicId?: string; projectCode: string; projectName: string }[]).map((project) => ( + {project.projectCode} - {project.projectName} ))} diff --git a/frontend/app/(admin)/admin/doc-control/drawings/contract/volumes/page.tsx b/frontend/app/(admin)/admin/doc-control/drawings/contract/volumes/page.tsx index 571d6ad..85d7821 100644 --- a/frontend/app/(admin)/admin/doc-control/drawings/contract/volumes/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/drawings/contract/volumes/page.tsx @@ -59,8 +59,8 @@ export default function ContractVolumesPage() { )} - {(projects as { id?: number; uuid?: string; projectCode: string; projectName: string }[]).map((project) => ( - + {(projects as { id?: number; publicId?: string; projectCode: string; projectName: string }[]).map((project) => ( + {project.projectCode} - {project.projectName} ))} diff --git a/frontend/app/(admin)/admin/doc-control/drawings/shop/main-categories/page.tsx b/frontend/app/(admin)/admin/doc-control/drawings/shop/main-categories/page.tsx index 9241fd8..02a7c3f 100644 --- a/frontend/app/(admin)/admin/doc-control/drawings/shop/main-categories/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/drawings/shop/main-categories/page.tsx @@ -70,8 +70,8 @@ export default function ShopMainCategoriesPage() { )} - {(projects as { id?: number; uuid?: string; projectCode: string; projectName: string }[]).map((project) => ( - + {(projects as { id?: number; publicId?: string; projectCode: string; projectName: string }[]).map((project) => ( + {project.projectCode} - {project.projectName} ))} diff --git a/frontend/app/(admin)/admin/doc-control/drawings/shop/sub-categories/page.tsx b/frontend/app/(admin)/admin/doc-control/drawings/shop/sub-categories/page.tsx index 0b7739f..aceab0e 100644 --- a/frontend/app/(admin)/admin/doc-control/drawings/shop/sub-categories/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/drawings/shop/sub-categories/page.tsx @@ -70,8 +70,8 @@ export default function ShopSubCategoriesPage() { )} - {(projects as { id?: number; uuid?: string; projectCode: string; projectName: string }[]).map((project) => ( - + {(projects as { id?: number; publicId?: string; projectCode: string; projectName: string }[]).map((project) => ( + {project.projectCode} - {project.projectName} ))} diff --git a/frontend/app/(admin)/admin/doc-control/numbering/[id]/edit/page.tsx b/frontend/app/(admin)/admin/doc-control/numbering/[id]/edit/page.tsx index 8b00656..25581da 100644 --- a/frontend/app/(admin)/admin/doc-control/numbering/[id]/edit/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/numbering/[id]/edit/page.tsx @@ -24,12 +24,15 @@ export default function EditTemplatePage() { const { data: correspondenceTypes = [] } = useCorrespondenceTypes(); const { data: projects = [] } = useProjects(); const projectId = template?.projectId || 1; - const { data: contracts = [] } = useContracts(projectId); - const contractId = contracts[0]?.id; + const { data: contractsData } = useContracts(projectId); + const contracts = Array.isArray(contractsData) ? contractsData : []; + const firstContract = contracts[0] as { id?: number; publicId?: string } | undefined; + const contractId = firstContract?.publicId ?? firstContract?.id; const { data: disciplines = [] } = useDisciplines(contractId); const selectedProjectName = - projects.find((p: { id?: number; uuid?: string; projectCode: string; projectName: string }) => p.id === projectId) + projects.find((p: { id?: number; publicId?: string; projectCode: string; projectName: string }) => + String(p.publicId ?? p.id) === String(projectId)) ?.projectName || 'LCBP3'; useEffect(() => { diff --git a/frontend/app/(admin)/admin/doc-control/numbering/new/page.tsx b/frontend/app/(admin)/admin/doc-control/numbering/new/page.tsx index f4c1f57..e8deace 100644 --- a/frontend/app/(admin)/admin/doc-control/numbering/new/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/numbering/new/page.tsx @@ -14,12 +14,15 @@ export default function NewTemplatePage() { const { data: correspondenceTypes = [] } = useCorrespondenceTypes(); const { data: projects = [] } = useProjects(); const projectId = 1; // Default or sync with selection - const { data: contracts = [] } = useContracts(projectId); - const contractId = contracts[0]?.id; + const { data: contractsData } = useContracts(projectId); + const contracts = Array.isArray(contractsData) ? contractsData : []; + const firstContract = contracts[0] as { id?: number; publicId?: string } | undefined; + const contractId = firstContract?.publicId ?? firstContract?.id; const { data: disciplines = [] } = useDisciplines(contractId); const selectedProjectName = - projects.find((p: { id: number; projectName: string }) => p.id === projectId)?.projectName || 'LCBP3'; + projects.find((p: { id?: number; publicId?: string; projectName: string }) => + String(p.publicId ?? p.id) === String(projectId))?.projectName || 'LCBP3'; const handleSave = async (data: Partial) => { try { diff --git a/frontend/app/(admin)/admin/doc-control/numbering/page.tsx b/frontend/app/(admin)/admin/doc-control/numbering/page.tsx index 35639ab..ba81c74 100644 --- a/frontend/app/(admin)/admin/doc-control/numbering/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/numbering/page.tsx @@ -53,7 +53,8 @@ export default function NumberingPage() { // Master Data const { data: correspondenceTypes = [] } = useCorrespondenceTypes(); - const { data: contracts = [] } = useContracts(selectedProjectId); + const { data: contractsData } = useContracts(selectedProjectId); + const contracts = Array.isArray(contractsData) ? contractsData : []; const firstContract = contracts[0] as { id?: number; publicId?: string } | undefined; const contractId = firstContract?.publicId ?? firstContract?.id; const { data: disciplines = [] } = useDisciplines(contractId); @@ -147,8 +148,7 @@ export default function NumberingPage() { .filter( (t) => !t.projectId || - String(t.project?.id ?? t.project?.publicId) === selectedProjectId || - t.project?.publicId === selectedProjectId + String(t.project?.publicId) === selectedProjectId ) .map((template) => ( diff --git a/frontend/app/(admin)/admin/doc-control/projects/page.tsx b/frontend/app/(admin)/admin/doc-control/projects/page.tsx index 4ddb725..ec67cf9 100644 --- a/frontend/app/(admin)/admin/doc-control/projects/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/projects/page.tsx @@ -33,7 +33,7 @@ import { import { Skeleton } from '@/components/ui/skeleton'; interface Project { - id: string; // ADR-019: uuid exposed as 'id' + publicId: string; // ADR-019: uuid exposed as 'publicId' projectCode: string; projectName: string; isActive: boolean; @@ -71,7 +71,7 @@ export default function ProjectsPage() { const confirmDelete = () => { if (projectToDelete) { - deleteProject.mutate(projectToDelete.id, { + deleteProject.mutate(projectToDelete.publicId, { onSuccess: () => { setDeleteDialogOpen(false); setProjectToDelete(null); @@ -145,7 +145,7 @@ export default function ProjectsPage() { ]; const handleEdit = (project: Project) => { - setEditingUuid(project.id); + setEditingUuid(project.publicId); reset({ projectCode: project.projectCode, projectName: project.projectName, diff --git a/frontend/app/(admin)/admin/doc-control/reference/tags/page.tsx b/frontend/app/(admin)/admin/doc-control/reference/tags/page.tsx index dee875c..f971cc5 100644 --- a/frontend/app/(admin)/admin/doc-control/reference/tags/page.tsx +++ b/frontend/app/(admin)/admin/doc-control/reference/tags/page.tsx @@ -27,10 +27,10 @@ export default function TagsPage() { accessorKey: 'project_id', header: 'Project', cell: ({ row }) => { - const item = row.original as Tag & { project?: { id?: number | string; projectName?: string; projectCode?: string } }; + const item = row.original as Tag & { project?: { id?: number | string; publicId?: string; projectName?: string; projectCode?: string } }; const project = item.project; if (!project) return Global; - return (project.projectName || project.projectCode || `Project ${project.id}`) as React.ReactNode; + return (project.projectName || project.projectCode || `Project ${project.publicId || project.id}`) as React.ReactNode; }, }, { @@ -75,10 +75,10 @@ export default function TagsPage() { const items = await masterDataService.getTags(); // ADR-019: Map project_id INT → project UUID for edit mode select matching return items.map((item) => { - const rec = item as Tag & { project?: { id?: number | string; uuid?: string }; project_id?: number | string }; + const rec = item as Tag & { project?: { id?: number | string; publicId?: string }; project_id?: number | string }; return { ...item, - project_id: rec.project?.id || rec.project?.uuid || (rec.project_id ? String(rec.project_id) : null), + project_id: rec.project?.publicId || rec.project?.id || (rec.project_id ? String(rec.project_id) : null), } as Tag; }); }} diff --git a/frontend/app/(dashboard)/drawings/page.tsx b/frontend/app/(dashboard)/drawings/page.tsx index b55d136..6c4b94f 100644 --- a/frontend/app/(dashboard)/drawings/page.tsx +++ b/frontend/app/(dashboard)/drawings/page.tsx @@ -40,8 +40,8 @@ export default function DrawingsPage() { )} - {projects.map((project: { id: string; projectName: string; projectCode: string }) => ( - + {projects.map((project: { id?: number; publicId?: string; projectName: string; projectCode: string }) => ( + {project.projectCode} - {project.projectName} ))} diff --git a/frontend/components/drawings/upload-form.tsx b/frontend/components/drawings/upload-form.tsx index 495e31f..7178dd2 100644 --- a/frontend/components/drawings/upload-form.tsx +++ b/frontend/components/drawings/upload-form.tsx @@ -25,6 +25,16 @@ import { useState, useEffect } from 'react'; import { Loader2 } from 'lucide-react'; import { Textarea } from '@/components/ui/textarea'; +// Helper to extract array data from various API response formats +const extractArrayData = (value: unknown): T[] => { + if (Array.isArray(value)) return value as T[]; + if (value && typeof value === 'object' && 'data' in value) { + const data = (value as { data?: unknown }).data; + if (Array.isArray(data)) return data as T[]; + } + return []; +}; + // Base Schema const baseSchema = z.object({ drawingType: z.enum(['CONTRACT', 'SHOP', 'AS_BUILT']), @@ -79,7 +89,8 @@ export function DrawingUploadForm() { const router = useRouter(); // Project list - const { data: projects = [], isLoading: isLoadingProjects } = useProjects(); + const { data: projectsData, isLoading: isLoadingProjects } = useProjects(); + const projects = extractArrayData<{ id?: number; publicId?: string; projectName: string; projectCode: string }>(projectsData); // Selected project for category fetching const [selectedProjectId, setSelectedProjectId] = useState(undefined); @@ -119,9 +130,9 @@ export function DrawingUploadForm() { } // Try to resolve UUID→INT from projects list, or pass UUID directly const project = projects.find( - (p: { id: string; uuid?: string }) => p.id === watchedProjectId || p.uuid === watchedProjectId - ) as { id: string; uuid?: string } | undefined; - setSelectedProjectId(project?.id ?? watchedProjectId); + (p: { id?: number; publicId?: string }) => String(p.publicId ?? p.id) === watchedProjectId + ) as { id?: number; publicId?: string } | undefined; + setSelectedProjectId(project?.publicId ?? project?.id ?? watchedProjectId); }, [watchedProjectId, projects]); const onSubmit = (data: DrawingFormData) => { @@ -181,11 +192,14 @@ export function DrawingUploadForm() { )} - {projects.map((project: { id: string; projectName: string; projectCode: string }) => ( - + {projects.map((project) => { + const projectValue = String(project.publicId ?? project.id ?? ''); + return ( + {project.projectCode} - {project.projectName} - ))} + ); + })} {errors.projectId &&

{errors.projectId.message}

}