فهرست منبع

RHL-021 feat(view): add ForbiddenView and NotFoundView components for enhanced user experience

Code_Uwe 1 ماه پیش
والد
کامیت
ad9c389cb6
2فایلهای تغییر یافته به همراه141 افزوده شده و 0 حذف شده
  1. 78 0
      components/system/ForbiddenView.jsx
  2. 63 0
      components/system/NotFoundView.jsx

+ 78 - 0
components/system/ForbiddenView.jsx

@@ -0,0 +1,78 @@
+"use client";
+
+import React from "react";
+import Link from "next/link";
+
+import { useAuth } from "@/components/auth/authContext";
+import { branchPath, homePath } from "@/lib/frontend/routes";
+
+import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
+import { Button } from "@/components/ui/button";
+import {
+	Card,
+	CardHeader,
+	CardTitle,
+	CardDescription,
+	CardContent,
+	CardFooter,
+} from "@/components/ui/card";
+
+/**
+ * ForbiddenView (RHL-021)
+ *
+ * A reusable UI block for "you are authenticated, but not allowed here".
+ *
+ * It relies on AuthContext when available to render the best CTA:
+ * - branch users -> link to their own branch root
+ * - admin/dev -> link to home (later: could link to a branch list)
+ */
+export default function ForbiddenView({ attemptedBranch = null }) {
+	const { status, user } = useAuth();
+
+	const isAuthed = status === "authenticated" && user;
+	const isBranchUser = isAuthed && user.role === "branch" && user.branchId;
+
+	const primaryHref = isBranchUser ? branchPath(user.branchId) : homePath();
+	const primaryLabel = isBranchUser ? "Go to my branch" : "Go to home";
+
+	return (
+		<Card>
+			<CardHeader>
+				<CardTitle>Access denied</CardTitle>
+				<CardDescription>
+					You are not allowed to access this resource.
+				</CardDescription>
+			</CardHeader>
+
+			<CardContent className="space-y-4">
+				<Alert variant="destructive">
+					<AlertTitle>Forbidden</AlertTitle>
+					<AlertDescription>
+						{attemptedBranch ? (
+							<span>
+								Your account is not permitted to access branch{" "}
+								<strong>{attemptedBranch}</strong>.
+							</span>
+						) : (
+							<span>Your account is not permitted to access this page.</span>
+						)}
+					</AlertDescription>
+				</Alert>
+
+				<p className="text-sm text-muted-foreground">
+					If you believe this is a mistake, please contact your administrator.
+				</p>
+			</CardContent>
+
+			<CardFooter className="flex flex-col gap-2 sm:flex-row sm:justify-end">
+				<Button variant="outline" asChild>
+					<Link href={homePath()}>Home</Link>
+				</Button>
+
+				<Button asChild>
+					<Link href={primaryHref}>{primaryLabel}</Link>
+				</Button>
+			</CardFooter>
+		</Card>
+	);
+}

+ 63 - 0
components/system/NotFoundView.jsx

@@ -0,0 +1,63 @@
+"use client";
+
+import React from "react";
+import Link from "next/link";
+
+import { useAuth } from "@/components/auth/authContext";
+import { branchPath, homePath } from "@/lib/frontend/routes";
+
+import { Button } from "@/components/ui/button";
+import {
+	Card,
+	CardHeader,
+	CardTitle,
+	CardDescription,
+	CardContent,
+	CardFooter,
+} from "@/components/ui/card";
+
+/**
+ * NotFoundView (RHL-021)
+ *
+ * Used by (protected)/not-found.jsx.
+ * Keeps UX consistent for invalid route params and unknown pages in the protected area.
+ */
+export default function NotFoundView() {
+	const { status, user } = useAuth();
+
+	const isAuthed = status === "authenticated" && user;
+	const ownBranchHref =
+		isAuthed && user.role === "branch" && user.branchId
+			? branchPath(user.branchId)
+			: null;
+
+	return (
+		<Card>
+			<CardHeader>
+				<CardTitle>Not found</CardTitle>
+				<CardDescription>
+					The page or resource you requested does not exist.
+				</CardDescription>
+			</CardHeader>
+
+			<CardContent className="space-y-2">
+				<p className="text-sm text-muted-foreground">
+					This can happen when route parameters are invalid (e.g.
+					year/month/day) or the URL is mistyped.
+				</p>
+			</CardContent>
+
+			<CardFooter className="flex flex-col gap-2 sm:flex-row sm:justify-end">
+				<Button variant="outline" asChild>
+					<Link href={homePath()}>Home</Link>
+				</Button>
+
+				{ownBranchHref ? (
+					<Button asChild>
+						<Link href={ownBranchHref}>Go to my branch</Link>
+					</Button>
+				) : null}
+			</CardFooter>
+		</Card>
+	);
+}