shoc-frontend-new/src/app/v/[token]/dispatch/_components/uplift-request-card.tsx
2026-08-11 08:57:38 -03:00

136 lines
4.2 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 { 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;
};
const WITHDRAWABLE_STATUSES = new Set(["Pending", "ChangesRequested"]);
function statusClassName(status: string): string {
return `vp-uplift-status-${String(status).toLowerCase()}`;
}
export function UpliftRequestCard({
request,
locked,
canAct,
uploadingPercent,
submitting,
errorMessage,
onWithdraw,
onRevise,
onCancelRevise,
}: UpliftRequestCardProps) {
const [revising, setRevising] = useState(false);
const canWithdraw = !locked && canAct && WITHDRAWABLE_STATUSES.has(request.status);
const canRevise = !locked && canAct && request.status === "ChangesRequested";
return (
<div className="vp-uplift-row">
<div className="vp-row">
<strong>Requested</strong>
<span>
${Number(request.requestedNTE).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}
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>
);
}