feat(app): add pagewrapper component with breadcrumbs
This commit is contained in:
parent
41ee7a7849
commit
599bdd2978
@ -1,4 +1,4 @@
|
|||||||
import { Separator, SidebarInset, SidebarTrigger } from '@boring.tools/ui'
|
import { SidebarInset } from '@boring.tools/ui'
|
||||||
import type { ReactNode } from 'react'
|
import type { ReactNode } from 'react'
|
||||||
import { Sidebar } from './Sidebar'
|
import { Sidebar } from './Sidebar'
|
||||||
|
|
||||||
@ -8,37 +8,8 @@ export const description =
|
|||||||
export const Layout = ({ children }: { children: ReactNode | ReactNode[] }) => {
|
export const Layout = ({ children }: { children: ReactNode | ReactNode[] }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* <Navigation /> */}
|
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<SidebarInset>
|
<SidebarInset>{children}</SidebarInset>
|
||||||
<header className="flex h-16 shrink-0 items-center gap-2">
|
|
||||||
<div className="flex items-center gap-2 px-4">
|
|
||||||
<SidebarTrigger className="-ml-1" />
|
|
||||||
{/* <Separator orientation="vertical" className="mr-2 h-4" /> */}
|
|
||||||
{/* <Breadcrumb>
|
|
||||||
<BreadcrumbList>
|
|
||||||
<BreadcrumbItem className="hidden md:block">
|
|
||||||
<BreadcrumbLink href="#">
|
|
||||||
Building Your Application
|
|
||||||
</BreadcrumbLink>
|
|
||||||
</BreadcrumbItem>
|
|
||||||
<BreadcrumbSeparator className="hidden md:block" />
|
|
||||||
<BreadcrumbItem>
|
|
||||||
<BreadcrumbPage>Data Fetching</BreadcrumbPage>
|
|
||||||
</BreadcrumbItem>
|
|
||||||
</BreadcrumbList>
|
|
||||||
</Breadcrumb> */}
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<Separator />
|
|
||||||
<div className="flex flex-1 flex-col gap-4 p-4 pt-0">
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<main className="flex flex-1 flex-col gap-4 p-4 lg:gap-6 lg:p-6">
|
|
||||||
{children}
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</SidebarInset>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
63
apps/app/src/components/PageWrapper.tsx
Normal file
63
apps/app/src/components/PageWrapper.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbLink,
|
||||||
|
BreadcrumbList,
|
||||||
|
BreadcrumbPage,
|
||||||
|
BreadcrumbSeparator,
|
||||||
|
Separator,
|
||||||
|
SidebarTrigger,
|
||||||
|
} from '@boring.tools/ui'
|
||||||
|
import { Link } from '@tanstack/react-router'
|
||||||
|
|
||||||
|
type Breadcrumbs = {
|
||||||
|
name: string
|
||||||
|
to: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PageWrapper = ({
|
||||||
|
children,
|
||||||
|
breadcrumbs,
|
||||||
|
}: { children: React.ReactNode; breadcrumbs?: Breadcrumbs[] }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<header className="flex h-16 shrink-0 items-center gap-2">
|
||||||
|
<div className="flex items-center gap-2 px-4">
|
||||||
|
<SidebarTrigger className="-ml-1" />
|
||||||
|
{/* <Separator orientation="vertical" className="mr-2 h-4" /> */}
|
||||||
|
<Separator orientation="vertical" className="mr-2 h-4" />
|
||||||
|
<Breadcrumb>
|
||||||
|
<BreadcrumbList>
|
||||||
|
{breadcrumbs?.map((crumb, key) => {
|
||||||
|
if (breadcrumbs.length - 1 === key) {
|
||||||
|
return (
|
||||||
|
<BreadcrumbItem key={crumb.to}>
|
||||||
|
<BreadcrumbPage>{crumb.name}</BreadcrumbPage>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-2" key={crumb.to}>
|
||||||
|
<BreadcrumbItem className="hidden md:block">
|
||||||
|
<BreadcrumbLink asChild>
|
||||||
|
<Link to={crumb.to}>{crumb.name}</Link>
|
||||||
|
</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator className="hidden md:block" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</BreadcrumbList>
|
||||||
|
</Breadcrumb>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<Separator />
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<main className="flex flex-1 flex-col gap-4 p-4 lg:gap-6 lg:p-6">
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -7,6 +7,7 @@ import {
|
|||||||
import { Link, Outlet, createLazyFileRoute } from '@tanstack/react-router'
|
import { Link, Outlet, createLazyFileRoute } from '@tanstack/react-router'
|
||||||
import { FileStackIcon, PencilIcon } from 'lucide-react'
|
import { FileStackIcon, PencilIcon } from 'lucide-react'
|
||||||
import { ChangelogDelete } from '../components/Changelog/Delete'
|
import { ChangelogDelete } from '../components/Changelog/Delete'
|
||||||
|
import { PageWrapper } from '../components/PageWrapper'
|
||||||
import { useChangelogById } from '../hooks/useChangelog'
|
import { useChangelogById } from '../hooks/useChangelog'
|
||||||
|
|
||||||
const Component = () => {
|
const Component = () => {
|
||||||
@ -25,24 +26,35 @@ const Component = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-5">
|
<PageWrapper
|
||||||
{!isPending && data && (
|
breadcrumbs={[
|
||||||
<div>
|
{
|
||||||
<div className="flex justify-between items-center">
|
name: 'Changelog',
|
||||||
<div className="flex gap-3 items-center">
|
to: '/changelog',
|
||||||
<FileStackIcon
|
},
|
||||||
strokeWidth={1.5}
|
{ name: data?.title ?? '', to: `/changelog/${data?.id}` },
|
||||||
className="w-10 h-10 text-muted-foreground"
|
]}
|
||||||
/>
|
>
|
||||||
<div>
|
<div className="flex flex-col gap-5">
|
||||||
<h1 className="text-3xl">{data.title}</h1>
|
{!isPending && data && (
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div className="flex gap-3 items-center">
|
||||||
|
<FileStackIcon
|
||||||
|
strokeWidth={1.5}
|
||||||
|
className="w-10 h-10 text-muted-foreground"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<h1 className="text-3xl">{data.title}</h1>
|
||||||
|
|
||||||
<p className="text-muted-foreground mt-2">{data.description}</p>
|
<p className="text-muted-foreground mt-2">
|
||||||
|
{data.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
{/* <Tooltip>
|
{/* <Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button variant={'ghost'}>
|
<Button variant={'ghost'}>
|
||||||
<TerminalSquareIcon strokeWidth={1.5} />
|
<TerminalSquareIcon strokeWidth={1.5} />
|
||||||
@ -64,28 +76,29 @@ const Component = () => {
|
|||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip> */}
|
</Tooltip> */}
|
||||||
|
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Link to={'/changelog/$id/edit'} params={{ id }}>
|
<Link to={'/changelog/$id/edit'} params={{ id }}>
|
||||||
<Button variant={'ghost'}>
|
<Button variant={'ghost'}>
|
||||||
<PencilIcon strokeWidth={1.5} />
|
<PencilIcon strokeWidth={1.5} />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>Edit</p>
|
<p>Edit</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<ChangelogDelete id={id} />
|
<ChangelogDelete id={id} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5">
|
||||||
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
)}
|
||||||
<Outlet />
|
</div>
|
||||||
</div>
|
</PageWrapper>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import { createLazyFileRoute } from '@tanstack/react-router'
|
|||||||
import { useNavigate } from '@tanstack/react-router'
|
import { useNavigate } from '@tanstack/react-router'
|
||||||
import { useForm } from 'react-hook-form'
|
import { useForm } from 'react-hook-form'
|
||||||
import type { z } from 'zod'
|
import type { z } from 'zod'
|
||||||
|
import { PageWrapper } from '../components/PageWrapper'
|
||||||
import { useChangelogCreate } from '../hooks/useChangelog'
|
import { useChangelogCreate } from '../hooks/useChangelog'
|
||||||
|
|
||||||
const Component = () => {
|
const Component = () => {
|
||||||
@ -40,75 +41,85 @@ const Component = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-5">
|
<PageWrapper
|
||||||
<h1 className="text-3xl">New changelog</h1>
|
breadcrumbs={[
|
||||||
|
{
|
||||||
|
name: 'Changelog',
|
||||||
|
to: '/changelog',
|
||||||
|
},
|
||||||
|
{ name: 'New', to: '/changelog/create' },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-5">
|
||||||
|
<h1 className="text-3xl">New changelog</h1>
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="space-y-8 max-w-screen-md"
|
className="space-y-8 max-w-screen-md"
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="title"
|
name="title"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Title</FormLabel>
|
<FormLabel>Title</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="My changelog" {...field} autoFocus />
|
<Input placeholder="My changelog" {...field} autoFocus />
|
||||||
</FormControl>{' '}
|
</FormControl>{' '}
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="description"
|
name="description"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Description</FormLabel>
|
<FormLabel>Description</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder="Some details about the changelog..."
|
placeholder="Some details about the changelog..."
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>{' '}
|
</FormControl>{' '}
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="isSemver"
|
name="isSemver"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md ">
|
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md ">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<div className="space-y-1 leading-none">
|
<div className="space-y-1 leading-none">
|
||||||
<FormLabel>Using Semver</FormLabel>
|
<FormLabel>Using Semver</FormLabel>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
If this changelog is following the{' '}
|
If this changelog is following the{' '}
|
||||||
<a
|
<a
|
||||||
href="https://semver.org/lang/de/"
|
href="https://semver.org/lang/de/"
|
||||||
className="text-emerald-700"
|
className="text-emerald-700"
|
||||||
>
|
>
|
||||||
semantic versioning?
|
semantic versioning?
|
||||||
</a>
|
</a>
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Button type="submit">Create</Button>
|
<Button type="submit">Create</Button>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
</PageWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
} from '@boring.tools/ui'
|
} from '@boring.tools/ui'
|
||||||
import { Link, createLazyFileRoute } from '@tanstack/react-router'
|
import { Link, createLazyFileRoute } from '@tanstack/react-router'
|
||||||
import { PlusCircleIcon } from 'lucide-react'
|
import { PlusCircleIcon } from 'lucide-react'
|
||||||
|
import { PageWrapper } from '../components/PageWrapper'
|
||||||
import { useChangelogList } from '../hooks/useChangelog'
|
import { useChangelogList } from '../hooks/useChangelog'
|
||||||
|
|
||||||
const Component = () => {
|
const Component = () => {
|
||||||
@ -24,45 +25,49 @@ const Component = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-5">
|
<PageWrapper breadcrumbs={[{ name: 'Changelog', to: '/changelog' }]}>
|
||||||
<h1 className="text-3xl">Changelogs</h1>
|
<>
|
||||||
|
<div className="flex flex-col gap-5">
|
||||||
|
<h1 className="text-3xl">Changelog</h1>
|
||||||
|
|
||||||
<div className="flex gap-10 w-full">
|
<div className="flex gap-10 w-full">
|
||||||
{!isPending &&
|
{!isPending &&
|
||||||
data &&
|
data &&
|
||||||
data.map((changelog) => {
|
data.map((changelog) => {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
to="/changelog/$id"
|
to="/changelog/$id"
|
||||||
params={{ id: changelog.id }}
|
params={{ id: changelog.id }}
|
||||||
key={changelog.id}
|
key={changelog.id}
|
||||||
>
|
>
|
||||||
<Card className="max-w-56 min-w-56 w-full h-36 hover:border-emerald-700 transition">
|
<Card className="max-w-56 min-w-56 w-full h-36 hover:border-emerald-700 transition">
|
||||||
<CardHeader className="flex items-center justify-center">
|
<CardHeader className="flex items-center justify-center">
|
||||||
<CardTitle>{changelog.title}</CardTitle>
|
<CardTitle>{changelog.title}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex items-center justify-center flex-col">
|
<CardContent className="flex items-center justify-center flex-col">
|
||||||
<span>Versions: {changelog.computed.versionCount}</span>
|
<span>Versions: {changelog.computed.versionCount}</span>
|
||||||
|
|
||||||
<span>Commits: {changelog.computed.commitCount}</span>
|
<span>Commits: {changelog.computed.commitCount}</span>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<Link to="/changelog/create">
|
<Link to="/changelog/create">
|
||||||
<Card className="max-w-56 min-w-56 w-full h-36 hover:border-emerald-700 transition">
|
<Card className="max-w-56 min-w-56 w-full h-36 hover:border-emerald-700 transition">
|
||||||
<CardHeader className="flex items-center justify-center">
|
<CardHeader className="flex items-center justify-center">
|
||||||
<CardTitle>New Changelog</CardTitle>
|
<CardTitle>New Changelog</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex items-center justify-center">
|
<CardContent className="flex items-center justify-center">
|
||||||
<PlusCircleIcon strokeWidth={1.5} className="w-10 h-10" />
|
<PlusCircleIcon strokeWidth={1.5} className="w-10 h-10" />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
</PageWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user