feat(app): use new sidebar navigation
All checks were successful
Build and Push Docker Image / tests (push) Successful in 38s
Build and Push Docker Image / build (push) Successful in 2m26s

This commit is contained in:
Lars Hampe 2024-10-20 09:43:29 +02:00
parent 094775a3a0
commit 99c3452a5f
6 changed files with 245 additions and 70 deletions

View File

@ -1,58 +1,43 @@
import { import { SidebarInset, SidebarTrigger } from '@boring.tools/ui'
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@boring.tools/ui'
import { SignOutButton, useUser } from '@clerk/clerk-react'
import { Link } from '@tanstack/react-router'
import type { ReactNode } from 'react' import type { ReactNode } from 'react'
import { Navigation } from './Navigation' import { Sidebar } from './Sidebar'
import { NavigationMobile } from './NavigationMobile'
export const description = export const description =
'A products dashboard with a sidebar navigation and a main content area. The dashboard has a header with a search input and a user menu. The sidebar has a logo, navigation links, and a card with a call to action. The main content area shows an empty state with a call to action.' 'A products dashboard with a sidebar navigation and a main content area. The dashboard has a header with a search input and a user menu. The sidebar has a logo, navigation links, and a card with a call to action. The main content area shows an empty state with a call to action.'
export const Layout = ({ children }: { children: ReactNode | ReactNode[] }) => { export const Layout = ({ children }: { children: ReactNode | ReactNode[] }) => {
const { user } = useUser()
return ( return (
<div className="grid min-h-screen w-full md:grid-cols-[220px_1fr] lg:grid-cols-[280px_1fr]"> <>
<Navigation /> {/* <Navigation /> */}
<div className="flex flex-col"> <Sidebar />
<header className="flex h-14 items-center gap-4 border-b bg-muted/40 px-4 lg:h-[60px] lg:px-6"> <SidebarInset>
<NavigationMobile /> <header className="flex h-16 shrink-0 items-center gap-2">
<div className="w-full flex-1" /> <div className="flex items-center gap-2 px-4">
<DropdownMenu> <SidebarTrigger className="-ml-1" />
<DropdownMenuTrigger asChild> {/* <Separator orientation="vertical" className="mr-2 h-4" /> */}
<Button {/* <Breadcrumb>
variant="outline" <BreadcrumbList>
size="icon" <BreadcrumbItem className="hidden md:block">
className="overflow-hidden rounded-full" <BreadcrumbLink href="#">
> Building Your Application
<img src={user?.imageUrl} alt={user?.fullName ?? 'Avatar'} /> </BreadcrumbLink>
</Button> </BreadcrumbItem>
</DropdownMenuTrigger> <BreadcrumbSeparator className="hidden md:block" />
<DropdownMenuContent align="end"> <BreadcrumbItem>
<DropdownMenuLabel>Settings</DropdownMenuLabel> <BreadcrumbPage>Data Fetching</BreadcrumbPage>
<DropdownMenuItem asChild> </BreadcrumbItem>
<Link to="/user">Profile</Link> </BreadcrumbList>
</DropdownMenuItem> </Breadcrumb> */}
<DropdownMenuSeparator /> </div>
<DropdownMenuItem>
<SignOutButton>
<button type="button">Sign out</button>
</SignOutButton>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</header> </header>
<main className="flex flex-1 flex-col gap-4 p-4 lg:gap-6 lg:p-6"> <div className="flex flex-1 flex-col gap-4 p-4 pt-0">
{children} <div className="flex flex-col">
</main> <main className="flex flex-1 flex-col gap-4 p-4 lg:gap-6 lg:p-6">
</div> {children}
</div> </main>
</div>
</div>
</SidebarInset>
</>
) )
} }

View File

@ -0,0 +1,91 @@
import { FileStackIcon } from 'lucide-react'
import {
Collapsible,
Sidebar as SidebarComp,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from '@boring.tools/ui'
import { Link } from '@tanstack/react-router'
import { SidebarUser } from './SidebarUser'
const items = [
{
title: 'Changelog',
url: '/changelog',
icon: FileStackIcon,
isActive: true,
},
]
export function Sidebar() {
return (
<SidebarComp>
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" asChild>
<Link to="/">
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
BT
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">boring.tools</span>
</div>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarMenu>
{items.map((item) => (
<Collapsible key={item.title} asChild defaultOpen={item.isActive}>
<SidebarMenuItem>
<SidebarMenuButton asChild tooltip={item.title}>
<Link to={item.url}>
<item.icon />
<span>{item.title}</span>
</Link>
</SidebarMenuButton>
{/* {item.items?.length ? (
<>
<CollapsibleTrigger asChild>
<SidebarMenuAction className="data-[state=open]:rotate-90">
<ChevronRightIcon />
<span className="sr-only">Toggle</span>
</SidebarMenuAction>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton asChild>
<Link to={subItem.url}>
<span>{subItem.title}</span>
</Link>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</>
) : null} */}
</SidebarMenuItem>
</Collapsible>
))}
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>
<SidebarUser />
</SidebarFooter>
</SidebarComp>
)
}

View File

@ -0,0 +1,96 @@
import { ChevronsUpDown } from 'lucide-react'
import {
Avatar,
AvatarFallback,
AvatarImage,
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from '@boring.tools/ui'
import { SignOutButton, useUser } from '@clerk/clerk-react'
import { Link } from '@tanstack/react-router'
export function SidebarUser() {
const { isMobile } = useSidebar()
const { user } = useUser()
return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage
src={user?.imageUrl}
alt={user?.fullName ?? 'Avatar'}
/>
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user?.fullName}</span>
<span className="truncate text-xs">
{user?.primaryEmailAddress?.emailAddress}
</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
side={isMobile ? 'bottom' : 'right'}
align="end"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage
src={user?.imageUrl}
alt={user?.fullName ?? 'Avatar'}
/>
<AvatarFallback className="rounded-lg">
{user?.firstName?.substring(0, 1)}
{user?.lastName?.substring(0, 1)}
</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{user?.fullName}
</span>
<span className="truncate text-xs">
{user?.primaryEmailAddress?.emailAddress}
</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<Link to="/user">Profile</Link>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<SignOutButton>
<button type="button">Sign out</button>
</SignOutButton>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
)
}

View File

@ -1,4 +1,8 @@
import { ThemeProvider, TooltipProvider } from '@boring.tools/ui' import {
SidebarProvider,
ThemeProvider,
TooltipProvider,
} from '@boring.tools/ui'
import { ClerkProvider } from '@clerk/clerk-react' import { ClerkProvider } from '@clerk/clerk-react'
import { RouterProvider, createRouter } from '@tanstack/react-router' import { RouterProvider, createRouter } from '@tanstack/react-router'
import { StrictMode } from 'react' import { StrictMode } from 'react'
@ -34,22 +38,24 @@ if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement) const root = ReactDOM.createRoot(rootElement)
root.render( root.render(
<StrictMode> <StrictMode>
<ClerkProvider publishableKey={PUBLISHABLE_KEY}> <SidebarProvider>
<ThemeProvider defaultTheme="dark" storageKey="ui-theme"> <ClerkProvider publishableKey={PUBLISHABLE_KEY}>
<TooltipProvider delayDuration={350}> <ThemeProvider defaultTheme="dark" storageKey="ui-theme">
<QueryClientProvider client={queryClient}> <TooltipProvider delayDuration={350}>
{import.meta.env.PROD && ( <QueryClientProvider client={queryClient}>
<script {import.meta.env.PROD && (
defer <script
src="https://umami.hashdot.co/script.js" defer
data-website-id="446678cc-e2d8-4b6f-8e8f-389cd7f6db28" src="https://umami.hashdot.co/script.js"
/> data-website-id="446678cc-e2d8-4b6f-8e8f-389cd7f6db28"
)} />
<RouterProvider router={router} /> )}
</QueryClientProvider> <RouterProvider router={router} />
</TooltipProvider> </QueryClientProvider>
</ThemeProvider> </TooltipProvider>
</ClerkProvider> </ThemeProvider>
</ClerkProvider>
</SidebarProvider>
</StrictMode>, </StrictMode>,
) )
} }

