dashboard layout (#597)

This commit is contained in:
Saeed Vaziry
2025-05-13 23:42:22 +03:00
committed by GitHub
parent 38bafd7654
commit a81e9b18b7
57 changed files with 1011 additions and 843 deletions

View File

@ -1,4 +1,4 @@
import AppLayoutTemplate from '@/layouts/app/app-header-layout';
import AppLayout from '@/layouts/app/layout';
import { type BreadcrumbItem } from '@/types';
import { type ReactNode } from 'react';
@ -7,4 +7,4 @@ interface AppLayoutProps {
breadcrumbs?: BreadcrumbItem[];
}
export default ({ children, ...props }: AppLayoutProps) => <AppLayoutTemplate {...props}>{children}</AppLayoutTemplate>;
export default ({ children, ...props }: AppLayoutProps) => <AppLayout {...props}>{children}</AppLayout>;

View File

@ -1,19 +0,0 @@
import { AppContent } from '@/components/app-content';
import { AppHeader } from '@/components/app-header';
import { AppShell } from '@/components/app-shell';
import { type BreadcrumbItem } from '@/types';
import type { PropsWithChildren } from 'react';
import { usePoll } from '@inertiajs/react';
import { Toaster } from '@/components/ui/sonner';
export default function AppHeaderLayout({ children }: PropsWithChildren<{ breadcrumbs?: BreadcrumbItem[] }>) {
usePoll(10000);
return (
<AppShell>
<AppHeader />
<AppContent>{children}</AppContent>
<Toaster />
</AppShell>
);
}

View File

@ -1,18 +0,0 @@
import { AppContent } from '@/components/app-content';
import { AppShell } from '@/components/app-shell';
import { AppSidebar } from '@/components/app-sidebar';
import { AppSidebarHeader } from '@/components/app-sidebar-header';
import { type BreadcrumbItem } from '@/types';
import { type PropsWithChildren } from 'react';
export default function AppSidebarLayout({ children, breadcrumbs = [] }: PropsWithChildren<{ breadcrumbs?: BreadcrumbItem[] }>) {
return (
<AppShell variant="sidebar">
<AppSidebar />
<AppContent variant="sidebar">
<AppSidebarHeader breadcrumbs={breadcrumbs} />
{children}
</AppContent>
</AppShell>
);
}

View File

@ -0,0 +1,35 @@
import { AppSidebar } from '@/components/app-sidebar';
import { AppHeader } from '@/components/app-header';
import { type BreadcrumbItem, NavItem } from '@/types';
import { CSSProperties, type PropsWithChildren } from 'react';
import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar';
import { usePoll } from '@inertiajs/react';
export default function Layout({
children,
secondNavItems,
secondNavTitle,
}: PropsWithChildren<{
breadcrumbs?: BreadcrumbItem[];
secondNavItems?: NavItem[];
secondNavTitle?: string;
}>) {
usePoll(10000);
return (
<SidebarProvider
style={
{
'--sidebar-width': '300px',
} as CSSProperties
}
defaultOpen={!!(secondNavItems && secondNavItems.length > 0)}
>
<AppSidebar secondNavItems={secondNavItems} secondNavTitle={secondNavTitle} />
<SidebarInset>
<AppHeader />
<div className="flex flex-1 flex-col">{children}</div>
</SidebarInset>
</SidebarProvider>
);
}

View File

@ -0,0 +1,102 @@
import { type NavItem } from '@/types';
import {
ChartPieIcon,
ClockIcon,
CogIcon,
DatabaseIcon,
FlameIcon,
FolderOpenIcon,
HomeIcon,
KeyIcon,
ListEndIcon,
LogsIcon,
MousePointerClickIcon,
Settings2Icon,
TerminalSquareIcon,
} from 'lucide-react';
import { ReactNode } from 'react';
import { Server } from '@/types/server';
import ServerHeader from '@/pages/servers/partials/header';
import Layout from '@/layouts/app/layout';
export default function ServerLayout({ server, children }: { server: Server; children: ReactNode }) {
// When server-side rendering, we only render the layout on the client...
if (typeof window === 'undefined') {
return null;
}
const sidebarNavItems: NavItem[] = [
{
title: 'Overview',
href: route('servers.show', { server: server.id }),
icon: HomeIcon,
},
{
title: 'Databases',
href: '#',
icon: DatabaseIcon,
},
{
title: 'Sites',
href: '#',
icon: MousePointerClickIcon,
},
{
title: 'File Manager',
href: '#',
icon: FolderOpenIcon,
},
{
title: 'Firewall',
href: '#',
icon: FlameIcon,
},
{
title: 'CronJobs',
href: '#',
icon: ClockIcon,
},
{
title: 'Workers',
href: '#',
icon: ListEndIcon,
},
{
title: 'SSH Keys',
href: '#',
icon: KeyIcon,
},
{
title: 'Services',
href: '#',
icon: CogIcon,
},
{
title: 'Metrics',
href: '#',
icon: ChartPieIcon,
},
{
title: 'Console',
href: '#',
icon: TerminalSquareIcon,
},
{
title: 'Logs',
href: '#',
icon: LogsIcon,
},
{
title: 'Settings',
href: '#',
icon: Settings2Icon,
},
];
return (
<Layout secondNavItems={sidebarNavItems} secondNavTitle={server.name}>
<ServerHeader server={server} />
<div className="p-4">{children}</div>
</Layout>
);
}

View File

@ -1,16 +1,12 @@
import Container from '@/components/container';
import { Button } from '@/components/ui/button';
import { Separator } from '@/components/ui/separator';
import { cn } from '@/lib/utils';
import { type NavItem } from '@/types';
import { Link } from '@inertiajs/react';
import { ListIcon, LockIcon, UserIcon } from 'lucide-react';
import { type PropsWithChildren } from 'react';
import { type BreadcrumbItem, type NavItem } from '@/types';
import { ListIcon, UserIcon } from 'lucide-react';
import { ReactNode } from 'react';
import Layout from '@/layouts/app/layout';
const sidebarNavItems: NavItem[] = [
{
title: 'Profile',
href: '/settings/profile',
href: route('profile'),
icon: UserIcon,
},
{
@ -18,51 +14,17 @@ const sidebarNavItems: NavItem[] = [
href: '/',
icon: ListIcon,
},
{
title: 'Password',
href: '/settings/password',
icon: LockIcon,
},
];
export default function SettingsLayout({ children }: PropsWithChildren) {
export default function SettingsLayout({ children, breadcrumbs }: { children: ReactNode; breadcrumbs?: BreadcrumbItem[] }) {
// When server-side rendering, we only render the layout on the client...
if (typeof window === 'undefined') {
return null;
}
const currentPath = window.location.pathname;
return (
<Container>
<div className="flex flex-col space-y-8 lg:flex-row lg:space-y-0 lg:space-x-12">
<aside className="w-full max-w-xl lg:w-60">
<nav className="flex flex-col space-y-1 space-x-0">
{sidebarNavItems.map((item, index) => (
<Button
key={`${item.href}-${index}`}
size="sm"
variant="ghost"
asChild
className={cn('w-full justify-start', {
'bg-muted': currentPath === item.href,
})}
>
<Link href={item.href} prefetch>
{item.icon && <item.icon />}
{item.title}
</Link>
</Button>
))}
</nav>
</aside>
<Separator className="my-6 md:hidden" />
<div className="flex-1">
<section className="space-y-12">{children}</section>
</div>
</div>
</Container>
<Layout breadcrumbs={breadcrumbs} secondNavItems={sidebarNavItems} secondNavTitle="Settings">
{children}
</Layout>
);
}