fix(collection-pool): align SupplierGroup with spec (a11y, chevron, assign)
This commit is contained in:
parent
c17f7c662e
commit
8e4289b104
1 changed files with 45 additions and 50 deletions
|
|
@ -3,6 +3,7 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
AlertTriangle,
|
||||
ChevronDown,
|
||||
Loader2,
|
||||
Search,
|
||||
Truck,
|
||||
|
|
@ -23,12 +24,12 @@ import type { OrderItem } from "@/lib/types";
|
|||
import { cn } from "@/lib/utils";
|
||||
|
||||
import {
|
||||
getAssignmentBadge,
|
||||
buildProximityMarkerMap,
|
||||
buildSupplierGroups,
|
||||
buildVisibleRows,
|
||||
type CollectionPoolGroup,
|
||||
type CollectionPoolSortMode,
|
||||
type CollectionPoolViewRow,
|
||||
getAssignmentBadge,
|
||||
getAssignmentState,
|
||||
getPoolAgeLabel,
|
||||
} from "./view-model";
|
||||
|
|
@ -734,6 +735,11 @@ function SupplierGroup({
|
|||
onOpenBulkAssign,
|
||||
onToggleReady,
|
||||
}: {
|
||||
// `group.rows` is typed as `CollectionPoolViewRow[]` in the helper, but
|
||||
// `PoolRow` (this page's existing row component) expects the richer
|
||||
// `CollectionPoolVisibleRow` (which extends `OrderItem`). The widening is
|
||||
// sound because `buildSupplierGroups` is fed `CollectionPoolVisibleRow[]`
|
||||
// — see Task 7 wiring.
|
||||
group: Omit<CollectionPoolGroup, "rows"> & { rows: CollectionPoolVisibleRow[] };
|
||||
busy: boolean;
|
||||
todayYmd: string;
|
||||
|
|
@ -763,43 +769,39 @@ function SupplierGroup({
|
|||
})();
|
||||
|
||||
const assignLabel = allSelected
|
||||
? `Assign all ${rowIds.length} ${rowIds.length === 1 ? "stop" : "stops"}`
|
||||
? `Assign ${rowIds.length} stops`
|
||||
: selectedCount > 0
|
||||
? `Assign ${selectedCount} selected`
|
||||
: `Assign all ${rowIds.length} ${rowIds.length === 1 ? "stop" : "stops"}`;
|
||||
: `Assign all ${rowIds.length}`;
|
||||
|
||||
const onAssignClick = () => {
|
||||
const ids = allSelected || selectedCount === 0 ? rowIds : rowIds.filter((id) => selectedIds.has(id));
|
||||
onOpenBulkAssign(group.supplierId, group.supplierName, ids);
|
||||
};
|
||||
|
||||
return (
|
||||
<section
|
||||
aria-labelledby={`supplier-${group.supplierId}`}
|
||||
data-od-id={`collection-pool-supplier-${group.supplierId}`}
|
||||
className="border-b last:border-b-0"
|
||||
>
|
||||
<header
|
||||
className={cn(
|
||||
"sticky top-0 z-10 flex flex-wrap items-center gap-3 border-b bg-muted/30 px-4 py-2.5",
|
||||
"lg:flex-nowrap",
|
||||
)}
|
||||
>
|
||||
<header className="sticky top-0 z-10 flex flex-wrap items-center gap-3 border-b bg-muted/30 px-4 py-2.5">
|
||||
<span
|
||||
className={cn("size-2.5 shrink-0 rounded-full", markerTokenToClassName(group.markerToken))}
|
||||
aria-hidden
|
||||
className={cn("size-2 shrink-0 rounded-full", markerTokenToClassName(group.markerToken))}
|
||||
/>
|
||||
<div className="flex min-w-0 flex-1 flex-wrap items-baseline gap-x-2 gap-y-0.5">
|
||||
<p className="truncate text-sm font-semibold text-foreground">
|
||||
{group.supplierName || "Unknown supplier"}
|
||||
</p>
|
||||
<span className="text-xs text-muted-foreground">·</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{rowIds.length} {rowIds.length === 1 ? "stop" : "stops"}
|
||||
</span>
|
||||
{eyebrowText && (
|
||||
<>
|
||||
<span className="text-xs text-muted-foreground">·</span>
|
||||
<span className="truncate text-xs text-muted-foreground">{eyebrowText}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{rowIds.length > 0 && (
|
||||
<h3
|
||||
id={`supplier-${group.supplierId}`}
|
||||
className="text-sm font-semibold text-foreground"
|
||||
>
|
||||
{group.supplierName || "Unknown supplier"}
|
||||
</h3>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
· {group.rows.length} {group.rows.length === 1 ? "stop" : "stops"}
|
||||
{eyebrowText ? <> · {eyebrowText}</> : null}
|
||||
</span>
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
<label className="inline-flex cursor-pointer items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={allSelected}
|
||||
|
|
@ -808,46 +810,39 @@ function SupplierGroup({
|
|||
}}
|
||||
onChange={() => onToggleSectionSelected(rowIds)}
|
||||
disabled={busy}
|
||||
aria-label={`Select all stops at ${group.supplierName || "unknown supplier"}`}
|
||||
className="size-4 shrink-0 cursor-pointer accent-primary disabled:cursor-not-allowed"
|
||||
className="size-4 cursor-pointer accent-primary disabled:cursor-not-allowed"
|
||||
aria-label={`Select all ${group.rows.length} stops at ${group.supplierName || "unknown supplier"}`}
|
||||
/>
|
||||
)}
|
||||
Select all
|
||||
</label>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="default"
|
||||
onClick={() => onOpenBulkAssign(group.supplierId, group.supplierName, rowIds)}
|
||||
disabled={busy || rowIds.length === 0 || allAssigned}
|
||||
title={allAssigned ? "All stops already assigned" : undefined}
|
||||
onClick={onAssignClick}
|
||||
disabled={busy || allAssigned}
|
||||
title={allAssigned ? "Every stop at this supplier is already assigned" : undefined}
|
||||
>
|
||||
<Truck className="mr-1.5 size-3.5" />
|
||||
{assignLabel}
|
||||
</Button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onToggleCollapsed(group.supplierId)}
|
||||
aria-label={collapsed ? `Expand ${group.supplierName || "supplier"} section` : `Collapse ${group.supplierName || "supplier"} section`}
|
||||
aria-expanded={!collapsed}
|
||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md border bg-background text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
||||
aria-controls={`supplier-${group.supplierId}-body`}
|
||||
className="inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
||||
>
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn(
|
||||
"inline-block text-sm leading-none transition-transform duration-150 ease-out",
|
||||
collapsed && "-rotate-90",
|
||||
)}
|
||||
>
|
||||
▼
|
||||
</span>
|
||||
<ChevronDown className={cn("size-4 transition-transform", collapsed && "-rotate-90")} />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<div
|
||||
id={`supplier-${group.supplierId}-body`}
|
||||
className={cn(
|
||||
"grid overflow-hidden transition-[max-height] duration-150 ease-out",
|
||||
collapsed ? "max-h-0" : "max-h-[2000px]",
|
||||
"grid overflow-hidden transition-[grid-template-rows] duration-150 ease-out",
|
||||
collapsed ? "grid-rows-[0fr]" : "grid-rows-[1fr]",
|
||||
)}
|
||||
aria-hidden={collapsed}
|
||||
>
|
||||
<ul className="divide-y">
|
||||
<ul className="divide-y min-h-0">
|
||||
{group.rows.map((row) => (
|
||||
<PoolRow
|
||||
key={row.id}
|
||||
|
|
|
|||
Loading…
Reference in a new issue