feat(tyres): по умолчанию только подходящая резина, «Показать все» разворачивает
Кладовщик у подъёмника выбирал размер из девятнадцати позиций глазами. Теперь экран показывает только подходящее выбранной машине (по подтверждённой применимости), с счётчиком и кнопкой-переключателем на полный список. Если по модели применимость не отмечена, фильтровать нечем — показываются все размеры и объясняется, почему: прятать правильную резину по незнанию хуже, чем показать лишнюю. Плашка на строке различает «подходит» (применимость) и «штатный» (справочник размеров), и предвыбор позиции теперь учитывает оба признака.
This commit is contained in:
@@ -375,6 +375,8 @@ export interface TyreStockRow {
|
|||||||
by_grade: Partial<Record<TyreGrade, number>>;
|
by_grade: Partial<Record<TyreGrade, number>>;
|
||||||
places: string[];
|
places: string[];
|
||||||
suggested: boolean;
|
suggested: boolean;
|
||||||
|
/** Подтверждённая применимость к выбранной машине. */
|
||||||
|
fits: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TyreSet {
|
export interface TyreSet {
|
||||||
@@ -410,6 +412,8 @@ export interface TyreContext {
|
|||||||
suggested_size: string | null;
|
suggested_size: string | null;
|
||||||
open_set: TyreSet | null;
|
open_set: TyreSet | null;
|
||||||
stock: TyreStockRow[];
|
stock: TyreStockRow[];
|
||||||
|
/** Известно ли, что подходит машине. false — фильтровать нечем, показываем всё. */
|
||||||
|
fit_known: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TyreClose {
|
export interface TyreClose {
|
||||||
|
|||||||
@@ -179,14 +179,19 @@ function IssueForm({ carId, onDone }: { carId: number; onDone: () => void }) {
|
|||||||
// Подставляем подсказанный размер, как только пришёл контекст.
|
// Подставляем подсказанный размер, как только пришёл контекст.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ctx || partId !== null) return;
|
if (!ctx || partId !== null) return;
|
||||||
const suggested = ctx.stock.find((s) => s.suggested && s.qty > 0);
|
const best = ctx.stock.find((s) => (s.fits || s.suggested) && s.qty > 0);
|
||||||
if (suggested) setPartId(suggested.part_id);
|
if (best) setPartId(best.part_id);
|
||||||
}, [ctx, partId]);
|
}, [ctx, partId]);
|
||||||
|
|
||||||
const visibleStock = useMemo(() => {
|
// По умолчанию показываем ТОЛЬКО подходящее этой машине — иначе кладовщик
|
||||||
if (!ctx) return [];
|
// выбирает размер из девятнадцати позиций глазами. Если применимость по
|
||||||
return showAll ? ctx.stock : ctx.stock.slice(0, 6);
|
// машине не заполнена, фильтровать нечем: прятать правильную резину по
|
||||||
}, [ctx, showAll]);
|
// незнанию хуже, чем показать лишнюю (то же правило, что у подбора запчастей).
|
||||||
|
const fitting = useMemo(
|
||||||
|
() => (ctx ? ctx.stock.filter((s) => s.fits || s.suggested) : []),
|
||||||
|
[ctx],
|
||||||
|
);
|
||||||
|
const visibleStock = showAll || !ctx?.fit_known ? (ctx?.stock ?? []) : fitting;
|
||||||
|
|
||||||
if (ctxQuery.isLoading) {
|
if (ctxQuery.isLoading) {
|
||||||
return <main className="p-4 text-sm text-muted-foreground">Загрузка…</main>;
|
return <main className="p-4 text-sm text-muted-foreground">Загрузка…</main>;
|
||||||
@@ -290,7 +295,20 @@ function IssueForm({ carId, onDone }: { carId: number; onDone: () => void }) {
|
|||||||
|
|
||||||
{/* Что выдаём */}
|
{/* Что выдаём */}
|
||||||
<section className="p-3 border-b">
|
<section className="p-3 border-b">
|
||||||
<h2 className="text-sm font-semibold mb-2">Что выдаём</h2>
|
<div className="flex items-baseline justify-between mb-2">
|
||||||
|
<h2 className="text-sm font-semibold">Что выдаём</h2>
|
||||||
|
{ctx.fit_known && (
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
{showAll ? `все размеры (${ctx.stock.length})` : `подходит: ${fitting.length}`}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{!ctx.fit_known && (
|
||||||
|
<p className="text-xs text-muted-foreground mb-2">
|
||||||
|
Для этой модели не отмечено, какая резина подходит, — показаны все размеры.
|
||||||
|
Отметить можно в карточке позиции на складе.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{visibleStock.length === 0 && (
|
{visibleStock.length === 0 && (
|
||||||
<p className="text-sm text-muted-foreground">На складе нет резины.</p>
|
<p className="text-sm text-muted-foreground">На складе нет резины.</p>
|
||||||
@@ -304,9 +322,16 @@ function IssueForm({ carId, onDone }: { carId: number; onDone: () => void }) {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{!showAll && ctx.stock.length > 6 && (
|
{ctx.fit_known && (
|
||||||
<Button variant="ghost" size="sm" className="mt-1" onClick={() => setShowAll(true)}>
|
<Button
|
||||||
Показать все размеры ({ctx.stock.length})
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="mt-1"
|
||||||
|
onClick={() => setShowAll((v) => !v)}
|
||||||
|
>
|
||||||
|
{showAll
|
||||||
|
? `Только подходящие (${fitting.length})`
|
||||||
|
: `Показать все размеры (${ctx.stock.length})`}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{chosen && chosen.qty < qty && (
|
{chosen && chosen.qty < qty && (
|
||||||
@@ -400,8 +425,10 @@ function StockRow({
|
|||||||
<span className="flex-1 min-w-0">
|
<span className="flex-1 min-w-0">
|
||||||
<span className="flex items-baseline gap-2 flex-wrap">
|
<span className="flex items-baseline gap-2 flex-wrap">
|
||||||
<span className="font-semibold">{row.size ?? row.name}</span>
|
<span className="font-semibold">{row.size ?? row.name}</span>
|
||||||
{row.suggested && (
|
{(row.fits || row.suggested) && (
|
||||||
<span className="text-xs rounded px-1.5 py-0.5 bg-primary/10 text-primary">штатный</span>
|
<span className="text-xs rounded px-1.5 py-0.5 bg-primary/10 text-primary">
|
||||||
|
{row.fits ? "подходит" : "штатный"}
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
<span className={`text-sm ${row.qty > 0 ? "text-muted-foreground" : "text-destructive"}`}>
|
<span className={`text-sm ${row.qty > 0 ? "text-muted-foreground" : "text-destructive"}`}>
|
||||||
{row.qty} шт
|
{row.qty} шт
|
||||||
|
|||||||
Reference in New Issue
Block a user