feat(repairs): external mode in create form + /repairs/new-external route

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-06-05 14:12:57 +03:00
co-authored by claude-flow
parent 7e6817b3ff
commit d0ebd9ddf5
2 changed files with 33 additions and 4 deletions
+1
View File
@@ -39,6 +39,7 @@ export default function App() {
<Route path="/repairs" element={<RequireAuth><RepairsFeedPage /></RequireAuth>} /> <Route path="/repairs" element={<RequireAuth><RepairsFeedPage /></RequireAuth>} />
<Route path="/repairs/new" element={<RequireAuth><CarSelectPage /></RequireAuth>} /> <Route path="/repairs/new" element={<RequireAuth><CarSelectPage /></RequireAuth>} />
<Route path="/repairs/new/:carId" element={<RequireAuth><CreateRepairPage /></RequireAuth>} /> <Route path="/repairs/new/:carId" element={<RequireAuth><CreateRepairPage /></RequireAuth>} />
<Route path="/repairs/new-external" element={<RequireAuth><CreateRepairPage /></RequireAuth>} />
<Route path="/repairs/:id" element={<RequireAuth><RepairDetailPage /></RequireAuth>} /> <Route path="/repairs/:id" element={<RequireAuth><RepairDetailPage /></RequireAuth>} />
<Route path="/repairs/:id/edit" element={<RequireAuth><EditRepairPage /></RequireAuth>} /> <Route path="/repairs/:id/edit" element={<RequireAuth><EditRepairPage /></RequireAuth>} />
<Route path="*" element={<Navigate to="/" replace />} /> <Route path="*" element={<Navigate to="/" replace />} />
@@ -15,7 +15,7 @@
* Сохранение: POST /repairs с `Idempotency-Key` (UUID v4 в state). * Сохранение: POST /repairs с `Idempotency-Key` (UUID v4 в state).
*/ */
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams, useLocation } from "react-router-dom";
import { ChevronLeft, Plus, Trash2, Pencil, Camera } from "lucide-react"; import { ChevronLeft, Plus, Trash2, Pencil, Camera } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -94,6 +94,9 @@ function uuid4(): string {
export default function CreateRepairPage() { export default function CreateRepairPage() {
const navigate = useNavigate(); const navigate = useNavigate();
const { carId: carIdRaw } = useParams<{ carId: string }>(); const { carId: carIdRaw } = useParams<{ carId: string }>();
const location = useLocation();
const isExternal = !carIdRaw;
const externalState = location.state as { plate: string; make: string | null } | null;
const carId = carIdRaw ? Number(carIdRaw) : 0; const carId = carIdRaw ? Number(carIdRaw) : 0;
// Idempotency-Key для этой формы — стабилен между ре-сабмитами (У20). // Idempotency-Key для этой формы — стабилен между ре-сабмитами (У20).
@@ -120,6 +123,24 @@ export default function CreateRepairPage() {
// Initial: подгрузить контекст машины. // Initial: подгрузить контекст машины.
useEffect(() => { useEffect(() => {
if (isExternal) {
if (!externalState) {
// Прямой заход/рефреш без данных — возвращаем к выбору машины.
navigate("/repairs/new", { replace: true });
return;
}
setCtx({
id: 0,
plate: externalState.plate,
brand: externalState.make,
model: null,
mileage_starline: null,
driver_id: null,
driver_name: null,
});
setMileage(null);
return;
}
if (!carId) return; if (!carId) return;
getCarContext(carId) getCarContext(carId)
.then((c) => { .then((c) => {
@@ -127,7 +148,8 @@ export default function CreateRepairPage() {
setMileage(c.mileage_starline); setMileage(c.mileage_starline);
}) })
.catch((e) => setCtxError((e as Error).message || "Не удалось загрузить машину")); .catch((e) => setCtxError((e as Error).message || "Не удалось загрузить машину"));
}, [carId]); // eslint-disable-next-line react-hooks/exhaustive-deps
}, [carId, isExternal]);
// У24 упрощено: пробег ввода в форме теперь один — главный «Пробег (км)» // У24 упрощено: пробег ввода в форме теперь один — главный «Пробег (км)»
// в шапке. На submit подставляется и в oil_change.mileage_at_change, чтобы // в шапке. На submit подставляется и в oil_change.mileage_at_change, чтобы
@@ -307,7 +329,9 @@ export default function CreateRepairPage() {
})); }));
const result = await createRepair( const result = await createRepair(
{ {
car_id: carId, ...(isExternal
? { external_car: { plate: externalState!.plate, make: externalState!.make } }
: { car_id: carId }),
mileage, mileage,
works: works.map((w) => ({ works: works.map((w) => ({
work_catalog_id: w.work_catalog_id, work_catalog_id: w.work_catalog_id,
@@ -385,7 +409,11 @@ export default function CreateRepairPage() {
<div className="font-medium">{ctx.plate}</div> <div className="font-medium">{ctx.plate}</div>
<div className="text-muted-foreground"> <div className="text-muted-foreground">
{[ctx.brand, ctx.model].filter(Boolean).join(" ")} {[ctx.brand, ctx.model].filter(Boolean).join(" ")}
{ctx.driver_name ? ` · ${ctx.driver_name}` : ` · Без водителя`} {isExternal
? " · С улицы"
: ctx.driver_name
? ` · ${ctx.driver_name}`
: " · Без водителя"}
</div> </div>
</div> </div>
</header> </header>