121 lines
5.1 KiB
TypeScript
121 lines
5.1 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { approvepo, rejectPo, requestEdits, requestVendorId } from "./actions";
|
|
import type { POStatus } from "@prisma/client";
|
|
|
|
export function ApprovalActions({
|
|
poId,
|
|
poStatus,
|
|
}: {
|
|
poId: string;
|
|
poStatus: POStatus;
|
|
}) {
|
|
const router = useRouter();
|
|
const [note, setNote] = useState("");
|
|
const [activeAction, setActiveAction] = useState<string | null>(null);
|
|
const [pending, setPending] = useState<string | null>(null);
|
|
const [error, setError] = useState("");
|
|
|
|
async function dispatch(action: string, requireNote = false) {
|
|
if (requireNote && !note.trim()) {
|
|
setError("A note is required for this action.");
|
|
return;
|
|
}
|
|
setPending(action);
|
|
setError("");
|
|
let result: { ok: true } | { error: string } | undefined;
|
|
if (action === "approve") result = await approvepo({ poId, note });
|
|
else if (action === "approve_note") result = await approvepo({ poId, note, withNote: true });
|
|
else if (action === "reject") result = await rejectPo({ poId, note });
|
|
else if (action === "request_edits") result = await requestEdits({ poId, note });
|
|
else if (action === "request_vendor_id") result = await requestVendorId({ poId });
|
|
|
|
if (result && "error" in result) {
|
|
setError(result.error);
|
|
setPending(null);
|
|
} else {
|
|
router.push("/approvals");
|
|
router.refresh();
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="rounded-lg border border-neutral-200 bg-white p-4 md:p-6">
|
|
<h3 className="text-base font-semibold text-neutral-900 mb-4">Decision</h3>
|
|
|
|
{(activeAction === "reject" || activeAction === "request_edits" || activeAction === "approve_note") && (
|
|
<div className="mb-4">
|
|
<label className="block text-sm font-medium text-neutral-700 mb-1.5">
|
|
Note {activeAction !== "approve_note" ? "(required)" : "(optional)"}
|
|
</label>
|
|
<textarea
|
|
rows={3}
|
|
value={note}
|
|
onChange={(e) => setNote(e.target.value)}
|
|
className="w-full rounded-lg border border-neutral-300 px-3 py-2.5 text-sm focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20 resize-none"
|
|
placeholder={
|
|
activeAction === "reject"
|
|
? "Reason for rejection…"
|
|
: activeAction === "request_edits"
|
|
? "What needs to be changed…"
|
|
: "Optional note for the submitter…"
|
|
}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{error && (
|
|
<p className="mb-4 text-sm text-danger-700 bg-danger-50 rounded-lg px-3 py-2">{error}</p>
|
|
)}
|
|
|
|
<div className="flex flex-col sm:flex-row flex-wrap items-stretch sm:items-center gap-2 sm:gap-3">
|
|
<button
|
|
onClick={() => {
|
|
setActiveAction("reject");
|
|
if (activeAction === "reject") dispatch("reject", true);
|
|
}}
|
|
disabled={!!pending}
|
|
className="w-full sm:w-auto rounded-lg border border-danger bg-danger-50 px-4 py-2.5 text-sm font-medium text-danger-700 hover:bg-danger-100 disabled:opacity-60 transition-colors"
|
|
>
|
|
{pending === "reject" ? "Rejecting…" : activeAction === "reject" ? "Confirm Reject" : "Reject"}
|
|
</button>
|
|
<button
|
|
onClick={() => {
|
|
setActiveAction("request_edits");
|
|
if (activeAction === "request_edits") dispatch("request_edits", true);
|
|
}}
|
|
disabled={!!pending}
|
|
className="w-full sm:w-auto rounded-lg border border-warning bg-warning-50 px-4 py-2.5 text-sm font-medium text-warning-700 hover:bg-warning-100 disabled:opacity-60 transition-colors"
|
|
>
|
|
{pending === "request_edits" ? "Sending…" : activeAction === "request_edits" ? "Send Edit Request" : "Request Edits"}
|
|
</button>
|
|
<button
|
|
onClick={() => dispatch("request_vendor_id")}
|
|
disabled={!!pending}
|
|
className="w-full sm:w-auto 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"
|
|
>
|
|
{pending === "request_vendor_id" ? "Sending…" : "Request Vendor ID"}
|
|
</button>
|
|
<button
|
|
onClick={() => {
|
|
setActiveAction("approve_note");
|
|
if (activeAction === "approve_note") dispatch("approve_note");
|
|
}}
|
|
disabled={!!pending}
|
|
className="w-full sm:w-auto 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"
|
|
>
|
|
{pending === "approve_note" ? "Approving…" : activeAction === "approve_note" ? "Confirm Approve with Remarks" : "Approve with Remarks"}
|
|
</button>
|
|
<button
|
|
onClick={() => dispatch("approve")}
|
|
disabled={!!pending}
|
|
className="w-full sm:w-auto rounded-lg bg-success px-4 py-2.5 text-sm font-semibold text-white hover:opacity-90 disabled:opacity-60 transition-opacity"
|
|
>
|
|
{pending === "approve" ? "Approving…" : "Approve"}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|