import { CRIMEAN_CITIES } from "../../constants/cities.js";
import React from "react";
import {
getOrderGroupDeliveryHalfDay,
getOrderGroupDeliveryStatusLabel,
getOrderGroupDeliveryStatusTone,
DRIVER_VISIBLE_DELIVERY_STATUSES,
isOrderGroupVisibleToDriver,
groupOrderGroupsByDate,
parseGroupDate,
ORDER_GROUP_DELIVERY_HALF_DAY_OPTIONS,
} from "../../services/orderGroupViews";
import { Badge } from "../UI/Badge";
import { Button } from "../UI/Button";
import { Input } from "../UI/Input";
import { Select } from "../UI/Select";
import { Panel } from "../UI/Panel";
import { SkeletonPage } from "../UI/Loading";
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", "picked_up"];
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, isLoading = false }) => {
const [filters, setFilters] = React.useState({
selectedDate: "",
deliveryStatus: "all",
selectedCity: "",
searchQuery: "",
halfDay: "all",
});
const [collapsedDates, setCollapsedDates] = React.useState({});
const hasActiveFilters = filters.selectedDate || filters.deliveryStatus !== "all" || filters.selectedCity || filters.searchQuery || filters.halfDay !== "all";
const resetAllFilters = () => {
setFilters({ selectedDate: "", deliveryStatus: "all", selectedCity: "", searchQuery: "", halfDay: "all" });
};
const toggleDate = (date) => {
setCollapsedDates((prev) => ({ ...prev, [date]: !prev[date] }));
};
const normalizePhoneForTel = (phone) => {
const cleaned = String(phone || "").trim();
if (!cleaned) return "";
if (cleaned.startsWith("+7")) return cleaned;
if (cleaned.startsWith("8")) return "+7" + cleaned.slice(1);
return "+7" + cleaned;
};
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 getSearchHaystack = (group) => {
return [
group.groupKey,
group.displayTitle,
group.customerName,
group.customerPhone,
group.customerDate,
Array.isArray(group.orderNumbers) ? group.orderNumbers.join(" ") : "",
group.deliveryAddress || group.delivery_address || "",
group.city || "",
].filter(Boolean).join(" ").toLowerCase();
};
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;
});
}
if (filters.halfDay !== "all") {
result = result.filter((group) => {
const groupHalfDay = getOrderGroupDeliveryHalfDay(group);
if (filters.halfDay === "unknown") {
return !groupHalfDay;
}
const labelMap = { morning: "Первая половина дня", afternoon: "Вторая половина дня" };
return groupHalfDay === labelMap[filters.halfDay];
});
}
const query = (filters.searchQuery || "").trim().toLowerCase();
if (query) {
result = result.filter((group) => getSearchHaystack(group).includes(query));
}
// Sort: problems first, then by status priority
const statusPriority = ["problem", "on_route", "loaded", "driver_assigned", "agreed", "delivered", "picked_up", "paid_storage"];
result.sort((a, b) => {
// has_delivery_problem always first
const aProblem = a.hasDeliveryProblem || a.has_delivery_problem;
const bProblem = b.hasDeliveryProblem || b.has_delivery_problem;
if (aProblem && !bProblem) return -1;
if (!aProblem && bProblem) return 1;
const sa = a.deliveryStatus || a.delivery_status || "unknown";
const sb = b.deliveryStatus || b.delivery_status || "unknown";
const ia = statusPriority.indexOf(sa);
const ib = statusPriority.indexOf(sb);
if (ia === -1 && ib === -1) return 0;
if (ia === -1) return 1;
if (ib === -1) return -1;
return ia - ib;
});
return result;
}, [driverOrderGroups, filters.selectedDate, filters.deliveryStatus, filters.selectedCity, filters.searchQuery, filters.halfDay]);
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]);
if (isLoading) {
return
Показываем только назначенные вам группы доставки. Выберите дату и город.
Сейчас у вас нет назначенных групп доставки.