mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-04 18:22:05 +00:00
Some checks are pending
Frontend checks / static (push) Waiting to run
Frontend checks / build (push) Waiting to run
Frontend checks / unit (push) Waiting to run
Frontend checks / Visual regression (push) Waiting to run
Frontend checks / browser-smoke (push) Waiting to run
Frontend checks / governance (push) Waiting to run
Frontend checks / ci-complete (push) Blocked by required conditions
Deploy Web / Resolve target (push) Waiting to run
Deploy Web / Deploy SPA to (push) Blocked by required conditions
Show work-order uplift requests read-only in the Vendor Portal
153 lines
5 KiB
TypeScript
153 lines
5 KiB
TypeScript
import { useState } from "react";
|
|
import { Text } from "@/components/ui/text";
|
|
import { UpliftRequestForm } from "@/app/v/[token]/dispatch/_components/uplift-request-form";
|
|
import type { VendorMediaUsage } from "@/domain/vendor-portal/lib/document-validation";
|
|
import type { UpliftFormValues } from "@/domain/vendor-portal/lib/uplift-validation";
|
|
import type { VendorPortalUpliftRequest } from "@/domain/vendor-portal/types/vendor-portal";
|
|
import { formatDateTime } from "@/lib/time-utils";
|
|
|
|
type UpliftRequestCardProps = {
|
|
request: VendorPortalUpliftRequest;
|
|
locked: boolean;
|
|
canAct: boolean;
|
|
uploadingPercent: number | undefined;
|
|
submitting: boolean;
|
|
errorMessage: string;
|
|
onWithdraw: (request: VendorPortalUpliftRequest) => void;
|
|
onRevise: (request: VendorPortalUpliftRequest, values: UpliftFormValues) => void;
|
|
onCancelRevise: () => void;
|
|
mediaUsage?: VendorMediaUsage;
|
|
};
|
|
|
|
const WITHDRAWABLE_STATUSES = new Set(["Pending", "ChangesRequested"]);
|
|
|
|
function statusClassName(status: string): string {
|
|
return `vp-uplift-status-${String(status).toLowerCase()}`;
|
|
}
|
|
|
|
function availableActions(request: VendorPortalUpliftRequest, locked: boolean, canAct: boolean) {
|
|
// Requests raised from the work order are visible but read-only to the vendor.
|
|
const actionable = !locked && canAct && request.raisedByVendor !== false;
|
|
return {
|
|
canWithdraw: actionable && WITHDRAWABLE_STATUSES.has(request.status),
|
|
canRevise: actionable && request.status === "ChangesRequested",
|
|
};
|
|
}
|
|
|
|
// A vendor request stores the requested new NTE total; a work-order request stores the increase.
|
|
function requestedTotal(request: VendorPortalUpliftRequest): number {
|
|
const requested = Number(request.requestedNTE);
|
|
return request.raisedByVendor === false ? Number(request.currentNTE ?? 0) + requested : requested;
|
|
}
|
|
|
|
export function UpliftRequestCard({
|
|
request,
|
|
locked,
|
|
canAct,
|
|
uploadingPercent,
|
|
submitting,
|
|
errorMessage,
|
|
onWithdraw,
|
|
onRevise,
|
|
onCancelRevise,
|
|
mediaUsage,
|
|
}: UpliftRequestCardProps) {
|
|
const [revising, setRevising] = useState(false);
|
|
const { canWithdraw, canRevise } = availableActions(request, locked, canAct);
|
|
|
|
return (
|
|
<div className="vp-uplift-row">
|
|
<div className="vp-row">
|
|
<strong>Requested</strong>
|
|
<span>
|
|
${requestedTotal(request).toFixed(2)} (was ${Number(request.currentNTE ?? 0).toFixed(2)})
|
|
</span>
|
|
</div>
|
|
<div className="vp-row">
|
|
<strong>Status</strong>
|
|
<span>
|
|
<span className={statusClassName(request.status)}>{request.status}</span>
|
|
</span>
|
|
</div>
|
|
{Boolean(request.expiresAt) && (
|
|
<div className="vp-row">
|
|
<strong>Expires</strong>
|
|
<span>{formatDateTime(request.expiresAt as string)}</span>
|
|
</div>
|
|
)}
|
|
{Boolean(request.vendorReason) && (
|
|
<div className="vp-row">
|
|
<strong>Reason</strong>
|
|
<span>{request.vendorReason}</span>
|
|
</div>
|
|
)}
|
|
{Boolean(request.evidenceFileName) && (
|
|
<div className="vp-row">
|
|
<strong>Evidence</strong>
|
|
<span>
|
|
{request.evidenceFileName}
|
|
{request.evidenceContentType ? ` · ${request.evidenceContentType}` : ""}
|
|
</span>
|
|
</div>
|
|
)}
|
|
{Boolean(request.decisionNote) && (
|
|
<div className="vp-row">
|
|
<strong>Decision</strong>
|
|
<span>
|
|
{request.decisionNote}
|
|
{request.decidedByName ? ` — ${request.decidedByName}` : ""}
|
|
</span>
|
|
</div>
|
|
)}
|
|
{request.notificationStatus === "Error" && Boolean(request.notificationError) && (
|
|
<div className="vp-row">
|
|
<strong>Notification</strong>
|
|
<span className="vp-error-inline">{request.notificationError}</span>
|
|
</div>
|
|
)}
|
|
<Text variant="error" when={Boolean(errorMessage)}>
|
|
{errorMessage}
|
|
</Text>
|
|
|
|
{revising && canRevise ? (
|
|
<UpliftRequestForm
|
|
mode="revise"
|
|
currentNte={request.currentNTE}
|
|
initialRequested={String(request.requestedNTE)}
|
|
initialReason={request.vendorReason ?? ""}
|
|
hasExistingEvidence={request.evidenceDocumentId != null}
|
|
mediaUsage={mediaUsage}
|
|
submitting={submitting}
|
|
uploadingPercent={uploadingPercent}
|
|
errorMessage=""
|
|
onSubmit={(values) => onRevise(request, values)}
|
|
onCancel={() => {
|
|
setRevising(false);
|
|
onCancelRevise();
|
|
}}
|
|
/>
|
|
) : (
|
|
<div className="vp-actions">
|
|
{canWithdraw && (
|
|
<button
|
|
type="button"
|
|
className="vp-btn vp-btn-ghost"
|
|
onClick={() => onWithdraw(request)}
|
|
>
|
|
Withdraw request
|
|
</button>
|
|
)}
|
|
{canRevise && (
|
|
<button
|
|
type="button"
|
|
className="vp-btn vp-btn-primary"
|
|
onClick={() => setRevising(true)}
|
|
>
|
|
Revise
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|