add checkbox

This commit is contained in:
natib21
2026-06-20 14:25:31 +00:00
parent db5e1568df
commit a3f9eabfdd
2 changed files with 128 additions and 26 deletions

View File

@@ -385,6 +385,7 @@ const FirstMilePage = () => {
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({});
const [assignOpen, setAssignOpen] = useState(false);
const [bulkMode, setBulkMode] = useState(false);
const [detailOpen, setDetailOpen] = useState(false);
const [tripSlipOpen, setTripSlipOpen] = useState(false);
const [tripSlipJob, setTripSlipJob] = useState<FirstMileJob | null>(null);
@@ -396,6 +397,11 @@ const FirstMilePage = () => {
[jobs, activeJobId],
);
const selectedIds = useMemo(
() => Object.keys(rowSelection).filter((id) => rowSelection[id]),
[rowSelection],
);
const matchesStatusFilter = (job: FirstMileJob) => {
switch (statusFilter) {
case "ALL":
@@ -445,11 +451,19 @@ const FirstMilePage = () => {
const openAssign = (jobId: string | null) => {
const resolved = jobId ?? filteredJobs.find((job) => job.status === "UNASSIGNED")?.id ?? null;
setBulkMode(false);
setActiveJobId(resolved);
setVehicleValue(null);
setAssignOpen(true);
};
const openBulkAssign = () => {
setBulkMode(true);
setActiveJobId(null);
setVehicleValue(null);
setAssignOpen(true);
};
const openDetail = (jobId: string) => {
setActiveJobId(jobId);
setDetailOpen(true);
@@ -457,6 +471,7 @@ const FirstMilePage = () => {
const closeAssign = () => {
setAssignOpen(false);
setBulkMode(false);
setActiveJobId(null);
setVehicleValue(null);
};
@@ -467,8 +482,7 @@ const FirstMilePage = () => {
};
const handleAssign = () => {
const jobId = activeJobId ?? filteredJobs.find((job) => job.status === "UNASSIGNED")?.id;
if (!jobId || !vehicleValue) {
if (!vehicleValue) {
toast({
title: "Select a vehicle",
description: "Choose a vehicle to assign to this pickup.",
@@ -480,15 +494,30 @@ const FirstMilePage = () => {
const vehicleLabel =
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) =>
current.map((job) =>
job.id === jobId
targetSet.has(job.id)
? { ...job, status: "ASSIGNED", assignedVehicle: vehicleLabel }
: job,
),
);
toast({ title: "Vehicle assigned", description: vehicleLabel });
toast({
title: "Vehicle assigned",
description: bulkMode
? `${targetIds.length} pickups → ${vehicleLabel}`
: vehicleLabel,
});
if (bulkMode) setRowSelection({});
closeAssign();
};
@@ -708,12 +737,23 @@ const FirstMilePage = () => {
onChange={(e) => setSearch(e.currentTarget.value)}
w={260}
/>
<Button
leftSection={<Truck size={16} />}
onClick={() => openAssign(null)}
>
Assign Mile
</Button>
<Group gap="sm">
{selectedIds.length > 0 && (
<Button
variant="light"
leftSection={<Truck size={16} />}
onClick={openBulkAssign}
>
Assign vehicle ({selectedIds.length})
</Button>
)}
<Button
leftSection={<Truck size={16} />}
onClick={() => openAssign(null)}
>
Assign Mile
</Button>
</Group>
</Group>
<Group gap="xs" wrap="wrap">
{FILTER_OPTIONS.map((option) => {
@@ -777,7 +817,15 @@ const FirstMilePage = () => {
centered
>
<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} />
) : (
<Text size="sm" c="dimmed">
@@ -797,8 +845,11 @@ const FirstMilePage = () => {
<Button variant="default" onClick={closeAssign}>
Cancel
</Button>
<Button onClick={handleAssign} disabled={!activeJob}>
{activeJob?.status === "ASSIGNED" ? "Reassign" : "Assign"}
<Button
onClick={handleAssign}
disabled={bulkMode ? selectedIds.length === 0 : !activeJob}
>
{!bulkMode && activeJob?.status === "ASSIGNED" ? "Reassign" : "Assign"}
</Button>
</Group>
</Stack>

View File

@@ -385,6 +385,7 @@ const LastMilePage = () => {
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({});
const [assignOpen, setAssignOpen] = useState(false);
const [bulkMode, setBulkMode] = useState(false);
const [detailOpen, setDetailOpen] = useState(false);
const [tripSlipOpen, setTripSlipOpen] = useState(false);
const [tripSlipJob, setTripSlipJob] = useState<LastMileJob | null>(null);
@@ -396,6 +397,11 @@ const LastMilePage = () => {
[jobs, activeJobId],
);
const selectedIds = useMemo(
() => Object.keys(rowSelection).filter((id) => rowSelection[id]),
[rowSelection],
);
const matchesStatusFilter = (job: LastMileJob) => {
switch (statusFilter) {
case "ALL":
@@ -445,11 +451,19 @@ const LastMilePage = () => {
const openAssign = (jobId: string | null) => {
const resolved = jobId ?? filteredJobs.find((job) => job.status === "UNASSIGNED")?.id ?? null;
setBulkMode(false);
setActiveJobId(resolved);
setVehicleValue(null);
setAssignOpen(true);
};
const openBulkAssign = () => {
setBulkMode(true);
setActiveJobId(null);
setVehicleValue(null);
setAssignOpen(true);
};
const openDetail = (jobId: string) => {
setActiveJobId(jobId);
setDetailOpen(true);
@@ -457,6 +471,7 @@ const LastMilePage = () => {
const closeAssign = () => {
setAssignOpen(false);
setBulkMode(false);
setActiveJobId(null);
setVehicleValue(null);
};
@@ -467,8 +482,7 @@ const LastMilePage = () => {
};
const handleAssign = () => {
const jobId = activeJobId ?? filteredJobs.find((job) => job.status === "UNASSIGNED")?.id;
if (!jobId || !vehicleValue) {
if (!vehicleValue) {
toast({
title: "Select a vehicle",
description: "Choose a vehicle to assign to this delivery.",
@@ -480,15 +494,30 @@ const LastMilePage = () => {
const vehicleLabel =
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) =>
current.map((job) =>
job.id === jobId
targetSet.has(job.id)
? { ...job, status: "ASSIGNED", assignedVehicle: vehicleLabel }
: job,
),
);
toast({ title: "Vehicle assigned", description: vehicleLabel });
toast({
title: "Vehicle assigned",
description: bulkMode
? `${targetIds.length} deliveries → ${vehicleLabel}`
: vehicleLabel,
});
if (bulkMode) setRowSelection({});
closeAssign();
};
@@ -708,12 +737,23 @@ const LastMilePage = () => {
onChange={(e) => setSearch(e.currentTarget.value)}
w={260}
/>
<Button
leftSection={<Truck size={16} />}
onClick={() => openAssign(null)}
>
Assign Mile
</Button>
<Group gap="sm">
{selectedIds.length > 0 && (
<Button
variant="light"
leftSection={<Truck size={16} />}
onClick={openBulkAssign}
>
Assign vehicle ({selectedIds.length})
</Button>
)}
<Button
leftSection={<Truck size={16} />}
onClick={() => openAssign(null)}
>
Assign Mile
</Button>
</Group>
</Group>
<Group gap="xs" wrap="wrap">
{FILTER_OPTIONS.map((option) => {
@@ -777,7 +817,15 @@ const LastMilePage = () => {
centered
>
<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} />
) : (
<Text size="sm" c="dimmed">
@@ -797,8 +845,11 @@ const LastMilePage = () => {
<Button variant="default" onClick={closeAssign}>
Cancel
</Button>
<Button onClick={handleAssign} disabled={!activeJob}>
{activeJob?.status === "ASSIGNED" ? "Reassign" : "Assign"}
<Button
onClick={handleAssign}
disabled={bulkMode ? selectedIds.length === 0 : !activeJob}
>
{!bulkMode && activeJob?.status === "ASSIGNED" ? "Reassign" : "Assign"}
</Button>
</Group>
</Stack>