repairs: тип масла в блоке замены (двигатель/вариатор/АКПП)

Для отчётов СТО по замене масла (задача 4078) нужно различать двигатель и
трансмиссию. В блоке «Замена масла» добавлен выбор типа oil_type
(engine|cvt|akpp) и он уходит в payload create. Для вариатора/АКПП поле
«следующая через (км)» скрыто — интервал задаётся по модели в отчёте СТО.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 13:12:08 +10:00
co-authored by Claude Opus 4.8
parent a5bd7308b5
commit 3c27b794dd
2 changed files with 45 additions and 11 deletions
@@ -61,6 +61,7 @@ export interface CreateOilChangePayload {
enabled: boolean; enabled: boolean;
mileage_at_change: number | null; mileage_at_change: number | null;
next_in_km: number; next_in_km: number;
oil_type: string; // engine | cvt | akpp
sticker_photo_uuid: string | null; sticker_photo_uuid: string | null;
sticker_extension: string | null; sticker_extension: string | null;
} }
@@ -61,11 +61,14 @@ interface PartRow {
qty: number; qty: number;
} }
type OilKind = "engine" | "cvt" | "akpp";
interface OilChangeState { interface OilChangeState {
enabled: boolean; enabled: boolean;
mileage_at_change: number | null; mileage_at_change: number | null;
mileage_is_dirty: boolean; // У24: мягкая подписка с шапочным mileage_is_dirty: boolean; // У24: мягкая подписка с шапочным
next_in_km: number; next_in_km: number;
oil_type: OilKind;
sticker: PhotoState | null; sticker: PhotoState | null;
} }
@@ -98,6 +101,7 @@ export default function CreateRepairPage() {
mileage_at_change: null, mileage_at_change: null,
mileage_is_dirty: false, mileage_is_dirty: false,
next_in_km: 10000, next_in_km: 10000,
oil_type: "engine",
sticker: null, sticker: null,
}); });
@@ -302,6 +306,7 @@ export default function CreateRepairPage() {
enabled: true, enabled: true,
mileage_at_change: oilChange.mileage_at_change, mileage_at_change: oilChange.mileage_at_change,
next_in_km: oilChange.next_in_km, next_in_km: oilChange.next_in_km,
oil_type: oilChange.oil_type,
sticker_photo_uuid: sticker_photo_uuid:
oilChange.sticker?.status === "uploaded" oilChange.sticker?.status === "uploaded"
? oilChange.sticker.presign.photo_uuid ? oilChange.sticker.presign.photo_uuid
@@ -579,6 +584,27 @@ export default function CreateRepairPage() {
</label> </label>
{oilChange.enabled && ( {oilChange.enabled && (
<div className="mt-3 space-y-3"> <div className="mt-3 space-y-3">
<div>
<Label className="text-sm">Тип масла</Label>
<div className="mt-1 grid grid-cols-3 gap-2">
{([["engine", "Двигатель"], ["cvt", "Вариатор"], ["akpp", "АКПП"]] as [OilKind, string][]).map(
([v, l]) => (
<button
type="button"
key={v}
onClick={() => setOilChange((oc) => ({ ...oc, oil_type: v }))}
className={`rounded-md border px-2 py-2 text-sm ${
oilChange.oil_type === v
? "border-primary bg-primary/10 font-medium text-primary"
: "border-input"
}`}
>
{l}
</button>
),
)}
</div>
</div>
<div> <div>
<Label className="text-sm">Пробег на момент замены</Label> <Label className="text-sm">Пробег на момент замены</Label>
<Input <Input
@@ -594,6 +620,7 @@ export default function CreateRepairPage() {
} }
/> />
</div> </div>
{oilChange.oil_type === "engine" ? (
<div> <div>
<Label className="text-sm">Следующая замена через (км)</Label> <Label className="text-sm">Следующая замена через (км)</Label>
<Input <Input
@@ -605,6 +632,12 @@ export default function CreateRepairPage() {
} }
/> />
</div> </div>
) : (
<p className="text-xs text-muted-foreground">
Интервал {oilChange.oil_type === "cvt" ? "вариатора" : "АКПП"} задаётся по модели авто в
отчёте СТО.
</p>
)}
<div> <div>
<Label className="text-sm">Фото стикера (одно)</Label> <Label className="text-sm">Фото стикера (одно)</Label>
<label className="mt-1 block border-2 border-dashed rounded-md p-4 text-center cursor-pointer hover:bg-accent/40"> <label className="mt-1 block border-2 border-dashed rounded-md p-4 text-center cursor-pointer hover:bg-accent/40">