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

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

View File

@@ -0,0 +1,49 @@
"use client";
import { CorrespondenceList } from "@/components/correspondences/list";
import { Pagination } from "@/components/common/pagination";
import { useCorrespondences } from "@/hooks/use-correspondence";
import { useSearchParams } from "next/navigation";
import { Loader2 } from "lucide-react";
export function CorrespondencesContent() {
const searchParams = useSearchParams();
const page = parseInt(searchParams.get("page") || "1");
const status = searchParams.get("status") || undefined;
const search = searchParams.get("search") || undefined;
const { data, isLoading, isError } = useCorrespondences({
page,
status,
search,
} as any);
if (isLoading) {
return (
<div className="flex justify-center py-8">
<Loader2 className="h-8 w-8 animate-spin" />
</div>
);
}
if (isError) {
return (
<div className="text-red-500 text-center py-8">
Failed to load correspondences.
</div>
);
}
return (
<>
<CorrespondenceList data={data} />
<div className="mt-4">
<Pagination
currentPage={data?.page || 1}
totalPages={data?.totalPages || 1}
total={data?.total || 0}
/>
</div>
</>
);
}

View File

@@ -26,8 +26,8 @@ export function CorrespondenceDetail({ data }: CorrespondenceDetailProps) {
if (confirm("Are you sure you want to submit this correspondence?")) {
// TODO: Implement Template Selection. Hardcoded to 1 for now.
submitMutation.mutate({
id: data.correspondence_id,
data: { templateId: 1 }
id: data.correspondenceId,
data: {}
});
}
};
@@ -37,7 +37,7 @@ export function CorrespondenceDetail({ data }: CorrespondenceDetailProps) {
const action = actionState === "approve" ? "APPROVE" : "REJECT";
processMutation.mutate({
id: data.correspondence_id,
id: data.correspondenceId,
data: {
action,
comments
@@ -61,9 +61,9 @@ export function CorrespondenceDetail({ data }: CorrespondenceDetailProps) {
</Button>
</Link>
<div>
<h1 className="text-2xl font-bold">{data.document_number}</h1>
<h1 className="text-2xl font-bold">{data.documentNumber}</h1>
<p className="text-muted-foreground">
Created on {format(new Date(data.created_at), "dd MMM yyyy HH:mm")}
Created on {format(new Date(data.createdAt), "dd MMM yyyy HH:mm")}
</p>
</div>
</div>
@@ -200,14 +200,14 @@ export function CorrespondenceDetail({ data }: CorrespondenceDetailProps) {
<div>
<p className="text-sm font-medium text-muted-foreground">From Organization</p>
<p className="font-medium mt-1">{data.from_organization?.org_name}</p>
<p className="text-xs text-muted-foreground">{data.from_organization?.org_code}</p>
<p className="font-medium mt-1">{data.fromOrganization?.orgName}</p>
<p className="text-xs text-muted-foreground">{data.fromOrganization?.orgCode}</p>
</div>
<div>
<p className="text-sm font-medium text-muted-foreground">To Organization</p>
<p className="font-medium mt-1">{data.to_organization?.org_name}</p>
<p className="text-xs text-muted-foreground">{data.to_organization?.org_code}</p>
<p className="font-medium mt-1">{data.toOrganization?.orgName}</p>
<p className="text-xs text-muted-foreground">{data.toOrganization?.orgCode}</p>
</div>
</CardContent>
</Card>

View File

@@ -25,10 +25,10 @@ import { CreateCorrespondenceDto } from "@/types/dto/correspondence/create-corre
const correspondenceSchema = z.object({
subject: z.string().min(5, "Subject must be at least 5 characters"),
description: z.string().optional(),
document_type_id: z.number().default(1),
from_organization_id: z.number().min(1, "Please select From Organization"),
to_organization_id: z.number().min(1, "Please select To Organization"),
importance: z.enum(["NORMAL", "HIGH", "URGENT"]).default("NORMAL"),
documentTypeId: z.number(),
fromOrganizationId: z.number().min(1, "Please select From Organization"),
toOrganizationId: z.number().min(1, "Please select To Organization"),
importance: z.enum(["NORMAL", "HIGH", "URGENT"]),
attachments: z.array(z.instanceof(File)).optional(),
});
@@ -48,7 +48,7 @@ export function CorrespondenceForm() {
resolver: zodResolver(correspondenceSchema),
defaultValues: {
importance: "NORMAL",
document_type_id: 1,
documentTypeId: 1,
} as any, // Cast to any to handle partial defaults for required fields
});
@@ -57,12 +57,12 @@ export function CorrespondenceForm() {
// Note: projectId is hardcoded to 1 for now as per requirements/context
const payload: CreateCorrespondenceDto = {
projectId: 1,
typeId: data.document_type_id,
typeId: data.documentTypeId,
title: data.subject,
description: data.description,
originatorId: data.from_organization_id, // Mapping From -> Originator (Impersonation)
originatorId: data.fromOrganizationId, // Mapping From -> Originator (Impersonation)
details: {
to_organization_id: data.to_organization_id,
to_organization_id: data.toOrganizationId,
importance: data.importance
},
// create-correspondence DTO does not have 'attachments' field at root usually, often handled separate or via multipart
@@ -102,7 +102,7 @@ export function CorrespondenceForm() {
<div className="space-y-2">
<Label>From Organization *</Label>
<Select
onValueChange={(v) => setValue("from_organization_id", parseInt(v))}
onValueChange={(v) => setValue("fromOrganizationId", parseInt(v))}
disabled={isLoadingOrgs}
>
<SelectTrigger>
@@ -116,15 +116,15 @@ export function CorrespondenceForm() {
))}
</SelectContent>
</Select>
{errors.from_organization_id && (
<p className="text-sm text-destructive">{errors.from_organization_id.message}</p>
{errors.fromOrganizationId && (
<p className="text-sm text-destructive">{errors.fromOrganizationId.message}</p>
)}
</div>
<div className="space-y-2">
<Label>To Organization *</Label>
<Select
onValueChange={(v) => setValue("to_organization_id", parseInt(v))}
onValueChange={(v) => setValue("toOrganizationId", parseInt(v))}
disabled={isLoadingOrgs}
>
<SelectTrigger>
@@ -138,8 +138,8 @@ export function CorrespondenceForm() {
))}
</SelectContent>
</Select>
{errors.to_organization_id && (
<p className="text-sm text-destructive">{errors.to_organization_id.message}</p>
{errors.toOrganizationId && (
<p className="text-sm text-destructive">{errors.toOrganizationId.message}</p>
)}
</div>
</div>

View File

@@ -21,10 +21,10 @@ interface CorrespondenceListProps {
export function CorrespondenceList({ data }: CorrespondenceListProps) {
const columns: ColumnDef<Correspondence>[] = [
{
accessorKey: "document_number",
accessorKey: "documentNumber",
header: "Document No.",
cell: ({ row }) => (
<span className="font-medium">{row.getValue("document_number")}</span>
<span className="font-medium">{row.getValue("documentNumber")}</span>
),
},
{
@@ -37,17 +37,17 @@ export function CorrespondenceList({ data }: CorrespondenceListProps) {
),
},
{
accessorKey: "from_organization.org_name",
accessorKey: "fromOrganization.orgName",
header: "From",
},
{
accessorKey: "to_organization.org_name",
accessorKey: "toOrganization.orgName",
header: "To",
},
{
accessorKey: "created_at",
header: "Date",
cell: ({ row }) => format(new Date(row.getValue("created_at")), "dd MMM yyyy"),
accessorKey: "createdAt",
header: "Created",
cell: ({ row }) => format(new Date(row.getValue("createdAt")), "dd MMM yyyy"),
},
{
accessorKey: "status",
@@ -60,13 +60,13 @@ export function CorrespondenceList({ data }: CorrespondenceListProps) {
const item = row.original;
return (
<div className="flex gap-2">
<Link href={`/correspondences/${item.correspondence_id}`}>
<Link href={`/correspondences/${row.original.correspondenceId}`}>
<Button variant="ghost" size="icon" title="View">
<Eye className="h-4 w-4" />
</Button>
</Link>
{item.status === "DRAFT" && (
<Link href={`/correspondences/${item.correspondence_id}/edit`}>
<Link href={`/correspondences/${row.original.correspondenceId}/edit`}>
<Button variant="ghost" size="icon" title="Edit">
<Edit className="h-4 w-4" />
</Button>