Строка в «Последних приходах» стала кликабельной, после проведения накладной открывается сразу она — видно, что именно получилось. Показываем снимки на момент приёмки (имя, артикул), а не текущие значения каталога: накладная — исторический документ, переименование номенклатуры не должно её переписывать. Единственное «сегодняшнее» поле — сколько от партии ещё лежит на складе: лежит целиком / осталось N / разошлось. Правки нет намеренно: проведённый приход уже стал партиями и движениями, и «поправить» его значило бы разъехаться с журналом — сторно делается в CRM, где видны последствия. В карточке это сказано текстом, чтобы не выглядело как недоделка. Заодно после проведения инвалидируются списки черновиков и приходов — иначе при возврате в раздел они показывались из кэша устаревшими. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
76 lines
4.4 KiB
TypeScript
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>
|
|
);
|
|
}
|