supersam/src/components/driver/DriverDeliveryPlanner.jsx

192 lines
7.7 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 from "react";
import {
filterOrderGroups,
getOrderGroupDeliveryHalfDay,
getOrderGroupDeliveryStatusLabel,
getOrderGroupDeliveryStatusTone,
ORDER_GROUP_DELIVERY_HALF_DAY_OPTIONS,
DRIVER_VISIBLE_DELIVERY_STATUSES,
isOrderGroupVisibleToDriver,
groupOrderGroupsByDate,
} from "../../services/orderGroupViews";
import { Badge } from "../UI/Badge";
import { Input } from "../UI/Input";
import { Select } from "../UI/Select";
import { Panel } from "../UI/Panel";
const DRIVER_DELIVERY_STATUS_OPTIONS = [
{ value: "all", label: "Все статусы" },
...DRIVER_VISIBLE_DELIVERY_STATUSES.map((status) => ({
value: status,
label: getOrderGroupDeliveryStatusLabel(status),
})),
];
export const DriverDeliveryPlanner = ({ orderGroups = [], onOpenOrder }) => {
const [filters, setFilters] = React.useState({
dateFrom: "",
dateTo: "",
deliveryHalfDay: "all",
deliveryStatus: "all",
});
const agreedOrderGroups = React.useMemo(
() => orderGroups.filter((group) => isOrderGroupVisibleToDriver(group)),
[orderGroups],
);
const filteredOrderGroups = React.useMemo(
() =>
filterOrderGroups(agreedOrderGroups, {
dateFrom: filters.dateFrom,
dateTo: filters.dateTo,
deliveryHalfDay: filters.deliveryHalfDay,
deliveryStatus: filters.deliveryStatus,
}),
[agreedOrderGroups, filters.dateFrom, filters.dateTo, filters.deliveryHalfDay, filters.deliveryStatus],
);
const groupedOrderGroups = React.useMemo(
() => groupOrderGroupsByDate(filteredOrderGroups),
[filteredOrderGroups],
);
return (
<div className="space-y-4">
<Panel className="space-y-3 p-5">
<div className="space-y-4">
<div className="flex flex-wrap items-center justify-between gap-3">
<div>
<h3 className="text-lg font-semibold">Мои доставки</h3>
<p className="mt-1 text-sm text-[var(--color-text-muted)]">
Показываем только согласованные к доставке группы. Можно сузить список по дате и половине дня.
</p>
</div>
<Badge tone="neutral">{filteredOrderGroups.length}</Badge>
</div>
<div className="grid gap-3 md:grid-cols-[repeat(4,minmax(0,1fr))]">
<label className="flex min-w-0 flex-col gap-2">
<span className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]">
Дата от
</span>
<Input
type="date"
value={filters.dateFrom}
onChange={(event) => setFilters((current) => ({ ...current, dateFrom: event.target.value }))}
/>
</label>
<label className="flex min-w-0 flex-col gap-2">
<span className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]">
Дата до
</span>
<Input
type="date"
value={filters.dateTo}
onChange={(event) => setFilters((current) => ({ ...current, dateTo: event.target.value }))}
/>
</label>
<label className="flex min-w-0 flex-col gap-2">
<span className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]">
Время суток
</span>
<Select
value={filters.deliveryHalfDay}
onChange={(event) =>
setFilters((current) => ({ ...current, deliveryHalfDay: event.target.value }))
}
>
{ORDER_GROUP_DELIVERY_HALF_DAY_OPTIONS.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</Select>
</label>
<label className="flex min-w-0 flex-col gap-2">
<span className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]">
Статус
</span>
<Select
value={filters.deliveryStatus}
onChange={(event) =>
setFilters((current) => ({ ...current, deliveryStatus: event.target.value }))
}
>
{DRIVER_DELIVERY_STATUS_OPTIONS.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</Select>
</label>
</div>
</div>
</Panel>
{groupedOrderGroups.length ? (
groupedOrderGroups.map((group) => (
<Panel key={group.date} className="space-y-4 p-5">
<div className="flex flex-wrap items-center justify-between gap-3">
<div>
<h4 className="text-lg font-semibold capitalize">
{new Date(`${group.date}T12:00:00`).toLocaleDateString("ru-RU", {
day: "numeric",
month: "long",
weekday: "long",
})}
</h4>
<p className="mt-1 text-sm text-[var(--color-text-muted)]">
{group.items.length} {group.items.length === 1 ? "группа" : "группы"}
</p>
</div>
<Badge tone="neutral">{group.date}</Badge>
</div>
<div className="grid gap-3">
{group.items.map((item) => (
<button
key={item.id}
type="button"
className="rounded-[24px] border border-[var(--color-border)] bg-[var(--color-surface-strong)] p-4 text-left transition hover:bg-[var(--color-accent-soft)]"
onClick={() => onOpenOrder?.(item.id)}
>
<div className="flex flex-wrap items-start justify-between gap-3">
<div>
<div className="font-medium text-[var(--color-text)]">
{item.displayTitle || item.customerName || item.groupKey}
</div>
<div className="mt-1 text-sm text-[var(--color-text-muted)]">
{item.customerDate} · {item.customerPhone}
{getOrderGroupDeliveryHalfDay(item) ? ` · ${getOrderGroupDeliveryHalfDay(item)}` : ""}
</div>
</div>
<Badge tone={getOrderGroupDeliveryStatusTone(item.deliveryStatus || item.delivery_status)}>
{getOrderGroupDeliveryStatusLabel(item.deliveryStatus || item.delivery_status)}
</Badge>
</div>
<div className="mt-3 grid gap-2 text-sm text-[var(--color-text-muted)] md:grid-cols-3">
<div>{item.orderNumbers?.[0] || "Номера не указаны"}</div>
<div>
{item.readyCount || 0}/{item.ordersCount || 0} готово
</div>
<div>{item.smsSentAt ? "SMS отправлено" : "SMS не отправлено"}</div>
</div>
</button>
))}
</div>
</Panel>
))
) : (
<Panel className="p-6">
<h4 className="text-lg font-semibold">Доставки не найдены</h4>
<p className="mt-2 text-sm text-[var(--color-text-muted)]">
Сейчас у вас нет назначенных групп доставки.
</p>
</Panel>
)}
</div>
);
};