Przeglądaj źródła

RHL-022 feat(explorer): add ExplorerPageShell and ExplorerSectionCard components for consistent layout

Code_Uwe 1 miesiąc temu
rodzic
commit
3b7328f465

+ 47 - 0
components/explorer/ExplorerPageShell.jsx

@@ -0,0 +1,47 @@
+import React from "react";
+
+/**
+ * ExplorerPageShell
+ *
+ * Consistent framing for all Explorer pages:
+ * - Title + optional description
+ * - Breadcrumbs as ReactNode (supports dropdown composition)
+ * - Optional actions (refresh, etc.)
+ *
+ * The shell intentionally does not "build" breadcrumbs to avoid tight coupling
+ * and to keep dropdown variants easy and clean.
+ *
+ * @param {{
+ *   title: string,
+ *   description?: string|null,
+ *   breadcrumbs?: React.ReactNode,
+ *   actions?: React.ReactNode,
+ *   children: React.ReactNode
+ * }} props
+ */
+export default function ExplorerPageShell({
+	title,
+	description = null,
+	breadcrumbs = null,
+	actions = null,
+	children,
+}) {
+	return (
+		<div className="space-y-4">
+			<div className="flex items-start justify-between gap-4">
+				<div className="space-y-1">
+					<h1 className="text-2xl font-semibold tracking-tight">{title}</h1>
+					{description ? (
+						<p className="text-sm text-muted-foreground">{description}</p>
+					) : null}
+				</div>
+
+				{actions ? <div className="shrink-0">{actions}</div> : null}
+			</div>
+
+			{breadcrumbs ? <div>{breadcrumbs}</div> : null}
+
+			{children}
+		</div>
+	);
+}

+ 41 - 0
components/explorer/ExplorerSectionCard.jsx

@@ -0,0 +1,41 @@
+import React from "react";
+import {
+	Card,
+	CardHeader,
+	CardTitle,
+	CardDescription,
+	CardContent,
+	CardAction,
+} from "@/components/ui/card";
+
+/**
+ * ExplorerSectionCard
+ *
+ * A reusable Card wrapper for Explorer content blocks.
+ * Keeps all levels visually consistent (spacing, typography, optional "count" badge).
+ *
+ * @param {{
+ *   title: string,
+ *   description?: string|null,
+ *   headerRight?: React.ReactNode,
+ *   children: React.ReactNode
+ * }} props
+ */
+export default function ExplorerSectionCard({
+	title,
+	description = null,
+	headerRight = null,
+	children,
+}) {
+	return (
+		<Card>
+			<CardHeader>
+				<CardTitle>{title}</CardTitle>
+				{description ? <CardDescription>{description}</CardDescription> : null}
+				{headerRight ? <CardAction>{headerRight}</CardAction> : null}
+			</CardHeader>
+
+			<CardContent>{children}</CardContent>
+		</Card>
+	);
+}