chore: add company select to backoffice booking

This commit is contained in:
Nathnael
2026-06-27 09:16:39 +00:00
parent 98e34593c4
commit db305d6fcd
2 changed files with 74 additions and 12 deletions

View File

@@ -180,8 +180,10 @@ export default function NewBookingPage() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [isGovernment, setIsGovernment] = useState(false); const [isGovernment, setIsGovernment] = useState(false);
const [governmentInstitution, setGovernmentInstitution] = useState("");
const [companyId, setCompanyId] = useState<string | null>(null); const [companyId, setCompanyId] = useState<string | null>(null);
// Government bookings bill to a real government company + an explicit profile.
const [govCompanyId, setGovCompanyId] = useState<string | null>(null);
const [govProfileId, setGovProfileId] = useState<string | null>(null);
const [freightType, setFreightType] = useState<FreightType>("CONTAINER"); const [freightType, setFreightType] = useState<FreightType>("CONTAINER");
const [originYardId, setOriginYardId] = useState<string | null>(null); const [originYardId, setOriginYardId] = useState<string | null>(null);
const [destinationYardId, setDestinationYardId] = useState<string | null>(null); const [destinationYardId, setDestinationYardId] = useState<string | null>(null);
@@ -220,6 +222,42 @@ export default function NewBookingPage() {
label: c.name || c.email || c.tin || c.id, label: c.name || c.email || c.tin || c.id,
})); }));
// Active government companies (kind=government) the booking can bill to.
const { data: govCompaniesPage, isLoading: govCompaniesLoading } = useQuery({
queryKey: ["companies", "government", "active"],
queryFn: () =>
customersService.list({
page: 1,
pageSize: 1000,
kind: "government",
status: "active",
}),
enabled: isGovernment,
});
const govCompanies = govCompaniesPage?.items ?? [];
const govCompanyOptions = govCompanies.map((c) => ({
value: c.id,
label: c.name || c.tin || c.id,
}));
// Profiles (importer/exporter) of the chosen government company — the booking
// must link to one explicitly.
const selectedGovCompany = govCompanies.find((c) => c.id === govCompanyId);
const govProfileOptions = (selectedGovCompany?.companyProfiles ?? [])
.filter((p) => p.status === "active")
.map((p) => ({
value: p.id,
label: `${p.type === "importer" ? "Import" : p.type === "exporter" ? "Export" : p.type}${
p.reference ? `${p.reference}` : ""
}`,
}));
// Reset the chosen profile when the government company changes.
useEffect(() => {
setGovProfileId(null);
}, [govCompanyId]);
// Day-level pool: fetch only the days that have a departure on the route (no // Day-level pool: fetch only the days that have a departure on the route (no
// train, no capacity). The batch engine assigns the train after booking. // train, no capacity). The batch engine assigns the train after booking.
const { data: availableDays, isLoading: daysLoading } = useQuery( const { data: availableDays, isLoading: daysLoading } = useQuery(
@@ -306,7 +344,7 @@ export default function NewBookingPage() {
Boolean(tradeDirection) && Boolean(tradeDirection) &&
Boolean(serviceTypeId) && Boolean(serviceTypeId) &&
departureSatisfied && departureSatisfied &&
(isGovernment ? governmentInstitution.trim().length >= 2 : Boolean(companyId)) && (isGovernment ? Boolean(govCompanyId && govProfileId) : Boolean(companyId)) &&
(freightType === "BULK" (freightType === "BULK"
? Boolean(cargoTypeId) && bulkWeight > 0 ? Boolean(cargoTypeId) && bulkWeight > 0
: allLinesValid); : allLinesValid);
@@ -320,8 +358,8 @@ export default function NewBookingPage() {
mutationFn: () => mutationFn: () =>
bookingsService.create({ bookingsService.create({
isGovernment, isGovernment,
governmentInstitution: isGovernment ? governmentInstitution : undefined, companyId: isGovernment ? govCompanyId || undefined : companyId || undefined,
companyId: isGovernment ? undefined : companyId || undefined, companyProfileId: isGovernment ? govProfileId || undefined : undefined,
freightType, freightType,
contractType: "NEW", contractType: "NEW",
equipmentReturn, equipmentReturn,
@@ -390,18 +428,37 @@ export default function NewBookingPage() {
<Stack gap="md"> <Stack gap="md">
<Switch <Switch
label="Government booking" label="Government booking"
description="No company required — institution name instead. Expedited to the scheduling queue." description="Bills to a government entity + profile. Expedited to the scheduling queue."
checked={isGovernment} checked={isGovernment}
onChange={(e) => setIsGovernment(e.currentTarget.checked)} onChange={(e) => setIsGovernment(e.currentTarget.checked)}
/> />
{isGovernment ? ( {isGovernment ? (
<TextInput <Group grow align="flex-start">
label="Government institution" <Select
placeholder="e.g. Ministry of Transport" label="Government entity"
value={governmentInstitution} placeholder="Select government company"
onChange={(e) => setGovernmentInstitution(e.currentTarget.value)} data={govCompanyOptions}
required value={govCompanyId}
/> onChange={setGovCompanyId}
searchable
required
disabled={govCompaniesLoading}
nothingFoundMessage="No active government companies"
/>
<Select
label="Profile"
placeholder={
govCompanyId ? "Select import/export profile" : "Pick an entity first"
}
data={govProfileOptions}
value={govProfileId}
onChange={setGovProfileId}
searchable
required
disabled={!govCompanyId}
nothingFoundMessage="No active profiles for this entity"
/>
</Group>
) : ( ) : (
<Select <Select
label="Customer" label="Customer"

View File

@@ -18,6 +18,9 @@ export type CompanyType =
/** Mirrors backend `CompanyStatus`. */ /** Mirrors backend `CompanyStatus`. */
export type CompanyStatus = "active" | "pending" | "suspended" | "blacklisted"; export type CompanyStatus = "active" | "pending" | "suspended" | "blacklisted";
/** Mirrors backend `CompanyKind` — commercial customer vs. government entity. */
export type CompanyKind = "commercial" | "government";
/** Mirrors backend `ProfileType` (the role a company plays). */ /** Mirrors backend `ProfileType` (the role a company plays). */
export type ProfileType = export type ProfileType =
| "importer" | "importer"
@@ -47,6 +50,7 @@ export interface Company {
id: string; id: string;
name: string; name: string;
type: CompanyType; type: CompanyType;
kind: CompanyKind;
status: CompanyStatus; status: CompanyStatus;
tin: string; tin: string;
vatNumber?: string | null; vatNumber?: string | null;
@@ -73,6 +77,7 @@ export interface CompanyListFilter {
pageSize: number; pageSize: number;
search?: string; search?: string;
type?: CompanyType; type?: CompanyType;
kind?: CompanyKind;
status?: CompanyStatus; status?: CompanyStatus;
} }