132 lines
4.5 KiB
JavaScript
132 lines
4.5 KiB
JavaScript
/**
|
|
* @file GroupDetailPage.jsx
|
|
* @description Detail view for a single order group. Reads groupId from URL
|
|
* params, loads drivers, and renders the OrderDetailPanel.
|
|
*/
|
|
import React from "react";
|
|
import { Navigate, useNavigate, useParams, useLocation } from "react-router-dom";
|
|
import { OrderDetailPanel } from "../components/orders/OrderDetailPanel";
|
|
import ErrorBoundary from "../components/ErrorBoundary";
|
|
import { Button } from "../components/UI/Button";
|
|
import { Panel } from "../components/UI/Panel";
|
|
import { SkeletonPanel } from "../components/UI/Loading";
|
|
import { useAuth } from "../context/AuthContext";
|
|
import { fetchDrivers } from "../services/supabase/userRepository";
|
|
import { useOrderGroups } from "../hooks/useOrderGroups";
|
|
|
|
const ALLOWED_ROLES = ["admin", "mega_admin", "manager", "logistician", "driver"];
|
|
|
|
export const GroupDetailPage = () => {
|
|
// ── Route Params & Auth ───────────────────────────────────────────────────
|
|
const { groupId } = useParams();
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const { user, isSessionLoading } = useAuth();
|
|
const userRole = user?.role;
|
|
|
|
// ALL hooks must be called before any early return (Rules of Hooks)
|
|
const {
|
|
allOrderGroups,
|
|
selectedOrderGroupId,
|
|
setSelectedOrderGroupId,
|
|
saveManualDeliveryChoice,
|
|
isSavingDeliveryChoice,
|
|
isSavingDriverAssignment,
|
|
isSavingStatusChange,
|
|
assignDriver,
|
|
changeDeliveryStatus,
|
|
saveShipmentData,
|
|
isLoading,
|
|
refetch,
|
|
} = useOrderGroups({ details: true, groupId });
|
|
|
|
// ── Drivers ────────────────────────────────────────────────────────────────
|
|
const [drivers, setDrivers] = React.useState([]);
|
|
|
|
React.useEffect(() => {
|
|
if (groupId) {
|
|
setSelectedOrderGroupId(groupId);
|
|
}
|
|
}, [groupId, setSelectedOrderGroupId]);
|
|
|
|
// Refetch data when navigating to this page to ensure fresh status
|
|
React.useEffect(() => {
|
|
refetch(false); // silent refetch, no loading spinner
|
|
}, [refetch]);
|
|
|
|
React.useEffect(() => {
|
|
let cancelled = false;
|
|
const load = async () => {
|
|
const result = await fetchDrivers();
|
|
if (cancelled) return;
|
|
if (result.data) {
|
|
setDrivers(result.data.filter((u) => u.role === "driver"));
|
|
}
|
|
};
|
|
load();
|
|
return () => { cancelled = true; };
|
|
}, []);
|
|
|
|
// ALL hooks must be called before any early return (Rules of Hooks)
|
|
const order = isLoading ? null : (allOrderGroups.find((g) => g.id === groupId) ||
|
|
allOrderGroups.find((g) => g.id === selectedOrderGroupId) ||
|
|
null);
|
|
|
|
// Preserve the tab the user came from when going back
|
|
const handleGoBack = React.useCallback(() => {
|
|
if (window.history.length > 1) {
|
|
navigate(-1);
|
|
} else {
|
|
navigate("/dashboard");
|
|
}
|
|
}, [navigate]);
|
|
|
|
// Wait for session restore before deciding redirect
|
|
if (isSessionLoading) {
|
|
return null;
|
|
}
|
|
|
|
// Auth guard: redirect to login if not authenticated
|
|
if (!user) {
|
|
return <Navigate to={`/login?redirect=${encodeURIComponent(location.pathname + location.search)}`} replace />;
|
|
}
|
|
|
|
// Role guard: only allowed roles can access group details
|
|
if (!ALLOWED_ROLES.includes(userRole)) {
|
|
return <Navigate to="/forbidden" replace />;
|
|
}
|
|
|
|
return (
|
|
<div className="mx-auto w-full max-w-3xl space-y-5">
|
|
<div className="flex items-center justify-between">
|
|
<Button variant="ghost" onClick={handleGoBack} className="text-sm">
|
|
← Назад к списку
|
|
</Button>
|
|
</div>
|
|
|
|
{isLoading ? (
|
|
<SkeletonPanel lines={6} />
|
|
) : order ? (
|
|
<ErrorBoundary compact>
|
|
<OrderDetailPanel
|
|
order={order}
|
|
canManageDelivery={["manager", "logistician", "admin", "mega_admin"].includes(userRole)}
|
|
onSaveManualDeliveryChoice={saveManualDeliveryChoice}
|
|
isSavingDeliveryChoice={isSavingDeliveryChoice}
|
|
isSavingDriverAssignment={isSavingDriverAssignment}
|
|
isSavingStatusChange={isSavingStatusChange}
|
|
drivers={drivers}
|
|
onAssignDriver={assignDriver}
|
|
onChangeDeliveryStatus={changeDeliveryStatus}
|
|
onSaveShipmentData={saveShipmentData}
|
|
userRole={userRole}
|
|
/>
|
|
</ErrorBoundary>
|
|
) : (
|
|
<Panel className="p-6 text-sm text-[var(--color-text-muted)]">
|
|
Группа не найдена.
|
|
</Panel>
|
|
)}
|
|
</div>
|
|
);
|
|
}; |