feat(overview): resolve dashboard layout from permissions, not role keys

role-dashboards.config.ts's ROLE_LAYOUTS was a hand-typed table of ~40
raw role/position keys (including edr_freight_app/xxx ad-hoc
sub-positions), matched against the user's held keys client-side.
Replaced with resolveOverviewLayout(), which just picks the
highest-priority key out of whatever GET /overview/layouts returns —
the same 'server filters by permission, frontend renders what comes
back' shape Reports already uses, so the frontend no longer needs to
know any individual permission key.

Hard cutover: no fallback to the old key table. Positions not covered
by the previous commit's grants (the ad-hoc department sub-positions)
will render the executive layout until granted a layout permission.
This commit is contained in:
ghost2023
2026-08-21 15:43:44 +03:00
parent 17e41aa767
commit 503ea30f58
6 changed files with 56 additions and 80 deletions

View File

@@ -3,7 +3,6 @@ import { AlertCircle } from "lucide-react";
import { Alert, Button, Skeleton, Stack } from "@mantine/core";
import { useQueryClient } from "@tanstack/react-query";
import { useAuth } from "@/auth/useAuth";
import { PageContainer } from "@/components/page";
import { ClearanceOverview } from "@/components/overview/layouts/ClearanceOverview";
import { ExecutiveOverview } from "@/components/overview/layouts/ExecutiveOverview";
@@ -20,7 +19,7 @@ import {
import { OverviewHero } from "@/components/overview/summary/OverviewHero";
import { OverviewHeroKpis } from "@/components/overview/summary/OverviewHeroKpis";
import { QUERY_KEYS } from "@/constants/QUERY_KEYS";
import { useOverview } from "@/hooks/useOverview";
import { useOverview, useOverviewLayouts } from "@/hooks/useOverview";
import type { OverviewRange } from "@/types/overview";
import "@/components/overview/summary/overview-summary.css";
@@ -57,13 +56,14 @@ function OverviewSkeleton() {
const OverviewPage = () => {
const [range, setRange] = useState<OverviewRange>("30d");
const queryClient = useQueryClient();
const { user } = useAuth();
const { data, isLoading, isError, error, refetch, isFetching } =
useOverview(range);
const { data: layouts, isLoading: layoutsLoading } = useOverviewLayouts();
// Hero, range control and headline KPIs are role-neutral; everything below
// them is chosen by role key.
const layoutKey = resolveOverviewLayout(user);
// them is chosen by which overview:<key>:view permissions the caller holds
// (GET /overview/layouts already filtered these server-side).
const layoutKey = resolveOverviewLayout(layouts?.map((l) => l.key));
const RoleLayout = layoutKey ? LAYOUTS[layoutKey] : null;
const accessDenied =
@@ -129,7 +129,7 @@ const OverviewPage = () => {
</Alert>
)}
{isLoading && !data ? (
{(isLoading || layoutsLoading) && !data ? (
<Stack mt="lg">
<OverviewSkeleton />
</Stack>