supersam/src/components/driver/DriverDeliveryDetail.jsx

202 lines
7.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from "react";
import { getAvailableTransitionsByRole, getOrderStatusComment, getStatusTone } from "../../constants/deliveryWorkflow";
import { getDeliveryCity, getDeliveryDay, getDeliveryHalfDay } from "../../services/driverDeliveries";
import { Badge } from "../UI/Badge";
import { Button } from "../UI/Button";
import { Panel } from "../UI/Panel";
const PROBLEM_REASONS = [
{ value: "client_absent", label: "Клиент не принял", description: "Клиент отказался или не вышел на связь" },
{ value: "damage", label: "Повреждение заказа", description: "Товар повреждён при транспортировке" },
{ value: "wrong_address", label: "Неверный адрес", description: "Адрес доставки указан неверно" },
{ value: "other", label: "Другое", description: "Иная причина проблемы доставки" },
];
const ProblemReasonModal = ({ onSelect, onCancel }) => (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40" onClick={onCancel}>
<Panel className="mx-4 w-full max-w-md space-y-4 p-6" onClick={(e) => e.stopPropagation()}>
<h3 className="text-lg font-semibold">Причина проблемы</h3>
<p className="text-sm text-[var(--color-text-muted)]">Укажите причину возникшей проблемы с доставкой.</p>
<div className="space-y-2">
{PROBLEM_REASONS.map((reason) => (
<Button
key={reason.value}
variant="secondary"
className="w-full rounded-[16px] p-3 text-left"
onClick={() => onSelect(reason.value, reason.label)}
>
<span className="font-medium">{reason.label}</span>
<p className="mt-0.5 text-xs text-[var(--color-text-muted)]">{reason.description}</p>
</Button>
))}
</div>
<div className="flex justify-end">
<Button variant="ghost" onClick={onCancel}>Отмена</Button>
</div>
</Panel>
</div>
);
const splitItem = (item) => {
if (!item) {
return { name: "Позиция", quantity: "" };
}
if (typeof item === "string") {
const [name, quantity] = item.split("|").map((part) => part.trim());
return {
name: name || item,
quantity: quantity || "",
};
}
if (typeof item === "object") {
return {
name: item.name || item.label || "Позиция",
quantity: typeof item.quantity === "number" ? String(item.quantity) : item.quantity || "",
};
}
return { name: "Позиция", quantity: "" };
};
export const DriverDeliveryDetail = ({ order, onStatusChange }) => {
const [showProblemModal, setShowProblemModal] = useState(false);
if (!order) {
return null;
}
const availableTransitions = getAvailableTransitionsByRole({
status: order.status,
role: "driver",
});
const orderItems = Array.isArray(order.items) ? order.items.map(splitItem) : [];
const currentStatus = order.status;
let actionButtons = [];
if (currentStatus === "Назначен водитель") {
actionButtons = [
{ value: "Доставлен", label: "Доставлено" },
{ value: "Проблема доставки", label: "Проблема" },
];
} else if (currentStatus === "Доставлен") {
actionButtons = [
{ value: "Назначен водитель", label: "Вернуть в работу" },
];
} else if (currentStatus === "Проблема доставки" || currentStatus === "Закрыт" || currentStatus === "Отменён") {
actionButtons = [];
} else {
actionButtons = availableTransitions.map((status) => ({
value: status,
label: status === "Проблема доставки" ? "Проблема" : status,
}));
}
return (
<div className="space-y-4 fs-zone-card">
{showProblemModal && (
<ProblemReasonModal
onSelect={(reasonValue, reasonLabel) => {
setShowProblemModal(false);
onStatusChange?.("Проблема доставки", { reason: reasonValue, reasonLabel });
}}
onCancel={() => setShowProblemModal(false)}
/>
)}
<Panel className="space-y-5 p-6">
<div className="flex flex-wrap items-start justify-between gap-4">
<div>
<p className="text-sm uppercase tracking-[0.2em] text-[var(--color-text-muted)]">Доставка</p>
<h2 className="mt-2 text-2xl font-semibold">{order.customer.address}</h2>
<p className="mt-2 text-sm text-[var(--color-text-muted)]">
{order.orderNumber} · {order.customer.name}
</p>
</div>
<div className="flex flex-wrap gap-2">
<Badge tone={getStatusTone(order.status)}>{order.status}</Badge>
</div>
</div>
<p className="text-sm leading-6 text-[var(--color-text-muted)]">
{getOrderStatusComment(order.status)}
</p>
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
<div>
<p className="text-xs text-[var(--color-text-muted)]">Клиент</p>
<p className="mt-1 font-medium">{order.customer.name}</p>
</div>
<div>
<p className="text-xs text-[var(--color-text-muted)]">Телефон</p>
<p className="mt-1 font-medium">{order.customer.phone}</p>
</div>
<div>
<p className="text-xs text-[var(--color-text-muted)]">Город</p>
<p className="mt-1 font-medium">{getDeliveryCity(order)}</p>
</div>
<div>
<p className="text-xs text-[var(--color-text-muted)]">Интервал доставки</p>
<p className="mt-1 font-medium">
{getDeliveryDay(order)} · {getDeliveryHalfDay(order)}
</p>
</div>
</div>
</Panel>
<Panel className="space-y-4 p-6">
<h3 className="text-lg font-semibold">Что везти</h3>
<div className="space-y-3">
{orderItems.map((item) => (
<div
key={`${item.name}-${item.quantity || "item"}`}
className="flex items-center justify-between gap-3 rounded-[20px] border border-[var(--color-border)] bg-[var(--color-surface)] px-4 py-3 text-sm"
>
<span>{item.name}</span>
{item.quantity ? <Badge tone="neutral">{item.quantity}</Badge> : null}
</div>
))}
</div>
</Panel>
<Panel className="space-y-4 p-6">
<h3 className="text-lg font-semibold">Комментарии для доставки</h3>
<div className="space-y-3 text-sm text-[var(--color-text)]">
<div className="rounded-[20px] bg-[var(--color-surface)] p-4">
{order.orderNotes?.[0]?.text || "Дополнительных комментариев нет."}
</div>
{order.comments?.length ? (
<div className="rounded-[20px] bg-[var(--color-surface)] p-4">
{order.comments.join(". ")}
</div>
) : null}
</div>
</Panel>
{actionButtons.length > 0 && (
<Panel className="space-y-4 p-6">
<h3 className="text-lg font-semibold">Быстрые действия</h3>
<div className="flex flex-wrap gap-2">
{actionButtons.map((btn) => (
<Button
key={btn.value}
variant={btn.value === "Проблема доставки" ? "ghost" : "secondary"}
onClick={() => {
if (btn.value === "Проблема доставки") {
setShowProblemModal(true);
return;
}
onStatusChange?.(btn.value);
}}
>
{btn.label}
</Button>
))}
</div>
</Panel>
)}
</div>
);
};