feat(36-02): add /admin layout route and placeholder index
- admin.tsx: createFileRoute('/admin') with sidebar shell (Items, Tags disabled with Soon badge)
- admin/index.tsx: createFileRoute('/admin/') placeholder with shield icon
- useEffect guard redirects non-admin users to /
This commit is contained in:
62
src/client/routes/admin.tsx
Normal file
62
src/client/routes/admin.tsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { createFileRoute, Outlet, useNavigate } from "@tanstack/react-router";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useAuth } from "../hooks/useAuth";
|
||||||
|
import { LucideIcon } from "../lib/iconData";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/admin")({
|
||||||
|
component: AdminLayout,
|
||||||
|
});
|
||||||
|
|
||||||
|
function AdminLayout() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { data: auth, isLoading } = useAuth();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isLoading && !auth?.user?.isAdmin) {
|
||||||
|
navigate({ to: "/" });
|
||||||
|
}
|
||||||
|
}, [auth, isLoading, navigate]);
|
||||||
|
|
||||||
|
// Don't render the shell until auth is confirmed
|
||||||
|
if (isLoading || !auth?.user?.isAdmin) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-[calc(100vh-3.5rem)]">
|
||||||
|
{/* Sidebar */}
|
||||||
|
<aside className="w-56 border-r border-gray-100 bg-white p-4 flex flex-col gap-1 shrink-0">
|
||||||
|
<p className="text-xs font-semibold text-gray-400 uppercase tracking-wide mb-3">
|
||||||
|
Admin
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Items — disabled (phase 37) */}
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-300 cursor-not-allowed"
|
||||||
|
title="Coming in a future release"
|
||||||
|
>
|
||||||
|
<LucideIcon name="package" size={16} />
|
||||||
|
<span>Items</span>
|
||||||
|
<span className="ml-auto text-xs bg-gray-100 text-gray-400 px-1.5 py-0.5 rounded">
|
||||||
|
Soon
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tags — disabled (phase 38) */}
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-300 cursor-not-allowed"
|
||||||
|
title="Coming in a future release"
|
||||||
|
>
|
||||||
|
<LucideIcon name="tag" size={16} />
|
||||||
|
<span>Tags</span>
|
||||||
|
<span className="ml-auto text-xs bg-gray-100 text-gray-400 px-1.5 py-0.5 rounded">
|
||||||
|
Soon
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<main className="flex-1 p-6 bg-gray-50">
|
||||||
|
<Outlet />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
18
src/client/routes/admin/index.tsx
Normal file
18
src/client/routes/admin/index.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { LucideIcon } from "../../lib/iconData";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/admin/")({
|
||||||
|
component: AdminIndex,
|
||||||
|
});
|
||||||
|
|
||||||
|
function AdminIndex() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center h-64 text-center">
|
||||||
|
<LucideIcon name="shield" size={32} className="text-gray-300 mb-3" />
|
||||||
|
<p className="text-sm text-gray-500">Admin Panel</p>
|
||||||
|
<p className="text-xs text-gray-400 mt-1">
|
||||||
|
Select a section from the sidebar
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user