fix: logistics UI — problem badge overlap, driver name, SMS labels, shipment report
1. LogisticsReadinessBoard: problem badge on separate line (no overlap) 2. DriverAssignmentPanel: show "Водитель назначен" instead of "Неизвестно" when driver name not resolved from join 3. SmsStatusCard: "confirmed" label = "Клиент согласовал дату" (not "Согласовано") 4. StatusActionPanel: when currentStatus=problem, all status buttons clickable (logistician can resolve problem by setting any status) 5. DriverShipmentReport: show ALL items — green check for delivered, red cross for not delivered, with reason comments
This commit is contained in:
parent
1ffd8bacc5
commit
17f93a65ae
|
|
@ -158,12 +158,12 @@ const renderRow = (group, onSelectSet) => (
|
|||
{group.assignedDriverName || <span className="text-[var(--color-text-muted)]">—</span>}
|
||||
</div>
|
||||
<div className="px-3 py-1.5">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Badge tone={getOrderGroupStatusTone(group)}>{getOrderGroupDisplayStatusLabel(group)}</Badge>
|
||||
{(group.hasDeliveryProblem || group.has_delivery_problem) && (
|
||||
<span
|
||||
title={group.deliveryProblemNote || group.delivery_problem_note || "Есть проблемы с отгрузкой позиций"}
|
||||
className="inline-flex items-center gap-0.5 rounded-full bg-[rgba(239,68,68,0.12)] px-1.5 py-0.5 text-[10px] font-bold text-[var(--color-danger)]"
|
||||
className="inline-flex w-fit items-center gap-0.5 rounded-full bg-[rgba(239,68,68,0.12)] px-1.5 py-0.5 text-[10px] font-bold text-[var(--color-danger)]"
|
||||
>
|
||||
⚠ Проблема
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ const DriverAssignmentPanel = ({
|
|||
Водитель назначен
|
||||
</p>
|
||||
<p className="mt-1 text-lg font-semibold">
|
||||
{order.assignedDriverName || "Неизвестно"}
|
||||
{order.assignedDriverName || "Водитель назначен"}
|
||||
</p>
|
||||
</div>
|
||||
<Badge tone="accent">Назначен</Badge>
|
||||
|
|
|
|||
|
|
@ -2,42 +2,60 @@
|
|||
const DriverShipmentReport = ({ shipmentData }) => {
|
||||
if (!Array.isArray(shipmentData) || shipmentData.length === 0) return null;
|
||||
|
||||
const deliveredItems = shipmentData.filter((i) => i.shipped);
|
||||
const notDeliveredItems = shipmentData.filter((i) => !i.shipped);
|
||||
|
||||
return (
|
||||
<Panel className="space-y-4 p-5 border-[var(--color-warning)]">
|
||||
<Panel className="space-y-4 p-5">
|
||||
<div className="flex items-center gap-2">
|
||||
<svg className="h-5 w-5 text-[var(--color-warning)]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<strong className="text-[var(--color-warning)]">Проблемы с доставкой позиций</strong>
|
||||
<span className="text-lg">📋</span>
|
||||
<strong>Отчёт об отгрузке</strong>
|
||||
<Badge tone={notDeliveredItems.length > 0 ? "warning" : "accent"}>
|
||||
{deliveredItems.length}/{shipmentData.length} доставлено
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-sm text-[var(--color-text-muted)]">
|
||||
Не доставлено {shipmentData.length} {shipmentData.length === 1 ? "позиция" : shipmentData.length < 5 ? "позиции" : "позиций"}. Остальное — доставлено.
|
||||
</p>
|
||||
|
||||
{notDeliveredItems.length > 0 && (
|
||||
<div className="rounded-xl border border-[var(--color-warning)] bg-[var(--color-warning-soft)] px-3 py-2 text-sm text-[var(--color-warning)]">
|
||||
⚠ Не доставлено {notDeliveredItems.length} {notDeliveredItems.length === 1 ? "позиция" : notDeliveredItems.length < 5 ? "позиции" : "позиций"}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
{shipmentData.map((item) => (
|
||||
<div
|
||||
key={item.id || item.name}
|
||||
className="rounded-[18px] border border-[var(--color-warning)] bg-[var(--color-warning-soft)] px-4 py-3 text-sm"
|
||||
className={[
|
||||
"rounded-[18px] border px-4 py-3 text-sm",
|
||||
item.shipped
|
||||
? "border-[var(--color-accent)] bg-[var(--color-accent-soft)]"
|
||||
: "border-[var(--color-warning)] bg-[var(--color-warning-soft)]"
|
||||
].join(" ")}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="text-[var(--color-text)]">{item.name}</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={item.shipped ? "text-[var(--color-accent)]" : "text-[var(--color-warning)]"}>
|
||||
{item.shipped ? "✓" : "✗"}
|
||||
</span>
|
||||
<span className={item.shipped ? "text-[var(--color-text-muted)] line-through" : "text-[var(--color-text)] font-medium"}>
|
||||
{item.name}
|
||||
</span>
|
||||
</div>
|
||||
{item.quantity || item.unit ? (
|
||||
<Badge tone="neutral">{[item.quantity, item.unit].filter(Boolean).join(" ")}</Badge>
|
||||
) : null}
|
||||
</div>
|
||||
{item.comment ? (
|
||||
<p className="mt-1 text-xs text-[var(--color-text-muted)]">Причина: {item.comment}</p>
|
||||
) : (
|
||||
<p className="mt-1 text-xs text-[var(--color-text-muted)] italic">Причина не указана</p>
|
||||
{!item.shipped && item.comment && (
|
||||
<p className="mt-1 ml-6 text-xs text-[var(--color-warning)]">Причина: {item.comment}</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</Panel>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
import React from "react";
|
||||
import { formatDateTime } from "../../utils/formatters";
|
||||
import { Badge } from "../UI/Badge";
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const NOTIF_LABELS = {
|
|||
paid_storage_sending: "Отправляется…",
|
||||
paid_storage_sent: "Платное хранение: отправлено",
|
||||
draft: "Черновик",
|
||||
confirmed: "Согласовано",
|
||||
confirmed: "Клиент согласовал дату",
|
||||
};
|
||||
|
||||
const NOTIF_TONES = {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ const StatusActionPanel = ({
|
|||
<div className="flex flex-wrap gap-2">
|
||||
{allStatuses.map((statusOption) => {
|
||||
const isCurrent = currentStatus === statusOption.value;
|
||||
const isClickable = statusOption.manual !== false && !isCurrent;
|
||||
const isClickable = (statusOption.manual !== false || currentStatus === 'problem') && !isCurrent;
|
||||
const blockedBySchedule = statusOption.requiresSchedule && !hasDeliverySchedule;
|
||||
return (
|
||||
<div key={statusOption.value} className="relative group">
|
||||
|
|
|
|||
Loading…
Reference in New Issue