prefetched sidebar main content
This commit is contained in:
parent
38b43174ec
commit
f591e18847
@ -2,18 +2,23 @@ import { AppSidebar } from "@/components/layout/app-sidebar";
|
|||||||
import Navbar from "@/components/layout/navbar";
|
import Navbar from "@/components/layout/navbar";
|
||||||
import { SidebarProvider } from "@/components/ui/sidebar";
|
import { SidebarProvider } from "@/components/ui/sidebar";
|
||||||
import { auth } from "@/server/auth";
|
import { auth } from "@/server/auth";
|
||||||
|
import { api, HydrateClient } from "@/trpc/server";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
async function Layout({ children }: { children: React.ReactNode }) {
|
async function Layout({ children }: { children: React.ReactNode }) {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
void api.app.getSidebarMain.prefetch();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarProvider>
|
<HydrateClient>
|
||||||
<AppSidebar user={session?.user} />
|
<SidebarProvider>
|
||||||
<div className="h-screen w-full bg-background">
|
<AppSidebar user={session?.user} />
|
||||||
<Navbar />
|
<div className="h-screen w-full bg-background">
|
||||||
<main className="space-y-4 p-4">{children}</main>
|
<Navbar />
|
||||||
</div>
|
<main className="space-y-4 p-4">{children}</main>
|
||||||
</SidebarProvider>
|
</div>
|
||||||
|
</SidebarProvider>
|
||||||
|
</HydrateClient>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -91,7 +91,7 @@ function ArticleFilterBar({
|
|||||||
sort: currentValue?.length ? currentValue : undefined,
|
sort: currentValue?.length ? currentValue : undefined,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className="w-full"
|
className="w-full lg:max-w-64"
|
||||||
messageUi={{
|
messageUi={{
|
||||||
selectIcon: FilterIcon,
|
selectIcon: FilterIcon,
|
||||||
select: "Sortieren",
|
select: "Sortieren",
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Folder, LifeBuoy, Newspaper, Send } from "lucide-react";
|
|
||||||
|
|
||||||
import { NavMain } from "./nav-main";
|
import { NavMain } from "./nav-main";
|
||||||
|
|
||||||
@ -19,71 +18,6 @@ import NavBranding from "./nav-branding";
|
|||||||
import { Icons } from "@/components/icons";
|
import { Icons } from "@/components/icons";
|
||||||
import { appConfig } from "@/config";
|
import { appConfig } from "@/config";
|
||||||
|
|
||||||
const data = {
|
|
||||||
user: {
|
|
||||||
name: "shadcn",
|
|
||||||
email: "m@example.com",
|
|
||||||
avatar: "/avatars/shadcn.jpg",
|
|
||||||
},
|
|
||||||
navMain: [
|
|
||||||
{
|
|
||||||
title: "Artikel",
|
|
||||||
url: "/artikel",
|
|
||||||
isActive: true,
|
|
||||||
icon: Newspaper,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Genesis",
|
|
||||||
url: "#",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Explorer",
|
|
||||||
url: "#",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Quantum",
|
|
||||||
url: "#",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Alle Artikel",
|
|
||||||
url: "/artikel",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Kategorien",
|
|
||||||
url: "/kategorien",
|
|
||||||
icon: Folder,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "History",
|
|
||||||
url: "#",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Starred",
|
|
||||||
url: "#",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Settings",
|
|
||||||
url: "#",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Alle Kategorien",
|
|
||||||
url: "#",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
navSecondary: [
|
|
||||||
{
|
|
||||||
title: "Discord",
|
|
||||||
url: appConfig.socials.discord,
|
|
||||||
icon: Icons.discord,
|
|
||||||
external: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export function AppSidebar({
|
export function AppSidebar({
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof Sidebar> & { user?: User | null }) {
|
}: React.ComponentProps<typeof Sidebar> & { user?: User | null }) {
|
||||||
@ -93,7 +27,8 @@ export function AppSidebar({
|
|||||||
<NavBranding subTitle="Wissen" />
|
<NavBranding subTitle="Wissen" />
|
||||||
</SidebarHeader>
|
</SidebarHeader>
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
<NavMain items={data.navMain} />
|
<NavMain />
|
||||||
|
|
||||||
<NavSecondary items={data.navSecondary} className="mt-auto" />
|
<NavSecondary items={data.navSecondary} className="mt-auto" />
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
<SidebarFooter>
|
<SidebarFooter>
|
||||||
@ -103,3 +38,14 @@ export function AppSidebar({
|
|||||||
</Sidebar>
|
</Sidebar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
navSecondary: [
|
||||||
|
{
|
||||||
|
title: "Discord",
|
||||||
|
url: appConfig.socials.discord,
|
||||||
|
icon: Icons.discord,
|
||||||
|
external: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|||||||
@ -1,18 +1,24 @@
|
|||||||
|
"use client";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
SidebarMenu,
|
SidebarMenu,
|
||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
|
useSidebar,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
import { Icons } from "@/components/icons";
|
import { Icons } from "@/components/icons";
|
||||||
import { appConfig } from "@/config";
|
import { appConfig } from "@/config";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { XIcon } from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
function NavBranding({ subTitle }: { subTitle: string }) {
|
function NavBranding({ subTitle }: { subTitle: string }) {
|
||||||
|
const { isMobile, toggleSidebar } = useSidebar();
|
||||||
return (
|
return (
|
||||||
<SidebarMenu>
|
<SidebarMenu className={cn(isMobile && "flex flex-row items-center")}>
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem className="w-full">
|
||||||
<SidebarMenuButton size="lg" asChild>
|
<SidebarMenuButton size="lg" asChild className="w-full">
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-foreground text-background">
|
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-foreground text-background">
|
||||||
<Icons.logo className="size-4" />
|
<Icons.logo className="size-4" />
|
||||||
@ -24,6 +30,15 @@ function NavBranding({ subTitle }: { subTitle: string }) {
|
|||||||
</Link>
|
</Link>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
|
{isMobile && (
|
||||||
|
<SidebarMenuItem className="w-max">
|
||||||
|
<SidebarMenuButton asChild>
|
||||||
|
<Button variant={"ghost"} size={"icon"} onClick={toggleSidebar}>
|
||||||
|
<XIcon className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</SidebarMenuItem>
|
||||||
|
)}
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ChevronRight, type LucideIcon } from "lucide-react";
|
import { ChevronRight, type LucideIcon } from "lucide-react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -18,22 +17,45 @@ import {
|
|||||||
SidebarMenuSubButton,
|
SidebarMenuSubButton,
|
||||||
SidebarMenuSubItem,
|
SidebarMenuSubItem,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
import { api } from "@/trpc/react";
|
||||||
|
import { Icons } from "@/components/icons";
|
||||||
|
|
||||||
export function NavMain({
|
export function NavMain() {
|
||||||
items,
|
const [{ articles, categories }] = api.app.getSidebarMain.useSuspenseQuery();
|
||||||
}: {
|
const items = [
|
||||||
items: {
|
{
|
||||||
title: string;
|
title: "Artikel",
|
||||||
url: string;
|
url: "/artikel",
|
||||||
icon: LucideIcon;
|
isActive: true,
|
||||||
isActive?: boolean;
|
icon: Icons.article,
|
||||||
items?: {
|
items: [
|
||||||
title: string;
|
...articles.map((article) => ({
|
||||||
url: string;
|
title: article.title,
|
||||||
hideBorder?: boolean;
|
url: `/artikel/${article.slug}`,
|
||||||
}[];
|
})),
|
||||||
}[];
|
{
|
||||||
}) {
|
title: "Alle Artikel",
|
||||||
|
url: "/artikel",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Kategorien",
|
||||||
|
url: "/kategorie",
|
||||||
|
icon: Icons.category,
|
||||||
|
items: [
|
||||||
|
...categories.map((category) => ({
|
||||||
|
title: category.name,
|
||||||
|
url: `/kategorie/${category.slug}`,
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
title: "Alle Kategorien",
|
||||||
|
url: "#",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
return (
|
return (
|
||||||
<SidebarGroup>
|
<SidebarGroup>
|
||||||
<SidebarGroupLabel>Platform</SidebarGroupLabel>
|
<SidebarGroupLabel>Platform</SidebarGroupLabel>
|
||||||
@ -77,3 +99,20 @@ export function NavMain({
|
|||||||
</SidebarGroup>
|
</SidebarGroup>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function NavMainSkeleton() {
|
||||||
|
return (
|
||||||
|
<SidebarGroup>
|
||||||
|
<SidebarGroupLabel>Platform</SidebarGroupLabel>
|
||||||
|
<SidebarMenu>
|
||||||
|
{Array.from({ length: 6 }).map((_, i) => (
|
||||||
|
<SidebarMenuItem key={i}>
|
||||||
|
<SidebarMenuButton asChild size="sm">
|
||||||
|
<Skeleton className="h-4 w-full" />
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</SidebarMenuItem>
|
||||||
|
))}
|
||||||
|
</SidebarMenu>
|
||||||
|
</SidebarGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
24
src/components/layout/app-sidebar/sidebar-trigger.tsx
Normal file
24
src/components/layout/app-sidebar/sidebar-trigger.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"use client";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { useSidebar } from "@/components/ui/sidebar";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { ChevronRightIcon, MenuIcon } from "lucide-react";
|
||||||
|
|
||||||
|
export function SidebarTrigger() {
|
||||||
|
const { toggleSidebar, state, isMobile } = useSidebar();
|
||||||
|
const open = state === "expanded";
|
||||||
|
return (
|
||||||
|
<Button onClick={toggleSidebar} size={"icon"} variant={"ghost"}>
|
||||||
|
{isMobile ? (
|
||||||
|
<MenuIcon className="size-4" />
|
||||||
|
) : (
|
||||||
|
<ChevronRightIcon
|
||||||
|
className={cn(
|
||||||
|
"transition-transform duration-150",
|
||||||
|
open && "rotate-180",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ import { hasPermission, Role } from "@/lib/validation/permissions";
|
|||||||
|
|
||||||
import { ModeToggle } from "../mode-switch";
|
import { ModeToggle } from "../mode-switch";
|
||||||
import EditorDropdown from "../editor-dropdown";
|
import EditorDropdown from "../editor-dropdown";
|
||||||
|
import { SidebarTrigger } from "./app-sidebar/sidebar-trigger";
|
||||||
|
|
||||||
async function Navbar() {
|
async function Navbar() {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
@ -13,10 +14,13 @@ async function Navbar() {
|
|||||||
: false;
|
: false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-14 items-center justify-end gap-4 border-b bg-background px-4">
|
<div className="flex h-14 items-center justify-between gap-4 border-b bg-sidebar px-4">
|
||||||
{isEditor && <EditorDropdown />}
|
<SidebarTrigger />
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
{isEditor && <EditorDropdown />}
|
||||||
|
|
||||||
<ModeToggle />
|
<ModeToggle />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,16 +24,24 @@ export const appRouter = createTRPCRouter({
|
|||||||
return { articles, categories };
|
return { articles, categories };
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getSidebarContent: publicProcedure.query(async ({ ctx }) => {
|
getSidebarMain: publicProcedure.query(async ({ ctx }) => {
|
||||||
return await ctx.db.query.categories.findMany({
|
const categories = await ctx.db.query.categories.findMany({
|
||||||
with: {
|
limit: 3,
|
||||||
articles: {
|
columns: {
|
||||||
columns: {
|
slug: true,
|
||||||
title: true,
|
name: true,
|
||||||
slug: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const articles = await ctx.db.query.articles.findMany({
|
||||||
|
limit: 3,
|
||||||
|
columns: {
|
||||||
|
slug: true,
|
||||||
|
title: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
categories,
|
||||||
|
articles,
|
||||||
|
};
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -29,8 +29,8 @@
|
|||||||
--chart-3: 197 37% 24%;
|
--chart-3: 197 37% 24%;
|
||||||
--chart-4: 43 74% 66%;
|
--chart-4: 43 74% 66%;
|
||||||
--chart-5: 27 87% 67%;
|
--chart-5: 27 87% 67%;
|
||||||
/* --sidebar-background: 0 0% 98%;
|
--sidebar-background: 0 0% 100%;
|
||||||
--sidebar-foreground: 240 5.3% 26.1%; */
|
--sidebar-foreground: 240 10% 3.9%;
|
||||||
--sidebar-primary: 240 5.9% 10%;
|
--sidebar-primary: 240 5.9% 10%;
|
||||||
--sidebar-primary-foreground: 0 0% 98%;
|
--sidebar-primary-foreground: 0 0% 98%;
|
||||||
--sidebar-accent: 240 4.8% 95.9%;
|
--sidebar-accent: 240 4.8% 95.9%;
|
||||||
@ -40,9 +40,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: 240 10% 3.9%;
|
--background: 240 10% 1%;
|
||||||
--foreground: 0 0% 98%;
|
--foreground: 0 0% 98%;
|
||||||
--card: 240 10% 3.9%;
|
--card: 240 10% 1%;
|
||||||
--card-foreground: 0 0% 98%;
|
--card-foreground: 0 0% 98%;
|
||||||
--popover: 240 10% 3.9%;
|
--popover: 240 10% 3.9%;
|
||||||
--popover-foreground: 0 0% 98%;
|
--popover-foreground: 0 0% 98%;
|
||||||
@ -64,8 +64,8 @@
|
|||||||
--chart-3: 30 80% 55%;
|
--chart-3: 30 80% 55%;
|
||||||
--chart-4: 280 65% 60%;
|
--chart-4: 280 65% 60%;
|
||||||
--chart-5: 340 75% 55%;
|
--chart-5: 340 75% 55%;
|
||||||
/* --sidebar-background: 240 5.9% 10%;
|
--sidebar-background: 240 10% 1%;
|
||||||
--sidebar-foreground: 240 4.8% 95.9%; */
|
--sidebar-foreground: 0 0% 98%;
|
||||||
--sidebar-primary: 224.3 76.3% 48%;
|
--sidebar-primary: 224.3 76.3% 48%;
|
||||||
--sidebar-primary-foreground: 0 0% 100%;
|
--sidebar-primary-foreground: 0 0% 100%;
|
||||||
--sidebar-accent: 240 3.7% 15.9%;
|
--sidebar-accent: 240 3.7% 15.9%;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user