import { Button, Group, Modal, Text, ThemeIcon, type ButtonProps, } from "@mantine/core"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import type { LucideIcon } from "lucide-react"; import { useState, type ReactNode } from "react"; import toast from "react-hot-toast"; import { useNavigate } from "react-router-dom"; import type { Freight } from "@edr/types"; import { PayNowButton } from "@/pages/bookings/payments/PayNowButton"; import { api } from "@/services/api"; import { deriveContractCustomerAction } from "./deriveContractCustomerAction"; interface ContractCustomerActionProps { contract: Freight.IContract; bookings: Freight.IBooking[]; size?: ButtonProps["size"]; /** Extra props for list-row button styling (ContractsList). */ listStyle?: boolean; } export function ContractCustomerAction({ contract, bookings, size = "xs", listStyle = false, }: ContractCustomerActionProps) { const navigate = useNavigate(); const action = deriveContractCustomerAction(contract, bookings); const buttonStyles = listStyle ? { root: { fontWeight: 600, fontSize: 13, paddingInline: 14, whiteSpace: "nowrap" as const, boxShadow: action.primary ? "0 1px 2px rgba(14,163,113,0.25)" : "none", }, } : undefined; if (action.type === "pay") { return ( ); } if (action.type === "initiate") { return ( ); } const Icon = action.icon; const variant = action.primary ? "filled" : "light"; return ( ); } /** * One-click bare booking instance under a self-clearance import/export contract * — ONE_TIME or GENERAL. No form, no date, no window gate: the new instance * lands in per-booking clearance (AWAITING_DOCUMENTS) with the customer on it. */ export function InitiateBookingButton({ contract, label = "Initiate booking", icon: Icon, size = "xs", listStyle = false, fullWidth = false, }: { contract: Freight.IContract; label?: string; icon: LucideIcon; size?: ButtonProps["size"]; listStyle?: boolean; fullWidth?: boolean; }) { const navigate = useNavigate(); const queryClient = useQueryClient(); const [confirmOpen, setConfirmOpen] = useState(false); const mutation = useMutation({ mutationFn: () => api.contracts.initiateBookingUnderContract.call({ id: contract.id, // Multi-route contracts must name a route; single-route auto-selects. contractRouteId: (contract.routes?.length ?? 0) > 1 ? contract.routes![0].id : undefined, }), onSuccess: (booking) => { queryClient.invalidateQueries({ queryKey: api.bookings.list.queryKey() }); queryClient.invalidateQueries({ queryKey: api.contracts.get.queryKey({ id: contract.id }), }); toast.success( "Booking initiated — upload your clearance documents to start the review.", ); setConfirmOpen(false); navigate(`/bookings/${booking.id}`); }, onError: (e: Error) => toast.error(e.message || "Could not initiate the booking"), }); return ( <> { if (!mutation.isPending) setConfirmOpen(false); }} centered radius="lg" size="md" closeOnClickOutside={!mutation.isPending} closeOnEscape={!mutation.isPending} withCloseButton={!mutation.isPending} title={ Initiate a new booking? } > This creates a new shipment booking under contract{" "} {contract.reference} . You'll upload the clearance documents next, and the shipment quantity is drawn down from your contract's reserved capacity. ); } /** Action column cell: doc button + primary customer action. */ export function ContractCustomerActionCell({ contract, bookings, docButton, }: { contract: Freight.IContract; bookings: Freight.IBooking[]; docButton: ReactNode; }) { return ( {docButton} ); }