260323:1050 fix CI : Verify Build frontend #02 correct _???
CI / CD Pipeline / build (push) Successful in 15m14s
CI / CD Pipeline / release (push) Failing after 20s
CI / CD Pipeline / deploy (push) Has been skipped

This commit is contained in:
admin
2026-03-23 10:50:20 +07:00
parent 32141f519a
commit e3c476f011
31 changed files with 3587 additions and 374 deletions
@@ -30,7 +30,7 @@ import {
} from '@/components/ui/alert-dialog';
import { Skeleton } from '@/components/ui/skeleton';
import { _Organization } from '@/types/organization';
import { Organization } from '@/types/organization';
import { getApiErrorMessage } from '@/types/api-error';
export default function UsersPage() {
@@ -49,7 +49,7 @@ export default function UsersPage() {
const { data: organizations = [] } = useOrganizations();
const userList = Array.isArray(users) ? users : [];
const organizationList = Array.isArray(organizations) ? organizations : [];
const organizationList: Organization[] = Array.isArray(organizations) ? organizations : [];
const deleteMutation = useDeleteUser();
const [dialogOpen, setDialogOpen] = useState(false);
@@ -23,8 +23,17 @@ export default function NewTemplatePage() {
const handleSave = async (data: Partial<NumberingTemplate>) => {
try {
await numberingApi.saveTemplate(data);
router.push('/admin/numbering');
// Correcting type mismatch by ensuring all required fields for SaveTemplateDto are present
await numberingApi.saveTemplate({
projectId: data.projectId!,
correspondenceTypeId: data.correspondenceTypeId ?? null,
formatTemplate: data.formatTemplate!,
disciplineId: data.disciplineId,
description: data.description,
resetSequenceYearly: data.resetSequenceYearly,
isActive: data.isActive,
});
router.push('/admin/doc-control/numbering');
} catch (_error) {
toast.error('Failed to create template');
}
@@ -6,7 +6,7 @@ import { Card } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Plus, Edit, Play } from 'lucide-react';
import { NumberingTemplate } from '@/lib/api/numbering';
import { NumberingTemplate, SaveTemplateDto } from '@/lib/api/numbering';
import { useTemplates, useSaveTemplate } from '@/hooks/use-numbering';
import { TemplateEditor } from '@/components/numbering/template-editor';
import { SequenceViewer } from '@/components/numbering/sequence-viewer';
@@ -71,7 +71,7 @@ export default function NumberingPage() {
setIsEditing(true);
};
const handleSave = async (data: Partial<NumberingTemplate>) => {
const handleSave = async (data: SaveTemplateDto) => {
try {
await saveTemplateMutation.mutateAsync(data);
toast.success(data.id ? 'Template updated' : 'Template created');
@@ -3,32 +3,33 @@
import { GenericCrudTable } from '@/components/admin/reference/generic-crud-table';
import { masterDataService } from '@/lib/services/master-data.service';
import { ColumnDef } from '@tanstack/react-table';
import { CorrespondenceType } from '@/types/master-data';
export default function CorrespondenceTypesPage() {
const columns: ColumnDef<unknown>[] = [
const columns: ColumnDef<CorrespondenceType>[] = [
{
accessorKey: 'typeCode',
accessorKey: 'type_code',
header: 'Code',
cell: ({ row }) => <span className="font-mono font-bold">{row.getValue('typeCode')}</span>,
cell: ({ row }) => <span className="font-mono font-bold">{row.getValue('type_code')}</span>,
},
{
accessorKey: 'typeName',
accessorKey: 'type_name',
header: 'Name',
},
{
accessorKey: 'sortOrder',
accessorKey: 'sort_order',
header: 'Sort Order',
},
{
accessorKey: 'isActive',
accessorKey: 'is_active',
header: 'Status',
cell: ({ row }) => (
<span
className={`px-2 py-1 rounded-full text-xs ${
row.getValue('isActive') ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
row.getValue('is_active') ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
}`}
>
{row.getValue('isActive') ? 'Active' : 'Inactive'}
{row.getValue('is_active') ? 'Active' : 'Inactive'}
</span>
),
},
@@ -51,10 +52,10 @@ export default function CorrespondenceTypesPage() {
deleteFn={(id) => masterDataService.deleteCorrespondenceType(id)}
columns={columns}
fields={[
{ name: 'typeCode', label: 'Code', type: 'text', required: true },
{ name: 'typeName', label: 'Name', type: 'text', required: true },
{ name: 'sortOrder', label: 'Sort Order', type: 'text' },
{ name: 'isActive', label: 'Active', type: 'checkbox' },
{ name: 'type_code', label: 'Code', type: 'text', required: true },
{ name: 'type_name', label: 'Name', type: 'text', required: true },
{ name: 'sort_order', label: 'Sort Order', type: 'text' },
{ name: 'is_active', label: 'Active', type: 'checkbox' },
]}
/>
</div>
@@ -4,6 +4,7 @@ import { GenericCrudTable } from '@/components/admin/reference/generic-crud-tabl
import { masterDataService } from '@/lib/services/master-data.service';
import { useContracts } from '@/hooks/use-master-data';
import { ColumnDef } from '@tanstack/react-table';
import { Discipline } from '@/types/master-data';
import { useState } from 'react';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
@@ -14,36 +15,36 @@ export default function DisciplinesPage() {
// Ensure we consistently use an array
const contracts = Array.isArray(contractsData) ? contractsData : [];
const columns: ColumnDef<unknown>[] = [
const columns: ColumnDef<Discipline>[] = [
{
accessorKey: 'disciplineCode',
accessorKey: 'discipline_code',
header: 'Code',
cell: ({ row }) => <span className="font-mono font-bold">{row.getValue('disciplineCode')}</span>,
cell: ({ row }) => <span className="font-mono font-bold">{row.getValue('discipline_code')}</span>,
},
{
accessorKey: 'codeNameTh',
accessorKey: 'code_name_th',
header: 'Name (TH)',
},
{
accessorKey: 'codeNameEn',
accessorKey: 'code_name_en',
header: 'Name (EN)',
},
{
accessorKey: 'isActive',
accessorKey: 'is_active',
header: 'Status',
cell: ({ row }) => (
<span
className={`px-2 py-1 rounded-full text-xs ${
row.getValue('isActive') ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
row.getValue('is_active') ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
}`}
>
{row.getValue('isActive') ? 'Active' : 'Inactive'}
{row.getValue('is_active') ? 'Active' : 'Inactive'}
</span>
),
},
];
const contractOptions = contracts.map((c: unknown) => ({
const contractOptions = contracts.map((c: { id: number; contractCode: string; contractName: string }) => ({
label: `${c.contractName} (${c.contractCode})`,
value: String(c.id),
}));
@@ -58,12 +59,12 @@ export default function DisciplinesPage() {
fetchFn={async () => {
const items = await masterDataService.getDisciplines(selectedContractId ? selectedContractId : undefined);
// ADR-019: Map contractId INT → contract UUID for edit mode select matching
return (items as Record<string, unknown>[]).map((item) => {
const rec = item as { contract?: { id?: number; uuid?: string }; contractId?: number };
return items.map((item) => {
const rec = item as Discipline & { contract?: { id?: number; uuid?: string }; contractId?: number | string };
return {
...item,
contractId: rec.contract?.id || rec.contract?.uuid || String(rec.contractId),
};
} as Discipline;
});
}}
createFn={(data) =>
@@ -85,7 +86,7 @@ export default function DisciplinesPage() {
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Contracts</SelectItem>
{contracts.map((c: unknown) => (
{contracts.map((c: { id: number; contractCode: string; contractName: string }) => (
<SelectItem key={c.id} value={String(c.id)}>
{c.contractName} ({c.contractCode})
</SelectItem>
@@ -103,19 +104,19 @@ export default function DisciplinesPage() {
options: contractOptions,
},
{
name: 'disciplineCode',
name: 'discipline_code',
label: 'Code',
type: 'text',
required: true,
},
{
name: 'codeNameTh',
name: 'code_name_th',
label: 'Name (TH)',
type: 'text',
required: true,
},
{ name: 'codeNameEn', label: 'Name (EN)', type: 'text' },
{ name: 'isActive', label: 'Active', type: 'checkbox' },
{ name: 'code_name_en', label: 'Name (EN)', type: 'text' },
{ name: 'is_active', label: 'Active', type: 'checkbox' },
]}
/>
</div>
@@ -3,22 +3,23 @@
import { GenericCrudTable } from '@/components/admin/reference/generic-crud-table';
import { masterDataService } from '@/lib/services/master-data.service';
import { ColumnDef } from '@tanstack/react-table';
import { DrawingCategory } from '@/types/master-data';
export default function DrawingCategoriesPage() {
const columns: ColumnDef<unknown>[] = [
const columns: ColumnDef<DrawingCategory>[] = [
{
accessorKey: 'subTypeCode',
accessorKey: 'sub_type_code',
header: 'Code',
cell: ({ row }) => <span className="font-mono font-bold">{row.getValue('subTypeCode')}</span>,
cell: ({ row }) => <span className="font-mono font-bold">{row.getValue('sub_type_code')}</span>,
},
{
accessorKey: 'subTypeName',
accessorKey: 'sub_type_name',
header: 'Name',
},
{
accessorKey: 'subTypeNumber',
accessorKey: 'sub_type_number',
header: 'Running Code',
cell: ({ row }) => <span className="font-mono">{row.getValue('subTypeNumber') || '-'}</span>,
cell: ({ row }) => <span className="font-mono">{row.getValue('sub_type_number') || '-'}</span>,
},
];
@@ -29,21 +30,25 @@ export default function DrawingCategoriesPage() {
title="Drawing Categories Management"
description="Manage drawing sub-types and categories"
queryKey={['drawing-categories']}
fetchFn={() => masterDataService.getSubTypes(1)} // Default contract ID 1
fetchFn={async () => {
const data = await masterDataService.getSubTypes(1);
return data as (DrawingCategory & { uuid?: string })[];
}}
createFn={(data: Record<string, unknown>) =>
masterDataService.createSubType({
...(data as unknown as Parameters<typeof masterDataService.createSubType>[0]),
contractId: 1,
correspondenceTypeId: 3,
})
} // Assuming 3 is Drawings, hardcoded for now to prevent error
updateFn={() => Promise.reject('Not implemented yet')}
deleteFn={() => Promise.reject('Not implemented yet')} // Delete might be restricted
columns={columns}
}
updateFn={(id, data) => masterDataService.updateRfaType(id, data as Parameters<typeof masterDataService.updateRfaType>[1])}
deleteFn={(id) => masterDataService.deleteRfaType(id)}
columns={columns as unknown as ColumnDef<{ id?: number; uuid?: string }>[]}
fields={[
{ name: 'subTypeCode', label: 'Code', type: 'text', required: true },
{ name: 'subTypeName', label: 'Name', type: 'text', required: true },
{ name: 'subTypeNumber', label: 'Running Code', type: 'text' },
{ name: 'sub_type_code', label: 'Code', type: 'text', required: true },
{ name: 'sub_type_name', label: 'Name', type: 'text', required: true },
{ name: 'sub_type_number', label: 'Running Code', type: 'text' },
{ name: 'is_active', label: 'Active', type: 'checkbox' },
]}
/>
</div>
@@ -5,6 +5,7 @@ import { masterDataService } from '@/lib/services/master-data.service';
import { useContracts } from '@/hooks/use-master-data';
import { ColumnDef } from '@tanstack/react-table';
import { useState } from 'react';
import { RfaType } from '@/types/master-data';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
export default function RfaTypesPage() {
@@ -14,18 +15,18 @@ export default function RfaTypesPage() {
// Ensure we consistently use an array
const contracts = Array.isArray(contractsData) ? contractsData : [];
const columns: ColumnDef<unknown>[] = [
const columns: ColumnDef<RfaType>[] = [
{
accessorKey: 'typeCode',
accessorKey: 'type_code',
header: 'Code',
cell: ({ row }) => <span className="font-mono font-bold">{row.getValue('typeCode')}</span>,
cell: ({ row }) => <span className="font-mono font-bold">{row.getValue('type_code')}</span>,
},
{
accessorKey: 'typeNameTh',
accessorKey: 'type_name_th',
header: 'Name (TH)',
},
{
accessorKey: 'typeNameEn',
accessorKey: 'type_name_en',
header: 'Name (EN)',
},
{
@@ -33,22 +34,22 @@ export default function RfaTypesPage() {
header: 'Remark',
},
{
accessorKey: 'isActive',
accessorKey: 'is_active',
header: 'Status',
cell: ({ row }) => (
<span
className={`px-2 py-1 rounded-full text-xs ${
row.getValue('isActive') ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
row.getValue('is_active') ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
}`}
>
{row.getValue('isActive') ? 'Active' : 'Inactive'}
{row.getValue('is_active') ? 'Active' : 'Inactive'}
</span>
),
},
];
const contractOptions = contracts.map((c: unknown) => ({
label: `${c.contractName} (${c.contractCode})`,
const contractOptions = contracts.map((c: { id: number | string; contract_name?: string; contract_code?: string; contractName?: string; contractCode?: string }) => ({
label: `${c.contractName || c.contract_name} (${c.contractCode || c.contract_code})`,
value: String(c.id),
}));
@@ -61,12 +62,12 @@ export default function RfaTypesPage() {
fetchFn={async () => {
const items = await masterDataService.getRfaTypes(selectedContractId ? selectedContractId : undefined);
// ADR-019: Map contractId INT → contract UUID for edit mode select matching
return (items as Record<string, unknown>[]).map((item) => {
const rec = item as { contract?: { id?: number; uuid?: string }; contractId?: number };
return items.map((item) => {
const rec = item as RfaType & { contract?: { id?: number | string; uuid?: string }; contract_id?: number | string };
return {
...item,
contractId: rec.contract?.id || rec.contract?.uuid || String(rec.contractId),
};
contractId: rec.contract?.id || rec.contract?.uuid || (rec.contract_id ? String(rec.contract_id) : null),
} as RfaType;
});
}}
createFn={(data) =>
@@ -86,9 +87,9 @@ export default function RfaTypesPage() {
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Contracts</SelectItem>
{contracts.map((c: unknown) => (
{contracts.map((c: { id: number | string; contract_name?: string; contract_code?: string; contractName?: string; contractCode?: string }) => (
<SelectItem key={c.id} value={String(c.id)}>
{c.contractName} ({c.contractCode})
{c.contractName || c.contract_name} ({c.contractCode || c.contract_code})
</SelectItem>
))}
</SelectContent>
@@ -103,11 +104,11 @@ export default function RfaTypesPage() {
required: true,
options: contractOptions,
},
{ name: 'typeCode', label: 'Code', type: 'text', required: true },
{ name: 'typeNameTh', label: 'Name (TH)', type: 'text', required: true },
{ name: 'typeNameEn', label: 'Name (EN)', type: 'text' },
{ name: 'type_code', label: 'Code', type: 'text', required: true },
{ name: 'type_name_th', label: 'Name (TH)', type: 'text', required: true },
{ name: 'type_name_en', label: 'Name (EN)', type: 'text' },
{ name: 'remark', label: 'Remark', type: 'textarea' },
{ name: 'isActive', label: 'Active', type: 'checkbox' },
{ name: 'is_active', label: 'Active', type: 'checkbox' },
]}
/>
</div>
@@ -6,6 +6,7 @@ import { projectService } from '@/lib/services/project.service';
import { CreateTagDto } from '@/types/dto/master/tag.dto';
import { ColumnDef } from '@tanstack/react-table';
import { useQuery } from '@tanstack/react-query';
import { Tag } from '@/types/master-data';
export default function TagsPage() {
const { data: projectsData } = useQuery({
@@ -15,19 +16,19 @@ export default function TagsPage() {
const projectOptions = [
{ label: 'Global (All Projects)', value: '__none__' },
...(projectsData || []).map((p: Record<string, unknown>) => ({
label: (p.projectName || p.projectCode || p.project_name || p.project_code || `Project ${p.id}`) as string,
...(projectsData || []).map((p: { id: number | string; projectName?: string; projectCode?: string }) => ({
label: (p.projectName || p.projectCode || `Project ${p.id}`) as string,
value: String(p.id), // p.id = UUID string via serialization
})),
];
const columns: ColumnDef<Record<string, unknown>>[] = [
const columns: ColumnDef<Tag>[] = [
{
accessorKey: 'project_id',
header: 'Project',
cell: ({ row }) => {
const item = row.original as Record<string, unknown>;
const project = item.project as Record<string, unknown> | null;
const item = row.original as Tag & { project?: { id?: number | string; projectName?: string; projectCode?: string } };
const project = item.project;
if (!project) return <span className="text-muted-foreground italic">Global</span>;
return (project.projectName || project.projectCode || `Project ${project.id}`) as React.ReactNode;
},
@@ -73,12 +74,12 @@ export default function TagsPage() {
fetchFn={async () => {
const items = await masterDataService.getTags();
// ADR-019: Map project_id INT → project UUID for edit mode select matching
return (items as Record<string, unknown>[]).map((item) => {
const rec = item as { project?: { id?: number; uuid?: string }; project_id?: number };
return items.map((item) => {
const rec = item as Tag & { project?: { id?: number | string; uuid?: string }; project_id?: number | string };
return {
...item,
project_id: rec.project?.id || rec.project?.uuid || (rec.project_id ? String(rec.project_id) : null),
};
} as Tag;
});
}}
createFn={(data: Record<string, unknown>) =>
@@ -14,7 +14,11 @@ interface NumberingError {
errorMessage: string;
stackTrace?: string;
createdAt: string;
context?: unknown;
context?: {
projectId?: number | string;
contractId?: number | string;
[key: string]: unknown;
};
}
const logService = {