Make product stock list collapsible

This commit is contained in:
lizerk 2026-08-18 10:06:08 +03:00
parent 0b63da0876
commit 4118f570fb

View File

@ -85,6 +85,7 @@ export default function ProductDetailPage() {
const [previewImage, setPreviewImage] = useState<CommerceProductImage | null>(null);
const [descriptionExpanded, setDescriptionExpanded] = useState(false);
const [expandedChannelKey, setExpandedChannelKey] = useState<string | null>(null);
const [stockExpanded, setStockExpanded] = useState(false);
const permissions = user?.auth?.permissions_by_app?.commerce ?? [];
const canManage = Boolean(
user?.auth?.is_staff ||
@ -357,29 +358,62 @@ export default function ProductDetailPage() {
</article>
<article className="app-surface min-w-0 rounded-2xl p-4">
<div className="mb-4 flex items-center gap-2">
<PackageCheck className="h-4 w-4" />
<h2 className="font-semibold">Остатки по источникам</h2>
</div>
{metrics.stock.length === 0 ? (
<p className="text-sm text-[var(--muted-foreground)]">Снимков остатков пока нет.</p>
<>
<div className="mb-3 flex items-center gap-2">
<PackageCheck className="h-4 w-4" />
<h2 className="font-semibold">Остатки по источникам</h2>
</div>
<p className="text-sm text-[var(--muted-foreground)]">Снимков остатков пока нет.</p>
</>
) : (
<div className="space-y-3">
{metrics.stock.map((stock) => (
<div key={`${stock.source}:${stock.warehouse_external_id}`} className="app-surface-muted rounded-lg p-3">
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-sm font-medium">{stock.warehouse_name || stock.warehouse_external_id}</p>
<p className="text-xs text-[var(--muted-foreground)]">{stock.source}</p>
<>
<button
type="button"
onClick={() => setStockExpanded((current) => !current)}
className="flex w-full min-w-0 items-center gap-3 text-left"
aria-expanded={stockExpanded}
aria-controls="product-stock-details"
>
<span className="app-action-secondary inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg">
<PackageCheck className="h-4 w-4" />
</span>
<span className="min-w-0 flex-1">
<span className="block font-semibold">Остатки по источникам</span>
<span className="app-text-muted mt-1 block text-xs">
Складов: {metrics.stock.length} · {formatDecimal(
String(metrics.stock.reduce(
(total, stock) => total + Number(stock.available),
0,
)),
4,
)} доступно
</span>
</span>
<ChevronDown
className={`app-text-muted h-4 w-4 shrink-0 transition-transform ${stockExpanded ? "rotate-180" : ""}`}
/>
</button>
{stockExpanded ? (
<div id="product-stock-details" className="mt-4 space-y-3">
{metrics.stock.map((stock) => (
<div key={`${stock.source}:${stock.warehouse_external_id}`} className="app-surface-muted rounded-lg p-3">
<div className="flex items-center justify-between gap-3">
<div>
<p className="text-sm font-medium">{stock.warehouse_name || stock.warehouse_external_id}</p>
<p className="text-xs text-[var(--muted-foreground)]">{stock.source}</p>
</div>
<p className="text-sm font-semibold">{formatDecimal(stock.available, 4)} доступно</p>
</div>
<p className="mt-2 text-xs text-[var(--muted-foreground)]">
Всего {formatDecimal(stock.quantity, 4)} · резерв {formatDecimal(stock.reserved, 4)} · в пути {formatDecimal(stock.in_transit, 4)}
</p>
</div>
<p className="text-sm font-semibold">{formatDecimal(stock.available, 4)} доступно</p>
</div>
<p className="mt-2 text-xs text-[var(--muted-foreground)]">
Всего {formatDecimal(stock.quantity, 4)} · резерв {formatDecimal(stock.reserved, 4)} · в пути {formatDecimal(stock.in_transit, 4)}
</p>
))}
</div>
))}
</div>
) : null}
</>
)}
</article>