mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 01:48:12 +00:00
resolve conflict
This commit is contained in:
@@ -7,14 +7,11 @@ import {
|
||||
Paperclip,
|
||||
Settings,
|
||||
SlidersHorizontal,
|
||||
<<<<<<< HEAD
|
||||
Train,
|
||||
Truck,
|
||||
Container,
|
||||
Package,
|
||||
=======
|
||||
TrainTrack,
|
||||
>>>>>>> 523d7e58422f1bde8024c2dd237092a7cf6aa190
|
||||
//TrainTrack,
|
||||
} from "lucide-react";
|
||||
|
||||
import { FreightDashboardLayout, type SidebarItem, type SidebarSection } from "@/components/layout";
|
||||
@@ -39,7 +36,7 @@ import RuleEngineLegacyRedirect from "./pages/ruleEngine/RuleEngineLegacyRedirec
|
||||
import RuleEngineResourcePage from "./pages/ruleEngine/RuleEngineResourcePage";
|
||||
import TrainsPage from "./pages/trains/TrainsPage";
|
||||
import { getCategorySidebarChildren } from "./pages/ruleEngine/config/resources";
|
||||
import TrainsPage from "./pages/trains/TrainsPage";
|
||||
//import TrainsPage from "./pages/trains/TrainsPage";
|
||||
import TrainDetailPage from "./pages/trains/TrainDetailPage";
|
||||
import WagonsPage from "./pages/wagons/WagonsPage";
|
||||
import ContainersPage from "./pages/containers_management/ContainersPage";
|
||||
@@ -63,7 +60,7 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [
|
||||
{
|
||||
label: "Train scheduling",
|
||||
href: "/dashboard/operations/train-scheduling",
|
||||
icon: <TrainTrack />,
|
||||
icon: <Train />,
|
||||
},
|
||||
...demoItems,
|
||||
],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { trainService } from '@/services/trainService';
|
||||
import { trainService } from '@/services/trains.service';
|
||||
|
||||
export const trainKeys = {
|
||||
all: ['trains'] as const,
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { useCargoes } from '@/hooks/useCargoes';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { LoadCargoDialog } from '@/components/cargoes/LoadCargoDialog';
|
||||
|
||||
export default function CargoesPage() {
|
||||
const { data: cargoes, refetch, isLoading } = useCargoes();
|
||||
if (isLoading) return <div>Loading cargoes...</div>;
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader><CardTitle>All Cargoes</CardTitle></CardHeader>
|
||||
<CardContent>
|
||||
<Table>
|
||||
<TableHeader><TableRow><TableHead>Reference</TableHead><TableHead>Description</TableHead><TableHead>Quantity</TableHead><TableHead>Weight</TableHead><TableHead>Status</TableHead><TableHead>Actions</TableHead></TableRow></TableHeader>
|
||||
<TableBody>
|
||||
{cargoes?.map(c => (
|
||||
<TableRow key={c.id}>
|
||||
<TableCell>{c.cargoReference}</TableCell>
|
||||
<TableCell>{c.description || '-'}</TableCell>
|
||||
<TableCell>{c.quantity}</TableCell>
|
||||
<TableCell>{c.weight} kg</TableCell>
|
||||
<TableCell><Badge variant="outline">{c.status}</Badge></TableCell>
|
||||
<TableCell>
|
||||
{c.status === 'PENDING' && <LoadCargoDialog cargoId={c.id} onSuccess={() => refetch()} />}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
"react": "19.2.6",
|
||||
"react-dom": "19.2.6",
|
||||
"react-hook-form": "^7.76.0",
|
||||
"react-hot-toast": "^2.6.0",
|
||||
"react-router-dom": "^6.27.0",
|
||||
"recharts": "^3.8.1",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,14 +6,12 @@ import { useNavigate } from "react-router-dom";
|
||||
import {
|
||||
AlertCircle,
|
||||
Check,
|
||||
CheckCircle2,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
LoaderCircle,
|
||||
} from "lucide-react";
|
||||
import { Button } from "@edr/ui-common";
|
||||
import { api } from "@/services/api";
|
||||
import { Freight } from "@edr/types";
|
||||
import type { CreateBookingPayload } from "@/services/bookings.service";
|
||||
import {
|
||||
BookingFormInputValues,
|
||||
@@ -32,13 +30,11 @@ import {
|
||||
Step5CargoDetails,
|
||||
Step8Review,
|
||||
} from "./new-booking-form/steps";
|
||||
import useAuth from "@/hooks/useAuth";
|
||||
|
||||
export default function NewBookingPage() {
|
||||
const navigate = useNavigate();
|
||||
const queryClient = useQueryClient();
|
||||
const [step, setStep] = useState(1);
|
||||
const { customer } = useAuth();
|
||||
const { data: referenceData, isLoading: refDataLoading } = useQuery(
|
||||
api.bookings.referenceData.queryOptions(),
|
||||
);
|
||||
@@ -48,7 +44,7 @@ export default function NewBookingPage() {
|
||||
api.bookings.create.call(payload),
|
||||
onSuccess: (booking) => {
|
||||
queryClient.invalidateQueries({ queryKey: api.bookings.list.queryKey() });
|
||||
setTimeout(() => navigate(`/bookings/${booking.id}`), 2500);
|
||||
navigate(`/bookings/${booking.id}`);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -132,23 +128,27 @@ export default function NewBookingPage() {
|
||||
return "";
|
||||
};
|
||||
|
||||
const selectedChild =
|
||||
data.cargoType !== "container" && data.bulkCommoditytype
|
||||
? cargoTree
|
||||
.find((g) => g.code.toLowerCase() === data.freightType)
|
||||
?.children?.find((c) => c.name === data.bulkCommoditytype)
|
||||
: undefined;
|
||||
|
||||
const cargoTypeId =
|
||||
data.cargoType === "container"
|
||||
? findContainerCargoTypeId()
|
||||
: (findCargoTypeId(
|
||||
data.freightType === "bulk"
|
||||
? data.bulkCommodity
|
||||
: data.breakBulkType,
|
||||
) ?? "");
|
||||
: (findCargoTypeId(data.bulkCommoditytype) ??
|
||||
cargoTree.find((g) => g.code.toLowerCase() === data.freightType)
|
||||
?.id ??
|
||||
"");
|
||||
|
||||
const cargoFreeText =
|
||||
data.cargoType === "container"
|
||||
? undefined
|
||||
: data.freightType === "bulk" && data.bulkCommodity === "Others"
|
||||
? data.bulkCommodityOther
|
||||
: data.freightType === "break_bulk" && data.breakBulkType === "Others"
|
||||
? data.breakBulkTypeOther
|
||||
: undefined;
|
||||
: selectedChild?.show_free_text_box
|
||||
? data.bulkCommoditytype
|
||||
: undefined;
|
||||
|
||||
// ── Build API payload ───────────────────────────────────────────────
|
||||
const apiPayload: CreateBookingPayload = {
|
||||
@@ -168,15 +168,16 @@ export default function NewBookingPage() {
|
||||
: direction === "domestic"
|
||||
? "DOMESTIC"
|
||||
: "IMPORT",
|
||||
freightType:
|
||||
data.cargoType === "container"
|
||||
? Freight.FreightType.Container
|
||||
: Freight.FreightType.Bulk,
|
||||
cargoTypeId: data.cargoType === "container" ? undefined : cargoTypeId,
|
||||
cargoTotalWeightVgm: totalWeight,
|
||||
isHazardous: data.isHazardous,
|
||||
paymentCurrency: "USD",
|
||||
allowConsolidation: data.consolidationEnabled,
|
||||
// @ts-ignore
|
||||
freightType:
|
||||
data.cargoType === "container"
|
||||
? ("CONTAINER" as const)
|
||||
: ("BULK" as const),
|
||||
containers:
|
||||
data.cargoType === "container"
|
||||
? data.containers.map((c) => ({
|
||||
@@ -185,7 +186,6 @@ export default function NewBookingPage() {
|
||||
vgmPerUnitTons: Number(c.vgm || 0),
|
||||
}))
|
||||
: [],
|
||||
...(customer?.company?.id ? { companyId: customer.company.id } : {}),
|
||||
...(data.previousContractRef
|
||||
? { previousContractId: data.previousContractRef }
|
||||
: {}),
|
||||
@@ -207,26 +207,6 @@ export default function NewBookingPage() {
|
||||
createMutation.mutate(apiPayload);
|
||||
});
|
||||
|
||||
if (createMutation.isSuccess) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center p-6">
|
||||
<div className="w-full max-w-sm rounded-2xl border border-border bg-card p-10 text-center shadow-sm">
|
||||
<div className="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-emerald-100">
|
||||
<CheckCircle2 className="h-7 w-7 text-emerald-600" />
|
||||
</div>
|
||||
<h2 className="text-xl font-bold">Contract Submitted</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Your request is queued for review by EDR Line Staff. You will be
|
||||
notified once approved.
|
||||
</p>
|
||||
<p className="mt-4 font-mono text-sm font-semibold text-primary">
|
||||
{createMutation.data?.reference}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
id="new-booking-form"
|
||||
@@ -245,7 +225,7 @@ export default function NewBookingPage() {
|
||||
<div className="mb-6 flex items-start gap-3 rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-800">
|
||||
<AlertCircle className="mt-0.5 h-4 w-4 shrink-0 text-red-500" />
|
||||
<div>
|
||||
<p className="font-semibold">Submission failed</p>
|
||||
<p className="font-semibold">Failed to save draft</p>
|
||||
<p className="mt-1 text-red-600">
|
||||
{createMutation.error instanceof Error
|
||||
? createMutation.error.message
|
||||
@@ -306,9 +286,7 @@ export default function NewBookingPage() {
|
||||
) : (
|
||||
<Check />
|
||||
)}
|
||||
{createMutation.isPending
|
||||
? "Submitting..."
|
||||
: "Submit Contract Request"}
|
||||
{createMutation.isPending ? "Saving Draft..." : "Save as Draft"}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,19 +1,6 @@
|
||||
import { DeepPartial, Path } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
|
||||
export const STATIONS = [
|
||||
"Addis Ababa",
|
||||
"Adama",
|
||||
"Mojo",
|
||||
"Awash",
|
||||
"Mieso",
|
||||
"Dire Dawa",
|
||||
"Aysha",
|
||||
"Ali Sabieh",
|
||||
"Holhol",
|
||||
"Djibouti City",
|
||||
] as const;
|
||||
|
||||
export const ETHIOPIA_STATIONS = new Set<string>([
|
||||
"Addis Ababa",
|
||||
"Adama",
|
||||
@@ -23,19 +10,6 @@ export const ETHIOPIA_STATIONS = new Set<string>([
|
||||
"Dire Dawa",
|
||||
]);
|
||||
|
||||
export const BULK_COMMODITIES = [
|
||||
"Coffee",
|
||||
"Beans",
|
||||
"Fertilizer",
|
||||
"Sugar",
|
||||
"Oil",
|
||||
"Livestock",
|
||||
"Steel",
|
||||
"Others",
|
||||
] as const;
|
||||
|
||||
export const BREAK_BULK_TYPES = ["Machinery", "Ro-Ro", "Others"] as const;
|
||||
|
||||
export const MOCK_VALID_CONTRACTS = [
|
||||
"EDR-2024-10001",
|
||||
"EDR-2024-10002",
|
||||
@@ -43,31 +17,6 @@ export const MOCK_VALID_CONTRACTS = [
|
||||
"EDR-2022-55442",
|
||||
];
|
||||
|
||||
export const CONTAINER_TYPES = [
|
||||
"Dry Container",
|
||||
"High Cubic",
|
||||
"Reefer Container",
|
||||
"Open Top",
|
||||
"Flat Rack",
|
||||
"Tank Container",
|
||||
"Open Side",
|
||||
] as const;
|
||||
|
||||
export const SHIPPING_LINES = [
|
||||
"MSC",
|
||||
"CMA CGM",
|
||||
"Evergreen",
|
||||
"COSCO",
|
||||
"Hapag-Lloyd",
|
||||
"ONE",
|
||||
"Yang Ming",
|
||||
"ZIM",
|
||||
"Messina Line",
|
||||
"Safmarine",
|
||||
"Wan Hai",
|
||||
"Ethiopian Shipping Lines (ESLSE)",
|
||||
] as const;
|
||||
|
||||
export const STEPS = [
|
||||
{ id: 1, label: "Contract Type", short: "Contract" },
|
||||
{ id: 2, label: "Service Type & Mile", short: "Service" },
|
||||
@@ -108,11 +57,8 @@ export const bookingFormSchema = z
|
||||
shippingLine: z.string(),
|
||||
cargoType: z.enum(["container", "bulk"], "Select a cargo type."),
|
||||
cargoWeight: z.string(),
|
||||
freightType: z.enum(["bulk", "break_bulk"]).optional(),
|
||||
bulkCommodity: z.string(),
|
||||
bulkCommodityOther: z.string(),
|
||||
breakBulkType: z.string(),
|
||||
breakBulkTypeOther: z.string(),
|
||||
freightType: z.string(), // parent group
|
||||
bulkCommoditytype: z.string(),
|
||||
isHazardous: z.boolean(),
|
||||
isRefrigerated: z.boolean(),
|
||||
containers: z.array(
|
||||
@@ -171,42 +117,10 @@ export const bookingFormSchema = z
|
||||
(data) =>
|
||||
!(
|
||||
data.cargoType === "bulk" &&
|
||||
data.freightType === "bulk" &&
|
||||
!data.bulkCommodity
|
||||
data.freightType &&
|
||||
!data.bulkCommoditytype
|
||||
),
|
||||
{ message: "Select a commodity.", path: ["bulkCommodity"] },
|
||||
)
|
||||
.refine(
|
||||
(data) =>
|
||||
!(
|
||||
data.cargoType === "bulk" &&
|
||||
data.freightType === "bulk" &&
|
||||
data.bulkCommodity === "Others" &&
|
||||
!data.bulkCommodityOther.trim()
|
||||
),
|
||||
{ message: "Specify the commodity.", path: ["bulkCommodityOther"] },
|
||||
)
|
||||
.refine(
|
||||
(data) =>
|
||||
!(
|
||||
data.cargoType === "bulk" &&
|
||||
data.freightType === "break_bulk" &&
|
||||
!data.breakBulkType
|
||||
),
|
||||
{ message: "Select a break-bulk type.", path: ["breakBulkType"] },
|
||||
)
|
||||
.refine(
|
||||
(data) =>
|
||||
!(
|
||||
data.cargoType === "bulk" &&
|
||||
data.freightType === "break_bulk" &&
|
||||
data.breakBulkType === "Others" &&
|
||||
!data.breakBulkTypeOther.trim()
|
||||
),
|
||||
{
|
||||
message: "Specify the break-bulk type.",
|
||||
path: ["breakBulkTypeOther"],
|
||||
},
|
||||
{ message: "Select a commodity.", path: ["bulkCommoditytype"] },
|
||||
)
|
||||
.refine(
|
||||
(data) => {
|
||||
@@ -268,10 +182,7 @@ export const initialBookingFormValues: DeepPartial<BookingFormValues> = {
|
||||
destinationYard: "",
|
||||
shippingLine: "",
|
||||
cargoWeight: "",
|
||||
bulkCommodity: "",
|
||||
bulkCommodityOther: "",
|
||||
breakBulkType: "",
|
||||
breakBulkTypeOther: "",
|
||||
bulkCommoditytype: "",
|
||||
isHazardous: false,
|
||||
isRefrigerated: false,
|
||||
containers: [{ type: "20ft", containerType: "", qty: "1", vgm: "" }],
|
||||
@@ -300,10 +211,7 @@ export const stepFields: Record<number, Array<Path<BookingFormValues>>> = {
|
||||
"cargoType",
|
||||
"cargoWeight",
|
||||
"freightType",
|
||||
"bulkCommodity",
|
||||
"bulkCommodityOther",
|
||||
"breakBulkType",
|
||||
"breakBulkTypeOther",
|
||||
"bulkCommoditytype",
|
||||
"containers",
|
||||
"consolidationEnabled",
|
||||
],
|
||||
|
||||
@@ -44,8 +44,7 @@ export function Step5CargoDetails({
|
||||
}) {
|
||||
const cargoType = form.watch("cargoType");
|
||||
const freightType = form.watch("freightType");
|
||||
const bulkCommodity = form.watch("bulkCommodity");
|
||||
const breakBulkType = form.watch("breakBulkType");
|
||||
const bulkCommoditytype = form.watch("bulkCommoditytype");
|
||||
const containers = form.watch("containers");
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
@@ -60,13 +59,21 @@ export function Step5CargoDetails({
|
||||
);
|
||||
}, [referenceData]);
|
||||
|
||||
const bulkCommodityOptions = useMemo(() => {
|
||||
const freightTypeGroups = useMemo(() => {
|
||||
if (!referenceData?.cargo_type) return [];
|
||||
return referenceData.cargo_type.flatMap(
|
||||
(group) => group.children?.map((c) => c.name) ?? [],
|
||||
return referenceData.cargo_type.filter(
|
||||
(g) => g.code !== "CONTAINER",
|
||||
);
|
||||
}, [referenceData]);
|
||||
|
||||
const commodityOptions = useMemo(() => {
|
||||
if (!referenceData?.cargo_type || !freightType) return [];
|
||||
const group = referenceData.cargo_type.find(
|
||||
(g) => g.code.toLowerCase() === freightType,
|
||||
);
|
||||
return group?.children?.map((c) => c.name) ?? [];
|
||||
}, [referenceData, freightType]);
|
||||
|
||||
function getOverweightAlert(
|
||||
type: "20ft" | "40ft",
|
||||
vgm: number,
|
||||
@@ -122,7 +129,7 @@ export function Step5CargoDetails({
|
||||
selected={cargoType === "container"}
|
||||
onClick={() => {
|
||||
field.onChange("container");
|
||||
form.setValue("freightType", undefined, {
|
||||
form.setValue("freightType", "", {
|
||||
shouldDirty: true,
|
||||
});
|
||||
}}
|
||||
@@ -194,82 +201,42 @@ export function Step5CargoDetails({
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid}>
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<OptionCard
|
||||
selected={freightType === "bulk"}
|
||||
onClick={() => field.onChange("bulk")}
|
||||
>
|
||||
<p className="font-semibold">Bulk</p>
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
Coffee, fertilizer, grain, ore, etc.
|
||||
</p>
|
||||
</OptionCard>
|
||||
<OptionCard
|
||||
selected={freightType === "break_bulk"}
|
||||
onClick={() => field.onChange("break_bulk")}
|
||||
>
|
||||
<p className="font-semibold">Break-Bulk</p>
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
Machinery, vehicles, project cargo, etc.
|
||||
</p>
|
||||
</OptionCard>
|
||||
{freightTypeGroups.map((group) => {
|
||||
const val = group.code.toLowerCase();
|
||||
return (
|
||||
<OptionCard
|
||||
key={group.code}
|
||||
selected={freightType === val}
|
||||
onClick={() => {
|
||||
field.onChange(val);
|
||||
form.setValue("bulkCommoditytype", "", {
|
||||
shouldDirty: true,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<p className="font-semibold">{group.name}</p>
|
||||
</OptionCard>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<FieldError errors={[fieldState.error]} />
|
||||
</Field>
|
||||
)}
|
||||
/>
|
||||
|
||||
{freightType === "bulk" && (
|
||||
{freightType && commodityOptions.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<Controller
|
||||
name="bulkCommodity"
|
||||
name="bulkCommoditytype"
|
||||
control={form.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<SelectField
|
||||
field={field}
|
||||
error={fieldState.error}
|
||||
label="Commodity *"
|
||||
placeholder="Select commodity *"
|
||||
>
|
||||
{bulkCommodityOptions.map((option) => (
|
||||
<SelectItem key={option} value={option}>
|
||||
{option}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectField>
|
||||
)}
|
||||
/>
|
||||
{bulkCommodity === "Others" && (
|
||||
<Controller
|
||||
name="bulkCommodityOther"
|
||||
control={form.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid}>
|
||||
<Input
|
||||
{...field}
|
||||
aria-invalid={fieldState.invalid}
|
||||
placeholder="Specify commodity *"
|
||||
/>
|
||||
<FieldError errors={[fieldState.error]} />
|
||||
</Field>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{freightType === "break_bulk" && (
|
||||
<div className="space-y-2">
|
||||
<Controller
|
||||
name="breakBulkType"
|
||||
control={form.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<SelectField
|
||||
field={field}
|
||||
error={fieldState.error}
|
||||
label="Break-bulk type *"
|
||||
label="Cargo type *"
|
||||
placeholder="Select type *"
|
||||
>
|
||||
{bulkCommodityOptions.map((option) => (
|
||||
{commodityOptions.map((option) => (
|
||||
<SelectItem key={option} value={option}>
|
||||
{option}
|
||||
</SelectItem>
|
||||
@@ -277,22 +244,6 @@ export function Step5CargoDetails({
|
||||
</SelectField>
|
||||
)}
|
||||
/>
|
||||
{breakBulkType === "Others" && (
|
||||
<Controller
|
||||
name="breakBulkTypeOther"
|
||||
control={form.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid}>
|
||||
<Input
|
||||
{...field}
|
||||
aria-invalid={fieldState.invalid}
|
||||
placeholder="Specify break-bulk type *"
|
||||
/>
|
||||
<FieldError errors={[fieldState.error]} />
|
||||
</Field>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -8,13 +8,16 @@ import type {
|
||||
UpdateFileUploadFieldDto,
|
||||
UpdateFileUploadSettingDto,
|
||||
} from "@/types/fileUploadSettings";
|
||||
import { bookingsService, CreateBookingPayload } from "./bookings.service";
|
||||
import {
|
||||
bookingsService,
|
||||
CreateBookingPayload,
|
||||
GeneratePriceResponse,
|
||||
} from "./bookings.service";
|
||||
import { consignmentsService } from "./consignments.service";
|
||||
import { trackingService } from "./tracking.service";
|
||||
import { fileUploadSettingsService } from "./fileUploadSettings.service";
|
||||
import { dropdownSettingsService } from "./dropdownSettings.service";
|
||||
import { authService } from "./auth.service";
|
||||
import { customersService } from "./customers.service";
|
||||
import { companiesService } from "./companies.service";
|
||||
import {
|
||||
CreateDropdownOptionDto,
|
||||
@@ -24,11 +27,6 @@ import {
|
||||
UpdateDropdownOptionDto,
|
||||
UpdateDropdownSettingDto,
|
||||
} from "@/types/dropdownSettings";
|
||||
import {
|
||||
CreateCustomerDto,
|
||||
Customer,
|
||||
UpdateCustomerDto,
|
||||
} from "@/types/customers";
|
||||
import type {
|
||||
CompanyInfoResponse,
|
||||
CreateCompanyPayload,
|
||||
@@ -144,6 +142,31 @@ export const api = {
|
||||
remove: endpoint<{ id: string }, void>("bookings", "remove", ({ id }) =>
|
||||
bookingsService.remove(id),
|
||||
),
|
||||
|
||||
cancel: endpoint<{ id: string; reason: string }, Freight.IBooking>(
|
||||
"bookings",
|
||||
"cancel",
|
||||
({ id, reason }) => bookingsService.cancel(id, reason),
|
||||
),
|
||||
|
||||
generatePrice: endpoint<{ id: string }, GeneratePriceResponse>(
|
||||
"bookings",
|
||||
"generatePrice",
|
||||
({ id }) => bookingsService.generatePrice(id),
|
||||
),
|
||||
|
||||
submit: endpoint<{ id: string }, Freight.IBooking>(
|
||||
"bookings",
|
||||
"submit",
|
||||
({ id }) => bookingsService.submit(id),
|
||||
),
|
||||
|
||||
uploadDocuments: endpoint<
|
||||
{ id: string; files: Record<string, File | File[] | null> },
|
||||
Freight.IBooking
|
||||
>("bookings", "uploadDocuments", ({ id, files }) =>
|
||||
bookingsService.uploadDocuments(id, files),
|
||||
),
|
||||
},
|
||||
|
||||
consignments: {
|
||||
|
||||
@@ -25,6 +25,21 @@ export interface ContractView {
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface PriceLineItem {
|
||||
code: string;
|
||||
description: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
}
|
||||
|
||||
export interface GeneratePriceResponse {
|
||||
bookingId: string;
|
||||
totalAmount: number;
|
||||
currency: string;
|
||||
lineItems: PriceLineItem[];
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export interface SignContractPayload {
|
||||
role: "CUSTOMER" | "STAFF";
|
||||
signatureImageBase64: string;
|
||||
@@ -53,6 +68,42 @@ export const bookingsService = {
|
||||
await client.delete(`/api/bookings/${id}`);
|
||||
},
|
||||
|
||||
cancel: async (id: string, reason: string): Promise<Freight.IBooking> => {
|
||||
const { data } = await client.post(`/api/bookings/${id}/cancel`, { reason });
|
||||
return data.data;
|
||||
},
|
||||
|
||||
generatePrice: async (id: string): Promise<GeneratePriceResponse> => {
|
||||
const { data } = await client.post(`/api/bookings/${id}/generate-price`);
|
||||
return data.data;
|
||||
},
|
||||
|
||||
submit: async (id: string): Promise<Freight.IBooking> => {
|
||||
const { data } = await client.post(`/api/bookings/${id}/submit`);
|
||||
return data.data;
|
||||
},
|
||||
|
||||
uploadDocuments: async (
|
||||
id: string,
|
||||
files: Record<string, File | File[] | null>,
|
||||
): Promise<Freight.IBooking> => {
|
||||
const formData = new FormData();
|
||||
for (const [key, fileOrFiles] of Object.entries(files)) {
|
||||
if (!fileOrFiles) continue;
|
||||
if (Array.isArray(fileOrFiles)) {
|
||||
for (const f of fileOrFiles) formData.append(key, f);
|
||||
} else {
|
||||
formData.append(key, fileOrFiles);
|
||||
}
|
||||
}
|
||||
const { data } = await client.post(
|
||||
`/api/bookings/${id}/documents`,
|
||||
formData,
|
||||
{ headers: { "Content-Type": "multipart/form-data" } },
|
||||
);
|
||||
return data.data;
|
||||
},
|
||||
|
||||
getContractView: async (id: string): Promise<ContractView> => {
|
||||
const { data } = await client.get(B.CONTRACT_VIEW(id));
|
||||
return data.data ?? data;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import { api } from "../crud";
|
||||
|
||||
import { URL_CONSTANTS } from "../../constants/URLS"
|
||||
Reference in New Issue
Block a user