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 (
}
onClick={(e) => {
e.stopPropagation();
navigate(action.to);
}}
styles={buttonStyles}
fw={listStyle ? undefined : 700}
fz={listStyle ? undefined : 13}
>
{action.label}
);
}
/**
* 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.
}
loading={mutation.isPending}
onClick={() => mutation.mutate()}
>
Yes, initiate booking
}
loading={mutation.isPending}
onClick={(e) => {
e.stopPropagation();
setConfirmOpen(true);
}}
styles={
listStyle
? {
root: {
fontWeight: 600,
fontSize: 13,
paddingInline: 14,
whiteSpace: "nowrap" as const,
boxShadow: "0 1px 2px rgba(14,163,113,0.25)",
},
}
: undefined
}
fw={listStyle ? undefined : 700}
fz={listStyle ? undefined : 13}
>
{label}
>
);
}
/** Action column cell: doc button + primary customer action. */
export function ContractCustomerActionCell({
contract,
bookings,
docButton,
}: {
contract: Freight.IContract;
bookings: Freight.IBooking[];
docButton: ReactNode;
}) {
return (
{docButton}
);
}