feat(app): add base layout
Some checks failed
Build and Push Docker Image / build (push) Failing after 48s
Some checks failed
Build and Push Docker Image / build (push) Failing after 48s
This commit is contained in:
parent
643fe98c39
commit
f0eca6885d
33
apps/app/biome.json
Normal file
33
apps/app/biome.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/1.9.2/schema.json",
|
||||||
|
"vcs": {
|
||||||
|
"enabled": false,
|
||||||
|
"clientKind": "git",
|
||||||
|
"useIgnoreFile": false
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"ignoreUnknown": false,
|
||||||
|
"ignore": ["*.gen.ts"]
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"indentStyle": "tab"
|
||||||
|
},
|
||||||
|
"organizeImports": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true,
|
||||||
|
"correctness": {
|
||||||
|
"noChildrenProp": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"javascript": {
|
||||||
|
"formatter": {
|
||||||
|
"quoteStyle": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
apps/app/src/components/Layout.tsx
Normal file
56
apps/app/src/components/Layout.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { CircleUser } from 'lucide-react'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@boring.tools/ui'
|
||||||
|
import { SignOutButton, useUser } from '@clerk/clerk-react'
|
||||||
|
import type { ReactNode } from 'react'
|
||||||
|
import { Navigation } from './Navigation'
|
||||||
|
import { NavigationMobile } from './NavigationMobile'
|
||||||
|
|
||||||
|
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.'
|
||||||
|
|
||||||
|
export const Layout = ({ children }: { children: ReactNode | ReactNode[] }) => {
|
||||||
|
const { user } = useUser()
|
||||||
|
return (
|
||||||
|
<div className="grid min-h-screen w-full md:grid-cols-[220px_1fr] lg:grid-cols-[280px_1fr]">
|
||||||
|
<Navigation />
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<header className="flex h-14 items-center gap-4 border-b bg-muted/40 px-4 lg:h-[60px] lg:px-6">
|
||||||
|
<NavigationMobile />
|
||||||
|
<div className="w-full flex-1"></div>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
className="overflow-hidden rounded-full"
|
||||||
|
>
|
||||||
|
<img src={user?.imageUrl} alt={user?.fullName ?? 'Avatar'} />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<SignOutButton>
|
||||||
|
<button type="button">Sign out</button>
|
||||||
|
</SignOutButton>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</header>
|
||||||
|
<main className="flex flex-1 flex-col gap-4 p-4 lg:gap-6 lg:p-6">
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
58
apps/app/src/components/Navigation.tsx
Normal file
58
apps/app/src/components/Navigation.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from '@boring.tools/ui'
|
||||||
|
import { Link } from '@tanstack/react-router'
|
||||||
|
import { BellIcon, HomeIcon } from 'lucide-react'
|
||||||
|
|
||||||
|
export const Navigation = () => {
|
||||||
|
return (
|
||||||
|
<div className="hidden border-r bg-muted/40 md:block">
|
||||||
|
<div className="flex h-full max-h-screen flex-col gap-2">
|
||||||
|
<div className="flex h-14 items-center border-b px-4 lg:h-[60px] lg:px-6">
|
||||||
|
<Link to="/" className="flex items-center gap-2 font-semibold">
|
||||||
|
<span className="">boring.tools</span>
|
||||||
|
</Link>
|
||||||
|
<Button variant="outline" size="icon" className="ml-auto h-8 w-8">
|
||||||
|
<BellIcon className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Toggle notifications</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<nav className="grid items-start px-2 text-sm font-medium lg:px-4">
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
className="flex items-center gap-3 rounded-lg px-3 py-2 transition-all hover:text-primary"
|
||||||
|
activeProps={{ className: 'bg-muted text-primary' }}
|
||||||
|
>
|
||||||
|
<HomeIcon className="h-4 w-4" />
|
||||||
|
Dashboard
|
||||||
|
</Link>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div className="mt-auto p-4">
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="p-2 pt-0 md:p-4">
|
||||||
|
<CardTitle>More Infos</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
If you want more informations about boring.tools, visit our
|
||||||
|
documenation!
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="p-2 pt-0 md:p-4 md:pt-0">
|
||||||
|
<a href="https://boring.tools">
|
||||||
|
<Button size="sm" className="w-full">
|
||||||
|
Documentation
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
64
apps/app/src/components/NavigationMobile.tsx
Normal file
64
apps/app/src/components/NavigationMobile.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
Sheet,
|
||||||
|
SheetContent,
|
||||||
|
SheetTrigger,
|
||||||
|
} from '@boring.tools/ui'
|
||||||
|
import { Link } from '@tanstack/react-router'
|
||||||
|
import { HomeIcon, MenuIcon } from 'lucide-react'
|
||||||
|
|
||||||
|
export const NavigationMobile = () => {
|
||||||
|
return (
|
||||||
|
<Sheet>
|
||||||
|
<SheetTrigger asChild>
|
||||||
|
<Button variant="outline" size="icon" className="shrink-0 md:hidden">
|
||||||
|
<MenuIcon className="h-5 w-5" />
|
||||||
|
<span className="sr-only">Toggle navigation menu</span>
|
||||||
|
</Button>
|
||||||
|
</SheetTrigger>
|
||||||
|
<SheetContent side="left" className="flex flex-col">
|
||||||
|
<nav className="grid gap-2 text-lg font-medium">
|
||||||
|
<Link
|
||||||
|
to="/"
|
||||||
|
className="flex items-center gap-2 text-lg font-semibold"
|
||||||
|
>
|
||||||
|
<span className="sr-only">boring.tools</span>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to="/"
|
||||||
|
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 hover:text-foreground"
|
||||||
|
activeProps={{
|
||||||
|
className: 'bg-muted text-foreground',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<HomeIcon className="h-5 w-5" />
|
||||||
|
Dashboard
|
||||||
|
</Link>
|
||||||
|
</nav>
|
||||||
|
<div className="mt-auto">
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="p-2 pt-0 md:p-4">
|
||||||
|
<CardTitle>More Infos</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
If you want more informations about boring.tools, visit our
|
||||||
|
documenation!
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="p-2 pt-0 md:p-4 md:pt-0">
|
||||||
|
<a href="https://boring.tools">
|
||||||
|
<Button size="sm" className="w-full">
|
||||||
|
Documentation
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
)
|
||||||
|
}
|
@ -2,6 +2,7 @@ import { ThemeToggle } from '@boring.tools/ui'
|
|||||||
import { SignIn, SignedIn, SignedOut, UserButton } from '@clerk/clerk-react'
|
import { SignIn, SignedIn, SignedOut, UserButton } from '@clerk/clerk-react'
|
||||||
import { Link, Outlet, createRootRoute } from '@tanstack/react-router'
|
import { Link, Outlet, createRootRoute } from '@tanstack/react-router'
|
||||||
import { TanStackRouterDevtools } from '@tanstack/router-devtools'
|
import { TanStackRouterDevtools } from '@tanstack/router-devtools'
|
||||||
|
import { Layout } from '../components/Layout'
|
||||||
|
|
||||||
export const Route = createRootRoute({
|
export const Route = createRootRoute({
|
||||||
component: () => (
|
component: () => (
|
||||||
@ -12,21 +13,10 @@ export const Route = createRootRoute({
|
|||||||
</div>
|
</div>
|
||||||
</SignedOut>
|
</SignedOut>
|
||||||
<SignedIn>
|
<SignedIn>
|
||||||
<>
|
<Layout>
|
||||||
<div className="p-2 flex gap-2">
|
|
||||||
<Link to="/" className="[&.active]:font-bold">
|
|
||||||
Home
|
|
||||||
</Link>{' '}
|
|
||||||
<Link to="/about" className="[&.active]:font-bold">
|
|
||||||
About
|
|
||||||
</Link>
|
|
||||||
<ThemeToggle />
|
|
||||||
<UserButton />
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<Outlet />
|
<Outlet />
|
||||||
{!import.meta.env.PROD && <TanStackRouterDevtools />}
|
{!import.meta.env.PROD && <TanStackRouterDevtools />}
|
||||||
</>
|
</Layout>
|
||||||
</SignedIn>
|
</SignedIn>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
Loading…
Reference in New Issue
Block a user