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>}
|
{group.assignedDriverName || <span className="text-[var(--color-text-muted)]">—</span>}
|
||||||
</div>
|
</div>
|
||||||
<div className="px-3 py-1.5">
|
<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>
|
<Badge tone={getOrderGroupStatusTone(group)}>{getOrderGroupDisplayStatusLabel(group)}</Badge>
|
||||||
{(group.hasDeliveryProblem || group.has_delivery_problem) && (
|
{(group.hasDeliveryProblem || group.has_delivery_problem) && (
|
||||||
<span
|
<span
|
||||||
title={group.deliveryProblemNote || group.delivery_problem_note || "Есть проблемы с отгрузкой позиций"}
|
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>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ const DriverAssignmentPanel = ({
|
||||||
Водитель назначен
|
Водитель назначен
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-1 text-lg font-semibold">
|
<p className="mt-1 text-lg font-semibold">
|
||||||
{order.assignedDriverName || "Неизвестно"}
|
{order.assignedDriverName || "Водитель назначен"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Badge tone="accent">Назначен</Badge>
|
<Badge tone="accent">Назначен</Badge>
|
||||||
|
|
|
||||||
|
|
@ -2,42 +2,60 @@
|
||||||
const DriverShipmentReport = ({ shipmentData }) => {
|
const DriverShipmentReport = ({ shipmentData }) => {
|
||||||
if (!Array.isArray(shipmentData) || shipmentData.length === 0) return null;
|
if (!Array.isArray(shipmentData) || shipmentData.length === 0) return null;
|
||||||
|
|
||||||
|
const deliveredItems = shipmentData.filter((i) => i.shipped);
|
||||||
|
const notDeliveredItems = shipmentData.filter((i) => !i.shipped);
|
||||||
|
|
||||||
return (
|
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">
|
<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}>
|
<span className="text-lg">📋</span>
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
<strong>Отчёт об отгрузке</strong>
|
||||||
</svg>
|
<Badge tone={notDeliveredItems.length > 0 ? "warning" : "accent"}>
|
||||||
<strong className="text-[var(--color-warning)]">Проблемы с доставкой позиций</strong>
|
{deliveredItems.length}/{shipmentData.length} доставлено
|
||||||
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-[var(--color-text-muted)]">
|
|
||||||
Не доставлено {shipmentData.length} {shipmentData.length === 1 ? "позиция" : shipmentData.length < 5 ? "позиции" : "позиций"}. Остальное — доставлено.
|
{notDeliveredItems.length > 0 && (
|
||||||
</p>
|
<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">
|
<div className="space-y-2">
|
||||||
{shipmentData.map((item) => (
|
{shipmentData.map((item) => (
|
||||||
<div
|
<div
|
||||||
key={item.id || item.name}
|
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">
|
<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 ? (
|
{item.quantity || item.unit ? (
|
||||||
<Badge tone="neutral">{[item.quantity, item.unit].filter(Boolean).join(" ")}</Badge>
|
<Badge tone="neutral">{[item.quantity, item.unit].filter(Boolean).join(" ")}</Badge>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{item.comment ? (
|
{!item.shipped && item.comment && (
|
||||||
<p className="mt-1 text-xs text-[var(--color-text-muted)]">Причина: {item.comment}</p>
|
<p className="mt-1 ml-6 text-xs text-[var(--color-warning)]">Причина: {item.comment}</p>
|
||||||
) : (
|
|
||||||
<p className="mt-1 text-xs text-[var(--color-text-muted)] italic">Причина не указана</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</Panel>
|
</Panel>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { formatDateTime } from "../../utils/formatters";
|
import { formatDateTime } from "../../utils/formatters";
|
||||||
import { Badge } from "../UI/Badge";
|
import { Badge } from "../UI/Badge";
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ const NOTIF_LABELS = {
|
||||||
paid_storage_sending: "Отправляется…",
|
paid_storage_sending: "Отправляется…",
|
||||||
paid_storage_sent: "Платное хранение: отправлено",
|
paid_storage_sent: "Платное хранение: отправлено",
|
||||||
draft: "Черновик",
|
draft: "Черновик",
|
||||||
confirmed: "Согласовано",
|
confirmed: "Клиент согласовал дату",
|
||||||
};
|
};
|
||||||
|
|
||||||
const NOTIF_TONES = {
|
const NOTIF_TONES = {
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ const StatusActionPanel = ({
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{allStatuses.map((statusOption) => {
|
{allStatuses.map((statusOption) => {
|
||||||
const isCurrent = currentStatus === statusOption.value;
|
const isCurrent = currentStatus === statusOption.value;
|
||||||
const isClickable = statusOption.manual !== false && !isCurrent;
|
const isClickable = (statusOption.manual !== false || currentStatus === 'problem') && !isCurrent;
|
||||||
const blockedBySchedule = statusOption.requiresSchedule && !hasDeliverySchedule;
|
const blockedBySchedule = statusOption.requiresSchedule && !hasDeliverySchedule;
|
||||||
return (
|
return (
|
||||||
<div key={statusOption.value} className="relative group">
|
<div key={statusOption.value} className="relative group">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue