690329:2209 Fixing refactor Correspondence GPT-5.3-Codex #04
CI / CD Pipeline / build (push) Successful in 20m35s
CI / CD Pipeline / deploy (push) Successful in 10m59s

This commit is contained in:
2026-03-29 22:09:40 +07:00
parent df3020012d
commit abbdebf2b9
21 changed files with 4426 additions and 24 deletions
@@ -3,11 +3,13 @@
import { CorrespondenceForm } from '@/components/correspondences/form';
import { useCorrespondence } from '@/hooks/use-correspondence';
import { Loader2 } from 'lucide-react';
import { useParams } from 'next/navigation';
import { useParams, useSearchParams } from 'next/navigation';
export default function EditCorrespondencePage() {
const params = useParams();
const searchParams = useSearchParams();
const uuid = (params?.uuid as string) ?? '';
const selectedRevisionId = searchParams.get('revId') ?? undefined;
const { data: correspondence, isLoading, isError } = useCorrespondence(uuid);
@@ -46,7 +48,7 @@ export default function EditCorrespondencePage() {
</div>
<div className="bg-card border rounded-lg p-6 shadow-sm">
<CorrespondenceForm initialData={correspondence} uuid={uuid} />
<CorrespondenceForm initialData={correspondence} uuid={uuid} selectedRevisionId={selectedRevisionId} />
</div>
</div>
);
@@ -3,11 +3,13 @@
import { CorrespondenceDetail } from '@/components/correspondences/detail';
import { useCorrespondence } from '@/hooks/use-correspondence';
import { Loader2 } from 'lucide-react';
import { useParams } from 'next/navigation';
import { useParams, useSearchParams } from 'next/navigation';
export default function CorrespondenceDetailPage() {
const params = useParams();
const searchParams = useSearchParams();
const uuid = (params?.uuid as string) ?? '';
const selectedRevisionId = searchParams.get('revId') ?? undefined;
const { data: correspondence, isLoading, isError } = useCorrespondence(uuid);
@@ -36,5 +38,5 @@ export default function CorrespondenceDetailPage() {
);
}
return <CorrespondenceDetail data={correspondence} />;
return <CorrespondenceDetail data={correspondence} selectedRevisionId={selectedRevisionId} />;
}
+23 -12
View File
@@ -22,26 +22,39 @@ import { Badge } from '@/components/ui/badge';
interface CorrespondenceDetailProps {
data: Correspondence;
selectedRevisionId?: string;
}
export function CorrespondenceDetail({ data }: CorrespondenceDetailProps) {
export function CorrespondenceDetail({ data, selectedRevisionId }: CorrespondenceDetailProps) {
const submitMutation = useSubmitCorrespondence();
const processMutation = useProcessWorkflow();
const cancelMutation = useCancelCorrespondence();
const { hasPermission } = useAuthStore();
const { user, hasPermission } = useAuthStore();
const [actionState, setActionState] = useState<'approve' | 'reject' | 'cancel' | null>(null);
const [comments, setComments] = useState('');
const [cancelReason, setCancelReason] = useState('');
if (!data) return <div>No data found</div>;
const currentRevision = data.revisions?.find((r) => r.isCurrent) || data.revisions?.[0];
const selectedRevision = selectedRevisionId
? data.revisions?.find((r) => r.publicId === selectedRevisionId)
: undefined;
const currentRevision = selectedRevision || data.revisions?.find((r) => r.isCurrent) || data.revisions?.[0];
const subject = currentRevision?.subject || '-';
const description = currentRevision?.description || '-';
const status = currentRevision?.status?.statusCode || 'UNKNOWN';
const attachments = currentRevision?.attachments || [];
const importance = (currentRevision?.details?.importance as string) || 'NORMAL';
const canEditMetadata = hasPermission('correspondence.edit');
const privilegedEditableStatuses = ['SUBCSC', 'SUBOWN', 'IN_REVIEW_CSC'];
const normalizedRole = (user?.role || '').toUpperCase().replace(/\s+/g, '_');
const isPrivilegedEditRole = ['SUPERADMIN', 'SUPER_ADMIN', 'ADMIN', 'DC', 'DOCUMENT_CONTROL'].includes(
normalizedRole
);
const canEditInStatus =
status === 'DRAFT' ||
(privilegedEditableStatuses.includes(status) && isPrivilegedEditRole);
const canEditDocument = canEditInStatus && (hasPermission('correspondence.edit') || isPrivilegedEditRole);
const toRecipients = data.recipients?.filter((r) => r.recipientType === 'TO') || [];
const ccRecipients = data.recipients?.filter((r) => r.recipientType === 'CC') || [];
@@ -100,15 +113,13 @@ export function CorrespondenceDetail({ data }: CorrespondenceDetailProps) {
</div>
</div>
<div className="flex gap-2">
{status === 'DRAFT' && (
<Can permission="correspondence.edit">
<Link href={`/correspondences/${data.publicId}/edit`}>
<Button variant="outline">
<Edit className="mr-2 h-4 w-4" />
Edit
</Button>
</Link>
</Can>
{canEditDocument && (
<Link href={`/correspondences/${data.publicId}/edit${selectedRevisionId ? `?revId=${selectedRevisionId}` : ''}`}>
<Button variant="outline">
<Edit className="mr-2 h-4 w-4" />
Edit
</Button>
</Link>
)}
{status === 'DRAFT' && (
<Can permission="correspondence.submit">
+14 -2
View File
@@ -75,6 +75,7 @@ interface InitialCorrespondenceData {
correspondenceTypeId?: number;
disciplineId?: number;
revisions?: Array<{
publicId?: string;
isCurrent?: boolean;
subject?: string;
title?: string;
@@ -124,7 +125,15 @@ const normalizePublicId = (value: unknown): string | undefined => {
return trimmed.length > 0 ? trimmed : undefined;
};
export function CorrespondenceForm({ initialData, uuid }: { initialData?: InitialCorrespondenceData; uuid?: string }) {
export function CorrespondenceForm({
initialData,
uuid,
selectedRevisionId,
}: {
initialData?: InitialCorrespondenceData;
uuid?: string;
selectedRevisionId?: string;
}) {
const router = useRouter();
const createMutation = useCreateCorrespondence();
const updateMutation = useUpdateCorrespondence();
@@ -138,7 +147,10 @@ export function CorrespondenceForm({ initialData, uuid }: { initialData?: Initia
const correspondenceTypes = extractArrayData<CorrespondenceTypeOption>(correspondenceTypesData);
// Extract initial values if editing
const currentRev = initialData?.revisions?.find((r) => r.isCurrent) || initialData?.revisions?.[0];
const selectedRevision = selectedRevisionId
? initialData?.revisions?.find((r) => normalizePublicId(r.publicId) === selectedRevisionId)
: undefined;
const currentRev = selectedRevision || initialData?.revisions?.find((r) => r.isCurrent) || initialData?.revisions?.[0];
const initialToRecipient = initialData?.recipients?.find((r) => r.recipientType === 'TO');
const initialCcRecipientIds =
initialData?.recipients
+23 -4
View File
@@ -8,12 +8,16 @@ import { Button } from '@/components/ui/button';
import { Eye, Edit } from 'lucide-react';
import Link from 'next/link';
import { format } from 'date-fns';
import { useAuthStore } from '@/lib/stores/auth-store';
interface CorrespondenceListProps {
data: CorrespondenceRevision[];
}
export function CorrespondenceList({ data }: CorrespondenceListProps) {
const { user, hasPermission } = useAuthStore();
const privilegedEditableStatuses = ['SUBCSC', 'SUBOWN', 'IN_REVIEW_CSC'];
const columns: ColumnDef<CorrespondenceRevision>[] = [
{
accessorKey: 'correspondence.correspondenceNumber',
@@ -90,18 +94,33 @@ export function CorrespondenceList({ data }: CorrespondenceListProps) {
// Edit/View link goes to the DOCUMENT detail (correspondence.publicId)
// Ideally we might pass ?revId=item.publicId to view specific revision, but detail page defaults to latest.
// For editing, we edit the document.
const docUuid = item.correspondence.publicId;
const docUuid = item.correspondence?.publicId;
const revId = item.publicId;
const statusCode = item.status?.statusCode;
const normalizedRole = (user?.role || '').toUpperCase().replace(/\s+/g, '_');
const isPrivilegedEditRole = ['SUPERADMIN', 'SUPER_ADMIN', 'ADMIN', 'DC', 'DOCUMENT_CONTROL'].includes(
normalizedRole
);
const canEditInStatus =
statusCode === 'DRAFT' ||
(typeof statusCode === 'string' &&
privilegedEditableStatuses.includes(statusCode) &&
isPrivilegedEditRole);
const canEdit = canEditInStatus && (hasPermission('correspondence.edit') || isPrivilegedEditRole);
if (!docUuid) {
return null;
}
return (
<div className="flex gap-2">
<Link href={`/correspondences/${docUuid}`}>
<Link href={`/correspondences/${docUuid}?revId=${revId}`}>
<Button variant="ghost" size="icon" title="View Details">
<Eye className="h-4 w-4" />
</Button>
</Link>
{statusCode === 'DRAFT' && (
<Link href={`/correspondences/${docUuid}/edit`}>
{canEdit && (
<Link href={`/correspondences/${docUuid}/edit?revId=${revId}`}>
<Button variant="ghost" size="icon" title="Edit">
<Edit className="h-4 w-4" />
</Button>