View File

@ -1,8 +1,5 @@
import { VersionUpdateInput } from '@boring.tools/schema' import { VersionUpdateInput } from '@boring.tools/schema'
import { import {
Alert,
AlertDescription,
AlertTitle,
Button, Button,
Calendar, Calendar,
Form, Form,
@ -44,7 +41,7 @@ import {
} from '../hooks/useChangelog' } from '../hooks/useChangelog'
import '@mdxeditor/editor/style.css' import '@mdxeditor/editor/style.css'
import { format } from 'date-fns' import { format } from 'date-fns'
import { CalendarIcon, TriangleAlertIcon } from 'lucide-react' import { CalendarIcon } from 'lucide-react'
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import { ChangelogVersionDelete } from '../components/Changelog/VersionDelete' import { ChangelogVersionDelete } from '../components/Changelog/VersionDelete'
import { VersionStatus } from '../components/Changelog/VersionStatus' import { VersionStatus } from '../components/Changelog/VersionStatus'

View File

@ -1 +1 @@
{"root":["./src/main.tsx","./src/routeTree.gen.ts","./src/vite-env.d.ts","./src/components/Layout.tsx","./src/components/Navigation.tsx","./src/components/NavigationMobile.tsx","./src/components/Changelog/Delete.tsx","./src/components/Changelog/VersionStatus.tsx","./src/hooks/useChangelog.ts","./src/routes/__root.tsx","./src/routes/changelog.$id.edit.lazy.tsx","./src/routes/changelog.$id.index.lazy.tsx","./src/routes/changelog.$id.lazy.tsx","./src/routes/changelog.$id.version.$versionId.tsx","./src/routes/changelog.$id.versionCreate.lazy.tsx","./src/routes/changelog.create.lazy.tsx","./src/routes/changelog.index.lazy.tsx","./src/routes/index.lazy.tsx","./src/routes/user/index.lazy.tsx","./src/utils/navigation-routes.ts","./src/utils/queryFetch.ts"],"version":"5.6.2"} {"root":["./src/main.tsx","./src/routeTree.gen.ts","./src/vite-env.d.ts","./src/components/Layout.tsx","./src/components/Navigation.tsx","./src/components/NavigationMobile.tsx","./src/components/Sidebar.tsx","./src/components/SidebarUser.tsx","./src/components/Changelog/Delete.tsx","./src/components/Changelog/VersionDelete.tsx","./src/components/Changelog/VersionStatus.tsx","./src/hooks/useChangelog.ts","./src/routes/__root.tsx","./src/routes/changelog.$id.edit.lazy.tsx","./src/routes/changelog.$id.index.lazy.tsx","./src/routes/changelog.$id.lazy.tsx","./src/routes/changelog.$id.version.$versionId.tsx","./src/routes/changelog.$id.versionCreate.lazy.tsx","./src/routes/changelog.create.lazy.tsx","./src/routes/changelog.index.lazy.tsx","./src/routes/index.lazy.tsx","./src/routes/user/index.lazy.tsx","./src/utils/navigation-routes.ts","./src/utils/queryFetch.ts"],"version":"5.6.2"}