Files
mechanic-pwa/mechanic-pwa/frontend/src/App.tsx
T
tremble7681andClaude Opus 4.8 4c1df109ca wh: карточка проведённого прихода в разделе «Приёмка» (чтение)
Строка в «Последних приходах» стала кликабельной, после проведения
накладной открывается сразу она — видно, что именно получилось.

Показываем снимки на момент приёмки (имя, артикул), а не текущие значения
каталога: накладная — исторический документ, переименование номенклатуры
не должно её переписывать. Единственное «сегодняшнее» поле — сколько от
партии ещё лежит на складе: лежит целиком / осталось N / разошлось.

Правки нет намеренно: проведённый приход уже стал партиями и движениями,
и «поправить» его значило бы разъехаться с журналом — сторно делается в
CRM, где видны последствия. В карточке это сказано текстом, чтобы не
выглядело как недоделка.

Заодно после проведения инвалидируются списки черновиков и приходов —
иначе при возврате в раздел они показывались из кэша устаревшими.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-30 15:29:39 +10:00

76 lines
4.4 KiB
TypeScript

import { Routes, Route, Navigate } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
import { useAuth } from "@/store/auth";
import { getMe } from "@/api/me";
import { useMeSync } from "@/hooks/useMeSync";
import Login from "@/pages/Login";
import ActionMenu from "@/pages/ActionMenu";
import Home from "@/pages/Home";
import VehicleCard from "@/pages/VehicleCard";
import InspectionEditor from "@/pages/InspectionEditor";
import InspectionReview from "@/pages/InspectionReview";
import TtStateDetail from "@/pages/TtStateDetail";
import RepairsFeedPage from "@/pages/repairs/RepairsFeedPage";
import RepairDetailPage from "@/pages/repairs/RepairDetailPage";
import EditRepairPage from "@/pages/repairs/EditRepairPage";
import CarSelectPage from "@/pages/repairs/CarSelectPage";
import CreateRepairPage from "@/pages/repairs/CreateRepairPage";
import ReceivingPage from "@/pages/warehouse/ReceivingPage";
import ReceivingDraftPage from "@/pages/warehouse/ReceivingDraftPage";
import ReceiptDetailPage from "@/pages/warehouse/ReceiptDetailPage";
import StocktakePage from "@/pages/warehouse/StocktakePage";
import StocktakeSessionPage from "@/pages/warehouse/StocktakeSessionPage";
function RequireAuth({ children }: { children: React.ReactNode }) {
const token = useAuth((s) => s.token);
if (!token) return <Navigate to="/login" replace />;
return <>{children}</>;
}
/**
* Складские разделы пока только админам. Сервер и так отдаёт 403, но без
* клиентской проверки прямой ссылкой можно было бы открыть пустой экран с
* ошибкой — это читается как поломка, а не как «вам сюда нельзя».
*/
function RequireAdmin({ children }: { children: React.ReactNode }) {
const token = useAuth((s) => s.token);
const meQuery = useQuery({ queryKey: ["me"], queryFn: getMe, staleTime: 60_000, enabled: !!token });
if (!token) return <Navigate to="/login" replace />;
if (meQuery.isLoading) return null;
if (!meQuery.data?.is_admin) return <Navigate to="/" replace />;
return <>{children}</>;
}
export default function App() {
// У35: фоновая сверка прав механика раз в 5 минут — выкидывает на /login
// с сообщением «Доступ отозван», если админ снял can_login_pwa или вывел
// пользователя из отдела механиков.
useMeSync();
return (
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/" element={<RequireAuth><ActionMenu /></RequireAuth>} />
<Route path="/vehicles" element={<RequireAuth><Home /></RequireAuth>} />
<Route path="/vehicles/:id" element={<RequireAuth><VehicleCard /></RequireAuth>} />
<Route path="/inspections/:id/edit" element={<RequireAuth><InspectionEditor /></RequireAuth>} />
<Route path="/inspections/:id" element={<RequireAuth><InspectionReview /></RequireAuth>} />
<Route path="/tt-states/:id" element={<RequireAuth><TtStateDetail /></RequireAuth>} />
{/* Раздел «Ремонт» — активируется в PR-5 (Фича 1). */}
<Route path="/repairs" element={<RequireAuth><RepairsFeedPage /></RequireAuth>} />
<Route path="/repairs/new" element={<RequireAuth><CarSelectPage /></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/edit" element={<RequireAuth><EditRepairPage /></RequireAuth>} />
{/* Склад — приёмка и инвентаризация с телефона (пока только админы). */}
<Route path="/wh/receiving" element={<RequireAdmin><ReceivingPage /></RequireAdmin>} />
<Route path="/wh/receiving/:id" element={<RequireAdmin><ReceivingDraftPage /></RequireAdmin>} />
<Route path="/wh/receipts/:id" element={<RequireAdmin><ReceiptDetailPage /></RequireAdmin>} />
<Route path="/wh/stocktake" element={<RequireAdmin><StocktakePage /></RequireAdmin>} />
<Route path="/wh/stocktake/:id" element={<RequireAdmin><StocktakeSessionPage /></RequireAdmin>} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
);
}