fix: city regex requires mandatory dot+space — no more Гор→ор, Годная→одная
This commit is contained in:
parent
9e992d8280
commit
50f504c2ff
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { CRIMEAN_CITIES } from "../../constants/cities.js";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
getOrderGroupDeliveryHalfDay,
|
getOrderGroupDeliveryHalfDay,
|
||||||
|
|
@ -29,27 +30,28 @@ const extractCity = (address) => {
|
||||||
const trimmed = address.trim();
|
const trimmed = address.trim();
|
||||||
if (!trimmed) return null;
|
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) {
|
if (cityMatch) {
|
||||||
return cityMatch[1].trim();
|
return cityMatch[1].trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
const knownCities = [
|
|
||||||
"Севастополь", "Ялта", "Симферополь", "Феодосия", "Евпатория",
|
for (const city of CRIMEAN_CITIES) {
|
||||||
"Керчь", "Алушта", "Бахчисарай", "Судак", "Инкерман",
|
|
||||||
"Джанкой", "Красногвардейское", "Раздольное", "Черноморское",
|
|
||||||
];
|
|
||||||
for (const city of knownCities) {
|
|
||||||
if (trimmed.toLowerCase().includes(city.toLowerCase())) {
|
if (trimmed.toLowerCase().includes(city.toLowerCase())) {
|
||||||
return city;
|
return city;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstSegment = trimmed.split(/[,;]/)[0].trim();
|
// Бахчисарайский р-н → Бахчисарай
|
||||||
if (firstSegment.length > 2 && firstSegment.length < 30 && !/^\d/.test(firstSegment)) {
|
const district = trimmed.match(/([А-ЯЁа-яё]+)ский\s*(?:р-н|район)/i);
|
||||||
return firstSegment;
|
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;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ export const OrdersTable = ({
|
||||||
filters,
|
filters,
|
||||||
setFilters,
|
setFilters,
|
||||||
statusOptions,
|
statusOptions,
|
||||||
|
cities = [],
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Panel className="p-0">
|
<Panel className="p-0">
|
||||||
|
|
@ -51,7 +52,7 @@ export const OrdersTable = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{filters && setFilters ? (
|
{filters && setFilters ? (
|
||||||
<OrderFilters filters={filters} setFilters={setFilters} statusOptions={statusOptions} />
|
<OrderFilters filters={filters} setFilters={setFilters} statusOptions={statusOptions} cities={cities} />
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
export const CRIMEAN_CITIES = [
|
||||||
|
"Севастополь","Ялта","Алушта","Евпатория","Саки","Феодосия",
|
||||||
|
"Керчь","Симферополь","Бахчисарай","Судак","Белогорск",
|
||||||
|
"Красноперекопск","Джанкой","Щёлкино","Гаспра","Гурзуф",
|
||||||
|
"Кореиз","Ливадия","Массандра","Ореанда","Симеиз",
|
||||||
|
"Форос","Партенит","Мисхор","Отрадное","Санаторное",
|
||||||
|
"Васильевка","Куйбышево","Инкерман","Балаклава",
|
||||||
|
"Утёс","Резниково","Заветное","Хмельницкое","Мирновка",
|
||||||
|
"Новосёловка","Гвардейское","Красногвардейское",
|
||||||
|
"Раздольное","Черноморское","Ленино","Советский",
|
||||||
|
"Нижнегорский","Первомайское","Октябрьское",
|
||||||
|
];
|
||||||
|
|
@ -83,6 +83,14 @@ export const DashboardPage = () => {
|
||||||
loadError,
|
loadError,
|
||||||
} = useOrderGroups();
|
} = 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) => {
|
const openGroupPage = React.useCallback((groupId) => {
|
||||||
navigate("/dashboard/group/" + groupId);
|
navigate("/dashboard/group/" + groupId);
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
@ -132,7 +140,7 @@ export const DashboardPage = () => {
|
||||||
if (activeSection === "orders") {
|
if (activeSection === "orders") {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 xl:space-y-8">
|
<div className="space-y-6 xl:space-y-8">
|
||||||
<OrdersTable orderGroups={filteredOrderGroups} selectedOrderGroupId={selectedOrderGroupId} onOpenOrder={openGroupPage} filters={filters} setFilters={setFilters} statusOptions={statusOptions} />
|
<OrdersTable orderGroups={filteredOrderGroups} selectedOrderGroupId={selectedOrderGroupId} onOpenOrder={openGroupPage} filters={filters} setFilters={setFilters} statusOptions={statusOptions} cities={cities} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -144,7 +152,7 @@ export const DashboardPage = () => {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 xl:space-y-8">
|
<div className="space-y-6 xl:space-y-8">
|
||||||
<OrdersTable orderGroups={filteredOrderGroups} selectedOrderGroupId={selectedOrderGroupId} onOpenOrder={openGroupPage} filters={filters} setFilters={setFilters} statusOptions={statusOptions} />
|
<OrdersTable orderGroups={filteredOrderGroups} selectedOrderGroupId={selectedOrderGroupId} onOpenOrder={openGroupPage} filters={filters} setFilters={setFilters} statusOptions={statusOptions} cities={cities} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { safeSupabaseCall } from "../safeSupabaseCall";
|
import { safeSupabaseCall } from "../safeSupabaseCall";
|
||||||
|
import { CRIMEAN_CITIES } from "../../constants/cities.js";
|
||||||
import { logAction } from "./actionLogService";
|
import { logAction } from "./actionLogService";
|
||||||
import logger from "../../utils/logger";
|
import logger from "../../utils/logger";
|
||||||
import { hasSupabaseConfig, supabase } from "../../supabaseClient";
|
import { hasSupabaseConfig, supabase } from "../../supabaseClient";
|
||||||
|
|
@ -90,22 +91,11 @@ export const mapOrderGroupRowToDeliveryGroup = (row) => {
|
||||||
|
|
||||||
const deliveryAddress = normalizeText(row.delivery_address) || extractAddressFromSourceOrders(row.source_orders);
|
const deliveryAddress = normalizeText(row.delivery_address) || extractAddressFromSourceOrders(row.source_orders);
|
||||||
const customerAddress = normalizeText(row.customer_address) || "";
|
const customerAddress = normalizeText(row.customer_address) || "";
|
||||||
const CRIMEAN_CITIES = [
|
|
||||||
"Севастополь","Ялта","Алушта","Евпатория","Саки","Феодосия",
|
|
||||||
"Керчь","Симферополь","Бахчисарай","Судак","Белогорск",
|
|
||||||
"Красноперекопск","Джанкой","Щёлкино","Гаспра","Гурзуф",
|
|
||||||
"Кореиз","Ливадия","Массандра","Ореанда","Симеиз",
|
|
||||||
"Форос","Партенит","Мисхор","Отрадное","Санаторное",
|
|
||||||
"Васильевка","Куйбышево","Инкерман","Балаклава",
|
|
||||||
"Утёс","Резниково","Заветное","Хмельницкое","Мирновка",
|
|
||||||
"Новосёловка","Гвардейское","Красногвардейское",
|
|
||||||
"Раздольное","Черноморское","Ленино","Советский",
|
|
||||||
"Нижнегорский","Первомайское","Октябрьское",
|
|
||||||
];
|
|
||||||
const extractCity = (addr) => {
|
const extractCity = (addr) => {
|
||||||
if (!addr) return "";
|
if (!addr) return "";
|
||||||
// 1) explicit marker: г. Ялта, пгт. Куйбышево, etc.
|
// 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();
|
if (m) return m[1].trim();
|
||||||
// 2) known city name anywhere in address (case-insensitive)
|
// 2) known city name anywhere in address (case-insensitive)
|
||||||
const lower = addr.toLowerCase();
|
const lower = addr.toLowerCase();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue