import { CRIMEAN_CITIES } from "../../constants/cities.js"; import React from "react"; import { getOrderGroupDeliveryHalfDay, getOrderGroupDeliveryStatusLabel, getOrderGroupDeliveryStatusTone, DRIVER_VISIBLE_DELIVERY_STATUSES, isOrderGroupVisibleToDriver, groupOrderGroupsByDate, parseGroupDate, } from "../../services/orderGroupViews"; import { Badge } from "../UI/Badge"; import { Input } from "../UI/Input"; import { Select } from "../UI/Select"; import { Panel } from "../UI/Panel"; const CHEVRON_DOWN = ( ); const CHEVRON_RIGHT = ( ); const extractCity = (address) => { if (!address || typeof address !== "string") return null; const trimmed = address.trim(); if (!trimmed) return null; const cityMatch = trimmed.match(/(?:г\.\s+|гор\.\s+|пос\.\s+|с\.\s+|дер\.\s+|пгт\.\s+|город\s+|село\s+|г\s+)([А-ЯЁA-Z][а-яёa-zA-Z\s\-]+?)(?:\s*[,;.]|\s|$)/i); if (cityMatch) { return cityMatch[1].trim(); } for (const city of CRIMEAN_CITIES) { if (trimmed.toLowerCase().includes(city.toLowerCase())) { return city; } } // Бахчисарайский р-н → Бахчисарай const district = trimmed.match(/([А-ЯЁа-яё]+)ский\s*(?:р-н|район)/i); if (district) { const base = district[1]; for (const city of CRIMEAN_CITIES) { if (city.toLowerCase().startsWith(base.toLowerCase())) return city; } } // no match → null (caller falls back to Севастополь) return null; }; const normalizeCity = (address) => { const city = extractCity(address); return city || "Севастополь"; }; const DRIVER_DELIVERY_STATUS_OPTIONS = [ { value: "all", label: "Все статусы" }, ...DRIVER_VISIBLE_DELIVERY_STATUSES.map((status) => ({ value: status, label: status === "driver_assigned" ? "Назначено вам" : getOrderGroupDeliveryStatusLabel(status), })), ]; const pluralGroups = (n) => { if (n === 1) return "группа"; if (n >= 2 && n < 5) return "группы"; return "групп"; }; /** Count items by status, return array of {status, label, tone, count} */ const countByStatus = (items) => { const map = new Map(); for (const item of items) { const s = item.deliveryStatus || item.delivery_status || "unknown"; map.set(s, (map.get(s) || 0) + 1); } const result = []; for (const [status, count] of map) { result.push({ status, label: status === "driver_assigned" ? "Назначено" : getOrderGroupDeliveryStatusLabel(status), tone: getOrderGroupDeliveryStatusTone(status), count, }); } // Sort: delivered last (green = done), others by severity const order = ["problem", "cancelled", "on_route", "loaded", "driver_assigned", "paid_storage", "delivered"]; result.sort((a, b) => { const ia = order.indexOf(a.status); const ib = order.indexOf(b.status); if (ia === -1 && ib === -1) return a.status.localeCompare(b.status); if (ia === -1) return 1; if (ib === -1) return -1; return ia - ib; }); return result; }; export const DriverDeliveryPlanner = ({ orderGroups = [], onOpenOrder, currentUser }) => { const [filters, setFilters] = React.useState({ selectedDate: "", deliveryStatus: "all", selectedCity: "", }); const [collapsedDates, setCollapsedDates] = React.useState({}); const toggleDate = (date) => { setCollapsedDates((prev) => ({ ...prev, [date]: !prev[date] })); }; const driverOrderGroups = React.useMemo( () => orderGroups.filter((group) => { const isVisible = isOrderGroupVisibleToDriver(group); const isAssignedToMe = currentUser && group.assignedDriverId === currentUser.id; return isVisible && isAssignedToMe; }), [orderGroups, currentUser], ); const dateDeliveryMap = React.useMemo(() => { const map = new Map(); driverOrderGroups.forEach((group) => { const date = group.deliveryDate; if (date) { map.set(date, (map.get(date) || 0) + 1); } }); return map; }, [driverOrderGroups]); const sortedDeliveryDates = React.useMemo(() => { return Array.from(dateDeliveryMap.keys()).sort(); }, [dateDeliveryMap]); const cityDeliveryMap = React.useMemo(() => { const map = new Map(); driverOrderGroups.forEach((group) => { const city = normalizeCity(group.deliveryAddress || group.delivery_address); map.set(city, (map.get(city) || 0) + 1); }); return map; }, [driverOrderGroups]); const sortedCities = React.useMemo(() => { return Array.from(cityDeliveryMap.keys()).sort((a, b) => { if (a === "Севастополь") return -1; if (b === "Севастополь") return 1; return a.localeCompare(b, "ru"); }); }, [cityDeliveryMap]); const filteredOrderGroups = React.useMemo(() => { let result = [...driverOrderGroups]; if (filters.selectedDate) { result = result.filter((group) => group.deliveryDate === filters.selectedDate); } if (filters.deliveryStatus !== "all") { result = result.filter((group) => (group.deliveryStatus || group.delivery_status) === filters.deliveryStatus); } if (filters.selectedCity) { result = result.filter((group) => { const city = normalizeCity(group.deliveryAddress || group.delivery_address); return city === filters.selectedCity; }); } return result; }, [driverOrderGroups, filters.selectedDate, filters.deliveryStatus, filters.selectedCity]); const groupedOrderGroups = React.useMemo( () => groupOrderGroupsByDate(filteredOrderGroups), [filteredOrderGroups], ); const deliveryCountLabel = `${filteredOrderGroups.length} ${ filteredOrderGroups.length === 1 ? "доставка" : filteredOrderGroups.length < 5 ? "доставки" : "доставок" }`; const isDateSelected = (date) => filters.selectedDate === date; // Compute per-date status summary for collapsed badges const dateStatusSummary = React.useMemo(() => { const summary = {}; for (const dg of groupedOrderGroups) { summary[dg.date] = countByStatus(dg.items); } return summary; }, [groupedOrderGroups]); return (

Мои доставки

{deliveryCountLabel}

Показываем только назначенные вам группы доставки. Выберите дату и город.

{/* Date pills */} {sortedDeliveryDates.length > 0 && (
{sortedDeliveryDates.map((date) => { const count = dateDeliveryMap.get(date) || 0; const selected = isDateSelected(date); return ( ); })}
)} {/* City pills */} {sortedCities.length > 1 && (
{sortedCities.map((city) => { const count = cityDeliveryMap.get(city) || 0; const selected = filters.selectedCity === city; return ( ); })}
)}
{groupedOrderGroups.length ? ( groupedOrderGroups.map((group) => { const isCollapsed = collapsedDates[group.date]; const statusCounts = dateStatusSummary[group.date] || []; // Group items by delivery status within each date const statusBuckets = new Map(); for (const item of group.items) { const s = item.deliveryStatus || item.delivery_status || "unknown"; const label = s === "driver_assigned" ? "Назначено вам" : getOrderGroupDeliveryStatusLabel(s); const tone = getOrderGroupDeliveryStatusTone(s); if (!statusBuckets.has(s)) { statusBuckets.set(s, { label, tone, items: [] }); } statusBuckets.get(s).items.push(item); } const statusOrder = ["driver_assigned", "loaded", "on_route", "delivered", "problem"]; const sortedBuckets = Array.from(statusBuckets.entries()).sort(([a], [b]) => { const ia = statusOrder.indexOf(a); const ib = statusOrder.indexOf(b); if (ia === -1 && ib === -1) return a.localeCompare(b); if (ia === -1) return 1; if (ib === -1) return -1; return ia - ib; }); return ( {!isCollapsed && (
{sortedBuckets.map(([statusValue, { label, tone, items }]) => (
{label} {items.length} {pluralGroups(items.length)}
{items.map((item) => ( ))}
))}
)}
); }) ) : (

Доставки не найдены

Сейчас у вас нет назначенных групп доставки.

)}
); };