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 { SidebarProvider } from "@/components/ui/sidebar";
|
||||
import { auth } from "@/server/auth";
|
||||
import { api, HydrateClient } from "@/trpc/server";
|
||||
import React from "react";
|
||||
|
||||
async function Layout({ children }: { children: React.ReactNode }) {
|
||||
const session = await auth();
|
||||
void api.app.getSidebarMain.prefetch();
|
||||
|
||||
return (
|
||||
<SidebarProvider>
|
||||
<AppSidebar user={session?.user} />
|
||||
<div className="h-screen w-full bg-background">
|
||||
<Navbar />
|
||||
<main className="space-y-4 p-4">{children}</main>
|
||||
</div>
|
||||
</SidebarProvider>
|
||||
<HydrateClient>
|
||||
<SidebarProvider>
|
||||
<AppSidebar user={session?.user} />
|
||||
<div className="h-screen w-full bg-background">
|
||||
<Navbar />
|
||||
<main className="space-y-4 p-4">{children}</main>
|
||||
</div>
|
||||
</SidebarProvider>
|
||||
</HydrateClient>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -91,7 +91,7 @@ function ArticleFilterBar({
|
||||
sort: currentValue?.length ? currentValue : undefined,
|
||||
});
|
||||
}}
|
||||
className="w-full"
|
||||
className="w-full lg:max-w-64"
|
||||
messageUi={{
|
||||
selectIcon: FilterIcon,
|
||||
select: "Sortieren",
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
import * as React from "react";
|
||||
import { Folder, LifeBuoy, Newspaper, Send } from "lucide-react";
|
||||
|
||||
import { NavMain } from "./nav-main";
|
||||
|
||||
@ -19,71 +18,6 @@ import NavBranding from "./nav-branding";
|
||||
import { Icons } from "@/components/icons";
|
||||
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({
|
||||
...props
|
||||
}: React.ComponentProps<typeof Sidebar> & { user?: User | null }) {
|
||||
@ -93,7 +27,8 @@ export function AppSidebar({
|
||||
<NavBranding subTitle="Wissen" />
|
||||
</SidebarHeader>
|
||||
<SidebarContent>
|
||||
<NavMain items={data.navMain} />
|
||||
<NavMain />
|
||||
|
||||
<NavSecondary items={data.navSecondary} className="mt-auto" />
|
||||
</SidebarContent>
|
||||
<SidebarFooter>
|
||||
@ -103,3 +38,14 @@ export function AppSidebar({
|
||||
</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 {
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@/components/ui/sidebar";
|
||||
import { Icons } from "@/components/icons";
|
||||
import { appConfig } from "@/config";
|
||||
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 }) {
|
||||
const { isMobile, toggleSidebar } = useSidebar();
|
||||
return (
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton size="lg" asChild>
|
||||
<SidebarMenu className={cn(isMobile && "flex flex-row items-center")}>
|
||||
<SidebarMenuItem className="w-full">
|
||||
<SidebarMenuButton size="lg" asChild className="w-full">
|
||||
<Link href="/">
|
||||
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-foreground text-background">
|
||||
<Icons.logo className="size-4" />
|
||||
@ -24,6 +30,15 @@ function NavBranding({ subTitle }: { subTitle: string }) {
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
{isMobile && (
|
||||
<SidebarMenuItem className="w-max">
|
||||
<SidebarMenuButton asChild>
|
||||
<Button variant={"ghost"} size={"icon"} onClick={toggleSidebar}>
|
||||
<XIcon className="size-4" />
|
||||
</Button>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
)}
|
||||
</SidebarMenu>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
"use client";
|
||||
|
||||
import { ChevronRight, type LucideIcon } from "lucide-react";
|
||||
|
||||
import {
|
||||
@ -18,22 +17,45 @@ import {
|
||||
SidebarMenuSubButton,
|
||||
SidebarMenuSubItem,
|
||||
} from "@/components/ui/sidebar";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { api } from "@/trpc/react";
|
||||
import { Icons } from "@/components/icons";
|
||||
|
||||
export function NavMain({
|
||||
items,
|
||||
}: {
|
||||
items: {
|
||||
title: string;
|
||||
url: string;
|
||||
icon: LucideIcon;
|
||||
isActive?: boolean;
|
||||
items?: {
|
||||
title: string;
|
||||
url: string;
|
||||
hideBorder?: boolean;
|
||||
}[];
|
||||
}[];
|
||||
}) {
|
||||
export function NavMain() {
|
||||
const [{ articles, categories }] = api.app.getSidebarMain.useSuspenseQuery();
|
||||
const items = [
|
||||
{
|
||||
title: "Artikel",
|
||||
url: "/artikel",
|
||||
isActive: true,
|
||||
icon: Icons.article,
|
||||
items: [
|
||||
...articles.map((article) => ({
|
||||
title: article.title,
|
||||
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 (
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>Platform</SidebarGroupLabel>
|
||||
@ -77,3 +99,20 @@ export function NavMain({
|
||||
</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 EditorDropdown from "../editor-dropdown";
|
||||
import { SidebarTrigger } from "./app-sidebar/sidebar-trigger";
|
||||
|
||||
async function Navbar() {
|
||||
const session = await auth();
|
||||
@ -13,10 +14,13 @@ async function Navbar() {
|
||||
: false;
|
||||
|
||||
return (
|
||||
<div className="flex h-14 items-center justify-end gap-4 border-b bg-background px-4">
|
||||
{isEditor && <EditorDropdown />}
|
||||
<div className="flex h-14 items-center justify-between gap-4 border-b bg-sidebar px-4">
|
||||
<SidebarTrigger />
|
||||
<div className="flex items-center gap-4">
|
||||
{isEditor && <EditorDropdown />}
|
||||
|
||||
<ModeToggle />
|
||||
<ModeToggle />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -24,16 +24,24 @@ export const appRouter = createTRPCRouter({
|
||||
return { articles, categories };
|
||||
}),
|
||||
|
||||
getSidebarContent: publicProcedure.query(async ({ ctx }) => {
|
||||
return await ctx.db.query.categories.findMany({
|
||||
with: {
|
||||
articles: {
|
||||
columns: {
|
||||
title: true,
|
||||
slug: true,
|
||||
},
|
||||
},
|
||||
getSidebarMain: publicProcedure.query(async ({ ctx }) => {
|
||||
const categories = await ctx.db.query.categories.findMany({
|
||||
limit: 3,
|
||||
columns: {
|
||||
slug: true,
|
||||
name: 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-4: 43 74% 66%;
|
||||
--chart-5: 27 87% 67%;
|
||||
/* --sidebar-background: 0 0% 98%;
|
||||
--sidebar-foreground: 240 5.3% 26.1%; */
|
||||
--sidebar-background: 0 0% 100%;
|
||||
--sidebar-foreground: 240 10% 3.9%;
|
||||
--sidebar-primary: 240 5.9% 10%;
|
||||
--sidebar-primary-foreground: 0 0% 98%;
|
||||
--sidebar-accent: 240 4.8% 95.9%;
|
||||
@ -40,9 +40,9 @@
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 240 10% 3.9%;
|
||||
--background: 240 10% 1%;
|
||||
--foreground: 0 0% 98%;
|
||||
--card: 240 10% 3.9%;
|
||||
--card: 240 10% 1%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--popover: 240 10% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
@ -64,8 +64,8 @@
|
||||
--chart-3: 30 80% 55%;
|
||||
--chart-4: 280 65% 60%;
|
||||
--chart-5: 340 75% 55%;
|
||||
/* --sidebar-background: 240 5.9% 10%;
|
||||
--sidebar-foreground: 240 4.8% 95.9%; */
|
||||
--sidebar-background: 240 10% 1%;
|
||||
--sidebar-foreground: 0 0% 98%;
|
||||
--sidebar-primary: 224.3 76.3% 48%;
|
||||
--sidebar-primary-foreground: 0 0% 100%;
|
||||
--sidebar-accent: 240 3.7% 15.9%;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user