From 63a59d42bb362bbe82a1b189b25152390b6dbb95 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 6 Aug 2026 07:01:15 +0000 Subject: [PATCH] feat(logistics): sort every table column --- .../logistics/LogisticsReadinessBoard.jsx | 112 ++++++++++++------ 1 file changed, 73 insertions(+), 39 deletions(-) diff --git a/src/components/logistics/LogisticsReadinessBoard.jsx b/src/components/logistics/LogisticsReadinessBoard.jsx index 566a05f..f61d468 100644 --- a/src/components/logistics/LogisticsReadinessBoard.jsx +++ b/src/components/logistics/LogisticsReadinessBoard.jsx @@ -55,7 +55,7 @@ const DEFAULT_FUNNEL_ORDER = [ const STORAGE_KEY = "logistics-section-order"; const COLLAPSED_KEY = "logistics-section-collapsed"; const PAGE_KEY = "logistics-board-page"; -const SORT_KEY = "logistics-board-created-sort"; +const SORT_KEY = "logistics-board-sort"; const PAGE_SIZE = 20; // Load custom order from localStorage, merge with defaults @@ -102,25 +102,35 @@ const saveCollapsedSections = (collapsedSet) => { const COLS = "grid-cols-[minmax(130px,2fr)_minmax(90px,1fr)_minmax(100px,0.8fr)_minmax(100px,1fr)_minmax(90px,1fr)_minmax(100px,1fr)_minmax(90px,0.8fr)_minmax(120px,1fr)]"; const MIN_W = "min-w-[1160px]"; -const TableHeader = ({ createdSort, onToggleCreatedSort }) => ( -
-
Клиент
-
Город
-
Тип
-
Дата доставки
-
Водитель
-
Статус
-
Обновлён
- +const TABLE_COLUMNS = [ + { key: "customer", label: "Клиент" }, + { key: "city", label: "Город" }, + { key: "deliveryType", label: "Тип" }, + { key: "deliveryDate", label: "Дата доставки" }, + { key: "driver", label: "Водитель" }, + { key: "status", label: "Статус" }, + { key: "updatedAt", label: "Обновлён" }, + { key: "createdAt", label: "Добавлен в базу" }, +]; + +const TableHeader = ({ sort, onSort }) => ( +
+ {TABLE_COLUMNS.map((column) => { + const active = sort.key === column.key; + return ( + + ); + })}
); @@ -173,7 +183,7 @@ const renderRow = (g, onSelectSet) => {
{g.city || "—"}
- { ); }; -const SortableSection = ({ statusValue, label, groups, isCollapsed, onToggle, onSelectSet, createdSort, onToggleCreatedSort }) => { +const SortableSection = ({ statusValue, label, groups, isCollapsed, onToggle, onSelectSet, sort, onSort }) => { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: statusValue }); const style = { @@ -236,7 +246,7 @@ const SortableSection = ({ statusValue, label, groups, isCollapsed, onToggle, on {!isCollapsed && (
- + {groups.map((g) => renderRow(g, onSelectSet))}
@@ -275,17 +285,20 @@ export const LogisticsReadinessBoard = ({ orderGroups = [], onSelectSet, statusO const custom = loadCustomOrder(); return custom || [...DEFAULT_FUNNEL_ORDER]; }); - const [createdSort, setCreatedSort] = React.useState(() => { + const [sort, setSort] = React.useState(() => { try { - return localStorage.getItem(SORT_KEY) === "asc" ? "asc" : "desc"; - } catch { - return "desc"; - } + const saved = JSON.parse(localStorage.getItem(SORT_KEY) || "null"); + if (saved?.key && ["asc", "desc"].includes(saved.direction)) return saved; + } catch {} + return { key: "createdAt", direction: "desc" }; }); - const toggleCreatedSort = React.useCallback(() => { - setCreatedSort((current) => { - const next = current === "desc" ? "asc" : "desc"; - try { localStorage.setItem(SORT_KEY, next); } catch {} + const handleSort = React.useCallback((key) => { + setSort((current) => { + const next = { + key, + direction: current.key === key && current.direction === "asc" ? "desc" : "asc", + }; + try { localStorage.setItem(SORT_KEY, JSON.stringify(next)); } catch {} return next; }); setPage(1); @@ -320,13 +333,34 @@ export const LogisticsReadinessBoard = ({ orderGroups = [], onSelectSet, statusO [filters, orderGroups], ); const rankedGroups = React.useMemo(() => { - const direction = createdSort === "asc" ? 1 : -1; + const direction = sort.direction === "asc" ? 1 : -1; + const text = (value) => String(value || "").trim().toLocaleLowerCase("ru"); + const time = (value) => { + const parsed = value ? new Date(value).getTime() : 0; + return Number.isFinite(parsed) ? parsed : 0; + }; + const valueFor = (group) => { + switch (sort.key) { + case "customer": return text(group.customerName || group.groupKey); + case "city": return text(group.city); + case "deliveryType": return text(group.deliveryType === "pickup" ? "Самовывоз" : "Доставка"); + case "deliveryDate": return time(group.deliveryDate); + case "driver": return text(group.assignedDriverName); + case "status": return text(getOrderGroupDisplayStatusLabel(group)); + case "updatedAt": return time(group.updatedAt); + case "createdAt": + default: return time(group.createdAt); + } + }; return [...filteredGroups].sort((a, b) => { - const aTime = a.createdAt ? new Date(a.createdAt).getTime() : 0; - const bTime = b.createdAt ? new Date(b.createdAt).getTime() : 0; - return (aTime - bTime) * direction; + const aValue = valueFor(a); + const bValue = valueFor(b); + const result = typeof aValue === "string" + ? aValue.localeCompare(bValue, "ru", { numeric: true, sensitivity: "base" }) + : aValue - bValue; + return result * direction; }); - }, [filteredGroups, createdSort]); + }, [filteredGroups, sort]); // Determine if filters are active — if so, show all results (no pagination) const hasActiveFilters = isFilterActive(filters); @@ -474,8 +508,8 @@ export const LogisticsReadinessBoard = ({ orderGroups = [], onSelectSet, statusO }); }} onSelectSet={onSelectSet} - createdSort={createdSort} - onToggleCreatedSort={toggleCreatedSort} + sort={sort} + onSort={handleSort} /> ); })}