mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 01:55:41 +00:00
add checkbox
This commit is contained in:
@@ -385,6 +385,7 @@ const FirstMilePage = () => {
|
|||||||
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({});
|
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
const [assignOpen, setAssignOpen] = useState(false);
|
const [assignOpen, setAssignOpen] = useState(false);
|
||||||
|
const [bulkMode, setBulkMode] = useState(false);
|
||||||
const [detailOpen, setDetailOpen] = useState(false);
|
const [detailOpen, setDetailOpen] = useState(false);
|
||||||
const [tripSlipOpen, setTripSlipOpen] = useState(false);
|
const [tripSlipOpen, setTripSlipOpen] = useState(false);
|
||||||
const [tripSlipJob, setTripSlipJob] = useState<FirstMileJob | null>(null);
|
const [tripSlipJob, setTripSlipJob] = useState<FirstMileJob | null>(null);
|
||||||
@@ -396,6 +397,11 @@ const FirstMilePage = () => {
|
|||||||
[jobs, activeJobId],
|
[jobs, activeJobId],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const selectedIds = useMemo(
|
||||||
|
() => Object.keys(rowSelection).filter((id) => rowSelection[id]),
|
||||||
|
[rowSelection],
|
||||||
|
);
|
||||||
|
|
||||||
const matchesStatusFilter = (job: FirstMileJob) => {
|
const matchesStatusFilter = (job: FirstMileJob) => {
|
||||||
switch (statusFilter) {
|
switch (statusFilter) {
|
||||||
case "ALL":
|
case "ALL":
|
||||||
@@ -445,11 +451,19 @@ const FirstMilePage = () => {
|
|||||||
|
|
||||||
const openAssign = (jobId: string | null) => {
|
const openAssign = (jobId: string | null) => {
|
||||||
const resolved = jobId ?? filteredJobs.find((job) => job.status === "UNASSIGNED")?.id ?? null;
|
const resolved = jobId ?? filteredJobs.find((job) => job.status === "UNASSIGNED")?.id ?? null;
|
||||||
|
setBulkMode(false);
|
||||||
setActiveJobId(resolved);
|
setActiveJobId(resolved);
|
||||||
setVehicleValue(null);
|
setVehicleValue(null);
|
||||||
setAssignOpen(true);
|
setAssignOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openBulkAssign = () => {
|
||||||
|
setBulkMode(true);
|
||||||
|
setActiveJobId(null);
|
||||||
|
setVehicleValue(null);
|
||||||
|
setAssignOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
const openDetail = (jobId: string) => {
|
const openDetail = (jobId: string) => {
|
||||||
setActiveJobId(jobId);
|
setActiveJobId(jobId);
|
||||||
setDetailOpen(true);
|
setDetailOpen(true);
|
||||||
@@ -457,6 +471,7 @@ const FirstMilePage = () => {
|
|||||||
|
|
||||||
const closeAssign = () => {
|
const closeAssign = () => {
|
||||||
setAssignOpen(false);
|
setAssignOpen(false);
|
||||||
|
setBulkMode(false);
|
||||||
setActiveJobId(null);
|
setActiveJobId(null);
|
||||||
setVehicleValue(null);
|
setVehicleValue(null);
|
||||||
};
|
};
|
||||||
@@ -467,8 +482,7 @@ const FirstMilePage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleAssign = () => {
|
const handleAssign = () => {
|
||||||
const jobId = activeJobId ?? filteredJobs.find((job) => job.status === "UNASSIGNED")?.id;
|
if (!vehicleValue) {
|
||||||
if (!jobId || !vehicleValue) {
|
|
||||||
toast({
|
toast({
|
||||||
title: "Select a vehicle",
|
title: "Select a vehicle",
|
||||||
description: "Choose a vehicle to assign to this pickup.",
|
description: "Choose a vehicle to assign to this pickup.",
|
||||||
@@ -480,15 +494,30 @@ const FirstMilePage = () => {
|
|||||||
const vehicleLabel =
|
const vehicleLabel =
|
||||||
VEHICLE_OPTIONS.find((option) => option.value === vehicleValue)?.label ?? vehicleValue;
|
VEHICLE_OPTIONS.find((option) => option.value === vehicleValue)?.label ?? vehicleValue;
|
||||||
|
|
||||||
|
const targetIds = bulkMode
|
||||||
|
? selectedIds
|
||||||
|
: [activeJobId ?? filteredJobs.find((job) => job.status === "UNASSIGNED")?.id].filter(
|
||||||
|
(id): id is string => Boolean(id),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!targetIds.length) return;
|
||||||
|
|
||||||
|
const targetSet = new Set(targetIds);
|
||||||
setJobs((current) =>
|
setJobs((current) =>
|
||||||
current.map((job) =>
|
current.map((job) =>
|
||||||
job.id === jobId
|
targetSet.has(job.id)
|
||||||
? { ...job, status: "ASSIGNED", assignedVehicle: vehicleLabel }
|
? { ...job, status: "ASSIGNED", assignedVehicle: vehicleLabel }
|
||||||
: job,
|
: job,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
toast({ title: "Vehicle assigned", description: vehicleLabel });
|
toast({
|
||||||
|
title: "Vehicle assigned",
|
||||||
|
description: bulkMode
|
||||||
|
? `${targetIds.length} pickups → ${vehicleLabel}`
|
||||||
|
: vehicleLabel,
|
||||||
|
});
|
||||||
|
if (bulkMode) setRowSelection({});
|
||||||
closeAssign();
|
closeAssign();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -708,12 +737,23 @@ const FirstMilePage = () => {
|
|||||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||||
w={260}
|
w={260}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Group gap="sm">
|
||||||
leftSection={<Truck size={16} />}
|
{selectedIds.length > 0 && (
|
||||||
onClick={() => openAssign(null)}
|
<Button
|
||||||
>
|
variant="light"
|
||||||
Assign Mile
|
leftSection={<Truck size={16} />}
|
||||||
</Button>
|
onClick={openBulkAssign}
|
||||||
|
>
|
||||||
|
Assign vehicle ({selectedIds.length})
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
leftSection={<Truck size={16} />}
|
||||||
|
onClick={() => openAssign(null)}
|
||||||
|
>
|
||||||
|
Assign Mile
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
<Group gap="xs" wrap="wrap">
|
<Group gap="xs" wrap="wrap">
|
||||||
{FILTER_OPTIONS.map((option) => {
|
{FILTER_OPTIONS.map((option) => {
|
||||||
@@ -777,7 +817,15 @@ const FirstMilePage = () => {
|
|||||||
centered
|
centered
|
||||||
>
|
>
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
{activeJob ? (
|
{bulkMode ? (
|
||||||
|
<Text size="sm">
|
||||||
|
Assigning a vehicle to{" "}
|
||||||
|
<Text span fw={600}>
|
||||||
|
{selectedIds.length}
|
||||||
|
</Text>{" "}
|
||||||
|
selected {selectedIds.length === 1 ? "pickup" : "pickups"}.
|
||||||
|
</Text>
|
||||||
|
) : activeJob ? (
|
||||||
<BookingInfo job={activeJob} />
|
<BookingInfo job={activeJob} />
|
||||||
) : (
|
) : (
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
@@ -797,8 +845,11 @@ const FirstMilePage = () => {
|
|||||||
<Button variant="default" onClick={closeAssign}>
|
<Button variant="default" onClick={closeAssign}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleAssign} disabled={!activeJob}>
|
<Button
|
||||||
{activeJob?.status === "ASSIGNED" ? "Reassign" : "Assign"}
|
onClick={handleAssign}
|
||||||
|
disabled={bulkMode ? selectedIds.length === 0 : !activeJob}
|
||||||
|
>
|
||||||
|
{!bulkMode && activeJob?.status === "ASSIGNED" ? "Reassign" : "Assign"}
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -385,6 +385,7 @@ const LastMilePage = () => {
|
|||||||
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({});
|
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
const [assignOpen, setAssignOpen] = useState(false);
|
const [assignOpen, setAssignOpen] = useState(false);
|
||||||
|
const [bulkMode, setBulkMode] = useState(false);
|
||||||
const [detailOpen, setDetailOpen] = useState(false);
|
const [detailOpen, setDetailOpen] = useState(false);
|
||||||
const [tripSlipOpen, setTripSlipOpen] = useState(false);
|
const [tripSlipOpen, setTripSlipOpen] = useState(false);
|
||||||
const [tripSlipJob, setTripSlipJob] = useState<LastMileJob | null>(null);
|
const [tripSlipJob, setTripSlipJob] = useState<LastMileJob | null>(null);
|
||||||
@@ -396,6 +397,11 @@ const LastMilePage = () => {
|
|||||||
[jobs, activeJobId],
|
[jobs, activeJobId],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const selectedIds = useMemo(
|
||||||
|
() => Object.keys(rowSelection).filter((id) => rowSelection[id]),
|
||||||
|
[rowSelection],
|
||||||
|
);
|
||||||
|
|
||||||
const matchesStatusFilter = (job: LastMileJob) => {
|
const matchesStatusFilter = (job: LastMileJob) => {
|
||||||
switch (statusFilter) {
|
switch (statusFilter) {
|
||||||
case "ALL":
|
case "ALL":
|
||||||
@@ -445,11 +451,19 @@ const LastMilePage = () => {
|
|||||||
|
|
||||||
const openAssign = (jobId: string | null) => {
|
const openAssign = (jobId: string | null) => {
|
||||||
const resolved = jobId ?? filteredJobs.find((job) => job.status === "UNASSIGNED")?.id ?? null;
|
const resolved = jobId ?? filteredJobs.find((job) => job.status === "UNASSIGNED")?.id ?? null;
|
||||||
|
setBulkMode(false);
|
||||||
setActiveJobId(resolved);
|
setActiveJobId(resolved);
|
||||||
setVehicleValue(null);
|
setVehicleValue(null);
|
||||||
setAssignOpen(true);
|
setAssignOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openBulkAssign = () => {
|
||||||
|
setBulkMode(true);
|
||||||
|
setActiveJobId(null);
|
||||||
|
setVehicleValue(null);
|
||||||
|
setAssignOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
const openDetail = (jobId: string) => {
|
const openDetail = (jobId: string) => {
|
||||||
setActiveJobId(jobId);
|
setActiveJobId(jobId);
|
||||||
setDetailOpen(true);
|
setDetailOpen(true);
|
||||||
@@ -457,6 +471,7 @@ const LastMilePage = () => {
|
|||||||
|
|
||||||
const closeAssign = () => {
|
const closeAssign = () => {
|
||||||
setAssignOpen(false);
|
setAssignOpen(false);
|
||||||
|
setBulkMode(false);
|
||||||
setActiveJobId(null);
|
setActiveJobId(null);
|
||||||
setVehicleValue(null);
|
setVehicleValue(null);
|
||||||
};
|
};
|
||||||
@@ -467,8 +482,7 @@ const LastMilePage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleAssign = () => {
|
const handleAssign = () => {
|
||||||
const jobId = activeJobId ?? filteredJobs.find((job) => job.status === "UNASSIGNED")?.id;
|
if (!vehicleValue) {
|
||||||
if (!jobId || !vehicleValue) {
|
|
||||||
toast({
|
toast({
|
||||||
title: "Select a vehicle",
|
title: "Select a vehicle",
|
||||||
description: "Choose a vehicle to assign to this delivery.",
|
description: "Choose a vehicle to assign to this delivery.",
|
||||||
@@ -480,15 +494,30 @@ const LastMilePage = () => {
|
|||||||
const vehicleLabel =
|
const vehicleLabel =
|
||||||
VEHICLE_OPTIONS.find((option) => option.value === vehicleValue)?.label ?? vehicleValue;
|
VEHICLE_OPTIONS.find((option) => option.value === vehicleValue)?.label ?? vehicleValue;
|
||||||
|
|
||||||
|
const targetIds = bulkMode
|
||||||
|
? selectedIds
|
||||||
|
: [activeJobId ?? filteredJobs.find((job) => job.status === "UNASSIGNED")?.id].filter(
|
||||||
|
(id): id is string => Boolean(id),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!targetIds.length) return;
|
||||||
|
|
||||||
|
const targetSet = new Set(targetIds);
|
||||||
setJobs((current) =>
|
setJobs((current) =>
|
||||||
current.map((job) =>
|
current.map((job) =>
|
||||||
job.id === jobId
|
targetSet.has(job.id)
|
||||||
? { ...job, status: "ASSIGNED", assignedVehicle: vehicleLabel }
|
? { ...job, status: "ASSIGNED", assignedVehicle: vehicleLabel }
|
||||||
: job,
|
: job,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
toast({ title: "Vehicle assigned", description: vehicleLabel });
|
toast({
|
||||||
|
title: "Vehicle assigned",
|
||||||
|
description: bulkMode
|
||||||
|
? `${targetIds.length} deliveries → ${vehicleLabel}`
|
||||||
|
: vehicleLabel,
|
||||||
|
});
|
||||||
|
if (bulkMode) setRowSelection({});
|
||||||
closeAssign();
|
closeAssign();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -708,12 +737,23 @@ const LastMilePage = () => {
|
|||||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||||
w={260}
|
w={260}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Group gap="sm">
|
||||||
leftSection={<Truck size={16} />}
|
{selectedIds.length > 0 && (
|
||||||
onClick={() => openAssign(null)}
|
<Button
|
||||||
>
|
variant="light"
|
||||||
Assign Mile
|
leftSection={<Truck size={16} />}
|
||||||
</Button>
|
onClick={openBulkAssign}
|
||||||
|
>
|
||||||
|
Assign vehicle ({selectedIds.length})
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
leftSection={<Truck size={16} />}
|
||||||
|
onClick={() => openAssign(null)}
|
||||||
|
>
|
||||||
|
Assign Mile
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
<Group gap="xs" wrap="wrap">
|
<Group gap="xs" wrap="wrap">
|
||||||
{FILTER_OPTIONS.map((option) => {
|
{FILTER_OPTIONS.map((option) => {
|
||||||
@@ -777,7 +817,15 @@ const LastMilePage = () => {
|
|||||||
centered
|
centered
|
||||||
>
|
>
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
{activeJob ? (
|
{bulkMode ? (
|
||||||
|
<Text size="sm">
|
||||||
|
Assigning a vehicle to{" "}
|
||||||
|
<Text span fw={600}>
|
||||||
|
{selectedIds.length}
|
||||||
|
</Text>{" "}
|
||||||
|
selected {selectedIds.length === 1 ? "delivery" : "deliveries"}.
|
||||||
|
</Text>
|
||||||
|
) : activeJob ? (
|
||||||
<BookingInfo job={activeJob} />
|
<BookingInfo job={activeJob} />
|
||||||
) : (
|
) : (
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
@@ -797,8 +845,11 @@ const LastMilePage = () => {
|
|||||||
<Button variant="default" onClick={closeAssign}>
|
<Button variant="default" onClick={closeAssign}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleAssign} disabled={!activeJob}>
|
<Button
|
||||||
{activeJob?.status === "ASSIGNED" ? "Reassign" : "Assign"}
|
onClick={handleAssign}
|
||||||
|
disabled={bulkMode ? selectedIds.length === 0 : !activeJob}
|
||||||
|
>
|
||||||
|
{!bulkMode && activeJob?.status === "ASSIGNED" ? "Reassign" : "Assign"}
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
Reference in New Issue
Block a user