fix: add explicit text color and order composition to OrderDetailPanel

This commit is contained in:
Codex 2026-05-19 10:09:55 +03:00
parent f6bbfe1ba2
commit 0056e71b39
1 changed files with 84 additions and 26 deletions

View File

@ -38,6 +38,33 @@ const renderList = (values) => {
const renderValue = (value) => value || "Нет данных"; const renderValue = (value) => value || "Нет данных";
const parseOrderList = (order) => {
if (!order) return [];
if (order.order_list_structured) {
try {
const parsed = JSON.parse(order.order_list_structured);
if (parsed && Array.isArray(parsed.orders)) {
return parsed.orders;
}
} catch {
/* ignore */
}
}
if (order.order_list) {
try {
const parsed = JSON.parse(order.order_list);
if (Array.isArray(parsed)) {
return parsed;
}
} catch {
/* ignore */
}
}
return [];
};
const getErrorMessage = (error, fallbackMessage) => { const getErrorMessage = (error, fallbackMessage) => {
if (!error) { if (!error) {
return fallbackMessage; return fallbackMessage;
@ -331,19 +358,19 @@ export const OrderDetailPanel = ({
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]"> <p className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]">
Дата доставки Дата доставки
</p> </p>
<p className="mt-1 text-base font-medium text-[var(--color-text)]">{formatDeliveryDateDisplay(order.deliveryDate)}</p> <p className="mt-1 text-base font-medium !text-[var(--color-text)]">{formatDeliveryDateDisplay(order.deliveryDate)}</p>
</div> </div>
<div> <div>
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]"> <p className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]">
Время доставки Время доставки
</p> </p>
<p className="mt-1 text-base font-medium text-[var(--color-text)]">{renderValue(order.deliveryTime || order.deliveryHalfDay)}</p> <p className="mt-1 text-base font-medium !text-[var(--color-text)]">{renderValue(order.deliveryTime || order.deliveryHalfDay)}</p>
</div> </div>
<div> <div>
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]"> <p className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]">
Водитель Водитель
</p> </p>
<p className="mt-1 text-base font-medium text-[var(--color-text)]">{order.assignedDriverId ? renderValue(order.assignedDriverName) : "Не назначен"}</p> <p className="mt-1 text-base font-medium !text-[var(--color-text)]">{order.assignedDriverId ? renderValue(order.assignedDriverName) : "Не назначен"}</p>
</div> </div>
<div> <div>
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]"> <p className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]">
@ -351,7 +378,7 @@ export const OrderDetailPanel = ({
</p> </p>
<a <a
href={`tel:${normalizePhoneForTel(order.customerPhone)}`} href={`tel:${normalizePhoneForTel(order.customerPhone)}`}
className="mt-1 block text-base font-medium text-[var(--color-accent)] hover:underline" className="mt-1 block text-base font-medium !text-[var(--color-accent)] hover:underline"
> >
{renderValue(order.customerPhone)} {renderValue(order.customerPhone)}
</a> </a>
@ -360,42 +387,42 @@ export const OrderDetailPanel = ({
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]"> <p className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-text-muted)]">
Адрес доставки Адрес доставки
</p> </p>
<p className="mt-1 text-base font-medium text-[var(--color-text)]">{renderValue(order.deliveryAddress)}</p> <p className="mt-1 text-base font-medium !text-[var(--color-text)]">{renderValue(order.deliveryAddress)}</p>
</div> </div>
</div> </div>
<div className="grid gap-x-4 gap-y-2 grid-cols-2 md:grid-cols-4"> <div className="grid gap-x-4 gap-y-2 grid-cols-2 md:grid-cols-4">
<div> <div>
<p className="text-xs text-[var(--color-text-muted)]">Номер счёта</p> <p className="text-xs text-[var(--color-text-muted)]">Номер счёта</p>
<p className="font-medium text-[var(--color-text)]">{renderValue(order.orderNumberSummary)}</p> <p className="font-medium !text-[var(--color-text)]">{renderValue(order.orderNumberSummary)}</p>
</div> </div>
<div> <div>
<p className="text-xs text-[var(--color-text-muted)]">Клиент</p> <p className="text-xs text-[var(--color-text-muted)]">Клиент</p>
<p className="font-medium text-[var(--color-text)]">{renderValue(order.customerName)}</p> <p className="font-medium !text-[var(--color-text)]">{renderValue(order.customerName)}</p>
</div> </div>
<div> <div>
<p className="text-xs text-[var(--color-text-muted)]">Дата счёта</p> <p className="text-xs text-[var(--color-text-muted)]">Дата счёта</p>
<p className="font-medium text-[var(--color-text)]">{renderValue(order.customerDate)}</p> <p className="font-medium !text-[var(--color-text)]">{renderValue(order.customerDate)}</p>
</div> </div>
<div> <div>
<p className="text-xs text-[var(--color-text-muted)]">Всего заказов</p> <p className="text-xs text-[var(--color-text-muted)]">Всего заказов</p>
<p className="font-medium text-[var(--color-text)]">{order.ordersCount ?? 0}</p> <p className="font-medium !text-[var(--color-text)]">{order.ordersCount ?? 0}</p>
</div> </div>
<div> <div>
<p className="text-xs text-[var(--color-text-muted)]">Готово</p> <p className="text-xs text-[var(--color-text-muted)]">Готово</p>
<p className="font-medium text-[var(--color-text)]">{order.readyCount ?? 0}</p> <p className="font-medium !text-[var(--color-text)]">{order.readyCount ?? 0}</p>
</div> </div>
<div> <div>
<p className="text-xs text-[var(--color-text-muted)]">Не готово</p> <p className="text-xs text-[var(--color-text-muted)]">Не готово</p>
<p className="font-medium text-[var(--color-text)]">{order.notReadyCount ?? 0}</p> <p className="font-medium !text-[var(--color-text)]">{order.notReadyCount ?? 0}</p>
</div> </div>
<div> <div>
<p className="text-xs text-[var(--color-text-muted)]">Обновлена</p> <p className="text-xs text-[var(--color-text-muted)]">Обновлена</p>
<p className="font-medium text-[var(--color-text)]">{formatDateTime(order.updatedAt)}</p> <p className="font-medium !text-[var(--color-text)]">{formatDateTime(order.updatedAt)}</p>
</div> </div>
<div> <div>
<p className="text-xs text-[var(--color-text-muted)]">Статус доставки</p> <p className="text-xs text-[var(--color-text-muted)]">Статус доставки</p>
<p className="font-medium text-[var(--color-text)]">{getOrderGroupDeliveryStatusLabel(order.deliveryStatus || order.delivery_status)}</p> <p className="font-medium !text-[var(--color-text)]">{getOrderGroupDeliveryStatusLabel(order.deliveryStatus || order.delivery_status)}</p>
</div> </div>
</div> </div>
</Panel> </Panel>
@ -491,7 +518,7 @@ export const OrderDetailPanel = ({
</p> </p>
</div> </div>
{isDeliveryAgreed ? ( {isDeliveryAgreed ? (
<div className="rounded-[24px] border border-[rgba(18,128,92,0.35)] bg-[var(--color-accent-soft)] p-4 text-[var(--color-text)]"> <div className="rounded-[24px] border border-[rgba(18,128,92,0.35)] bg-[var(--color-accent-soft)] p-4 !text-[var(--color-text)]">
<div className="flex flex-wrap items-center justify-between gap-3"> <div className="flex flex-wrap items-center justify-between gap-3">
<div> <div>
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-accent)]"> <p className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--color-accent)]">
@ -511,7 +538,7 @@ export const OrderDetailPanel = ({
type="button" type="button"
aria-label="Дата доставки" aria-label="Дата доставки"
aria-expanded={isCalendarOpen} aria-expanded={isCalendarOpen}
className="flex min-h-[54px] w-full items-center justify-between rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface)] px-4 text-left text-sm font-medium text-[var(--color-text)] transition hover:border-[var(--color-accent)] focus:border-[var(--color-accent)] focus:outline-none" className="flex min-h-[54px] w-full items-center justify-between rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface)] px-4 text-left text-sm font-medium !text-[var(--color-text)] transition hover:border-[var(--color-accent)] focus:border-[var(--color-accent)] focus:outline-none"
onClick={() => setIsCalendarOpen((current) => !current)} onClick={() => setIsCalendarOpen((current) => !current)}
> >
<span>{formatDateForDisplay(deliveryDate)}</span> <span>{formatDateForDisplay(deliveryDate)}</span>
@ -536,7 +563,7 @@ export const OrderDetailPanel = ({
type="button" type="button"
disabled={!canGoBack} disabled={!canGoBack}
aria-label="Предыдущий месяц" aria-label="Предыдущий месяц"
className="flex h-9 w-9 items-center justify-center rounded-full border border-[var(--color-border)] text-sm text-[var(--color-text-muted)] transition hover:border-[var(--color-accent)] hover:text-[var(--color-text)] disabled:cursor-not-allowed disabled:opacity-40" className="flex h-9 w-9 items-center justify-center rounded-full border border-[var(--color-border)] text-sm text-[var(--color-text-muted)] transition hover:border-[var(--color-accent)] hover:!text-[var(--color-text)] disabled:cursor-not-allowed disabled:opacity-40"
onClick={() => setCurrentMonth((month) => addMonths(month, -1))} onClick={() => setCurrentMonth((month) => addMonths(month, -1))}
> >
@ -544,7 +571,7 @@ export const OrderDetailPanel = ({
<button <button
type="button" type="button"
aria-label="Следующий месяц" aria-label="Следующий месяц"
className="flex h-9 w-9 items-center justify-center rounded-full border border-[var(--color-border)] text-sm text-[var(--color-text-muted)] transition hover:border-[var(--color-accent)] hover:text-[var(--color-text)]" className="flex h-9 w-9 items-center justify-center rounded-full border border-[var(--color-border)] text-sm text-[var(--color-text-muted)] transition hover:border-[var(--color-accent)] hover:!text-[var(--color-text)]"
onClick={() => setCurrentMonth((month) => addMonths(month, 1))} onClick={() => setCurrentMonth((month) => addMonths(month, 1))}
> >
@ -580,10 +607,10 @@ export const OrderDetailPanel = ({
className={[ className={[
"relative flex aspect-square items-center justify-center rounded-xl border text-sm font-semibold transition", "relative flex aspect-square items-center justify-center rounded-xl border text-sm font-semibold transition",
isSelected isSelected
? "border-[var(--color-accent)] bg-[var(--color-accent-soft)] text-[var(--color-text)]" ? "border-[var(--color-accent)] bg-[var(--color-accent-soft)] !text-[var(--color-text)]"
: isWeekend : isWeekend
? "border-dashed border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-text-muted)]" ? "border-dashed border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-text-muted)]"
: "border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-text-muted)] hover:border-[var(--color-accent)] hover:text-[var(--color-text)]", : "border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-text-muted)] hover:border-[var(--color-accent)] hover:!text-[var(--color-text)]",
isDisabled ? "cursor-not-allowed opacity-45" : "", isDisabled ? "cursor-not-allowed opacity-45" : "",
].join(" ")} ].join(" ")}
onClick={() => { onClick={() => {
@ -622,8 +649,8 @@ export const OrderDetailPanel = ({
className={[ className={[
"min-h-[54px] rounded-2xl border px-4 text-left text-sm font-medium transition", "min-h-[54px] rounded-2xl border px-4 text-left text-sm font-medium transition",
deliveryTime === option deliveryTime === option
? "border-[var(--color-accent)] bg-[var(--color-accent-soft)] text-[var(--color-text)]" ? "border-[var(--color-accent)] bg-[var(--color-accent-soft)] !text-[var(--color-text)]"
: "border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-text-muted)] hover:border-[var(--color-accent)] hover:text-[var(--color-text)]", : "border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-text-muted)] hover:border-[var(--color-accent)] hover:!text-[var(--color-text)]",
].join(" ")} ].join(" ")}
onClick={() => { onClick={() => {
setDeliveryTime(option); setDeliveryTime(option);
@ -654,6 +681,37 @@ export const OrderDetailPanel = ({
{renderList(order.orderNumbers)} {renderList(order.orderNumbers)}
</Panel> </Panel>
<Panel className="space-y-4 p-5">
<strong>Состав заказа</strong>
<div className="space-y-3">
{(() => {
const orders = parseOrderList(order);
if (!orders.length) {
return <p className="text-sm text-[var(--color-text-muted)]">Нет данных</p>;
}
return orders.map((orderItem, idx) => (
<div key={idx} className="rounded-[20px] border border-[var(--color-border)] bg-[var(--color-surface-strong)] p-4">
<p className="font-medium !text-[var(--color-text)] mb-2">{orderItem.nom || `Заказ ${idx + 1}`}</p>
{orderItem.items && orderItem.items.length > 0 ? (
<div className="space-y-1">
{orderItem.items.map((item, itemIdx) => (
<div key={itemIdx} className="flex justify-between text-sm">
<span className="text-[var(--color-text)]">{item.product_name}</span>
<span className="text-[var(--color-text-muted)]">
{item.product_quantity} {item.product_ed}
</span>
</div>
))}
</div>
) : (
<p className="text-sm text-[var(--color-text-muted)]">Позиции не указаны</p>
)}
</div>
));
})()}
</div>
</Panel>
{userRole !== "driver" ? ( {userRole !== "driver" ? (
<Panel className="space-y-4 p-5"> <Panel className="space-y-4 p-5">
<strong>Дополнительные данные</strong> <strong>Дополнительные данные</strong>
@ -661,29 +719,29 @@ export const OrderDetailPanel = ({
{order.firstSmsSentAt ? ( {order.firstSmsSentAt ? (
<div> <div>
<p className="text-xs text-[var(--color-text-muted)]">1-е SMS отправлено</p> <p className="text-xs text-[var(--color-text-muted)]">1-е SMS отправлено</p>
<p className="mt-1 font-medium text-[var(--color-text)]">{formatDateTime(order.firstSmsSentAt)}</p> <p className="mt-1 font-medium !text-[var(--color-text)]">{formatDateTime(order.firstSmsSentAt)}</p>
</div> </div>
) : null} ) : null}
{order.secondSmsSentAt ? ( {order.secondSmsSentAt ? (
<div> <div>
<p className="text-xs text-[var(--color-text-muted)]">2-е SMS отправлено</p> <p className="text-xs text-[var(--color-text-muted)]">2-е SMS отправлено</p>
<p className="mt-1 font-medium text-[var(--color-text)]">{formatDateTime(order.secondSmsSentAt)}</p> <p className="mt-1 font-medium !text-[var(--color-text)]">{formatDateTime(order.secondSmsSentAt)}</p>
</div> </div>
) : null} ) : null}
{!order.firstSmsSentAt && !order.secondSmsSentAt ? ( {!order.firstSmsSentAt && !order.secondSmsSentAt ? (
<div> <div>
<p className="text-xs text-[var(--color-text-muted)]">SMS отправлено</p> <p className="text-xs text-[var(--color-text-muted)]">SMS отправлено</p>
<p className="mt-1 font-medium text-[var(--color-text)]">Нет</p> <p className="mt-1 font-medium !text-[var(--color-text)]">Нет</p>
</div> </div>
) : null} ) : null}
<div> <div>
<p className="text-xs text-[var(--color-text-muted)]">Ручное согласование выполнено</p> <p className="text-xs text-[var(--color-text-muted)]">Ручное согласование выполнено</p>
<p className="mt-1 font-medium text-[var(--color-text)]">{order.manualConfirmationAt ? formatDateTime(order.manualConfirmationAt) : "Нет"}</p> <p className="mt-1 font-medium !text-[var(--color-text)]">{order.manualConfirmationAt ? formatDateTime(order.manualConfirmationAt) : "Нет"}</p>
</div> </div>
{order.createdFromExchangeAt ? ( {order.createdFromExchangeAt ? (
<div> <div>
<p className="text-xs text-[var(--color-text-muted)]">Создано из обмена</p> <p className="text-xs text-[var(--color-text-muted)]">Создано из обмена</p>
<p className="mt-1 font-medium text-[var(--color-text)]">{formatDateTime(order.createdFromExchangeAt)}</p> <p className="mt-1 font-medium !text-[var(--color-text)]">{formatDateTime(order.createdFromExchangeAt)}</p>
</div> </div>
) : null} ) : null}
</div> </div>