From b69ea40f3508b267bfb640b58b61d4baf8b218f6 Mon Sep 17 00:00:00 2001 From: houseassassin Date: Tue, 12 May 2026 11:36:36 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20filter=20bar=20in=20kanban=20=E2=80=94?= =?UTF-8?q?=20by=20manager,=20object=20type,=20overdue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- src/admin/orders/KanbanBoard.jsx | 103 ++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/src/admin/orders/KanbanBoard.jsx b/src/admin/orders/KanbanBoard.jsx index bc8837a..13d2d07 100644 --- a/src/admin/orders/KanbanBoard.jsx +++ b/src/admin/orders/KanbanBoard.jsx @@ -10,8 +10,92 @@ import OrderModal from './OrderModal'; const STATUSES = ['новая', 'замер', 'согласование', 'монтаж', 'закрыт']; +function KanbanFilters({ filters, orders, onChange }) { + const managers = [...new Set(orders.map((o) => o.assigned_to).filter(Boolean))].sort(); + const objectTypes = { + industrial: 'Промышленный', + commercial: 'Коммерческий', + infrastructure: 'Инфраструктура', + residential: 'Частный', + other: 'Другое', + }; + + const hasFilters = filters.manager || filters.objectType || filters.overdueOnly; + + return ( +
+ {/* Manager filter */} + + + {/* Object type filter */} + + + {/* Overdue toggle */} + + + {/* Clear button */} + {hasFilters && ( + + )} + + {hasFilters && ( + + Фильтр активен + + )} +
+ ); +} + const KanbanBoard = () => { - const { ordersByStatus, loading, updateStatus } = useOrders(); + const { orders, ordersByStatus, loading, updateStatus } = useOrders(); const { session } = useAuth(); const { showNewOrder, setShowNewOrder } = useOutletContext() || {}; const isMobile = useIsMobile(); @@ -21,6 +105,7 @@ const KanbanBoard = () => { const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 5 } })); const [localOverrides, setLocalOverrides] = useState({}); + const [filters, setFilters] = useState({ manager: '', objectType: '', overdueOnly: false }); const findOrderStatus = (orderId) => { if (localOverrides[orderId]) return localOverrides[orderId]; @@ -76,6 +161,19 @@ const KanbanBoard = () => { return acc; }, {}); + const filteredOrdersByStatus = STATUSES.reduce((acc, status) => { + acc[status] = (effectiveOrdersByStatus[status] || []).filter((o) => { + if (filters.manager && o.assigned_to !== filters.manager) return false; + if (filters.objectType && o.object_type !== filters.objectType) return false; + if (filters.overdueOnly) { + const overdue = o.scheduled_at && new Date(o.scheduled_at) < new Date() && o.status !== 'закрыт'; + if (!overdue) return false; + } + return true; + }); + return acc; + }, {}); + if (loading) { return (
@@ -86,6 +184,7 @@ const KanbanBoard = () => { return ( <> +
{