feat(order-detail): collapsible order composition with table layout

- Extracted IIFE into CollapsibleOrderComposition component to fix React hooks error
- Состав заказа свернут по умолчанию, разворачивается по клику
- Позиции отображаются в табличном виде (grid 1fr + auto)
- Количество и единицы измерения не обрезаются (whitespace-nowrap)
- Парсинг работает из source_orders, order_list, order_list_structured
This commit is contained in:
Codex 2026-05-19 15:33:57 +03:00
parent 0d3be0502c
commit a2196d232b
1 changed files with 60 additions and 30 deletions

View File

@ -232,6 +232,64 @@ const normalizeDateForInput = (value) => {
return "";
};
const CollapsibleOrderComposition = ({ order }) => {
const [isExpanded, setIsExpanded] = React.useState(false);
const orders = parseOrderList(order);
const totalPositions = orders.reduce((sum, o) => sum + (o.items?.length || 0), 0);
return (
<div className="space-y-3">
<button
type="button"
className="flex w-full items-center justify-between text-left"
onClick={() => setIsExpanded(!isExpanded)}
>
<span className="font-semibold">Состав заказа</span>
<span className="flex items-center gap-2 text-sm text-[var(--color-text-muted)]">
{totalPositions > 0 ? `${totalPositions} поз.` : ''}
<svg
className="h-4 w-4 transition-transform"
style={{ transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)' }}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
</svg>
</span>
</button>
{isExpanded && (
<div className="space-y-3">
{!orders.length ? (
<p className="text-sm text-[var(--color-text-muted)]">Позиции не указаны</p>
) : (
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-3 text-sm">{orderItem.nom || orderItem.name || `Заказ ${idx + 1}`}</p>
{orderItem.items && orderItem.items.length > 0 ? (
<div className="space-y-2">
{orderItem.items.map((item, itemIdx) => (
<div key={itemIdx} className="grid grid-cols-[1fr_auto] gap-x-4 gap-y-1 text-sm">
<span className="text-[var(--color-text)] min-w-0">{item.product_name || item.name || item.title || ''}</span>
<span className="text-[var(--color-text-muted)] whitespace-nowrap text-right">
{item.product_quantity || item.quantity || item.count || item.amount || ""} {item.product_ed || item.unit || ""}
</span>
</div>
))}
</div>
) : (
<p className="text-sm text-[var(--color-text-muted)]">Позиции не указаны</p>
)}
</div>
))
)}
</div>
)}
</div>
);
};
export const OrderDetailPanel = ({
order,
canManageDelivery = false,
@ -692,37 +750,9 @@ export const OrderDetailPanel = ({
{renderList(order.orderNumbers)}
</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 || orderItem.name || `Заказ ${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 || item.name || item.title || JSON.stringify(item)}</span>
<span className="text-[var(--color-text-muted)]">
{item.product_quantity || item.quantity || item.count || item.amount || ""} {item.product_ed || item.unit || ""}
</span>
</div>
))}
</div>
) : (
<p className="text-sm text-[var(--color-text-muted)]">Позиции не указаны</p>
)}
</div>
));
})()}
</div>
<Panel className="space-y-4 p-5">
<CollapsibleOrderComposition order={order} />
</Panel>
{userRole !== "driver" ? (
<Panel className="space-y-4 p-5">
<strong>Дополнительные данные</strong>