feat: full site build — Project/Melody schema (Option A), admin CRUD, public sections, uploads, email+SMTP, internal analytics, legal pages, docs
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import type { ReactNode } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
type AdminShellProps = {
|
||||
children: ReactNode;
|
||||
userEmail: string;
|
||||
signOutAction: (formData: FormData) => Promise<void>;
|
||||
};
|
||||
|
||||
const navigation = [
|
||||
{ label: "Dashboard", href: "/admin" },
|
||||
{ label: "Categories", href: "/admin/categories" },
|
||||
{ label: "Projects", href: "/admin/projects" },
|
||||
{ label: "Apps", href: "/admin/projects?type=APP" },
|
||||
{ label: "Melodies", href: "/admin/melodies" },
|
||||
{ label: "Analytics", href: "/admin/analytics" },
|
||||
{ label: "Media", active: false },
|
||||
];
|
||||
|
||||
export default function AdminShell({ children, userEmail, signOutAction }: AdminShellProps) {
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background text-foreground lg:grid lg:grid-cols-[16rem_minmax(0,1fr)]">
|
||||
<aside className="border-b border-border bg-card lg:sticky lg:top-0 lg:h-screen lg:border-b-0 lg:border-e">
|
||||
<div className="flex h-full flex-col gap-8 p-5 sm:p-6 lg:p-5">
|
||||
<div>
|
||||
<Link href="/admin" className="text-lg font-bold tracking-tight">
|
||||
Diyaa Admin
|
||||
</Link>
|
||||
<p className="mt-1 text-sm text-muted-foreground">Content workspace</p>
|
||||
</div>
|
||||
|
||||
<nav aria-label="Admin navigation" className="grid gap-1">
|
||||
{navigation.map((item) =>
|
||||
item.href ? (
|
||||
<Link
|
||||
key={item.label}
|
||||
href={item.href ?? "/admin"}
|
||||
className={
|
||||
pathname === item.href.split("?")[0] || (item.href.split("?")[0] !== "/admin" && pathname.startsWith(`${item.href.split("?")[0]}/`))
|
||||
? "rounded-md bg-accent px-3 py-2 text-sm font-medium text-accent-foreground"
|
||||
: "rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground"
|
||||
}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
) : (
|
||||
<span
|
||||
key={item.label}
|
||||
aria-disabled="true"
|
||||
className="flex cursor-not-allowed items-center justify-between rounded-md px-3 py-2 text-sm text-muted-foreground opacity-60"
|
||||
>
|
||||
{item.label}
|
||||
<span className="text-xs">Soon</span>
|
||||
</span>
|
||||
),
|
||||
)}
|
||||
</nav>
|
||||
|
||||
<div className="mt-auto border-t border-border pt-5">
|
||||
<p className="truncate text-sm text-muted-foreground" title={userEmail}>
|
||||
{userEmail}
|
||||
</p>
|
||||
<form action={signOutAction} className="mt-3">
|
||||
<Button type="submit" variant="outline" className="w-full">
|
||||
Sign out
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main className="min-w-0 p-4 sm:p-6 lg:p-8">{children}</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user