diff --git a/src/components/orders/OrderDetailPanel.jsx b/src/components/orders/OrderDetailPanel.jsx index c966f75..62b56a8 100644 --- a/src/components/orders/OrderDetailPanel.jsx +++ b/src/components/orders/OrderDetailPanel.jsx @@ -50,11 +50,13 @@ import { getOrderGroupDeliveryStatusLabel, getOrderGroupDisplayStatusLabel, getOrderGroupStatusTone, + DELIVERY_GROUP_STATUS_LABELS, } from "../../services/orderGroupViews"; +import { getErrorMessage, normalizeNom } from "../../utils/deliveryUtils"; const DELIVERY_TIME_OPTIONS = ["Первая половина дня", "Вторая половина дня"]; const WEEK_DAY_LABELS = ["ПН", "ВТ", "СР", "ЧТ", "ПТ", "СБ", "ВС"]; -const STATUS_LABELS = { pending_confirmation: 'Ожидает согласования', agreed: 'Согласовано', driver_assigned: 'Назначен водитель', loaded: 'Загружено', on_route: 'В пути', delivered: 'Доставлено', pickup: 'Самовывоз', requires_address: 'Требуется адрес', problem: 'Проблема', cancelled: 'Отменено' }; +const STATUS_LABELS = DELIVERY_GROUP_STATUS_LABELS; const ConfirmModal = ({ open, title, message, onConfirm, onCancel }) => { if (!open) return null; @@ -125,12 +127,6 @@ const renderList = (values) => { const renderValue = (value) => value || "Нет данных"; -const normalizeNom = (nom) => { - if (!nom) return ''; - // 1C escapes backslashes: "СФ Т\\ЕА-33584" → normalize for comparison - return String(nom).replace(/\\\\/g, '\\').trim(); -}; - const getAllBillNumbers = (order) => { const orders = parseOrderList(order); if (!orders.length) return order.orderNumbers || []; @@ -194,22 +190,6 @@ const parseOrderList = (order) => { return []; }; -const getErrorMessage = (error, fallbackMessage) => { - if (!error) { - return fallbackMessage; - } - - if (error instanceof Error) { - return error.message || fallbackMessage; - } - - if (typeof error === "string") { - return error || fallbackMessage; - } - - return error?.message || fallbackMessage; -}; - const normalizeDeliveryTimeChoice = (value) => { const normalized = value ? String(value).trim() : ""; const deliveryTime = DELIVERY_TIME_ALIASES[normalized] || normalized; @@ -604,6 +584,10 @@ export const OrderDetailPanel = ({ const [pickupTimeSlot, setPickupTimeSlot] = React.useState(DELIVERY_TIME_OPTIONS[0]); const [deliveryAddress, setDeliveryAddress] = React.useState(order?.originalDeliveryAddress || order?.deliveryAddress || order?.customerAddress || ""); const [confirmAction, setConfirmAction] = React.useState(null); + const [isEditingDate, setIsEditingDate] = React.useState(false); + const handleShipmentChange = React.useCallback((state) => { + setShipmentState(state); + }, []); const minSelectableDateKey = React.useMemo(() => getNextSelectableDateKey(), []); const [currentMonth, setCurrentMonth] = React.useState(() => { const existingDeliveryDate = fromDateKey(order?.deliveryDate); @@ -655,7 +639,6 @@ export const OrderDetailPanel = ({ const isDeliveryAgreed = ["agreed", "driver_assigned", "loaded", "on_route", "delivered"].includes(order.deliveryStatus || order.delivery_status); const isPickupOrder = order.deliveryType === "pickup" || order.deliveryStatus === "pickup" || order.delivery_status === "pickup"; - const [isEditingDate, setIsEditingDate] = React.useState(false); // Show "agreed" banner only when selected tab matches the already-agreed type const agreedTypeMatchesTab = isDeliveryAgreed && !isEditingDate && ( (deliveryType === "pickup" && isPickupOrder) @@ -667,10 +650,6 @@ export const OrderDetailPanel = ({ order.deliveryTime || order.deliveryHalfDay, ].filter((value) => value && value !== "Нет данных").join(" · "); - const handleShipmentChange = React.useCallback((state) => { - setShipmentState(state); - }, []); - const handleSaveDeliveryChoice = async () => { const effectiveDate = deliveryType === "pickup" ? pickupDate : deliveryDate; const effectiveTime = deliveryType === "pickup" ? pickupTimeSlot : deliveryTime; @@ -735,7 +714,7 @@ export const OrderDetailPanel = ({

- {(order.deliveryType === "pickup" || order.deliveryStatus === "pickup" || order.delivery_status === "pickup") ? "Карточка группы самовывоза" : "Карточка группы доставки"} + {isPickupOrder ? "Карточка группы самовывоза" : "Карточка группы доставки"}

{order.displayTitle || order.customerName || order.groupKey} @@ -754,7 +733,7 @@ export const OrderDetailPanel = ({

{(() => { - const isPickup = order.deliveryType === "pickup" || order.deliveryStatus === "pickup" || order.delivery_status === "pickup"; + const isPickup = isPickupOrder; const deliveryTypeLabel = isPickup ? "Самовывоз" : (order.deliveryStatus === "requires_address" || order.delivery_status === "requires_address") @@ -882,7 +861,7 @@ export const OrderDetailPanel = ({

{formatDateTime(order.updatedAt)}

-

{(order.deliveryType === "pickup" || order.deliveryStatus === "pickup" || order.delivery_status === "pickup") ? "Статус самовывоза" : "Статус доставки"}

+

{isPickupOrder ? "Статус самовывоза" : "Статус доставки"}

{getOrderGroupDeliveryStatusLabel(order.deliveryStatus || order.delivery_status)}

@@ -1173,7 +1152,7 @@ export const OrderDetailPanel = ({ ) : null} - {canManageDelivery && ["manager", "logistician", "admin", "mega_admin"].includes(userRole) && (order.deliveryType !== "pickup" && order.deliveryStatus !== "pickup" && order.delivery_status !== "pickup") ? ( + {canManageDelivery && ["manager", "logistician", "admin", "mega_admin"].includes(userRole) && !isPickupOrder ? (
Назначение водителя diff --git a/src/hooks/useOrderGroups.js b/src/hooks/useOrderGroups.js index 6d3969b..451523a 100644 --- a/src/hooks/useOrderGroups.js +++ b/src/hooks/useOrderGroups.js @@ -7,22 +7,7 @@ import { ORDER_GROUP_DISPLAY_STATUS_OPTIONS, getOrderGroupDisplayStatusValue, } from "../services/orderGroupViews"; - -const getErrorMessage = (error, fallbackMessage) => { - if (!error) { - return fallbackMessage; - } - - if (error instanceof Error) { - return error.message || fallbackMessage; - } - - if (typeof error === "string") { - return error || fallbackMessage; - } - - return error?.message || fallbackMessage; -}; +import { getErrorMessage } from "../utils/deliveryUtils"; export const useOrderGroups = () => { const [orderGroups, setOrderGroups] = React.useState(() => []); diff --git a/src/services/orderGroupViews.js b/src/services/orderGroupViews.js index b24c798..d10ac25 100644 --- a/src/services/orderGroupViews.js +++ b/src/services/orderGroupViews.js @@ -5,6 +5,7 @@ const getDeliveryDate = (group) => normalizeDate(group.deliveryDate || group.cus export const DELIVERY_GROUP_STATUS_LABELS = { pending_confirmation: "Ожидает согласования", manual_confirmation_required: "Взят в ручное управление", + requires_address: "Требуется адрес", agreed: "Согласовано", driver_assigned: "Назначен водитель", loaded: "Загружено", diff --git a/src/services/supabase/orderGroupRepository.js b/src/services/supabase/orderGroupRepository.js index 80f0907..8ad2a0d 100644 --- a/src/services/supabase/orderGroupRepository.js +++ b/src/services/supabase/orderGroupRepository.js @@ -8,6 +8,7 @@ import { getOrderGroupDeliveryStatusLabel, getOrderGroupStatusLabel, } from "../orderGroupViews"; +import { normalizeNom } from "../../utils/deliveryUtils"; const requireSupabase = () => { if (!hasSupabaseConfig || !supabase) { @@ -68,7 +69,6 @@ export const mapOrderGroupRowToDeliveryGroup = (row) => { if (!Array.isArray(srcOrders) || !srcOrders.length) return orderNumbers; const seen = new Set(); const result = []; - const normalizeNom = (nom) => String(nom || '').replace(/\\\\/g, '\\').trim(); for (const src of srcOrders) { if (src && Array.isArray(src.orderList)) { for (const ol of src.orderList) { diff --git a/src/utils/deliveryUtils.js b/src/utils/deliveryUtils.js new file mode 100644 index 0000000..d7e49e3 --- /dev/null +++ b/src/utils/deliveryUtils.js @@ -0,0 +1,58 @@ +/** + * Shared delivery/delivery-group utilities. + * Single source of truth for isPickupOrder, getErrorMessage, normalizeNom, and STATUS_LABELS. + */ + +// ── Pickup detection ────────────────────────────────────────────────────── + +/** + * Returns true if the order group is a pickup (self-pickup) order. + * Uses canonical deliveryStatus field (mapper normalizes both delivery_status variants). + */ +export const isPickupOrder = (order) => + order?.deliveryType === "pickup" || + order?.deliveryStatus === "pickup" || + order?.delivery_status === "pickup"; + +// ── Error messages ──────────────────────────────────────────────────────── + +export const getErrorMessage = (error, fallbackMessage) => { + if (!error) { + return fallbackMessage; + } + if (error instanceof Error) { + return error.message || fallbackMessage; + } + if (typeof error === "string") { + return error || fallbackMessage; + } + return error?.message || fallbackMessage; +}; + +// ── Nom normalisation ────────────────────────────────────────────────────── + +/** + * 1C escapes backslashes: "СФ Т\\ЕА-33584" → normalise for comparison. + */ +export const normalizeNom = (nom) => { + if (!nom) return ""; + return String(nom).replace(/\\\\/g, "\\").trim(); +}; + +// ── Status labels ───────────────────────────────────────────────────────── + +export const DELIVERY_STATUS_LABELS = { + pending_confirmation: "Ожидает подтверждения", + requires_address: "Требуется адрес", + agreed: "Согласовано", + driver_assigned: "Водитель назначен", + loaded: "Загружен", + on_route: "В пути", + delivered: "Доставлено", + problem: "Проблема", + cancelled: "Отменено", + pickup: "Самовывоз", +}; + +export const getOrderGroupDeliveryStatusLabel = (status) => + DELIVERY_STATUS_LABELS[status] || status || "Ожидает подтверждения"; \ No newline at end of file