diff --git a/src/components/orders/CalendarWidget.jsx b/src/components/orders/CalendarWidget.jsx
new file mode 100644
index 0000000..2290a4a
--- /dev/null
+++ b/src/components/orders/CalendarWidget.jsx
@@ -0,0 +1,185 @@
+import React from "react";
+
+const WEEK_DAY_LABELS = ["ПН", "ВТ", "СР", "ЧТ", "ПТ", "СБ", "ВС"];
+
+const toDateKey = (date) => {
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, "0");
+ const day = String(date.getDate()).padStart(2, "0");
+ return `${year}-${month}-${day}`;
+};
+
+const isWeekendDate = (date) => {
+ const day = date.getDay();
+ return day === 0 || day === 6;
+};
+
+const isSelectableCalendarDate = (date, minDateKey) => {
+ const dateKey = toDateKey(date);
+ return dateKey >= minDateKey && !isWeekendDate(date);
+};
+
+const formatDateForDisplay = (value) => {
+ if (!value) {
+ return "Выберите дату";
+ }
+
+ const [year, month, day] = value.split("-").map(Number);
+ if (!year || !month || !day) {
+ return value;
+ }
+
+ return new Date(year, month - 1, day).toLocaleDateString("ru-RU", {
+ day: "2-digit",
+ month: "2-digit",
+ year: "numeric",
+ });
+};
+
+const CalendarWidget = ({
+ label,
+ selectedDate,
+ onDateChange,
+ minDateKey,
+ isCalendarOpen,
+ setIsCalendarOpen,
+ currentMonth,
+ setCurrentMonth,
+ calendarDays,
+ monthLabel,
+ canGoBack,
+ timeOptions,
+ selectedTime,
+ onTimeChange,
+ layoutClassName,
+ calendarClassName,
+ timeClassName,
+}) => {
+ return (
+
+
+
+ {isCalendarOpen ? (
+
+
+
+
+ {label}
+
+
+ {monthLabel}
+
+
+
+
+
+
+
+
+ {WEEK_DAY_LABELS.map((day) => (
+
+ {day}
+
+ ))}
+
+
+ {calendarDays.map((day, index) => {
+ if (!day) {
+ return
;
+ }
+
+ const dateKey = toDateKey(day);
+ const isWeekend = isWeekendDate(day);
+ const isSelectable = isSelectableCalendarDate(day, minDateKey);
+ const isSelected = dateKey === selectedDate;
+ const isDisabled = !isSelectable;
+ const dayNumber = String(day.getDate()).padStart(2, "0");
+
+ return (
+
+ );
+ })}
+
+
+ Выходные отмечены пунктиром и недоступны.
+
+
+ ) : null}
+
+
+ {timeOptions.map((option) => (
+
+ ))}
+
+
+ );
+};
+
+export { CalendarWidget, formatDateForDisplay };
\ No newline at end of file
diff --git a/src/components/orders/DriverAssignmentPanel.jsx b/src/components/orders/DriverAssignmentPanel.jsx
new file mode 100644
index 0000000..af511cb
--- /dev/null
+++ b/src/components/orders/DriverAssignmentPanel.jsx
@@ -0,0 +1,89 @@
+import React from "react";
+import { Badge } from "../UI/Badge";
+import { Button } from "../UI/Button";
+import { Select } from "../UI/Select";
+import { Panel } from "../UI/Panel";
+
+const DriverAssignmentPanel = ({
+ order,
+ userRole,
+ canManageDelivery,
+ isSavingDeliveryChoice,
+ selectedDriverId,
+ onDriverSelect,
+ onConfirmDriver,
+ driverMessage,
+ drivers,
+}) => {
+ if (!canManageDelivery || !["manager", "logistician", "admin", "mega_admin"].includes(userRole) || !order) {
+ return null;
+ }
+
+ const isPickupOrder = order.deliveryType === "pickup" || order.deliveryStatus === "pickup" || order.delivery_status === "pickup";
+ if (isPickupOrder) return null;
+
+ const ds = order.deliveryStatus || order.delivery_status;
+ const isDriverLocked = ["loaded", "on_route", "delivered"].includes(ds);
+
+ return (
+
+
+
Назначение водителя
+
+ {(() => {
+ if (["loaded", "on_route", "delivered"].includes(ds)) {
+ return "Доставка в процессе — сменить водителя нельзя.";
+ }
+ return order.assignedDriverId
+ ? "Назначен водитель. Вы можете изменить назначение."
+ : "Выберите водителя для доставки.";
+ })()}
+
+
+ {order.assignedDriverId ? (
+
+
+
+
+ Водитель назначен
+
+
+ {order.assignedDriverName || "Неизвестно"}
+
+
+
Назначен
+
+
+ ) : null}
+ {!isDriverLocked ? (
+
+
+
+
+ ) : null}
+ {driverMessage ? (
+ {driverMessage}
+ ) : null}
+
+ );
+};
+
+export { DriverAssignmentPanel };
\ No newline at end of file
diff --git a/src/components/orders/OrderDetailPanel.jsx b/src/components/orders/OrderDetailPanel.jsx
index 62b56a8..ebc0580 100644
--- a/src/components/orders/OrderDetailPanel.jsx
+++ b/src/components/orders/OrderDetailPanel.jsx
@@ -42,9 +42,11 @@ import React from "react";
import { formatDateTime } from "../../utils/formatters";
import { Badge } from "../UI/Badge";
import { Button } from "../UI/Button";
-import { Select } from "../UI/Select";
import { Panel } from "../UI/Panel";
import { DriverShipmentPanel } from "../driver/DriverShipmentPanel";
+import { CalendarWidget } from "./CalendarWidget";
+import { StatusActionPanel } from "./StatusActionPanel";
+import { DriverAssignmentPanel } from "./DriverAssignmentPanel";
import { supabase } from "../../supabaseClient";
import {
getOrderGroupDeliveryStatusLabel,
@@ -55,7 +57,6 @@ import {
import { getErrorMessage, normalizeNom } from "../../utils/deliveryUtils";
const DELIVERY_TIME_OPTIONS = ["Первая половина дня", "Вторая половина дня"];
-const WEEK_DAY_LABELS = ["ПН", "ВТ", "СР", "ЧТ", "ПТ", "СБ", "ВС"];
const STATUS_LABELS = DELIVERY_GROUP_STATUS_LABELS;
const ConfirmModal = ({ open, title, message, onConfirm, onCancel }) => {
@@ -253,11 +254,6 @@ const isFutureDeliveryDate = (value) => {
return !isWeekendDate(parsedDate) && toDateKey(parsedDate) >= getNextSelectableDateKey();
};
-const isSelectableCalendarDate = (date, minDateKey) => {
- const dateKey = toDateKey(date);
- return dateKey >= minDateKey && !isWeekendDate(date);
-};
-
const formatDateForDisplay = (value) => {
if (!value) {
return "Выберите дату";
@@ -950,193 +946,45 @@ export const OrderDetailPanel = ({
) : null}
) : deliveryType === "delivery" ? (
-
-
-
- {isCalendarOpen ? (
-
-
-
-
- Календарь доставки
-
-
- {monthLabel}
-
-
-
-
-
-
-
-
- {WEEK_DAY_LABELS.map((day) => (
-
- {day}
-
- ))}
-
-
- {calendarDays.map((day, index) => {
- if (!day) {
- return
;
- }
-
- const dateKey = toDateKey(day);
- const isWeekend = isWeekendDate(day);
- const isSelectable = isSelectableCalendarDate(day, minSelectableDateKey);
- const isSelected = dateKey === deliveryDate;
- const isDisabled = !isSelectable;
- const dayNumber = String(day.getDate()).padStart(2, "0");
-
- return (
-
- );
- })}
-
-
- Выходные отмечены пунктиром и недоступны.
-
-
- ) : null}
-
-
- {DELIVERY_TIME_OPTIONS.map((option) => (
-
- ))}
-
-
+ { setDeliveryDate(dateKey); setFormMessage(""); }}
+ minDateKey={minSelectableDateKey}
+ isCalendarOpen={isCalendarOpen}
+ setIsCalendarOpen={setIsCalendarOpen}
+ currentMonth={currentMonth}
+ setCurrentMonth={setCurrentMonth}
+ calendarDays={calendarDays}
+ monthLabel={monthLabel}
+ canGoBack={canGoBack}
+ timeOptions={DELIVERY_TIME_OPTIONS}
+ selectedTime={deliveryTime}
+ onTimeChange={(option) => { setDeliveryTime(option); setFormMessage(""); }}
+ layoutClassName="flex flex-col gap-3 md:flex-row md:items-start md:relative md:z-10"
+ calendarClassName="relative space-y-3 md:min-w-0 md:flex-1 md:pr-4"
+ timeClassName="grid gap-2 sm:grid-cols-2 md:w-[320px] md:flex-none"
+ />
) : (
-
-
-
- {isCalendarOpen ? (
-
-
-
-
Календарь самовывоза
-
{monthLabel}
-
-
-
-
-
-
-
- {WEEK_DAY_LABELS.map((day) => (
{day}
))}
-
-
- {calendarDays.map((day, index) => {
- if (!day) return
;
- const dateKey = toDateKey(day);
- const isWeekend = isWeekendDate(day);
- const isSelectable = isSelectableCalendarDate(day, minSelectableDateKey);
- const isSelected = dateKey === pickupDate;
- const isDisabled = !isSelectable;
- const dayNumber = String(day.getDate()).padStart(2, "0");
- return (
-
- );
- })}
-
-
Выходные отмечены пунктиром и недоступны.
-
- ) : null}
-
-
- {DELIVERY_TIME_OPTIONS.map((option) => (
-
- ))}
-
-
+ { setPickupDate(dateKey); setFormMessage(""); }}
+ minDateKey={minSelectableDateKey}
+ isCalendarOpen={isCalendarOpen}
+ setIsCalendarOpen={setIsCalendarOpen}
+ currentMonth={currentMonth}
+ setCurrentMonth={setCurrentMonth}
+ calendarDays={calendarDays}
+ monthLabel={monthLabel}
+ canGoBack={canGoBack}
+ timeOptions={DELIVERY_TIME_OPTIONS}
+ selectedTime={pickupTimeSlot}
+ onTimeChange={(option) => { setPickupTimeSlot(option); setFormMessage(""); }}
+ layoutClassName="space-y-3"
+ calendarClassName="relative"
+ timeClassName="grid gap-2 sm:grid-cols-2"
+ />
)}