From 50f504c2ffbd320dd00580df691a24bbe3bdb873 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 28 May 2026 11:05:38 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20city=20regex=20requires=20mandatory=20do?= =?UTF-8?q?t+space=20=E2=80=94=20no=20more=20=D0=93=D0=BE=D1=80=E2=86=92?= =?UTF-8?q?=D0=BE=D1=80,=20=D0=93=D0=BE=D0=B4=D0=BD=D0=B0=D1=8F=E2=86=92?= =?UTF-8?q?=D0=BE=D0=B4=D0=BD=D0=B0=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../driver/DriverDeliveryPlanner.jsx | 22 ++++++++++--------- src/components/orders/OrdersTable.jsx | 3 ++- src/constants/cities.js | 12 ++++++++++ src/pages/DashboardPage.jsx | 12 ++++++++-- src/services/supabase/orderGroupRepository.js | 16 +++----------- 5 files changed, 39 insertions(+), 26 deletions(-) create mode 100644 src/constants/cities.js diff --git a/src/components/driver/DriverDeliveryPlanner.jsx b/src/components/driver/DriverDeliveryPlanner.jsx index 50f5dc0..0e12fc7 100644 --- a/src/components/driver/DriverDeliveryPlanner.jsx +++ b/src/components/driver/DriverDeliveryPlanner.jsx @@ -1,3 +1,4 @@ +import { CRIMEAN_CITIES } from "../../constants/cities.js"; import React from "react"; import { getOrderGroupDeliveryHalfDay, @@ -29,27 +30,28 @@ const extractCity = (address) => { const trimmed = address.trim(); if (!trimmed) return null; - const cityMatch = trimmed.match(/(?:г\.?\s*|г\s+)([А-ЯЁA-Z][а-яёa-zA-Z\s\-]+?)(?:\s*[,;.]|$)/i); + 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(); } - const knownCities = [ - "Севастополь", "Ялта", "Симферополь", "Феодосия", "Евпатория", - "Керчь", "Алушта", "Бахчисарай", "Судак", "Инкерман", - "Джанкой", "Красногвардейское", "Раздольное", "Черноморское", - ]; - for (const city of knownCities) { + + for (const city of CRIMEAN_CITIES) { if (trimmed.toLowerCase().includes(city.toLowerCase())) { return city; } } - const firstSegment = trimmed.split(/[,;]/)[0].trim(); - if (firstSegment.length > 2 && firstSegment.length < 30 && !/^\d/.test(firstSegment)) { - return firstSegment; + // Бахчисарайский р-н → Бахчисарай + 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; }; diff --git a/src/components/orders/OrdersTable.jsx b/src/components/orders/OrdersTable.jsx index 910bcf3..a060f24 100644 --- a/src/components/orders/OrdersTable.jsx +++ b/src/components/orders/OrdersTable.jsx @@ -36,6 +36,7 @@ export const OrdersTable = ({ filters, setFilters, statusOptions, + cities = [], }) => { return ( @@ -51,7 +52,7 @@ export const OrdersTable = ({ {filters && setFilters ? ( - + ) : null} diff --git a/src/constants/cities.js b/src/constants/cities.js new file mode 100644 index 0000000..f8b0751 --- /dev/null +++ b/src/constants/cities.js @@ -0,0 +1,12 @@ +export const CRIMEAN_CITIES = [ + "Севастополь","Ялта","Алушта","Евпатория","Саки","Феодосия", + "Керчь","Симферополь","Бахчисарай","Судак","Белогорск", + "Красноперекопск","Джанкой","Щёлкино","Гаспра","Гурзуф", + "Кореиз","Ливадия","Массандра","Ореанда","Симеиз", + "Форос","Партенит","Мисхор","Отрадное","Санаторное", + "Васильевка","Куйбышево","Инкерман","Балаклава", + "Утёс","Резниково","Заветное","Хмельницкое","Мирновка", + "Новосёловка","Гвардейское","Красногвардейское", + "Раздольное","Черноморское","Ленино","Советский", + "Нижнегорский","Первомайское","Октябрьское", +]; diff --git a/src/pages/DashboardPage.jsx b/src/pages/DashboardPage.jsx index 9281915..6cd0df8 100644 --- a/src/pages/DashboardPage.jsx +++ b/src/pages/DashboardPage.jsx @@ -83,6 +83,14 @@ export const DashboardPage = () => { loadError, } = useOrderGroups(); + const cities = React.useMemo(() => { + const set = new Set(); + for (const g of allOrderGroups) { + if (g.city) set.add(g.city); + } + return [...set].sort(); + }, [allOrderGroups]); + const openGroupPage = React.useCallback((groupId) => { navigate("/dashboard/group/" + groupId); }, [navigate]); @@ -132,7 +140,7 @@ export const DashboardPage = () => { if (activeSection === "orders") { return (
- +
); } @@ -144,7 +152,7 @@ export const DashboardPage = () => { } return (
- +
); }; diff --git a/src/services/supabase/orderGroupRepository.js b/src/services/supabase/orderGroupRepository.js index 251649a..85ed179 100644 --- a/src/services/supabase/orderGroupRepository.js +++ b/src/services/supabase/orderGroupRepository.js @@ -1,4 +1,5 @@ import { safeSupabaseCall } from "../safeSupabaseCall"; +import { CRIMEAN_CITIES } from "../../constants/cities.js"; import { logAction } from "./actionLogService"; import logger from "../../utils/logger"; import { hasSupabaseConfig, supabase } from "../../supabaseClient"; @@ -90,22 +91,11 @@ export const mapOrderGroupRowToDeliveryGroup = (row) => { const deliveryAddress = normalizeText(row.delivery_address) || extractAddressFromSourceOrders(row.source_orders); const customerAddress = normalizeText(row.customer_address) || ""; - const CRIMEAN_CITIES = [ - "Севастополь","Ялта","Алушта","Евпатория","Саки","Феодосия", - "Керчь","Симферополь","Бахчисарай","Судак","Белогорск", - "Красноперекопск","Джанкой","Щёлкино","Гаспра","Гурзуф", - "Кореиз","Ливадия","Массандра","Ореанда","Симеиз", - "Форос","Партенит","Мисхор","Отрадное","Санаторное", - "Васильевка","Куйбышево","Инкерман","Балаклава", - "Утёс","Резниково","Заветное","Хмельницкое","Мирновка", - "Новосёловка","Гвардейское","Красногвардейское", - "Раздольное","Черноморское","Ленино","Советский", - "Нижнегорский","Первомайское","Октябрьское", - ]; + const extractCity = (addr) => { if (!addr) return ""; // 1) explicit marker: г. Ялта, пгт. Куйбышево, etc. - const m = addr.match(/(?:г\.|гор\.?|пос\.|с\.|село|дер\.|пгт|город)\s*([А-ЯЁа-яёA-Za-z\-\s]+?)(?:[,\\s]|$)/i); + const m = addr.match(/(?:г\.\s|гор\.\s|пос\.\s|с\.\s|дер\.\s|пгт\.\s|город\s|село\s|г\s)\s*([А-ЯЁа-яёA-Za-z\-\s]+?)(?:[,\\s]|$)/i); if (m) return m[1].trim(); // 2) known city name anywhere in address (case-insensitive) const lower = addr.toLowerCase();