This commit is contained in:
@@ -63,7 +63,7 @@ export default function OrganizationsPage() {
|
||||
|
||||
const confirmDelete = () => {
|
||||
if (orgToDelete) {
|
||||
deleteOrg.mutate(orgToDelete.id, {
|
||||
deleteOrg.mutate(orgToDelete.uuid, {
|
||||
onSuccess: () => {
|
||||
setDeleteDialogOpen(false);
|
||||
setOrgToDelete(null);
|
||||
|
||||
@@ -64,7 +64,7 @@ export default function UsersPage() {
|
||||
|
||||
const confirmDelete = () => {
|
||||
if (userToDelete) {
|
||||
deleteMutation.mutate(userToDelete.userId, {
|
||||
deleteMutation.mutate(userToDelete.uuid, {
|
||||
onSuccess: () => {
|
||||
setDeleteDialogOpen(false);
|
||||
setUserToDelete(null);
|
||||
@@ -186,7 +186,7 @@ export default function UsersPage() {
|
||||
<SelectContent>
|
||||
<SelectItem value="all">All Organizations</SelectItem>
|
||||
{Array.isArray(organizations) && (organizations as Organization[]).map((org) => (
|
||||
<SelectItem key={org.id} value={org.id.toString()}>
|
||||
<SelectItem key={org.uuid} value={(org.id ?? org.uuid).toString()}>
|
||||
{org.organizationCode} - {org.organizationName}
|
||||
</SelectItem>
|
||||
))}
|
||||
|
||||
@@ -56,7 +56,8 @@ interface Project {
|
||||
}
|
||||
|
||||
interface Contract {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number; // Excluded from API responses (ADR-019)
|
||||
contractCode: string;
|
||||
contractName: string;
|
||||
projectId: number;
|
||||
@@ -112,7 +113,7 @@ export default function ContractsPage() {
|
||||
});
|
||||
|
||||
const updateContract = useMutation({
|
||||
mutationFn: ({ id, data }: { id: number, data: UpdateContractDto }) => apiClient.patch(`/contracts/${id}`, data).then(res => res.data),
|
||||
mutationFn: ({ uuid, data }: { uuid: string, data: UpdateContractDto }) => apiClient.patch(`/contracts/${uuid}`, data).then(res => res.data),
|
||||
onSuccess: () => {
|
||||
toast.success("Contract updated successfully");
|
||||
queryClient.invalidateQueries({ queryKey: ['contracts'] });
|
||||
@@ -122,7 +123,7 @@ export default function ContractsPage() {
|
||||
});
|
||||
|
||||
const deleteContract = useMutation({
|
||||
mutationFn: (id: number) => apiClient.delete(`/contracts/${id}`).then(res => res.data),
|
||||
mutationFn: (uuid: string) => apiClient.delete(`/contracts/${uuid}`).then(res => res.data),
|
||||
onSuccess: () => {
|
||||
toast.success("Contract deleted successfully");
|
||||
queryClient.invalidateQueries({ queryKey: ['contracts'] });
|
||||
@@ -131,7 +132,7 @@ export default function ContractsPage() {
|
||||
});
|
||||
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [editingId, setEditingId] = useState<number | null>(null);
|
||||
const [editingUuid, setEditingUuid] = useState<string | null>(null);
|
||||
|
||||
// Stats for Delete Dialog
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
@@ -144,7 +145,7 @@ export default function ContractsPage() {
|
||||
|
||||
const confirmDelete = () => {
|
||||
if (contractToDelete) {
|
||||
deleteContract.mutate(contractToDelete.id, {
|
||||
deleteContract.mutate(contractToDelete.uuid, {
|
||||
onSuccess: () => {
|
||||
setDeleteDialogOpen(false);
|
||||
setContractToDelete(null);
|
||||
@@ -212,7 +213,7 @@ export default function ContractsPage() {
|
||||
];
|
||||
|
||||
const handleEdit = (contract: Contract) => {
|
||||
setEditingId(contract.id);
|
||||
setEditingUuid(contract.uuid);
|
||||
reset({
|
||||
contractCode: contract.contractCode,
|
||||
contractName: contract.contractName,
|
||||
@@ -225,7 +226,7 @@ export default function ContractsPage() {
|
||||
};
|
||||
|
||||
const handleCreate = () => {
|
||||
setEditingId(null);
|
||||
setEditingUuid(null);
|
||||
reset({
|
||||
contractCode: "",
|
||||
contractName: "",
|
||||
@@ -243,8 +244,8 @@ export default function ContractsPage() {
|
||||
projectId: parseInt(data.projectId),
|
||||
};
|
||||
|
||||
if (editingId) {
|
||||
updateContract.mutate({ id: editingId, data: submitData });
|
||||
if (editingUuid) {
|
||||
updateContract.mutate({ uuid: editingUuid, data: submitData });
|
||||
} else {
|
||||
createContract.mutate(submitData);
|
||||
}
|
||||
@@ -289,7 +290,7 @@ export default function ContractsPage() {
|
||||
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingId ? "Edit Contract" : "New Contract"}</DialogTitle>
|
||||
<DialogTitle>{editingUuid ? "Edit Contract" : "New Contract"}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
|
||||
|
||||
@@ -363,7 +364,7 @@ export default function ContractsPage() {
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" disabled={createContract.isPending || updateContract.isPending}>
|
||||
{editingId ? "Save Changes" : "Create Contract"}
|
||||
{editingUuid ? "Save Changes" : "Create Contract"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
|
||||
@@ -44,7 +44,8 @@ import {
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
|
||||
interface Project {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number; // Excluded from API responses (ADR-019)
|
||||
projectCode: string;
|
||||
projectName: string;
|
||||
isActive: boolean;
|
||||
@@ -69,7 +70,7 @@ export default function ProjectsPage() {
|
||||
const deleteProject = useDeleteProject();
|
||||
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [editingId, setEditingId] = useState<number | null>(null);
|
||||
const [editingUuid, setEditingUuid] = useState<string | null>(null);
|
||||
|
||||
// Stats for Delete Dialog
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
@@ -82,7 +83,7 @@ export default function ProjectsPage() {
|
||||
|
||||
const confirmDelete = () => {
|
||||
if (projectToDelete) {
|
||||
deleteProject.mutate(projectToDelete.id, {
|
||||
deleteProject.mutate(projectToDelete.uuid, {
|
||||
onSuccess: () => {
|
||||
setDeleteDialogOpen(false);
|
||||
setProjectToDelete(null);
|
||||
@@ -156,7 +157,7 @@ export default function ProjectsPage() {
|
||||
];
|
||||
|
||||
const handleEdit = (project: Project) => {
|
||||
setEditingId(project.id);
|
||||
setEditingUuid(project.uuid);
|
||||
reset({
|
||||
projectCode: project.projectCode,
|
||||
projectName: project.projectName,
|
||||
@@ -166,7 +167,7 @@ export default function ProjectsPage() {
|
||||
};
|
||||
|
||||
const handleCreate = () => {
|
||||
setEditingId(null);
|
||||
setEditingUuid(null);
|
||||
reset({
|
||||
projectCode: "",
|
||||
projectName: "",
|
||||
@@ -176,9 +177,9 @@ export default function ProjectsPage() {
|
||||
};
|
||||
|
||||
const onSubmit = (data: ProjectFormData) => {
|
||||
if (editingId) {
|
||||
if (editingUuid) {
|
||||
updateProject.mutate(
|
||||
{ id: editingId, data },
|
||||
{ uuid: editingUuid, data },
|
||||
{
|
||||
onSuccess: () => setDialogOpen(false),
|
||||
}
|
||||
@@ -232,7 +233,7 @@ export default function ProjectsPage() {
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{editingId ? "Edit Project" : "New Project"}
|
||||
{editingUuid ? "Edit Project" : "New Project"}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
|
||||
@@ -241,7 +242,7 @@ export default function ProjectsPage() {
|
||||
<Input
|
||||
placeholder="e.g. LCBP3"
|
||||
{...register("projectCode")}
|
||||
disabled={!!editingId} // Code is immutable after creation usually
|
||||
disabled={!!editingUuid} // Code is immutable after creation usually
|
||||
/>
|
||||
{errors.projectCode && (
|
||||
<p className="text-sm text-red-500">{errors.projectCode.message}</p>
|
||||
@@ -280,7 +281,7 @@ export default function ProjectsPage() {
|
||||
type="submit"
|
||||
disabled={createProject.isPending || updateProject.isPending}
|
||||
>
|
||||
{editingId ? "Save Changes" : "Create Project"}
|
||||
{editingUuid ? "Save Changes" : "Create Project"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
|
||||
+7
-8
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { circulationService } from "@/lib/services/circulation.service";
|
||||
import { Circulation, UpdateCirculationRoutingDto } from "@/types/circulation";
|
||||
@@ -42,14 +42,13 @@ function getStatusVariant(status: string): "default" | "secondary" | "destructiv
|
||||
|
||||
export default function CirculationDetailPage() {
|
||||
const params = useParams();
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
const id = params.id as string;
|
||||
const uuid = params.uuid as string;
|
||||
|
||||
const { data: circulation, isLoading, error } = useQuery<Circulation>({
|
||||
queryKey: ["circulation", id],
|
||||
queryFn: () => circulationService.getById(id),
|
||||
enabled: !!id,
|
||||
queryKey: ["circulation", uuid],
|
||||
queryFn: () => circulationService.getByUuid(uuid),
|
||||
enabled: !!uuid,
|
||||
});
|
||||
|
||||
const completeMutation = useMutation({
|
||||
@@ -57,7 +56,7 @@ export default function CirculationDetailPage() {
|
||||
circulationService.updateRouting(routingId, data),
|
||||
onSuccess: () => {
|
||||
toast.success("Task completed successfully");
|
||||
queryClient.invalidateQueries({ queryKey: ["circulation", id] });
|
||||
queryClient.invalidateQueries({ queryKey: ["circulation", uuid] });
|
||||
},
|
||||
onError: () => {
|
||||
toast.error("Failed to update task status");
|
||||
@@ -146,7 +145,7 @@ export default function CirculationDetailPage() {
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground">Linked Document</p>
|
||||
<Link
|
||||
href={`/correspondences/${circulation.correspondenceId}`}
|
||||
href={`/correspondences/${circulation.correspondence.uuid}`}
|
||||
className="font-medium text-primary hover:underline"
|
||||
>
|
||||
{circulation.correspondence.correspondence_number}
|
||||
@@ -83,7 +83,7 @@ export default function CreateCirculationPage() {
|
||||
mutationFn: (data: CreateCirculationDto) => circulationService.create(data),
|
||||
onSuccess: (result) => {
|
||||
toast.success("Circulation created successfully");
|
||||
router.push(`/circulation/${result.id}`);
|
||||
router.push(`/circulation/${result.uuid}`);
|
||||
},
|
||||
onError: () => {
|
||||
toast.error("Failed to create circulation");
|
||||
@@ -232,7 +232,7 @@ export default function CreateCirculationPage() {
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{selectedAssignees.map((userId) => {
|
||||
const user = users.find(
|
||||
(u: { userId: number }) => u.userId === userId
|
||||
(u) => u.userId === userId
|
||||
);
|
||||
return user ? (
|
||||
<Badge
|
||||
@@ -267,16 +267,16 @@ export default function CreateCirculationPage() {
|
||||
<CommandList>
|
||||
<CommandEmpty>No user found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{users.map((user: { userId: number; username: string; firstName?: string; lastName?: string }) => (
|
||||
{users.map((user) => (
|
||||
<CommandItem
|
||||
key={user.userId}
|
||||
key={user.userId ?? user.uuid}
|
||||
value={user.username}
|
||||
onSelect={() => toggleAssignee(user.userId)}
|
||||
onSelect={() => user.userId && toggleAssignee(user.userId)}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
selectedAssignees.includes(user.userId)
|
||||
user.userId != null && selectedAssignees.includes(user.userId)
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { CorrespondenceForm } from "@/components/correspondences/form";
|
||||
import { useCorrespondence } from "@/hooks/use-correspondence";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { useParams } from "next/navigation";
|
||||
|
||||
export default function EditCorrespondencePage() {
|
||||
const params = useParams();
|
||||
const id = Number(params?.id);
|
||||
|
||||
const { data: correspondence, isLoading, isError } = useCorrespondence(id);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex bg-muted/20 min-h-screen justify-center items-center">
|
||||
<Loader2 className="h-8 w-8 animate-spin" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError || !correspondence) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-screen">
|
||||
<h1 className="text-xl font-bold text-red-500">Failed to load correspondence</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto py-6">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold">Edit Correspondence</h1>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
{correspondence.correspondenceNumber}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-card border rounded-lg p-6 shadow-sm">
|
||||
<CorrespondenceForm initialData={correspondence} id={id} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+7
-13
@@ -3,27 +3,22 @@
|
||||
import { CorrespondenceDetail } from "@/components/correspondences/detail";
|
||||
import { useCorrespondence } from "@/hooks/use-correspondence";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { notFound, useParams } from "next/navigation";
|
||||
import { useParams } from "next/navigation";
|
||||
|
||||
export default function CorrespondenceDetailPage() {
|
||||
const params = useParams();
|
||||
const id = Number(params?.id); // useParams returns string | string[]
|
||||
const uuid = (params?.uuid as string) ?? '';
|
||||
|
||||
if (isNaN(id)) {
|
||||
// We can't use notFound() directly in client component render without breaking sometimes,
|
||||
// but typically it works. Better to handle gracefully or redirect.
|
||||
// For now, let's keep it or return 404 UI.
|
||||
// Actually notFound() is for server components mostly.
|
||||
// Let's just return our error UI if ID is invalid.
|
||||
const { data: correspondence, isLoading, isError } = useCorrespondence(uuid);
|
||||
|
||||
if (!uuid) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-screen">
|
||||
<h1 className="text-xl font-bold text-red-500">Invalid Correspondence ID</h1>
|
||||
<h1 className="text-xl font-bold text-red-500">Invalid Correspondence UUID</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const { data: correspondence, isLoading, isError } = useCorrespondence(id);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex bg-muted/20 min-h-screen justify-center items-center">
|
||||
@@ -33,11 +28,10 @@ export default function CorrespondenceDetailPage() {
|
||||
}
|
||||
|
||||
if (isError || !correspondence) {
|
||||
// Optionally handle 404 vs other errors differently, but for now simple handling
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-screen">
|
||||
<h1 className="text-xl font-bold text-red-500">Failed to load correspondence</h1>
|
||||
<p>Please try again later or verify the ID.</p>
|
||||
<p>Please try again later or verify the UUID.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+8
-6
@@ -1,4 +1,3 @@
|
||||
import { drawingApi } from "@/lib/api/drawings";
|
||||
import { notFound } from "next/navigation";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ArrowLeft, Download, FileText, GitCompare } from "lucide-react";
|
||||
@@ -8,19 +7,22 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { RevisionHistory } from "@/components/drawings/revision-history";
|
||||
import { format } from "date-fns";
|
||||
import { drawingApi } from "@/lib/api/drawings";
|
||||
|
||||
export default async function DrawingDetailPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ id: string }>;
|
||||
params: Promise<{ uuid: string }>;
|
||||
}) {
|
||||
const { id: rawId } = await params;
|
||||
const id = parseInt(rawId);
|
||||
if (isNaN(id)) {
|
||||
const { uuid } = await params;
|
||||
if (!uuid) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const drawing = await drawingApi.getById(id);
|
||||
// TODO: Replace mock drawingApi with real service call using UUID
|
||||
// For now, keep using the mock API with a numeric fallback
|
||||
const drawingId = parseInt(uuid);
|
||||
const drawing = !isNaN(drawingId) ? await drawingApi.getById(drawingId) : undefined;
|
||||
|
||||
if (!drawing) {
|
||||
notFound();
|
||||
@@ -104,7 +104,7 @@ export function OrganizationDialog({
|
||||
|
||||
if (organization) {
|
||||
updateOrg.mutate(
|
||||
{ id: organization.id, data: submitData },
|
||||
{ uuid: organization.uuid, data: submitData },
|
||||
{ onSuccess: () => onOpenChange(false) }
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -151,7 +151,7 @@ export function UserDialog({ open, onOpenChange, user }: UserDialogProps) {
|
||||
|
||||
if (user) {
|
||||
updateUser.mutate(
|
||||
{ id: user.userId, data: payload },
|
||||
{ uuid: user.uuid, data: payload },
|
||||
{ onSuccess: () => onOpenChange(false) }
|
||||
);
|
||||
} else {
|
||||
@@ -230,10 +230,13 @@ export function UserDialog({ open, onOpenChange, user }: UserDialogProps) {
|
||||
<SelectValue placeholder="Select Organization" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{/* TODO: ADR-019 — Backend DTO needs to accept UUID for primaryOrganization.
|
||||
Currently using org.id which is excluded from API responses.
|
||||
Temporary: org.id may still exist in some query responses. */}
|
||||
{organizations?.map((org: any) => (
|
||||
<SelectItem
|
||||
key={org.id}
|
||||
value={org.id.toString()}
|
||||
key={org.uuid ?? org.id}
|
||||
value={(org.id ?? 0).toString()}
|
||||
>
|
||||
{org.organizationCode} - {org.organizationName}
|
||||
</SelectItem>
|
||||
|
||||
@@ -113,7 +113,7 @@ export function CirculationList({ data }: CirculationListProps) {
|
||||
const item = row.original;
|
||||
return (
|
||||
<div className="flex gap-1">
|
||||
<Link href={`/circulation/${item.id}`}>
|
||||
<Link href={`/circulation/${item.uuid}`}>
|
||||
<Button variant="ghost" size="icon" title="View Details">
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
@@ -39,7 +39,7 @@ export function CorrespondenceDetail({ data }: CorrespondenceDetailProps) {
|
||||
const handleSubmit = () => {
|
||||
if (confirm("Are you sure you want to submit this correspondence?")) {
|
||||
submitMutation.mutate({
|
||||
id: data.id,
|
||||
uuid: data.uuid,
|
||||
data: {}
|
||||
});
|
||||
}
|
||||
@@ -50,7 +50,7 @@ export function CorrespondenceDetail({ data }: CorrespondenceDetailProps) {
|
||||
|
||||
const action = actionState === "approve" ? "APPROVE" : "REJECT";
|
||||
processMutation.mutate({
|
||||
id: data.id,
|
||||
uuid: data.uuid,
|
||||
data: {
|
||||
action,
|
||||
comments
|
||||
@@ -83,7 +83,7 @@ export function CorrespondenceDetail({ data }: CorrespondenceDetailProps) {
|
||||
<div className="flex gap-2">
|
||||
{/* EDIT BUTTON LOGIC: Show if DRAFT */}
|
||||
{status === "DRAFT" && (
|
||||
<Link href={`/correspondences/${data.id}/edit`}>
|
||||
<Link href={`/correspondences/${data.uuid}/edit`}>
|
||||
<Button variant="outline">
|
||||
<Edit className="mr-2 h-4 w-4" />
|
||||
Edit
|
||||
|
||||
@@ -42,7 +42,7 @@ const correspondenceSchema = z.object({
|
||||
|
||||
type FormData = z.infer<typeof correspondenceSchema>;
|
||||
|
||||
export function CorrespondenceForm({ initialData, id }: { initialData?: any, id?: number }) {
|
||||
export function CorrespondenceForm({ initialData, uuid }: { initialData?: any, uuid?: string }) {
|
||||
const router = useRouter();
|
||||
const createMutation = useCreateCorrespondence();
|
||||
const updateMutation = useUpdateCorrespondence();
|
||||
@@ -107,10 +107,10 @@ export function CorrespondenceForm({ initialData, id }: { initialData?: any, id?
|
||||
},
|
||||
};
|
||||
|
||||
if (id && initialData) {
|
||||
if (uuid && initialData) {
|
||||
// UPDATE Mode
|
||||
updateMutation.mutate({ id, data: payload }, {
|
||||
onSuccess: () => router.push(`/correspondences/${id}`)
|
||||
updateMutation.mutate({ uuid, data: payload }, {
|
||||
onSuccess: () => router.push(`/correspondences/${uuid}`)
|
||||
});
|
||||
} else {
|
||||
// CREATE Mode
|
||||
@@ -420,7 +420,7 @@ export function CorrespondenceForm({ initialData, id }: { initialData?: any, id?
|
||||
</Button>
|
||||
<Button type="submit" disabled={isPending}>
|
||||
{isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||
{id ? "Update Correspondence" : "Create Correspondence"}
|
||||
{uuid ? "Update Correspondence" : "Create Correspondence"}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -59,15 +59,15 @@ export function CorrespondenceList({ data }: CorrespondenceListProps) {
|
||||
id: "actions",
|
||||
cell: ({ row }) => {
|
||||
const item = row.original;
|
||||
// Edit/View link goes to the DOCUMENT detail (correspondence.id)
|
||||
// Ideally we might pass ?revId=item.id to view specific revision, but detail page defaults to latest.
|
||||
// Edit/View link goes to the DOCUMENT detail (correspondence.uuid)
|
||||
// Ideally we might pass ?revId=item.uuid to view specific revision, but detail page defaults to latest.
|
||||
// For editing, we edit the document.
|
||||
const docId = item.correspondence.id;
|
||||
const docUuid = item.correspondence.uuid;
|
||||
const statusCode = item.status?.statusCode;
|
||||
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<Link href={`/correspondences/${docId}`}>
|
||||
<Link href={`/correspondences/${docUuid}`}>
|
||||
<Button variant="ghost" size="icon" title="View Details">
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -89,7 +89,7 @@ export function CorrespondenceList({ data }: CorrespondenceListProps) {
|
||||
<FileText className="h-4 w-4" />
|
||||
</Button>
|
||||
{statusCode === "DRAFT" && (
|
||||
<Link href={`/correspondences/${docId}/edit`}>
|
||||
<Link href={`/correspondences/${docUuid}/edit`}>
|
||||
<Button variant="ghost" size="icon" title="Edit">
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
@@ -58,7 +58,7 @@ export function DrawingCard({ drawing }: { drawing: Drawing }) {
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<Link href={`/drawings/${drawing.drawingId}`}>
|
||||
<Link href={`/drawings/${drawing.uuid}`}>
|
||||
<Button variant="outline" size="sm">
|
||||
<Eye className="mr-2 h-4 w-4" />
|
||||
View
|
||||
|
||||
@@ -16,7 +16,8 @@ import { useSearchSuggestions } from "@/hooks/use-search";
|
||||
|
||||
/** Search suggestion item returned from the API */
|
||||
interface SearchSuggestion {
|
||||
id: string | number;
|
||||
uuid: string;
|
||||
id?: string | number; // Excluded from API responses (ADR-019)
|
||||
type: string;
|
||||
title: string;
|
||||
documentNumber?: string;
|
||||
@@ -97,12 +98,11 @@ export function GlobalSearch() {
|
||||
<CommandGroup heading="Suggestions">
|
||||
{(suggestions as SearchSuggestion[]).map((item) => (
|
||||
<CommandItem
|
||||
key={`${item.type}-${item.id}`}
|
||||
key={`${item.type}-${item.uuid}`}
|
||||
onSelect={() => {
|
||||
setQuery(item.title);
|
||||
// Assumption: item has type and id.
|
||||
// If type is missing, we might need a map or check usage in backend response
|
||||
router.push(`/${item.type}s/${item.id}`);
|
||||
// ADR-019: Use UUID for public routes
|
||||
router.push(`/${item.type}s/${item.uuid}`);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -26,7 +26,7 @@ export function NotificationsDropdown() {
|
||||
|
||||
const handleNotificationClick = (notification: Notification) => {
|
||||
if (!notification.isRead) {
|
||||
markAsRead.mutate(notification.notificationId);
|
||||
markAsRead.mutate(notification.uuid);
|
||||
}
|
||||
if (notification.link) {
|
||||
router.push(notification.link);
|
||||
|
||||
@@ -117,7 +117,7 @@ export function TemplateTester({ open, onOpenChange, template }: TemplateTesterP
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{(organizations as Organization[])?.map((org) => (
|
||||
<SelectItem key={org.id} value={org.id.toString()}>
|
||||
<SelectItem key={org.uuid} value={(org.id ?? org.uuid).toString()}>
|
||||
{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.id} value={org.id.toString()}>
|
||||
<SelectItem key={org.uuid} value={(org.id ?? org.uuid).toString()}>
|
||||
{org.organizationCode} - {org.organizationName}
|
||||
</SelectItem>
|
||||
))}
|
||||
|
||||
@@ -44,7 +44,7 @@ export function SearchResults({ results, query, loading }: SearchResultsProps) {
|
||||
};
|
||||
|
||||
const getLink = (result: SearchResult) => {
|
||||
return `/${result.type}s/${result.id}`; // Assuming routes are plural (correspondences, rfas, drawings)
|
||||
return `/${result.type}s/${result.uuid}`; // ADR-019: Use UUID for public routes
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -54,7 +54,7 @@ export function SearchResults({ results, query, loading }: SearchResultsProps) {
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={`${result.type}-${result.id}-${index}`}
|
||||
key={`${result.type}-${result.uuid}-${index}`}
|
||||
className="p-6 hover:shadow-md transition-shadow group"
|
||||
>
|
||||
<Link href={getLink(result)}>
|
||||
|
||||
@@ -15,7 +15,7 @@ export const correspondenceKeys = {
|
||||
lists: () => [...correspondenceKeys.all, 'list'] as const,
|
||||
list: (params: SearchCorrespondenceDto) => [...correspondenceKeys.lists(), params] as const,
|
||||
details: () => [...correspondenceKeys.all, 'detail'] as const,
|
||||
detail: (id: number | string) => [...correspondenceKeys.details(), id] as const,
|
||||
detail: (uuid: string) => [...correspondenceKeys.details(), uuid] as const,
|
||||
};
|
||||
|
||||
// --- Queries ---
|
||||
@@ -28,11 +28,11 @@ export function useCorrespondences(params: SearchCorrespondenceDto) {
|
||||
});
|
||||
}
|
||||
|
||||
export function useCorrespondence(id: number | string) {
|
||||
export function useCorrespondence(uuid: string) {
|
||||
return useQuery({
|
||||
queryKey: correspondenceKeys.detail(id),
|
||||
queryFn: () => correspondenceService.getById(id),
|
||||
enabled: !!id,
|
||||
queryKey: correspondenceKeys.detail(uuid),
|
||||
queryFn: () => correspondenceService.getByUuid(uuid),
|
||||
enabled: !!uuid,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -59,11 +59,11 @@ export function useUpdateCorrespondence() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number | string; data: Partial<CreateCorrespondenceDto> }) =>
|
||||
correspondenceService.update(id, data),
|
||||
onSuccess: (_, { id }) => {
|
||||
mutationFn: ({ uuid, data }: { uuid: string; data: Partial<CreateCorrespondenceDto> }) =>
|
||||
correspondenceService.update(uuid, data),
|
||||
onSuccess: (_, { uuid }) => {
|
||||
toast.success('Correspondence updated successfully');
|
||||
queryClient.invalidateQueries({ queryKey: correspondenceKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: correspondenceKeys.detail(uuid) });
|
||||
queryClient.invalidateQueries({ queryKey: correspondenceKeys.lists() });
|
||||
},
|
||||
onError: (error: ApiError) => {
|
||||
@@ -78,7 +78,7 @@ export function useDeleteCorrespondence() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: number | string) => correspondenceService.delete(id),
|
||||
mutationFn: (uuid: string) => correspondenceService.delete(uuid),
|
||||
onSuccess: () => {
|
||||
toast.success('Correspondence deleted successfully');
|
||||
queryClient.invalidateQueries({ queryKey: correspondenceKeys.lists() });
|
||||
@@ -95,11 +95,11 @@ export function useSubmitCorrespondence() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: SubmitCorrespondenceDto }) =>
|
||||
correspondenceService.submit(id, data),
|
||||
onSuccess: (_, { id }) => {
|
||||
mutationFn: ({ uuid, data }: { uuid: string; data: SubmitCorrespondenceDto }) =>
|
||||
correspondenceService.submit(uuid, data),
|
||||
onSuccess: (_, { uuid }) => {
|
||||
toast.success('Correspondence submitted successfully');
|
||||
queryClient.invalidateQueries({ queryKey: correspondenceKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: correspondenceKeys.detail(uuid) });
|
||||
queryClient.invalidateQueries({ queryKey: correspondenceKeys.lists() });
|
||||
},
|
||||
onError: (error: ApiError) => {
|
||||
@@ -114,11 +114,11 @@ export function useProcessWorkflow() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number | string; data: WorkflowActionDto }) =>
|
||||
correspondenceService.processWorkflow(id, data),
|
||||
onSuccess: (_, { id }) => {
|
||||
mutationFn: ({ uuid, data }: { uuid: string; data: WorkflowActionDto }) =>
|
||||
correspondenceService.processWorkflow(uuid, data),
|
||||
onSuccess: (_, { uuid }) => {
|
||||
toast.success('Action completed successfully');
|
||||
queryClient.invalidateQueries({ queryKey: correspondenceKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: correspondenceKeys.detail(uuid) });
|
||||
queryClient.invalidateQueries({ queryKey: correspondenceKeys.lists() });
|
||||
},
|
||||
onError: (error: ApiError) => {
|
||||
|
||||
@@ -17,7 +17,7 @@ export const drawingKeys = {
|
||||
lists: () => [...drawingKeys.all, 'list'] as const,
|
||||
list: (type: DrawingType, params: DrawingSearchParams) => [...drawingKeys.lists(), type, params] as const,
|
||||
details: () => [...drawingKeys.all, 'detail'] as const,
|
||||
detail: (type: DrawingType, id: number | string) => [...drawingKeys.details(), type, id] as const,
|
||||
detail: (type: DrawingType, uuid: string) => [...drawingKeys.details(), type, uuid] as const,
|
||||
};
|
||||
|
||||
// --- Queries ---
|
||||
@@ -33,7 +33,7 @@ export function useDrawings(type: DrawingType, params: DrawingSearchParams) {
|
||||
if (response && response.data) {
|
||||
const mappedData = response.data.map((d: ContractDrawing) => ({
|
||||
...d,
|
||||
drawingId: d.id,
|
||||
uuid: d.uuid,
|
||||
drawingNumber: d.contractDrawingNo,
|
||||
type: 'CONTRACT',
|
||||
}));
|
||||
@@ -46,7 +46,7 @@ export function useDrawings(type: DrawingType, params: DrawingSearchParams) {
|
||||
if (response && response.data) {
|
||||
const mappedData = response.data.map((d: ShopDrawing) => ({
|
||||
...d,
|
||||
drawingId: d.id,
|
||||
uuid: d.uuid,
|
||||
type: 'SHOP',
|
||||
title: d.currentRevision?.title || 'Untitled',
|
||||
revision: d.currentRevision?.revisionNumber,
|
||||
@@ -61,7 +61,7 @@ export function useDrawings(type: DrawingType, params: DrawingSearchParams) {
|
||||
if (response && response.data) {
|
||||
const mappedData = response.data.map((d: AsBuiltDrawing) => ({
|
||||
...d,
|
||||
drawingId: d.id,
|
||||
uuid: d.uuid,
|
||||
type: 'AS_BUILT',
|
||||
title: d.currentRevision?.title || 'Untitled',
|
||||
revision: d.currentRevision?.revisionNumber,
|
||||
@@ -76,19 +76,19 @@ export function useDrawings(type: DrawingType, params: DrawingSearchParams) {
|
||||
});
|
||||
}
|
||||
|
||||
export function useDrawing(type: DrawingType, id: number | string) {
|
||||
export function useDrawing(type: DrawingType, uuid: string) {
|
||||
return useQuery({
|
||||
queryKey: drawingKeys.detail(type, id),
|
||||
queryKey: drawingKeys.detail(type, uuid),
|
||||
queryFn: async () => {
|
||||
if (type === 'CONTRACT') {
|
||||
return contractDrawingService.getById(id);
|
||||
return contractDrawingService.getByUuid(uuid);
|
||||
} else if (type === 'SHOP') {
|
||||
return shopDrawingService.getById(id);
|
||||
return shopDrawingService.getByUuid(uuid);
|
||||
} else {
|
||||
return asBuiltDrawingService.getById(id);
|
||||
return asBuiltDrawingService.getByUuid(uuid);
|
||||
}
|
||||
},
|
||||
enabled: !!id,
|
||||
enabled: !!uuid,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@ export function useCreateOrganization() {
|
||||
export function useUpdateOrganization() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: UpdateOrganizationDto }) =>
|
||||
masterDataService.updateOrganization(id, data),
|
||||
mutationFn: ({ uuid, data }: { uuid: string; data: UpdateOrganizationDto }) =>
|
||||
masterDataService.updateOrganization(uuid, data),
|
||||
onSuccess: () => {
|
||||
toast.success('Organization updated successfully');
|
||||
queryClient.invalidateQueries({ queryKey: masterDataKeys.organizations() });
|
||||
@@ -61,7 +61,7 @@ export function useUpdateOrganization() {
|
||||
export function useDeleteOrganization() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => masterDataService.deleteOrganization(id),
|
||||
mutationFn: (uuid: string) => masterDataService.deleteOrganization(uuid),
|
||||
onSuccess: () => {
|
||||
toast.success('Organization deleted successfully');
|
||||
queryClient.invalidateQueries({ queryKey: masterDataKeys.organizations() });
|
||||
|
||||
@@ -7,7 +7,7 @@ import { getApiErrorMessage } from '@/types/api-error';
|
||||
export const projectKeys = {
|
||||
all: ['projects'] as const,
|
||||
list: (params: SearchProjectDto) => [...projectKeys.all, 'list', params] as const,
|
||||
detail: (id: number) => [...projectKeys.all, 'detail', id] as const,
|
||||
detail: (uuid: string) => [...projectKeys.all, 'detail', uuid] as const,
|
||||
};
|
||||
|
||||
export function useProjects(params?: SearchProjectDto) {
|
||||
@@ -36,7 +36,7 @@ export function useCreateProject() {
|
||||
export function useUpdateProject() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: UpdateProjectDto }) => projectService.update(id, data),
|
||||
mutationFn: ({ uuid, data }: { uuid: string; data: UpdateProjectDto }) => projectService.update(uuid, data),
|
||||
onSuccess: () => {
|
||||
toast.success("Project updated successfully");
|
||||
queryClient.invalidateQueries({ queryKey: projectKeys.all });
|
||||
@@ -52,7 +52,7 @@ export function useUpdateProject() {
|
||||
export function useDeleteProject() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => projectService.delete(id),
|
||||
mutationFn: (uuid: string) => projectService.delete(uuid),
|
||||
onSuccess: () => {
|
||||
toast.success("Project deleted successfully");
|
||||
queryClient.invalidateQueries({ queryKey: projectKeys.all });
|
||||
|
||||
@@ -7,7 +7,7 @@ import { getApiErrorMessage } from '@/types/api-error';
|
||||
export const userKeys = {
|
||||
all: ['users'] as const,
|
||||
list: (params?: SearchUserDto) => [...userKeys.all, 'list', params] as const,
|
||||
detail: (id: number) => [...userKeys.all, 'detail', id] as const,
|
||||
detail: (uuid: string) => [...userKeys.all, 'detail', uuid] as const,
|
||||
};
|
||||
|
||||
export function useUsers(params?: SearchUserDto) {
|
||||
@@ -43,7 +43,7 @@ export function useCreateUser() {
|
||||
export function useUpdateUser() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: UpdateUserDto }) => userService.update(id, data),
|
||||
mutationFn: ({ uuid, data }: { uuid: string; data: UpdateUserDto }) => userService.update(uuid, data),
|
||||
onSuccess: () => {
|
||||
toast.success("User updated successfully");
|
||||
queryClient.invalidateQueries({ queryKey: userKeys.all });
|
||||
@@ -59,7 +59,7 @@ export function useUpdateUser() {
|
||||
export function useDeleteUser() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => userService.delete(id),
|
||||
mutationFn: (uuid: string) => userService.delete(uuid),
|
||||
onSuccess: () => {
|
||||
toast.success("User deleted successfully");
|
||||
queryClient.invalidateQueries({ queryKey: userKeys.all });
|
||||
|
||||
@@ -3,6 +3,7 @@ import { NotificationResponse } from "@/types/notification";
|
||||
// Mock Data
|
||||
let mockNotifications = [
|
||||
{
|
||||
uuid: "019575a0-0001-7000-8000-000000000001",
|
||||
notificationId: 1,
|
||||
title: "RFA Approved",
|
||||
message: "RFA-001 has been approved by the Project Manager.",
|
||||
@@ -12,6 +13,7 @@ let mockNotifications = [
|
||||
link: "/rfas/1",
|
||||
},
|
||||
{
|
||||
uuid: "019575a0-0002-7000-8000-000000000002",
|
||||
notificationId: 2,
|
||||
title: "New Correspondence",
|
||||
message: "You have received a new correspondence from Contractor A.",
|
||||
@@ -21,6 +23,7 @@ let mockNotifications = [
|
||||
link: "/correspondences/3",
|
||||
},
|
||||
{
|
||||
uuid: "019575a0-0003-7000-8000-000000000003",
|
||||
notificationId: 3,
|
||||
title: "Drawing Revision Required",
|
||||
message: "Drawing S-201 requires revision based on recent comments.",
|
||||
|
||||
@@ -18,8 +18,8 @@ export const asBuiltDrawingService = {
|
||||
/**
|
||||
* Get details by ID
|
||||
*/
|
||||
getById: async (id: string | number) => {
|
||||
const response = await apiClient.get(`/drawings/asbuilt/${id}`);
|
||||
getByUuid: async (uuid: string) => {
|
||||
const response = await apiClient.get(`/drawings/asbuilt/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -34,8 +34,8 @@ export const asBuiltDrawingService = {
|
||||
/**
|
||||
* Create New Revision
|
||||
*/
|
||||
createRevision: async (id: string | number, data: CreateAsBuiltDrawingRevisionDto) => {
|
||||
const response = await apiClient.post(`/drawings/asbuilt/${id}/revisions`, data);
|
||||
createRevision: async (uuid: string, data: CreateAsBuiltDrawingRevisionDto) => {
|
||||
const response = await apiClient.post(`/drawings/asbuilt/${uuid}/revisions`, data);
|
||||
return response.data;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -19,9 +19,9 @@ export const circulationService = {
|
||||
/**
|
||||
* ดึงรายละเอียดใบเวียนตาม ID
|
||||
*/
|
||||
getById: async (id: string | number) => {
|
||||
// GET /circulations/:id
|
||||
const response = await apiClient.get(`/circulations/${id}`);
|
||||
getByUuid: async (uuid: string) => {
|
||||
// GET /circulations/:uuid
|
||||
const response = await apiClient.get(`/circulations/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -47,8 +47,8 @@ export const circulationService = {
|
||||
/**
|
||||
* ลบ/ยกเลิกใบเวียน
|
||||
*/
|
||||
delete: async (id: string | number) => {
|
||||
const response = await apiClient.delete(`/circulations/${id}`);
|
||||
delete: async (uuid: string) => {
|
||||
const response = await apiClient.delete(`/circulations/${uuid}`);
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -17,8 +17,8 @@ export const contractDrawingService = {
|
||||
/**
|
||||
* ดึงรายละเอียดตาม ID
|
||||
*/
|
||||
getById: async (id: string | number) => {
|
||||
const response = await apiClient.get(`/drawings/contract/${id}`);
|
||||
getByUuid: async (uuid: string) => {
|
||||
const response = await apiClient.get(`/drawings/contract/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -33,16 +33,16 @@ export const contractDrawingService = {
|
||||
/**
|
||||
* แก้ไขข้อมูลแบบสัญญา
|
||||
*/
|
||||
update: async (id: string | number, data: UpdateContractDrawingDto) => {
|
||||
const response = await apiClient.put(`/drawings/contract/${id}`, data);
|
||||
update: async (uuid: string, data: UpdateContractDrawingDto) => {
|
||||
const response = await apiClient.put(`/drawings/contract/${uuid}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* ลบแบบสัญญา (Soft Delete)
|
||||
*/
|
||||
delete: async (id: string | number) => {
|
||||
const response = await apiClient.delete(`/drawings/contract/${id}`);
|
||||
delete: async (uuid: string) => {
|
||||
const response = await apiClient.delete(`/drawings/contract/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -19,11 +19,11 @@ export const contractService = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Get contract by ID
|
||||
* GET /contracts/:id
|
||||
* Get contract by UUID
|
||||
* GET /contracts/:uuid
|
||||
*/
|
||||
getById: async (id: number) => {
|
||||
const response = await apiClient.get(`/contracts/${id}`);
|
||||
getByUuid: async (uuid: string) => {
|
||||
const response = await apiClient.get(`/contracts/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -38,19 +38,19 @@ export const contractService = {
|
||||
|
||||
/**
|
||||
* Update contract
|
||||
* PATCH /contracts/:id
|
||||
* PATCH /contracts/:uuid
|
||||
*/
|
||||
update: async (id: number, data: UpdateContractDto) => {
|
||||
const response = await apiClient.patch(`/contracts/${id}`, data);
|
||||
update: async (uuid: string, data: UpdateContractDto) => {
|
||||
const response = await apiClient.patch(`/contracts/${uuid}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete contract
|
||||
* DELETE /contracts/:id
|
||||
* DELETE /contracts/:uuid
|
||||
*/
|
||||
delete: async (id: number) => {
|
||||
const response = await apiClient.delete(`/contracts/${id}`);
|
||||
delete: async (uuid: string) => {
|
||||
const response = await apiClient.delete(`/contracts/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -15,8 +15,8 @@ export const correspondenceService = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getById: async (id: string | number) => {
|
||||
const response = await apiClient.get(`/correspondences/${id}`);
|
||||
getByUuid: async (uuid: string) => {
|
||||
const response = await apiClient.get(`/correspondences/${uuid}`);
|
||||
return response.data.data; // Unwrap NestJS Interceptor 'data' wrapper
|
||||
},
|
||||
|
||||
@@ -25,13 +25,13 @@ export const correspondenceService = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
update: async (id: string | number, data: Partial<CreateCorrespondenceDto>) => {
|
||||
const response = await apiClient.put(`/correspondences/${id}`, data);
|
||||
update: async (uuid: string, data: Partial<CreateCorrespondenceDto>) => {
|
||||
const response = await apiClient.put(`/correspondences/${uuid}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
delete: async (id: string | number) => {
|
||||
const response = await apiClient.delete(`/correspondences/${id}`);
|
||||
delete: async (uuid: string) => {
|
||||
const response = await apiClient.delete(`/correspondences/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -40,33 +40,33 @@ export const correspondenceService = {
|
||||
/**
|
||||
* ส่งเอกสาร (Submit) เพื่อเริ่ม Workflow
|
||||
*/
|
||||
submit: async (id: string | number, data: SubmitCorrespondenceDto) => {
|
||||
const response = await apiClient.post(`/correspondences/${id}/submit`, data);
|
||||
submit: async (uuid: string, data: SubmitCorrespondenceDto) => {
|
||||
const response = await apiClient.post(`/correspondences/${uuid}/submit`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* ดำเนินการ Workflow (เช่น Approve, Reject) ในขั้นตอนปัจจุบัน
|
||||
*/
|
||||
processWorkflow: async (id: string | number, data: WorkflowActionDto) => {
|
||||
const response = await apiClient.post(`/correspondences/${id}/workflow`, data);
|
||||
processWorkflow: async (uuid: string, data: WorkflowActionDto) => {
|
||||
const response = await apiClient.post(`/correspondences/${uuid}/workflow`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* เพิ่มเอกสารอ้างอิง
|
||||
*/
|
||||
addReference: async (id: string | number, data: AddReferenceDto) => {
|
||||
const response = await apiClient.post(`/correspondences/${id}/references`, data);
|
||||
addReference: async (uuid: string, data: AddReferenceDto) => {
|
||||
const response = await apiClient.post(`/correspondences/${uuid}/references`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* ลบเอกสารอ้างอิง
|
||||
*/
|
||||
removeReference: async (id: string | number, data: RemoveReferenceDto) => {
|
||||
removeReference: async (uuid: string, data: RemoveReferenceDto) => {
|
||||
// ใช้ DELETE method โดยส่ง body ไปด้วย (axios รองรับผ่าน config.data)
|
||||
const response = await apiClient.delete(`/correspondences/${id}/references`, {
|
||||
const response = await apiClient.delete(`/correspondences/${uuid}/references`, {
|
||||
data: data
|
||||
});
|
||||
return response.data;
|
||||
|
||||
@@ -78,14 +78,14 @@ export const masterDataService = {
|
||||
},
|
||||
|
||||
/** แก้ไของค์กร */
|
||||
updateOrganization: async (id: number, data: UpdateOrganizationDto) => {
|
||||
const response = await apiClient.put(`/organizations/${id}`, data);
|
||||
updateOrganization: async (uuid: string, data: UpdateOrganizationDto) => {
|
||||
const response = await apiClient.put(`/organizations/${uuid}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/** ลบองค์กร */
|
||||
deleteOrganization: async (id: number) => {
|
||||
const response = await apiClient.delete(`/organizations/${id}`);
|
||||
deleteOrganization: async (uuid: string) => {
|
||||
const response = await apiClient.delete(`/organizations/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ export const notificationService = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
markAsRead: async (id: number) => {
|
||||
const response = await apiClient.patch(`/notifications/${id}/read`);
|
||||
markAsRead: async (uuid: string) => {
|
||||
const response = await apiClient.put(`/notifications/${uuid}/read`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
|
||||
@@ -20,11 +20,11 @@ export const organizationService = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Get organization by ID
|
||||
* GET /organizations/:id
|
||||
* Get organization by UUID
|
||||
* GET /organizations/:uuid
|
||||
*/
|
||||
getById: async (id: number) => {
|
||||
const response = await apiClient.get(`/organizations/${id}`);
|
||||
getByUuid: async (uuid: string) => {
|
||||
const response = await apiClient.get(`/organizations/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -39,19 +39,19 @@ export const organizationService = {
|
||||
|
||||
/**
|
||||
* Update organization
|
||||
* PATCH /organizations/:id
|
||||
* PATCH /organizations/:uuid
|
||||
*/
|
||||
update: async (id: number, data: UpdateOrganizationDto) => {
|
||||
const response = await apiClient.patch(`/organizations/${id}`, data);
|
||||
update: async (uuid: string, data: UpdateOrganizationDto) => {
|
||||
const response = await apiClient.patch(`/organizations/${uuid}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete organization
|
||||
* DELETE /organizations/:id
|
||||
* DELETE /organizations/:uuid
|
||||
*/
|
||||
delete: async (id: number) => {
|
||||
const response = await apiClient.delete(`/organizations/${id}`);
|
||||
delete: async (uuid: string) => {
|
||||
const response = await apiClient.delete(`/organizations/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -23,9 +23,9 @@ export const projectService = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/** ดึงรายละเอียดโครงการตาม ID */
|
||||
getById: async (id: string | number) => {
|
||||
const response = await apiClient.get(`/projects/${id}`);
|
||||
/** ดึงรายละเอียดโครงการตาม UUID */
|
||||
getByUuid: async (uuid: string) => {
|
||||
const response = await apiClient.get(`/projects/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -36,14 +36,14 @@ export const projectService = {
|
||||
},
|
||||
|
||||
/** แก้ไขโครงการ */
|
||||
update: async (id: string | number, data: UpdateProjectDto) => {
|
||||
const response = await apiClient.put(`/projects/${id}`, data);
|
||||
update: async (uuid: string, data: UpdateProjectDto) => {
|
||||
const response = await apiClient.put(`/projects/${uuid}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/** ลบโครงการ (Soft Delete) */
|
||||
delete: async (id: string | number) => {
|
||||
const response = await apiClient.delete(`/projects/${id}`);
|
||||
delete: async (uuid: string) => {
|
||||
const response = await apiClient.delete(`/projects/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ export const shopDrawingService = {
|
||||
/**
|
||||
* ดึงรายละเอียดตาม ID (ควรได้ Revision History มาด้วย)
|
||||
*/
|
||||
getById: async (id: string | number) => {
|
||||
const response = await apiClient.get(`/drawings/shop/${id}`);
|
||||
getByUuid: async (uuid: string) => {
|
||||
const response = await apiClient.get(`/drawings/shop/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -34,8 +34,8 @@ export const shopDrawingService = {
|
||||
/**
|
||||
* สร้าง Revision ใหม่สำหรับ Shop Drawing เดิม
|
||||
*/
|
||||
createRevision: async (id: string | number, data: CreateShopDrawingRevisionDto) => {
|
||||
const response = await apiClient.post(`/drawings/shop/${id}/revisions`, data);
|
||||
createRevision: async (uuid: string, data: CreateShopDrawingRevisionDto) => {
|
||||
const response = await apiClient.post(`/drawings/shop/${uuid}/revisions`, data);
|
||||
return response.data;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -12,7 +12,8 @@ interface RawUser {
|
||||
const transformUser = (user: RawUser): User => {
|
||||
return {
|
||||
...(user as unknown as User),
|
||||
userId: (user.user_id ?? user.userId) as number,
|
||||
uuid: (user.uuid as string) ?? '',
|
||||
userId: (user.user_id ?? user.userId) as number | undefined,
|
||||
roles: (user.assignments?.map((a) => a.role) ?? []) as User['roles'],
|
||||
};
|
||||
};
|
||||
@@ -45,8 +46,8 @@ export const userService = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getById: async (id: number) => {
|
||||
const response = await apiClient.get<RawUser>(`/users/${id}`);
|
||||
getByUuid: async (uuid: string) => {
|
||||
const response = await apiClient.get<RawUser>(`/users/${uuid}`);
|
||||
return transformUser(response.data);
|
||||
},
|
||||
|
||||
@@ -55,13 +56,13 @@ export const userService = {
|
||||
return transformUser(response.data);
|
||||
},
|
||||
|
||||
update: async (id: number, data: UpdateUserDto) => {
|
||||
const response = await apiClient.put<RawUser>(`/users/${id}`, data);
|
||||
update: async (uuid: string, data: UpdateUserDto) => {
|
||||
const response = await apiClient.put<RawUser>(`/users/${uuid}`, data);
|
||||
return transformUser(response.data);
|
||||
},
|
||||
|
||||
delete: async (id: number) => {
|
||||
const response = await apiClient.delete(`/users/${id}`);
|
||||
delete: async (uuid: string) => {
|
||||
const response = await apiClient.delete(`/users/${uuid}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ export interface CirculationRouting {
|
||||
* Main Circulation entity
|
||||
*/
|
||||
export interface Circulation {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number; // Excluded from API responses (ADR-019)
|
||||
correspondenceId?: number;
|
||||
organizationId: number;
|
||||
circulationNo: string;
|
||||
@@ -52,16 +53,19 @@ export interface Circulation {
|
||||
// Joined relations from API
|
||||
routings?: CirculationRouting[];
|
||||
correspondence?: {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number;
|
||||
correspondence_number: string;
|
||||
};
|
||||
organization?: {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number;
|
||||
organization_code: string;
|
||||
organization_name: string;
|
||||
};
|
||||
creator?: {
|
||||
user_id: number;
|
||||
uuid: string;
|
||||
user_id?: number;
|
||||
username: string;
|
||||
first_name?: string;
|
||||
last_name?: string;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
export interface Organization {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number; // Excluded from API responses (ADR-019)
|
||||
organizationName: string;
|
||||
organizationCode: string;
|
||||
}
|
||||
|
||||
export interface Attachment {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number; // Excluded from API responses (ADR-019)
|
||||
name: string;
|
||||
url: string;
|
||||
size?: number;
|
||||
@@ -15,7 +17,8 @@ export interface Attachment {
|
||||
|
||||
// Used in List View mainly
|
||||
export interface CorrespondenceRevision {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number; // Excluded from API responses (ADR-019)
|
||||
revisionNumber: number;
|
||||
revisionLabel?: string; // e.g. "A", "00"
|
||||
subject: string;
|
||||
@@ -36,20 +39,22 @@ export interface CorrespondenceRevision {
|
||||
|
||||
// Nested Relation from Backend Refactor
|
||||
correspondence: {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number; // Excluded from API responses (ADR-019)
|
||||
correspondenceNumber: string;
|
||||
projectId: number;
|
||||
originatorId?: number;
|
||||
isInternal: boolean;
|
||||
originator?: Organization;
|
||||
project?: { id: number; projectName: string; projectCode: string };
|
||||
project?: { uuid: string; id?: number; projectName: string; projectCode: string };
|
||||
type?: { id: number; typeName: string; typeCode: string };
|
||||
};
|
||||
}
|
||||
|
||||
// Keep explicit Correspondence for Detail View if needed, or merge concepts
|
||||
export interface Correspondence {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number; // Excluded from API responses (ADR-019)
|
||||
correspondenceNumber: string;
|
||||
projectId: number;
|
||||
originatorId?: number;
|
||||
@@ -59,7 +64,7 @@ export interface Correspondence {
|
||||
|
||||
// Relations
|
||||
originator?: Organization;
|
||||
project?: { id: number; projectName: string; projectCode: string };
|
||||
project?: { uuid: string; id?: number; projectName: string; projectCode: string };
|
||||
type?: { id: number; typeName: string; typeCode: string };
|
||||
revisions?: CorrespondenceRevision[]; // Nested revisions
|
||||
recipients?: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Entity Interfaces
|
||||
export interface DrawingRevision {
|
||||
revisionId: number;
|
||||
uuid: string;
|
||||
revisionId?: number; // Excluded from API responses (ADR-019)
|
||||
revisionNumber: string;
|
||||
title?: string; // Added
|
||||
legacyDrawingNumber?: string; // Added
|
||||
@@ -14,7 +15,8 @@ export interface DrawingRevision {
|
||||
}
|
||||
|
||||
export interface ContractDrawing {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number; // Excluded from API responses (ADR-019)
|
||||
contractDrawingNo: string;
|
||||
title: string;
|
||||
projectId: number;
|
||||
@@ -26,7 +28,8 @@ export interface ContractDrawing {
|
||||
}
|
||||
|
||||
export interface ShopDrawing {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number; // Excluded from API responses (ADR-019)
|
||||
drawingNumber: string;
|
||||
projectId: number;
|
||||
mainCategoryId: number;
|
||||
@@ -38,7 +41,8 @@ export interface ShopDrawing {
|
||||
}
|
||||
|
||||
export interface AsBuiltDrawing {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number; // Excluded from API responses (ADR-019)
|
||||
drawingNumber: string;
|
||||
projectId: number;
|
||||
mainCategoryId: number;
|
||||
@@ -50,7 +54,8 @@ export interface AsBuiltDrawing {
|
||||
|
||||
// Unified Type for List
|
||||
export interface Drawing {
|
||||
drawingId: number;
|
||||
uuid?: string;
|
||||
drawingId?: number; // Excluded from API responses (ADR-019)
|
||||
drawingNumber: string;
|
||||
title: string; // Display title (from current revision for Shop/AsBuilt)
|
||||
discipline?: string | { disciplineCode: string; disciplineName: string };
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export interface Notification {
|
||||
notificationId: number;
|
||||
uuid: string;
|
||||
notificationId?: number; // Excluded from API responses (ADR-019)
|
||||
title: string;
|
||||
message: string;
|
||||
type: "INFO" | "SUCCESS" | "WARNING" | "ERROR";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export interface Organization {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number; // Excluded from API responses (ADR-019)
|
||||
organizationCode: string;
|
||||
organizationName: string;
|
||||
roleId?: number; // NEW - organization role (OWNER, DESIGNER, CONSULTANT, CONTRACTOR, THIRD_PARTY)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export interface SearchResult {
|
||||
id: number;
|
||||
uuid: string;
|
||||
id?: number; // Excluded from API responses (ADR-019)
|
||||
type: "correspondence" | "rfa" | "drawing";
|
||||
title: string;
|
||||
description?: string;
|
||||
|
||||
@@ -12,7 +12,8 @@ export interface UserOrganization {
|
||||
}
|
||||
|
||||
export interface User {
|
||||
userId: number;
|
||||
uuid: string;
|
||||
userId?: number; // Excluded from API responses (ADR-019)
|
||||
username: string;
|
||||
email: string;
|
||||
firstName: string;
|
||||
|
||||
Reference in New Issue
Block a user