260318:1401 Fix UUID #05
Build and Deploy / deploy (push) Failing after 11m8s

This commit is contained in:
admin
2026-03-18 14:01:32 +07:00
parent ba642e7e42
commit e5769269a8
37 changed files with 460 additions and 328 deletions
@@ -31,7 +31,7 @@ export default function NumberingPage() {
useEffect(() => {
if (projects.length > 0 && !selectedProjectId) {
const first = projects[0] as any;
setSelectedProjectId(String(first.id || first.uuid));
setSelectedProjectId(String(first.id ?? first.uuid));
}
}, [projects, selectedProjectId]);
@@ -41,7 +41,7 @@ export default function NumberingPage() {
const [isTesting, setIsTesting] = useState(false);
const [testTemplate, setTestTemplate] = useState<NumberingTemplate | null>(null);
const selectedProject = projects.find((p: any) => String(p.id || p.uuid) === selectedProjectId) as any;
const selectedProject = projects.find((p: any) => String(p.id ?? p.uuid) === selectedProjectId) as any;
const selectedProjectName = selectedProject?.projectName || 'Unknown Project';
// Master Data
@@ -109,7 +109,7 @@ export default function NumberingPage() {
</SelectTrigger>
<SelectContent>
{(projects as any[]).map((project) => (
<SelectItem key={project.uuid || project.id} value={String(project.id || project.uuid)}>
<SelectItem key={project.id ?? project.uuid} value={String(project.id ?? project.uuid)}>
{project.projectCode} - {project.projectName}
</SelectItem>
))}
@@ -137,7 +137,7 @@ export default function NumberingPage() {
<div className="lg:col-span-2 space-y-4">
<div className="grid gap-4">
{templates
.filter((t: any) => !t.projectId || String(t.projectId) === selectedProjectId || t.project?.uuid === selectedProjectId)
.filter((t: any) => !t.projectId || String(t.project?.id ?? t.project?.uuid) === selectedProjectId || t.project?.uuid === selectedProjectId)
.map((template) => (
<Card key={template.id} className="p-6 hover:shadow-md transition-shadow">
<div className="flex justify-between items-start">
@@ -6,11 +6,11 @@ import { useRFA } from "@/hooks/use-rfa";
import { Loader2 } from "lucide-react";
export default function RFADetailPage() {
const { id } = useParams();
const { uuid } = useParams();
if (!id) notFound();
if (!uuid) notFound();
const { data: rfa, isLoading, isError } = useRFA(String(id));
const { data: rfa, isLoading, isError } = useRFA(String(uuid));
if (isLoading) {
return (
@@ -22,12 +22,12 @@ import { toast } from "sonner";
export default function TransmittalDetailPage() {
const params = useParams();
const id = params.id as string;
const uuid = params.uuid as string;
const { data: transmittal, isLoading, error } = useQuery<Transmittal>({
queryKey: ["transmittal", id],
queryFn: () => transmittalService.getById(id),
enabled: !!id,
queryKey: ["transmittal", uuid],
queryFn: () => transmittalService.getByUuid(uuid),
enabled: !!uuid,
});
const handlePrint = () => {
@@ -100,7 +100,7 @@ export default function TransmittalDetailPage() {
<p className="text-sm text-muted-foreground">Generated From</p>
{transmittal.correspondence ? (
<Link
href={`/correspondences/${transmittal.correspondenceId}`}
href={`/correspondences/${transmittal.correspondence.uuid}`}
className="font-medium text-primary hover:underline"
>
{transmittal.correspondence.correspondence_number}
+38 -2
View File
@@ -1,22 +1,41 @@
"use client";
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { TransmittalList } from "@/components/transmittal/transmittal-list";
import { transmittalService } from "@/lib/services/transmittal.service";
import { projectService } from "@/lib/services/project.service";
import { Button } from "@/components/ui/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Plus, RefreshCw } from "lucide-react";
import Link from "next/link";
import { TransmittalListResponse } from "@/types/transmittal";
export default function TransmittalPage() {
// ADR-019: Dynamic project selection via UUID
const [selectedProjectUuid, setSelectedProjectUuid] = useState<string>("");
const { data: projectsData } = useQuery({
queryKey: ["projects-for-transmittals"],
queryFn: () => projectService.getAll(),
});
const projects = projectsData?.data || projectsData || [];
const {
data,
isLoading,
error,
refetch,
} = useQuery<TransmittalListResponse>({
queryKey: ["transmittals"],
queryFn: () => transmittalService.getAll({ projectId: 1 }),
queryKey: ["transmittals", selectedProjectUuid],
queryFn: () => transmittalService.getAll({ projectId: selectedProjectUuid }),
enabled: !!selectedProjectUuid,
});
return (
@@ -47,6 +66,23 @@ export default function TransmittalPage() {
</div>
</div>
{/* ADR-019: Project filter */}
<div className="flex items-center gap-3">
<span className="text-sm font-medium text-muted-foreground">Project:</span>
<Select value={selectedProjectUuid} onValueChange={setSelectedProjectUuid}>
<SelectTrigger className="w-[280px]">
<SelectValue placeholder="Select a project" />
</SelectTrigger>
<SelectContent>
{(Array.isArray(projects) ? projects : []).map((p: { uuid: string; projectName?: string; projectCode?: string }) => (
<SelectItem key={p.uuid} value={p.uuid}>
{p.projectName || p.projectCode}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{error && (
<div className="bg-destructive/10 text-destructive px-4 py-3 rounded-md">
Failed to load transmittals.
@@ -53,7 +53,7 @@ export function TemplateTester({ open, onOpenChange, template }: TemplateTesterP
const [loading, setLoading] = useState(false);
// Master Data Hooks
const projectId = template?.projectId || 1;
const projectId = (template as any)?.project?.id ?? (template as any)?.project?.uuid ?? template?.projectId ?? 1;
const { data: organizations } = useOrganizations({ isActive: true });
const { data: correspondenceTypes } = useCorrespondenceTypes();
const { data: contracts } = useContracts(projectId);
@@ -117,7 +117,7 @@ export function TemplateTester({ open, onOpenChange, template }: TemplateTesterP
</SelectTrigger>
<SelectContent>
{(organizations as Organization[])?.map((org) => (
<SelectItem key={org.uuid} value={(org.id ?? org.uuid).toString()}>
<SelectItem key={org.uuid} value={org.uuid}>
{org.organizationCode} - {org.organizationName}
</SelectItem>
))}
@@ -137,7 +137,7 @@ export function TemplateTester({ open, onOpenChange, template }: TemplateTesterP
</SelectTrigger>
<SelectContent>
{(organizations as Organization[])?.map((org) => (
<SelectItem key={org.uuid} value={(org.id ?? org.uuid).toString()}>
<SelectItem key={org.uuid} value={org.uuid}>
{org.organizationCode} - {org.organizationName}
</SelectItem>
))}
+1 -1
View File
@@ -30,7 +30,7 @@ export function RFADetail({ data }: RFADetailProps) {
processMutation.mutate(
{
id: data.rfaId,
uuid: data.uuid,
data: {
action: apiAction,
comments: comments,
+41 -9
View File
@@ -19,6 +19,7 @@ import {
import { useRouter } from "next/navigation";
import { useCreateRFA } from "@/hooks/use-rfa";
import { useDisciplines, useContracts } from "@/hooks/use-master-data";
import { useProjects } from "@/hooks/use-projects";
import { CreateRFADto } from "@/types/rfa";
import { useState, useEffect } from "react";
import { correspondenceService } from "@/lib/services/correspondence.service";
@@ -30,6 +31,7 @@ const rfaItemSchema = z.object({
unit: z.string().min(1, "Unit is required"),
});
const rfaSchema = z.object({
projectId: z.string().min(1, "Project is required"), // ADR-019: UUID
contractId: z.string().min(1, "Contract is required"),
disciplineId: z.number().min(1, "Discipline is required"),
rfaTypeId: z.number().min(1, "Type is required"),
@@ -49,9 +51,9 @@ export function RFAForm() {
const router = useRouter();
const createMutation = useCreateRFA();
// Dynamic Contract Loading (Default Project Context: 1)
const currentProjectId = 1;
const { data: contracts, isLoading: isLoadingContracts } = useContracts(currentProjectId);
// ADR-019: Dynamic project selection
const { data: projectsData, isLoading: isLoadingProjects } = useProjects();
const projects = projectsData?.data || projectsData || [];
const {
register,
@@ -63,6 +65,7 @@ export function RFAForm() {
} = useForm<RFAFormData>({
resolver: zodResolver(rfaSchema),
defaultValues: {
projectId: "",
contractId: "",
disciplineId: 0,
rfaTypeId: 0,
@@ -77,6 +80,9 @@ export function RFAForm() {
},
});
const selectedProjectId = watch("projectId");
const { data: contracts, isLoading: isLoadingContracts } = useContracts(selectedProjectId);
const selectedContractId = watch("contractId");
const { data: disciplines, isLoading: isLoadingDisciplines } = useDisciplines(selectedContractId);
@@ -97,7 +103,7 @@ export function RFAForm() {
const fetchPreview = async () => {
try {
const res = await correspondenceService.previewNumber({
projectId: currentProjectId,
projectId: selectedProjectId,
typeId: rfaTypeId, // RfaTypeId acts as TypeId
disciplineId,
// RFA uses 'TO' organization as recipient
@@ -112,7 +118,7 @@ export function RFAForm() {
const timer = setTimeout(fetchPreview, 500);
return () => clearTimeout(timer);
}, [rfaTypeId, disciplineId, toOrganizationId, currentProjectId]);
}, [rfaTypeId, disciplineId, toOrganizationId, selectedProjectId]);
const { fields, append, remove } = useFieldArray({
control,
@@ -122,7 +128,7 @@ export function RFAForm() {
const onSubmit = (data: RFAFormData) => {
const payload: CreateRFADto = {
...data,
projectId: currentProjectId,
// ADR-019: projectId is already a UUID string from the form
};
createMutation.mutate(payload as any, {
onSuccess: () => {
@@ -178,19 +184,45 @@ export function RFAForm() {
<Input id="description" {...register("description")} placeholder="Enter key description" />
</div>
{/* ADR-019: Project selector */}
<div>
<Label>Project *</Label>
<Select
onValueChange={(val) => {
setValue("projectId", val);
setValue("contractId", ""); // Reset contract when project changes
}}
disabled={isLoadingProjects}
>
<SelectTrigger>
<SelectValue placeholder={isLoadingProjects ? "Loading..." : "Select Project"} />
</SelectTrigger>
<SelectContent>
{(Array.isArray(projects) ? projects : []).map((p: { uuid: string; projectName?: string; projectCode?: string }) => (
<SelectItem key={p.uuid} value={p.uuid}>
{p.projectName || p.projectCode}
</SelectItem>
))}
</SelectContent>
</Select>
{errors.projectId && (
<p className="text-sm text-destructive mt-1">{errors.projectId.message}</p>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<Label>Contract *</Label>
<Select
onValueChange={(val) => setValue("contractId", val)}
disabled={isLoadingContracts}
disabled={!selectedProjectId || isLoadingContracts}
>
<SelectTrigger>
<SelectValue placeholder={isLoadingContracts ? "Loading..." : "Select Contract"} />
</SelectTrigger>
<SelectContent>
{contracts?.map((c: any) => (
<SelectItem key={c.id} value={String(c.id)}>
{contracts?.map((c: { uuid: string; contractName?: string; name?: string; contractCode?: string }) => (
<SelectItem key={c.uuid} value={c.uuid}>
{c.contractName || c.name || c.contractCode}
</SelectItem>
))}
+2 -2
View File
@@ -89,7 +89,7 @@ export function RFAList({ data }: RFAListProps) {
return (
<div className="flex gap-2">
<Link href={`/rfas/${row.original.id}`}>
<Link href={`/rfas/${row.original.uuid}`}>
<Button variant="ghost" size="icon" title="View Details">
<Eye className="h-4 w-4" />
</Button>
@@ -97,7 +97,7 @@ export function RFAList({ data }: RFAListProps) {
<Button variant="ghost" size="icon" title="View File" onClick={handleViewFile}>
<FileText className="h-4 w-4" />
</Button>
<Link href={`/rfas/${row.original.id}/edit`}>
<Link href={`/rfas/${row.original.uuid}/edit`}>
<Button variant="ghost" size="icon" title="Edit">
<Edit className="h-4 w-4" />
</Button>
@@ -9,6 +9,8 @@ import { z } from "zod";
import { transmittalService } from "@/lib/services/transmittal.service";
import { correspondenceService } from "@/lib/services/correspondence.service";
import { projectService } from "@/lib/services/project.service";
import { organizationService } from "@/lib/services/organization.service";
import { CreateTransmittalDto } from "@/types/dto/transmittal/transmittal.dto";
// UI Components
@@ -59,6 +61,8 @@ const itemSchema = z.object({
// Main form schema
const formSchema = z.object({
projectId: z.string().min(1, "Project is required"), // ADR-019: UUID
recipientOrganizationId: z.string().min(1, "Recipient is required"), // ADR-019: UUID
correspondenceId: z.string().min(1, "Correspondence is required"), // ADR-019: UUID string
subject: z.string().min(1, "Subject is required"),
purpose: z.enum(["FOR_APPROVAL", "FOR_INFORMATION", "FOR_REVIEW", "OTHER"]),
@@ -75,6 +79,9 @@ export function TransmittalForm() {
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
defaultValues: {
projectId: "",
recipientOrganizationId: "",
correspondenceId: "",
subject: "",
purpose: "FOR_APPROVAL",
remarks: "",
@@ -89,6 +96,19 @@ export function TransmittalForm() {
name: "items",
});
// ADR-019: Fetch projects and organizations for UUID-based selectors
const { data: projectsData, isLoading: isLoadingProjects } = useQuery({
queryKey: ["projects-dropdown"],
queryFn: () => projectService.getAll(),
});
const projectsList = projectsData?.data || projectsData || [];
const { data: orgsData, isLoading: isLoadingOrgs } = useQuery({
queryKey: ["organizations-dropdown"],
queryFn: () => organizationService.getAll(),
});
const orgsList = orgsData?.data || orgsData || [];
// Fetch correspondences (for header linkage)
const { data: correspondences } = useQuery({
queryKey: ["correspondences-dropdown"],
@@ -99,7 +119,8 @@ export function TransmittalForm() {
mutationFn: (data: CreateTransmittalDto) => transmittalService.create(data),
onSuccess: (result) => {
toast.success("Transmittal created successfully");
router.push(`/transmittals/${result.id}`);
// ADR-019: Navigate using UUID from correspondence
router.push(`/transmittals/${result.correspondence?.uuid || result.uuid}`);
},
onError: () => {
toast.error("Failed to create transmittal");
@@ -107,13 +128,13 @@ export function TransmittalForm() {
});
const onSubmit = (data: FormData) => {
// Better fix: Add missing recipientOrganizationId mock
// ADR-019: All IDs are now UUID strings from the form
const cleanPayload: CreateTransmittalDto = {
projectId: 1,
recipientOrganizationId: 99, // Mock
projectId: data.projectId,
recipientOrganizationId: data.recipientOrganizationId,
correspondenceId: data.correspondenceId,
subject: data.subject,
purpose: data.purpose as any,
purpose: data.purpose as string,
remarks: data.remarks,
items: data.items.map(item => ({
itemType: item.itemType,
@@ -139,6 +160,59 @@ export function TransmittalForm() {
<CardTitle>Transmittal Details</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
{/* ADR-019: Project & Recipient Organization selectors */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<FormField
control={form.control}
name="projectId"
render={({ field }) => (
<FormItem>
<FormLabel>Project</FormLabel>
<Select onValueChange={field.onChange} value={field.value} disabled={isLoadingProjects}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={isLoadingProjects ? "Loading..." : "Select Project"} />
</SelectTrigger>
</FormControl>
<SelectContent>
{(Array.isArray(projectsList) ? projectsList : []).map((p: { uuid: string; projectName?: string; projectCode?: string }) => (
<SelectItem key={p.uuid} value={p.uuid}>
{p.projectName || p.projectCode}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="recipientOrganizationId"
render={({ field }) => (
<FormItem>
<FormLabel>Recipient Organization</FormLabel>
<Select onValueChange={field.onChange} value={field.value} disabled={isLoadingOrgs}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={isLoadingOrgs ? "Loading..." : "Select Organization"} />
</SelectTrigger>
</FormControl>
<SelectContent>
{(Array.isArray(orgsList) ? orgsList : []).map((o: { uuid: string; organizationName?: string; orgCode?: string }) => (
<SelectItem key={o.uuid} value={o.uuid}>
{o.organizationName || o.orgCode}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Linked Correspondence (Ref No) */}
<FormField
@@ -59,7 +59,7 @@ export function TransmittalList({ data }: TransmittalListProps) {
cell: ({ row }) => {
const item = row.original;
return (
<Link href={`/transmittals/${item.id}`}>
<Link href={`/transmittals/${item.uuid}`}>
<Button variant="ghost" size="icon" title="View Details">
<Eye className="h-4 w-4" />
</Button>
+13 -13
View File
@@ -11,7 +11,7 @@ export const rfaKeys = {
lists: () => [...rfaKeys.all, 'list'] as const,
list: (params: SearchRfaDto) => [...rfaKeys.lists(), params] as const,
details: () => [...rfaKeys.all, 'detail'] as const,
detail: (id: number | string) => [...rfaKeys.details(), id] as const,
detail: (uuid: string) => [...rfaKeys.details(), uuid] as const,
};
// --- Queries ---
@@ -24,11 +24,11 @@ export function useRFAs(params: SearchRfaDto) {
});
}
export function useRFA(id: number | string) {
export function useRFA(uuid: string) {
return useQuery({
queryKey: rfaKeys.detail(id),
queryFn: () => rfaService.getById(id),
enabled: !!id,
queryKey: rfaKeys.detail(uuid),
queryFn: () => rfaService.getByUuid(uuid),
enabled: !!uuid,
});
}
@@ -55,11 +55,11 @@ export function useUpdateRFA() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, data }: { id: number | string; data: UpdateRfaDto }) =>
rfaService.update(id, data),
onSuccess: (_, { id }) => {
mutationFn: ({ uuid, data }: { uuid: string; data: UpdateRfaDto }) =>
rfaService.update(uuid, data),
onSuccess: (_, { uuid }) => {
toast.success('RFA updated successfully');
queryClient.invalidateQueries({ queryKey: rfaKeys.detail(id) });
queryClient.invalidateQueries({ queryKey: rfaKeys.detail(uuid) });
queryClient.invalidateQueries({ queryKey: rfaKeys.lists() });
},
onError: (error: unknown) => {
@@ -74,11 +74,11 @@ export function useProcessRFA() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, data }: { id: number | string; data: WorkflowActionDto }) =>
rfaService.processWorkflow(id, data),
onSuccess: (_, { id }) => {
mutationFn: ({ uuid, data }: { uuid: string; data: WorkflowActionDto }) =>
rfaService.processWorkflow(uuid, data),
onSuccess: (_, { uuid }) => {
toast.success('Workflow status updated successfully');
queryClient.invalidateQueries({ queryKey: rfaKeys.detail(id) });
queryClient.invalidateQueries({ queryKey: rfaKeys.detail(uuid) });
queryClient.invalidateQueries({ queryKey: rfaKeys.lists() });
},
onError: (error: unknown) => {
+17 -16
View File
@@ -1,9 +1,9 @@
// File: lib/services/rfa.service.ts
import apiClient from "@/lib/api/client";
import {
CreateRfaDto,
UpdateRfaDto,
SearchRfaDto
import {
CreateRfaDto,
UpdateRfaDto,
SearchRfaDto
} from "@/types/dto/rfa/rfa.dto";
// DTO สำหรับการอนุมัติ (อาจจะย้ายไปไว้ใน folder dto/rfa/ ก็ได้ในอนาคต)
@@ -26,9 +26,9 @@ export const rfaService = {
/**
* ดึงรายละเอียด RFA และประวัติ Workflow
*/
getById: async (id: string | number) => {
// GET /rfas/:id
const response = await apiClient.get(`/rfas/${id}`);
getByUuid: async (uuid: string) => {
// GET /rfas/:uuid (ADR-019)
const response = await apiClient.get(`/rfas/${uuid}`);
return response.data;
},
@@ -44,26 +44,27 @@ export const rfaService = {
/**
* แก้ไข RFA (เฉพาะสถานะ Draft)
*/
update: async (id: string | number, data: UpdateRfaDto) => {
// PUT /rfas/:id
const response = await apiClient.put(`/rfas/${id}`, data);
update: async (uuid: string, data: UpdateRfaDto) => {
// PUT /rfas/:uuid (ADR-019)
const response = await apiClient.put(`/rfas/${uuid}`, data);
return response.data;
},
/**
* ดำเนินการ Workflow (อนุมัติ / ตีกลับ / ส่งต่อ)
*/
processWorkflow: async (id: string | number, actionData: WorkflowActionDto) => {
// POST /rfas/:id/workflow
const response = await apiClient.post(`/rfas/${id}/workflow`, actionData);
processWorkflow: async (uuid: string, actionData: WorkflowActionDto) => {
// POST /rfas/:uuid/workflow (ADR-019)
const response = await apiClient.post(`/rfas/${uuid}/workflow`, actionData);
return response.data;
},
/**
* (Optional) ลบ RFA (Soft Delete)
*/
delete: async (id: string | number) => {
const response = await apiClient.delete(`/rfas/${id}`);
delete: async (uuid: string) => {
// DELETE /rfas/:uuid (ADR-019)
const response = await apiClient.delete(`/rfas/${uuid}`);
return response.data;
}
};
};
+15 -15
View File
@@ -1,9 +1,9 @@
// File: lib/services/transmittal.service.ts
import apiClient from "@/lib/api/client";
import {
CreateTransmittalDto,
UpdateTransmittalDto,
SearchTransmittalDto
import {
CreateTransmittalDto,
UpdateTransmittalDto,
SearchTransmittalDto
} from "@/types/dto/transmittal/transmittal.dto";
export const transmittalService = {
@@ -17,11 +17,11 @@ export const transmittalService = {
},
/**
* ดึงรายละเอียด Transmittal ตาม ID
* ดึงรายละเอียด Transmittal ตาม UUID (ADR-019)
*/
getById: async (id: string | number) => {
// GET /transmittals/:id
const response = await apiClient.get(`/transmittals/${id}`);
getByUuid: async (uuid: string) => {
// GET /transmittals/:uuid
const response = await apiClient.get(`/transmittals/${uuid}`);
return response.data;
},
@@ -37,18 +37,18 @@ export const transmittalService = {
/**
* แก้ไขข้อมูล Transmittal (เฉพาะ Draft)
*/
update: async (id: string | number, data: UpdateTransmittalDto) => {
// PUT /transmittals/:id
const response = await apiClient.put(`/transmittals/${id}`, data);
update: async (uuid: string, data: UpdateTransmittalDto) => {
// PUT /transmittals/:uuid (ADR-019)
const response = await apiClient.put(`/transmittals/${uuid}`, data);
return response.data;
},
/**
* ลบเอกสาร (Soft Delete)
*/
delete: async (id: string | number) => {
// DELETE /transmittals/:id
const response = await apiClient.delete(`/transmittals/${id}`);
delete: async (uuid: string) => {
// DELETE /transmittals/:uuid (ADR-019)
const response = await apiClient.delete(`/transmittals/${uuid}`);
return response.data;
}
};
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "lcbp3-frontend",
"version": "1.5.1",
"version": "1.8.0",
"private": true,
"scripts": {
"dev": "next dev",
+5 -2
View File
@@ -8,7 +8,8 @@ export interface RFAItem {
}
export interface RFA {
id: number; // Shared PK with Correspondence
uuid: string; // ADR-019: from correspondence.uuid
id?: number; // Excluded from API responses (ADR-019)
rfaTypeId: number;
createdBy: number;
disciplineId?: number;
@@ -35,12 +36,14 @@ export interface RFA {
};
// Shared Correspondence Relation
correspondence?: {
id: number;
uuid: string;
id?: number; // Excluded from API responses (ADR-019)
correspondenceNumber: string;
projectId: number;
originatorId?: number;
createdAt?: string;
project?: {
uuid: string;
projectName: string;
projectCode: string;
};
+9 -7
View File
@@ -28,8 +28,9 @@ export interface TransmittalItem {
* Main Transmittal entity
*/
export interface Transmittal {
id: number;
correspondenceId: number;
uuid: string; // ADR-019: from correspondence.uuid
id?: number; // Excluded from API responses (ADR-019)
correspondenceId?: number | string;
transmittalNo: string;
subject: string;
purpose?: TransmittalPurpose;
@@ -38,7 +39,8 @@ export interface Transmittal {
// Joined relations from API
items?: TransmittalItem[];
correspondence?: {
id: number;
uuid: string;
id?: number; // Excluded from API responses (ADR-019)
correspondence_number: string;
project_id: number;
};
@@ -70,9 +72,9 @@ export interface CreateTransmittalItemDto {
* DTO for creating a transmittal
*/
export interface CreateTransmittalDto {
projectId?: number;
recipientOrganizationId?: number;
correspondenceId: number;
projectId?: number | string; // ADR-019: Accept UUID
recipientOrganizationId?: number | string; // ADR-019: Accept UUID
correspondenceId: number | string; // ADR-019: Accept UUID
subject: string;
purpose?: TransmittalPurpose;
remarks?: string;
@@ -85,6 +87,6 @@ export interface CreateTransmittalDto {
export interface SearchTransmittalDto {
page?: number;
limit?: number;
projectId?: number;
projectId?: number | string; // ADR-019: Accept UUID
search?: string;
}