feat(app): add pagewrapper component with breadcrumbs
All checks were successful
Build and Push Docker Image / tests (push) Successful in 44s
Build and Push Docker Image / build (push) Successful in 1m56s

This commit is contained in:
Lars Hampe 2024-10-20 20:53:30 +02:00
parent 41ee7a7849
commit 599bdd2978
5 changed files with 230 additions and 167 deletions

View File

@ -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>
</> </>
) )
} }

View 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>
</>
)
}

View File

@ -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>
) )
} }

View File

@ -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>
) )
} }

View File

@ -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>
) )
} }