diff --git a/src/components/logistics/LogisticsReadinessBoard.jsx b/src/components/logistics/LogisticsReadinessBoard.jsx index ca5301b..8d2287e 100644 --- a/src/components/logistics/LogisticsReadinessBoard.jsx +++ b/src/components/logistics/LogisticsReadinessBoard.jsx @@ -12,9 +12,17 @@ import { OrderFilters } from "../orders/OrderFilters"; import { formatDateTime } from "../../utils/formatters"; export const LogisticsReadinessBoard = ({ orderGroups = [], onSelectSet, statusOptions = ORDER_GROUP_DISPLAY_STATUS_OPTIONS }) => { - const [filters, setFilters] = React.useState({ query: "", displayStatus: "all" }); + const [filters, setFilters] = React.useState({ query: "", displayStatus: "all", city: "" }); const [collapsedSections, setCollapsedSections] = React.useState(new Set()); + const cities = React.useMemo(() => { + const set = new Set(); + for (const g of orderGroups) { + if (g.city) set.add(g.city); + } + return [...set].sort(); + }, [orderGroups]); + const filteredGroups = React.useMemo( () => filterOrderGroups(orderGroups, filters), [filters, orderGroups], @@ -55,9 +63,9 @@ export const LogisticsReadinessBoard = ({ orderGroups = [], onSelectSet, statusO Клиент - Телефон + Город Дата доставки - Заказы + Водитель Статус Обновлён @@ -78,6 +86,7 @@ export const LogisticsReadinessBoard = ({ orderGroups = [], onSelectSet, statusO filters={filters} setFilters={setFilters} statusOptions={statusOptions} + cities={cities} /> @@ -147,7 +156,7 @@ export const LogisticsReadinessBoard = ({ orderGroups = [], onSelectSet, statusO
{group.deliveryDate || "—"}
- {group.customerPhone || "—"} + {group.city || group.customerAddress || "—"} {group.deliveryDate @@ -156,7 +165,7 @@ export const LogisticsReadinessBoard = ({ orderGroups = [], onSelectSet, statusO } - {group.ordersCount || 0} {group.ordersCount === 1 ? "заказ" : group.ordersCount < 5 ? "заказа" : "заказов"} + {group.assignedDriverName || } {getOrderGroupDisplayStatusLabel(group)} diff --git a/src/components/orders/OrderFilters.jsx b/src/components/orders/OrderFilters.jsx index 6168c0c..cd55c17 100644 --- a/src/components/orders/OrderFilters.jsx +++ b/src/components/orders/OrderFilters.jsx @@ -3,7 +3,7 @@ import { Input } from "../UI/Input"; import { Panel } from "../UI/Panel"; import { DatePicker } from "../UI/DatePicker"; -export const OrderFilters = ({ filters, setFilters, statusOptions = [] }) => { +export const OrderFilters = ({ filters, setFilters, statusOptions = [], cities = [] }) => { const statusValue = filters.displayStatus || filters.status || "all"; const selectedStatusLabel = statusOptions.find((option) => option.value === statusValue)?.label || statusValue; const [isStatusOpen, setIsStatusOpen] = React.useState(false); @@ -38,8 +38,8 @@ export const OrderFilters = ({ filters, setFilters, statusOptions = [] }) => { return (
- {/* Row 1: Status + Search */} -
+ {/* Row 1: Status + City + Search */} +
+ {cities.length > 0 && ( + + )} + { : userRole === "logistician" ? [ { key: "logistics", label: "Логистика", description: "Группы доставки по готовности к уведомлению.", badge: String(allOrderGroups.length || orderGroups.length || 0) }, - { key: "orders", label: "Группы", description: "Реестр групп доставки.", badge: null }, ] : [ { key: section.key, label: section.label, description: section.description, badge: String(allOrderGroups.length || orderGroups.length || 0) }, ]; - const guideSectionMeta = { key: "guide", label: "Справка", description: "Карта продукта, роли, сценарии и частые вопросы." }; - const activeSectionMeta = activeSection === "guide" ? guideSectionMeta : navItems.find((n) => n.key === activeSection) || navItems[0]; - const isGuideOpen = activeSection === "guide"; + const activeSectionMeta = navItems.find((n) => n.key === activeSection) || navItems[0]; + const isGuideOpen = false; if (!user) { return ; } const renderActiveSection = () => { - if (activeSection === "guide") return ; + if (activeSection === "analytics") return
; if (activeSection === "users") return
; if (activeSection === "stop_words") return
; @@ -159,8 +156,8 @@ export const DashboardPage = () => { onInstallApp={onInstallApp} isInstalled={isInstalled} isInstallAvailable={isInstallAvailable} - onOpenGuide={() => setActiveSection("guide")} - isGuideOpen={isGuideOpen} + onOpenGuide={undefined} + isGuideOpen={false} navItems={navItems} activeSection={activeSection} onSectionChange={setActiveSection} diff --git a/src/services/orderGroupViews.js b/src/services/orderGroupViews.js index fd28939..e9eaac9 100644 --- a/src/services/orderGroupViews.js +++ b/src/services/orderGroupViews.js @@ -251,6 +251,8 @@ export const filterOrderGroups = (groups, filters = {}) => { getOrderGroupStatusLabel(group.status), group.deliveryStatus, getOrderGroupDeliveryStatusLabel(group.deliveryStatus), + group.city, + group.customerAddress, ] .filter(Boolean) .join(" ")) @@ -289,6 +291,14 @@ export const filterOrderGroups = (groups, filters = {}) => { } } + const cityFilter = (filters.city || "").trim().toLowerCase(); + if (cityFilter) { + const groupCity = (group.city || "").toLowerCase(); + if (!groupCity.includes(cityFilter) && cityFilter !== groupCity) { + return false; + } + } + if (!query) { return true; } diff --git a/src/services/supabase/orderGroupRepository.js b/src/services/supabase/orderGroupRepository.js index 4303822..c7a2319 100644 --- a/src/services/supabase/orderGroupRepository.js +++ b/src/services/supabase/orderGroupRepository.js @@ -89,6 +89,15 @@ export const mapOrderGroupRowToDeliveryGroup = (row) => { }; const deliveryAddress = normalizeText(row.delivery_address) || extractAddressFromSourceOrders(row.source_orders); + const customerAddress = normalizeText(row.customer_address) || ""; + const extractCity = (addr) => { + if (!addr) return ""; + const m = addr.match(/(?:г\.|гор\.?|пос\.|с\.|село|дер\.|пгт|город)\s*([А-ЯЁа-яёA-Za-z\-\s]+?)(?:[,\\s]|$)/i); + if (m) return m[1].trim(); + const words = addr.split(/[,.]/)[0].trim(); + return words; + }; + const city = extractCity(customerAddress) || extractCity(deliveryAddress) || ""; return { id: row.id, @@ -107,6 +116,8 @@ export const mapOrderGroupRowToDeliveryGroup = (row) => { customerPhoneNormalized: parsedKey.phone || normalizePhone(customerPhone), customerDate, deliveryAddress, + customerAddress, + city, assignedDriverId: row.assigned_driver_id || null, assignedDriverName: row.assigned_driver?.name || "", ordersCount, @@ -154,6 +165,8 @@ export const mapOrderGroupRowToDeliveryGroup = (row) => { customerPhone, customerDate, deliveryAddress, + customerAddress, + city, rawDeliveryHalfDay, rawDeliveryTime, row.delivery_window, @@ -203,7 +216,7 @@ export const updateOrderGroupDeliveryChoice = async ({ const { data, error } = await client .from("order_groups") - .select("id, group_key, order_numbers, status, delivery_status, sms_sent_at, created_at, updated_at, created_from_exchange_at, source_key, customer_name, customer_phone, customer_phone_normalized, customer_date, orders_total, orders_ready, orders_not_ready, source_orders, order_list, order_list_structured, delivery_invitation_id, delivery_link, notification_status, sms_attempts, first_sms_sent_at, second_sms_sent_at, last_sms_error, next_notification_check_at, delivery_date, delivery_time, delivery_address, delivery_date_source, manual_confirmation_at, paid_storage_at, assigned_driver_id, assigned_driver:users!order_groups_assigned_driver_id_fkey(id, name)") + .select("id, group_key, order_numbers, status, delivery_status, sms_sent_at, created_at, updated_at, created_from_exchange_at, source_key, customer_name, customer_phone, customer_phone_normalized, customer_date, orders_total, orders_ready, orders_not_ready, source_orders, order_list, order_list_structured, delivery_invitation_id, delivery_link, notification_status, sms_attempts, first_sms_sent_at, second_sms_sent_at, last_sms_error, next_notification_check_at, delivery_date, delivery_time, delivery_address, customer_address, delivery_date_source, manual_confirmation_at, paid_storage_at, assigned_driver_id, assigned_driver:users!order_groups_assigned_driver_id_fkey(id, name)") .eq("id", orderGroupId) .single(); @@ -357,7 +370,7 @@ export const fetchOrderGroups = async () => { const client = requireSupabase(); const { data, error } = await client .from("order_groups") - .select("id, group_key, order_numbers, status, delivery_status, sms_sent_at, created_at, updated_at, created_from_exchange_at, source_key, customer_name, customer_phone, customer_phone_normalized, customer_date, orders_total, orders_ready, orders_not_ready, source_orders, order_list, order_list_structured, delivery_invitation_id, delivery_link, notification_status, sms_attempts, first_sms_sent_at, second_sms_sent_at, last_sms_error, next_notification_check_at, delivery_date, delivery_time, delivery_address, delivery_date_source, manual_confirmation_at, paid_storage_at, assigned_driver_id, assigned_driver:users!order_groups_assigned_driver_id_fkey(id, name)") + .select("id, group_key, order_numbers, status, delivery_status, sms_sent_at, created_at, updated_at, created_from_exchange_at, source_key, customer_name, customer_phone, customer_phone_normalized, customer_date, orders_total, orders_ready, orders_not_ready, source_orders, order_list, order_list_structured, delivery_invitation_id, delivery_link, notification_status, sms_attempts, first_sms_sent_at, second_sms_sent_at, last_sms_error, next_notification_check_at, delivery_date, delivery_time, delivery_address, customer_address, delivery_date_source, manual_confirmation_at, paid_storage_at, assigned_driver_id, assigned_driver:users!order_groups_assigned_driver_id_fkey(id, name)") .order("updated_at", { ascending: false }); if (error) {