From d297fd044fe0c6be5dba5412c0f38a088893a33f Mon Sep 17 00:00:00 2001 From: Hardik Date: Sat, 16 May 2026 16:32:44 +0530 Subject: [PATCH] fix(po): add Submit for Approval button on draft PO detail and edit pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A draft PO had no way to be submitted without creating a brand new PO. Two surfaces are now fixed: Edit page (/po/[id]/edit): - updatePo action now accepts intent "submit" (alongside "save" and "resubmit"), which transitions a DRAFT PO to MGR_REVIEW, creates a SUBMITTED action, and fires notifications — identical behaviour to the new-PO submit flow - EditPoForm shows a primary "Submit for Approval" button when the PO is in DRAFT status; "Resubmit for Approval" remains for EDITS_REQUESTED PO detail page (/po/[id]): - New submitDraftPo server action in po/[id]/actions.ts handles the same DRAFT → MGR_REVIEW transition without requiring the full edit form - New SubmitDraftButton client component renders next to the Edit link in the detail header for DRAFT POs owned by the current user - Button order: Edit · Submit for Approval · Discard Co-Authored-By: Claude Sonnet 4.6 --- .../app/(portal)/po/[id]/actions.ts | 36 +++++++++++++++++++ .../app/(portal)/po/[id]/edit/actions.ts | 14 ++++---- .../(portal)/po/[id]/edit/edit-po-form.tsx | 9 ++++- .../components/po/po-detail.tsx | 6 ++++ .../components/po/submit-draft-button.tsx | 36 +++++++++++++++++++ 5 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 App/pelagia-portal/components/po/submit-draft-button.tsx diff --git a/App/pelagia-portal/app/(portal)/po/[id]/actions.ts b/App/pelagia-portal/app/(portal)/po/[id]/actions.ts index 219d0f9..aa28917 100644 --- a/App/pelagia-portal/app/(portal)/po/[id]/actions.ts +++ b/App/pelagia-portal/app/(portal)/po/[id]/actions.ts @@ -48,6 +48,42 @@ export async function provideVendorId({ return { ok: true }; } +export async function submitDraftPo( + poId: string +): Promise<{ ok: true } | { error: string }> { + const session = await auth(); + if (!session?.user) return { error: "Unauthorized" }; + + const po = await db.purchaseOrder.findUnique({ + where: { id: poId }, + include: { submitter: true }, + }); + if (!po) return { error: "PO not found" }; + if (po.status !== "DRAFT") return { error: "Only draft purchase orders can be submitted." }; + if (po.submitterId !== session.user.id && session.user.role !== "SUPERUSER") { + return { error: "You can only submit your own purchase orders." }; + } + + await db.purchaseOrder.update({ + where: { id: poId }, + data: { + status: "MGR_REVIEW", + submittedAt: new Date(), + actions: { + create: { actionType: "SUBMITTED", actorId: session.user.id }, + }, + }, + }); + + const managers = await db.user.findMany({ where: { role: "MANAGER", isActive: true } }); + await notify({ event: "PO_SUBMITTED", po, recipients: [po.submitter, ...managers] }); + + revalidatePath(`/po/${poId}`); + revalidatePath("/dashboard"); + revalidatePath("/my-orders"); + return { ok: true }; +} + export async function discardDraftPo( poId: string ): Promise<{ ok: true } | { error: string }> { diff --git a/App/pelagia-portal/app/(portal)/po/[id]/edit/actions.ts b/App/pelagia-portal/app/(portal)/po/[id]/edit/actions.ts index 8d5d7e6..7a390a8 100644 --- a/App/pelagia-portal/app/(portal)/po/[id]/edit/actions.ts +++ b/App/pelagia-portal/app/(portal)/po/[id]/edit/actions.ts @@ -41,7 +41,7 @@ export async function updatePo( return { error: "You can only edit your own purchase orders." }; } - const intent = formData.get("intent") as "save" | "resubmit"; + const intent = formData.get("intent") as "save" | "submit" | "resubmit"; const parsed = createPoSchema.safeParse({ title: formData.get("title"), @@ -74,7 +74,9 @@ export async function updatePo( 0 ); + const isSubmit = intent === "submit" && po.status === "DRAFT"; const isResubmit = intent === "resubmit" && po.status === "EDITS_REQUESTED"; + const shouldSubmit = isSubmit || isResubmit; // Before mutating, snapshot the current PO state so the manager can see // exactly what the submitter changed when they resubmit after edits requested. @@ -150,8 +152,8 @@ export async function updatePo( tcPaymentTerms: data.tcPaymentTerms ?? null, tcOthers: data.tcOthers ?? null, totalAmount: total, - status: isResubmit ? "MGR_REVIEW" : "DRAFT", - submittedAt: isResubmit ? new Date() : po.submittedAt, + status: shouldSubmit ? "MGR_REVIEW" : "DRAFT", + submittedAt: shouldSubmit ? new Date() : po.submittedAt, lineItems: { deleteMany: {}, create: data.lineItems.map((item, idx) => ({ @@ -168,18 +170,18 @@ export async function updatePo( })), }, actions: { - create: isResubmit + create: shouldSubmit ? { actionType: "SUBMITTED", actorId: session.user.id, - ...(resubmitSnapshot ? { metadata: { editSnapshot: resubmitSnapshot } } : {}), + ...(isResubmit && resubmitSnapshot ? { metadata: { editSnapshot: resubmitSnapshot } } : {}), } : undefined, }, }, }); - if (isResubmit) { + if (shouldSubmit) { const [fullPo, managers] = await Promise.all([ db.purchaseOrder.findUnique({ where: { id: poId }, include: { submitter: true } }), db.user.findMany({ where: { role: "MANAGER", isActive: true } }), diff --git a/App/pelagia-portal/app/(portal)/po/[id]/edit/edit-po-form.tsx b/App/pelagia-portal/app/(portal)/po/[id]/edit/edit-po-form.tsx index 519bd01..21de1be 100644 --- a/App/pelagia-portal/app/(portal)/po/[id]/edit/edit-po-form.tsx +++ b/App/pelagia-portal/app/(portal)/po/[id]/edit/edit-po-form.tsx @@ -53,12 +53,13 @@ export function EditPoForm({ po, vessels, accounts, vendors }: Props) { accountId: li.accountId ?? undefined, })) ); - const [submitting, setSubmitting] = useState<"save" | "resubmit" | null>(null); + const [submitting, setSubmitting] = useState<"save" | "submit" | "resubmit" | null>(null); const [error, setError] = useState(""); const hasPerLineAccounts = po.lineItems.some((li) => li.accountId); const [multiAccount, setMultiAccount] = useState(hasPerLineAccounts); const [defaultAccountId, setDefaultAccountId] = useState(po.accountId ?? ""); + const canSubmit = po.status === "DRAFT"; const canResubmit = po.status === "EDITS_REQUESTED"; async function handleSubmit(intent: "save" | "resubmit") { @@ -283,6 +284,12 @@ export function EditPoForm({ po, vessels, accounts, vendors }: Props) { className="rounded-lg border border-neutral-300 bg-white px-4 py-2.5 text-sm font-medium text-neutral-700 hover:bg-neutral-50 disabled:opacity-60 transition-colors"> {submitting === "save" ? "Saving…" : "Save Draft"} + {canSubmit && ( + + )} {canResubmit && ( + {error && {error}} + + ); +}