fix: date format dd.MM.yyyy in logistics board, align columns via CSS grid
- LogisticsReadinessBoard: formatDate() for delivery date (was raw yyyy-MM-dd) - LogisticsReadinessBoard: replaced <table> with CSS grid for aligned columns - OrdersTable: replaced <table> with CSS grid for aligned columns - Both tables: 6-column grid with fixed proportions, all columns visible
This commit is contained in:
parent
d313a3b6b9
commit
491b7705fd
|
|
@ -10,7 +10,7 @@ import { Badge } from "../UI/Badge";
|
||||||
import { Panel } from "../UI/Panel";
|
import { Panel } from "../UI/Panel";
|
||||||
import { SkeletonPage } from "../UI/Loading";
|
import { SkeletonPage } from "../UI/Loading";
|
||||||
import { OrderFilters } from "../orders/OrderFilters";
|
import { OrderFilters } from "../orders/OrderFilters";
|
||||||
import { formatDateTime } from "../../utils/formatters";
|
import { formatDate, formatDateTime } from "../../utils/formatters";
|
||||||
|
|
||||||
export const LogisticsReadinessBoard = ({ orderGroups = [], onSelectSet, statusOptions = ORDER_GROUP_DISPLAY_STATUS_OPTIONS, isLoading = false }) => {
|
export const LogisticsReadinessBoard = ({ orderGroups = [], onSelectSet, statusOptions = ORDER_GROUP_DISPLAY_STATUS_OPTIONS, isLoading = false }) => {
|
||||||
const [filters, setFilters] = React.useState({ query: "", displayStatus: "all", city: "" });
|
const [filters, setFilters] = React.useState({ query: "", displayStatus: "all", city: "" });
|
||||||
|
|
@ -60,17 +60,17 @@ export const LogisticsReadinessBoard = ({ orderGroups = [], onSelectSet, statusO
|
||||||
|
|
||||||
const totalGroups = filteredGroups.length;
|
const totalGroups = filteredGroups.length;
|
||||||
|
|
||||||
|
const COLS = "grid-cols-[minmax(140px,2fr)_minmax(80px,1fr)_minmax(100px,1.2fr)_minmax(80px,1fr)_minmax(100px,1fr)_minmax(120px,1fr)]";
|
||||||
|
|
||||||
const TableHeader = () => (
|
const TableHeader = () => (
|
||||||
<thead className="bg-[var(--color-surface-strong)] text-left text-xs uppercase tracking-[0.14em] text-[var(--color-text-muted)]">
|
<div className={`grid ${COLS} gap-0 border-b border-[var(--color-border)] bg-[var(--color-surface-strong)] text-xs uppercase tracking-[0.14em] text-[var(--color-text-muted)]`}>
|
||||||
<tr>
|
<div className="px-4 py-3 font-medium">Клиент</div>
|
||||||
<th className="px-4 py-3 font-medium">Клиент</th>
|
<div className="px-4 py-3 font-medium">Город</div>
|
||||||
<th className="px-4 py-3 font-medium hidden sm:table-cell">Город</th>
|
<div className="px-4 py-3 font-medium">Дата доставки</div>
|
||||||
<th className="px-4 py-3 font-medium hidden md:table-cell">Дата доставки</th>
|
<div className="px-4 py-3 font-medium">Водитель</div>
|
||||||
<th className="px-4 py-3 font-medium hidden lg:table-cell">Водитель</th>
|
<div className="px-4 py-3 font-medium">Статус</div>
|
||||||
<th className="px-4 py-3 font-medium">Статус</th>
|
<div className="px-4 py-3 font-medium">Обновлён</div>
|
||||||
<th className="px-4 py-3 font-medium hidden md:table-cell">Обновлён</th>
|
</div>
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|
@ -146,42 +146,38 @@ export const LogisticsReadinessBoard = ({ orderGroups = [], onSelectSet, statusO
|
||||||
|
|
||||||
{!isCollapsed && (
|
{!isCollapsed && (
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="min-w-full border-collapse border-t border-[var(--color-border)]">
|
<TableHeader />
|
||||||
<TableHeader />
|
{groups.map((group) => (
|
||||||
<tbody>
|
<button
|
||||||
{groups.map((group) => (
|
key={group.id}
|
||||||
<tr
|
type="button"
|
||||||
key={group.id}
|
className={`grid ${COLS} gap-0 w-full border-t border-[var(--color-border)] text-left transition hover:bg-[var(--color-accent-soft)]`}
|
||||||
className="cursor-pointer border-t border-[var(--color-border)] transition hover:bg-[var(--color-accent-soft)]"
|
onClick={() => { if (onSelectSet) onSelectSet(group.id); }}
|
||||||
onClick={() => { if (onSelectSet) onSelectSet(group.id); }}
|
>
|
||||||
>
|
<div className="px-4 py-2.5">
|
||||||
<td className="px-4 py-2.5">
|
<div className="font-medium">{group.displayTitle || group.customerName || group.groupKey}</div>
|
||||||
<div className="font-medium">{group.displayTitle || group.customerName || group.groupKey}</div>
|
<div className="text-xs text-[var(--color-text-muted)]">{group.customerPhone || "—"}</div>
|
||||||
<div className="text-xs text-[var(--color-text-muted)] sm:hidden">{group.customerPhone || "—"}</div>
|
</div>
|
||||||
<div className="text-xs text-[var(--color-text-muted)] md:hidden">{group.deliveryDate || "—"}</div>
|
<div className="px-4 py-2.5 text-sm">
|
||||||
</td>
|
{group.city || group.customerAddress || "—"}
|
||||||
<td className="px-4 py-2.5 text-sm hidden sm:table-cell">
|
</div>
|
||||||
{group.city || group.customerAddress || "—"}
|
<div className="px-4 py-2.5 text-sm">
|
||||||
</td>
|
{group.deliveryDate
|
||||||
<td className="px-4 py-2.5 text-sm hidden md:table-cell">
|
? <span>{formatDate(group.deliveryDate)}{group.deliveryTime ? <span className="text-[var(--color-text-muted)]"> · {group.deliveryTime}</span> : ""}</span>
|
||||||
{group.deliveryDate
|
: <span className="text-[var(--color-text-muted)]">—</span>
|
||||||
? <span>{group.deliveryDate}{group.deliveryTime ? <span className="text-[var(--color-text-muted)]"> · {group.deliveryTime}</span> : ""}</span>
|
}
|
||||||
: <span className="text-[var(--color-text-muted)]">—</span>
|
</div>
|
||||||
}
|
<div className="px-4 py-2.5 text-sm">
|
||||||
</td>
|
{group.assignedDriverName || <span className="text-[var(--color-text-muted)]">—</span>}
|
||||||
<td className="px-4 py-2.5 text-sm hidden lg:table-cell">
|
</div>
|
||||||
{group.assignedDriverName || <span className="text-[var(--color-text-muted)]">—</span>}
|
<div className="px-4 py-2.5">
|
||||||
</td>
|
<Badge tone={getOrderGroupStatusTone(group)}>{getOrderGroupDisplayStatusLabel(group)}</Badge>
|
||||||
<td className="px-4 py-2.5">
|
</div>
|
||||||
<Badge tone={getOrderGroupStatusTone(group)}>{getOrderGroupDisplayStatusLabel(group)}</Badge>
|
<div className="px-4 py-2.5 text-sm text-[var(--color-text-muted)]">
|
||||||
</td>
|
{formatDateTime(group.updatedAt)}
|
||||||
<td className="px-4 py-2.5 text-sm text-[var(--color-text-muted)] hidden md:table-cell">
|
</div>
|
||||||
{formatDateTime(group.updatedAt)}
|
</button>
|
||||||
</td>
|
))}
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
|
||||||
|
|
@ -141,57 +141,51 @@ export const OrdersTable = ({
|
||||||
Группы не найдены. Попробуйте изменить поиск или статус.
|
Группы не найдены. Попробуйте изменить поиск или статус.
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<table className="min-w-full border-collapse">
|
<div>
|
||||||
<thead className="bg-[var(--color-surface-strong)] text-left text-xs uppercase tracking-[0.16em] text-[var(--color-text-muted)]">
|
<div className="grid grid-cols-[minmax(180px,2.5fr)_minmax(100px,1.2fr)_minmax(100px,1fr)_minmax(80px,1fr)_minmax(120px,1.2fr)_minmax(130px,1.2fr)] gap-0 border-b border-[var(--color-border)] bg-[var(--color-surface-strong)] text-xs uppercase tracking-[0.16em] text-[var(--color-text-muted)]">
|
||||||
<tr>
|
<div className="px-5 py-4 font-medium">Группа / Клиент</div>
|
||||||
<th className="px-5 py-4 font-medium">Группа / Клиент</th>
|
<div className="px-5 py-4 font-medium">Счёта</div>
|
||||||
<th className="px-5 py-4 font-medium">Счёта</th>
|
<div className="px-5 py-4 font-medium">Статус</div>
|
||||||
<th className="px-5 py-4 font-medium">Статус</th>
|
<div className="px-5 py-4 font-medium">Водитель</div>
|
||||||
<th className="px-5 py-4 font-medium">Водитель</th>
|
<div className="px-5 py-4 font-medium">Дата доставки</div>
|
||||||
<th className="px-5 py-4 font-medium">Дата доставки</th>
|
<div className="px-5 py-4 font-medium">Обновлён</div>
|
||||||
<th className="px-5 py-4 font-medium">Обновлён</th>
|
</div>
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{orderGroups.map((group) => (
|
{orderGroups.map((group) => (
|
||||||
<tr
|
<button
|
||||||
key={group.id}
|
key={group.id}
|
||||||
className={[
|
type="button"
|
||||||
"cursor-pointer border-t border-[var(--color-border)] transition hover:bg-[var(--color-accent-soft)]",
|
className={`grid grid-cols-[minmax(180px,2.5fr)_minmax(100px,1.2fr)_minmax(100px,1fr)_minmax(80px,1fr)_minmax(120px,1.2fr)_minmax(130px,1.2fr)] gap-0 w-full border-t border-[var(--color-border)] text-left transition hover:bg-[var(--color-accent-soft)] ${selectedOrderGroupId === group.id ? "bg-[var(--color-accent-soft)]" : ""}`}
|
||||||
selectedOrderGroupId === group.id ? "bg-[var(--color-accent-soft)]" : "",
|
|
||||||
].join(" ")}
|
|
||||||
onClick={() => onOpenOrder(group.id)}
|
onClick={() => onOpenOrder(group.id)}
|
||||||
>
|
>
|
||||||
<td className="px-5 py-4">
|
<div className="px-5 py-4">
|
||||||
<div className="font-medium">{group.displayTitle || group.customerName || group.groupKey}</div>
|
<div className="font-medium">{group.displayTitle || group.customerName || group.groupKey}</div>
|
||||||
<div className="mt-1 text-sm text-[var(--color-text-muted)]">
|
<div className="mt-1 text-sm text-[var(--color-text-muted)]">
|
||||||
{[group.customerName, group.customerPhone].filter(Boolean).join(" · ")}
|
{[group.customerName, group.customerPhone].filter(Boolean).join(" · ")}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-[var(--color-text-muted)]">{group.groupKey}</div>
|
<div className="text-xs text-[var(--color-text-muted)]">{group.groupKey}</div>
|
||||||
</td>
|
</div>
|
||||||
<td className="max-w-[260px] px-5 py-4 text-sm text-[var(--color-text-muted)]">
|
<div className="max-w-[260px] px-5 py-4 text-sm text-[var(--color-text-muted)]">
|
||||||
{renderOrderNumbers(group)}
|
{renderOrderNumbers(group)}
|
||||||
</td>
|
</div>
|
||||||
<td className="px-5 py-4 text-center">
|
<div className="px-5 py-4">
|
||||||
<Badge tone={getOrderGroupStatusTone(group)}>{getOrderGroupDisplayStatusLabel(group)}</Badge>
|
<Badge tone={getOrderGroupStatusTone(group)}>{getOrderGroupDisplayStatusLabel(group)}</Badge>
|
||||||
</td>
|
</div>
|
||||||
<td className="px-5 py-4 text-sm">
|
<div className="px-5 py-4 text-sm">
|
||||||
{group.assignedDriverName || <span className="text-[var(--color-text-muted)]">—</span>}
|
{group.assignedDriverName || <span className="text-[var(--color-text-muted)]">—</span>}
|
||||||
</td>
|
</div>
|
||||||
<td className="px-5 py-4 text-sm">
|
<div className="px-5 py-4 text-sm">
|
||||||
{group.deliveryDate ? (
|
{group.deliveryDate ? (
|
||||||
<span>{fmtDate(group.deliveryDate)}{group.deliveryTime ? <span className="text-[var(--color-text-muted)]"> · {group.deliveryTime}</span> : ""}</span>
|
<span>{fmtDate(group.deliveryDate)}{group.deliveryTime ? <span className="text-[var(--color-text-muted)]"> · {group.deliveryTime}</span> : ""}</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-[var(--color-text-muted)]">—</span>
|
<span className="text-[var(--color-text-muted)]">—</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</div>
|
||||||
<td className="px-5 py-4 text-sm text-[var(--color-text-muted)]">
|
<div className="px-5 py-4 text-sm text-[var(--color-text-muted)]">
|
||||||
{formatDateTime(group.updatedAt)}
|
{formatDateTime(group.updatedAt)}
|
||||||
</td>
|
</div>
|
||||||
</tr>
|
</button>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</div>
|
||||||
</table>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue