Implement reject step functionality for contract approval process and enhance contract editing experience

This commit is contained in:
Marshal
2026-07-07 08:56:44 +00:00
parent 75f30e2054
commit f4d28e77fa
5 changed files with 90 additions and 13 deletions

View File

@@ -117,6 +117,18 @@ export function deriveContractCustomerAction(
};
}
// Saved-but-not-submitted contract — send the customer back into the wizard to
// finish editing and submit it for review.
if (contract.status === "DRAFT" || contract.status === "RENEWAL_DRAFT") {
return {
type: "navigate",
label: "Continue draft",
to: `/contracts/${id}/edit`,
primary: true,
icon: PencilLine,
};
}
const payable = findPayableBookingForContract(id, bookings);
if (payable) {
return {

View File

@@ -73,10 +73,6 @@ import {
MUTED,
} from "./contract-ui";
// Statuses where a customer may create a shipment booking themselves. Reached
// only after self-clearance is approved by Operations (Path A) or, for DOMESTIC,
// directly at counter-sign.
const PATH_A_BOOKABLE = ["FULLY_EXECUTED", "CONTRACT_ACTIVE"];
// Statuses where the customer uploads clearance documents on the contract. Used
// by both paths: Path B (customs, GL-reviewed) and Path A self-clearance
// (non-customs IMPORT/EXPORT, Operations-reviewed).
@@ -263,10 +259,14 @@ export default function ContractDetailPage() {
);
}
// Staff returned the contract for changes — send the customer to the full edit
// wizard (edit any term + replace documents → resubmit) rather than the
// read-only detail.
if (contract.status === "CHANGES_REQUESTED") {
// Not yet submitted (customer saved a draft) or staff returned the contract for
// changes — send the customer to the full edit wizard (edit any term + replace
// documents → submit) rather than the read-only detail.
if (
contract.status === "DRAFT" ||
contract.status === "RENEWAL_DRAFT" ||
contract.status === "CHANGES_REQUESTED"
) {
return <Navigate to={`/contracts/${contract.id}/edit`} replace />;
}
@@ -305,9 +305,13 @@ export default function ContractDetailPage() {
const clearanceFinalized =
contract.clearanceStatus === "CLEARANCE_READY_FOR_BOOKING" ||
contract.status === "CLEARANCE_READY_FOR_BOOKING";
const canBookShipment =
!customsPath && PATH_A_BOOKABLE.includes(contract.status);
const bookingAction = getContractBookingAction(contract, contractBookings);
// Whether the customer may open a new self-service booking. Derived from the
// shared booking-action helper so it honours the ONE_TIME single-slot rule:
// once a non-terminal booking exists on a ONE_TIME contract there is no free
// slot, so the action is "none" and no booking button is shown.
const canBookShipment =
bookingAction.kind === "book" || bookingAction.kind === "rebook";
const canRequestShipment = bookingAction.kind === "request";
// Customs + clearance finalized: GL is preparing the booking — surface a
// status notice instead of any action.

View File

@@ -81,9 +81,10 @@ type PriceModalMode = "submit" | "draft";
/**
* The contract wizard, used both to create a new contract and — in `edit` mode —
* to edit & resubmit a contract staff returned with CHANGES_REQUESTED. Edit mode
* hydrates the form from the saved contract, lets the customer change any term
* and replace documents, then runs the same update → price → submit flow.
* to continue an unsubmitted DRAFT or edit & resubmit a contract staff returned
* with CHANGES_REQUESTED. Edit mode hydrates the form from the saved contract,
* lets the customer change any term and replace documents, then runs the same
* update → price → submit flow.
*/
export default function NewContractPage({
mode = "create",