mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
KM-Based Maintenance Scheduling ✅
This commit is contained in:
@@ -4087,10 +4087,11 @@ export class WarehouseInventoryService {
|
||||
await this.invoices.assertClearanceAllowed(item.id);
|
||||
|
||||
const approvedAt = new Date();
|
||||
const signatureImageUrl = signature?.signatureImageUrl ?? null;
|
||||
const approval = {
|
||||
approvedAt: approvedAt.toISOString(),
|
||||
signerDisplayName: name,
|
||||
signatureImageUrl: signature?.signatureImageUrl ?? null,
|
||||
signatureImageUrl,
|
||||
userId,
|
||||
};
|
||||
const existingNotes = this.stripCustomerDeliveryApproval(item.notes);
|
||||
@@ -4114,7 +4115,7 @@ export class WarehouseInventoryService {
|
||||
|
||||
// Sign the structured handover record(s) for this booking (self-haul: before
|
||||
// the truck leaves). Kept alongside the legacy approval note.
|
||||
await this.handover.signForBooking(bookingId, userId, name);
|
||||
await this.handover.signForBooking(bookingId, userId, name, signatureImageUrl);
|
||||
|
||||
return {
|
||||
bookingId,
|
||||
@@ -4149,6 +4150,8 @@ export class WarehouseInventoryService {
|
||||
throw new BadRequestException('Please enter your full name to sign the handover');
|
||||
}
|
||||
|
||||
const signature = await this.signatures.getForUser(userId).catch(() => null);
|
||||
|
||||
const [h]: Array<{
|
||||
bookingId: string;
|
||||
reference: string;
|
||||
@@ -4181,7 +4184,7 @@ export class WarehouseInventoryService {
|
||||
);
|
||||
if (inv) await this.invoices.assertClearanceAllowed(inv.id);
|
||||
|
||||
const signed = await this.handover.sign(handoverId, userId, name);
|
||||
const signed = await this.handover.sign(handoverId, userId, name, signature?.signatureImageUrl ?? null);
|
||||
const allSigned = await this.handover.isFullySigned(h.bookingId);
|
||||
|
||||
if (inv) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import { isViewable } from "@edr/ui-common";
|
||||
|
||||
import { api } from "@/services/api";
|
||||
import { fetchViewableFile, downloadStoredFile } from "@/services/files.service";
|
||||
import { warehouseService } from "@/services/warehouse.service";
|
||||
import { useFileViewer } from "@/hooks/useFileViewer";
|
||||
import { BookingActionModal } from "@/pages/bookings/clearance/BookingActionModal";
|
||||
import { getBookingNextAction } from "@/pages/bookings/clearance/bookingNextAction";
|
||||
@@ -241,6 +242,11 @@ export function DocumentsTab({ booking }: { booking: Freight.IBooking }) {
|
||||
}),
|
||||
);
|
||||
|
||||
const { data: handovers = [] } = useQuery({
|
||||
queryKey: ["bookingHandovers", booking.id],
|
||||
queryFn: () => warehouseService.bookingHandovers(booking.id),
|
||||
});
|
||||
|
||||
const customerDocs = useMemo(
|
||||
() => (clearance?.documents ?? []).filter((d) => d.uploadedBy === "customer"),
|
||||
[clearance],
|
||||
@@ -486,6 +492,69 @@ export function DocumentsTab({ booking }: { booking: Freight.IBooking }) {
|
||||
</SectionCard>
|
||||
)}
|
||||
|
||||
{/* ── 4. Handover signatures ──────────────────────────────────────── */}
|
||||
{handovers.length > 0 && (
|
||||
<SectionCard>
|
||||
<CardTitle>Handover signatures</CardTitle>
|
||||
<Text fz="12.5px" c="dimmed" mt={4} mb="sm">
|
||||
Records of goods handover and customer signatures.
|
||||
</Text>
|
||||
<Stack gap={0}>
|
||||
{handovers.map((h, i) => (
|
||||
<Box
|
||||
key={h.id}
|
||||
px="md"
|
||||
py="sm"
|
||||
style={{
|
||||
borderBottom:
|
||||
i !== handovers.length - 1
|
||||
? `1px solid ${BORDER}`
|
||||
: "none",
|
||||
}}
|
||||
>
|
||||
<Group justify="space-between" align="flex-start">
|
||||
<Box>
|
||||
<Text fw={500} fz="14px">
|
||||
{h.reference}
|
||||
</Text>
|
||||
<Text fz="12.5px" c="dimmed" mt={2}>
|
||||
{h.mileType === "SELF_HAUL"
|
||||
? "Customer truck delivery"
|
||||
: `EDR delivery${h.truckPlate ? ` (${h.truckPlate})` : ""}`}
|
||||
</Text>
|
||||
{h.signedAt && (
|
||||
<Text fz="12.5px" c="dimmed" mt={1}>
|
||||
Signed by {h.signerName || "Unknown"} on{" "}
|
||||
{new Date(h.signedAt).toLocaleDateString()}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
<Pill
|
||||
tone={h.signedAt ? "green" : "blue"}
|
||||
label={h.signedAt ? "Signed" : "Pending signature"}
|
||||
/>
|
||||
</Group>
|
||||
{h.signedAt && h.signatureImageUrl && (
|
||||
<Box mt="sm">
|
||||
<img
|
||||
src={h.signatureImageUrl}
|
||||
alt="Signature"
|
||||
style={{
|
||||
maxHeight: "60px",
|
||||
maxWidth: "200px",
|
||||
border: `1px solid ${BORDER}`,
|
||||
borderRadius: "4px",
|
||||
padding: "4px",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
</Stack>
|
||||
</SectionCard>
|
||||
)}
|
||||
|
||||
{/* ── Warehouse documents (one-click bundle) ──────────────────────── */}
|
||||
<SectionCard>
|
||||
<CardTitle>Warehouse documents</CardTitle>
|
||||
|
||||
@@ -41,6 +41,22 @@ export interface BookingScheduleView {
|
||||
} | null;
|
||||
}
|
||||
|
||||
export interface BookingHandover {
|
||||
id: string;
|
||||
bookingId: string;
|
||||
truckAssignmentId?: string | null;
|
||||
edrAssignmentId?: string | null;
|
||||
truckPlate?: string | null;
|
||||
mileType: 'SELF_HAUL' | 'EDR_LAST_MILE';
|
||||
reference: string;
|
||||
generatedAt: string;
|
||||
signedAt?: string | null;
|
||||
signerName?: string | null;
|
||||
signedByUserId?: string | null;
|
||||
signatureImageUrl?: string | null;
|
||||
deliveredAt?: string | null;
|
||||
}
|
||||
|
||||
export const warehouseService = {
|
||||
listInventory: async (filter?: InventoryFilter): Promise<WarehouseInventoryItem[]> => {
|
||||
const { data } = await client.get("/warehouse-inventory", {
|
||||
@@ -59,4 +75,9 @@ export const warehouseService = {
|
||||
const { data } = await client.get(`/warehouse-inventory/booking-schedule/${bookingId}`);
|
||||
return data?.data ?? data ?? { schedule: null, wagon: null };
|
||||
},
|
||||
|
||||
bookingHandovers: async (bookingId: string): Promise<BookingHandover[]> => {
|
||||
const { data } = await client.get(`/warehouse-inventory/bookings/${bookingId}/handovers`);
|
||||
return data?.data ?? data ?? [];
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user