feat: better navigation in backoffice

This commit is contained in:
Nathnael
2026-08-07 12:24:30 +00:00
parent d5d7c91e24
commit 1e9149ce00
12 changed files with 861 additions and 676 deletions

View File

@@ -0,0 +1,32 @@
import { ShieldOff } from "lucide-react";
import { useAuth } from "@/auth/useAuth";
import { Button } from "@/components/ui/button";
/**
* Terminal page for an account with no freight permissions and no IAM admin
* role. Reached via `resolveLandingPath`, which needs one destination that can
* never bounce — every other page is permission-gated.
*/
export default function NoAccessPage() {
const { user, logout } = useAuth();
return (
<div className="flex min-h-screen items-center justify-center p-6">
<div className="w-full max-w-md space-y-4 rounded-lg border p-8 text-center">
<ShieldOff className="mx-auto h-10 w-10 text-muted-foreground" />
<h1 className="text-lg font-semibold">No access assigned</h1>
<p className="text-sm text-muted-foreground">
Your account has no permissions assigned yet, so there are no pages to
show. Contact your administrator to have a position or role assigned.
</p>
{user?.email ? (
<p className="text-xs text-muted-foreground">Signed in as {user.email}</p>
) : null}
<Button variant="outline" onClick={logout}>
Log out
</Button>
</div>
</div>
);
}

View File

@@ -64,7 +64,9 @@ const LoginPage = () => {
try {
await verifyMfa({ email: normalizedIdentifier, otp: otp.trim() });
navigate("/dashboard/overview", { replace: true });
// "/" resolves to the user's own landing page once the session lands —
// not every user can see the overview.
navigate("/", { replace: true });
} catch (err) {
setError(extractApiError(err).message);
} finally {

View File

@@ -30,6 +30,7 @@ import {
} from "lucide-react";
import { useAuth } from "@/auth/useAuth";
import { resolveLandingPath } from "@/lib/landing";
import { canAccessRuleEngineResource } from "@/lib/permissions";
import RuleEngineFormDialog from "@/components/ruleEngine/RuleEngineFormDialog";
import {
@@ -239,8 +240,8 @@ const CargoTypesPage = () => {
[levelNodes, term],
);
if (!config) return <Navigate to="/dashboard/overview" replace />;
if (!canView) return <Navigate to="/dashboard/overview" replace />;
if (!config) return <Navigate to={resolveLandingPath(user)} replace />;
if (!canView) return <Navigate to={resolveLandingPath(user)} replace />;
// A bad/stale :id (after data loads) → fall back to the root list.
if (!isLoading && currentId && !current) return <Navigate to={BASE_PATH} replace />;

View File

@@ -1,4 +1,5 @@
import { useAuth } from "@/auth/useAuth";
import { resolveLandingPath } from "@/lib/landing";
import {
canAccessRuleEngineResource,
canApproveRuleEngineChange,
@@ -584,7 +585,7 @@ const RuleEngineResourcePage = () => {
}
if (!canView) {
return <Navigate to="/dashboard/overview" replace />;
return <Navigate to={resolveLandingPath(user)} replace />;
}
const openCreate = () => {