New CPM with Laravel 12 and React
This commit is contained in:
27
resources/js/layouts/app-layout.tsx
Normal file
27
resources/js/layouts/app-layout.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import AppLayoutTemplate from '@/layouts/app/app-sidebar-layout';
|
||||
import { type BreadcrumbItem } from '@/types';
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
interface AppLayoutProps {
|
||||
children: ReactNode;
|
||||
breadcrumbs?: BreadcrumbItem[];
|
||||
}
|
||||
|
||||
export default ({ children, breadcrumbs, ...props }: AppLayoutProps) => (
|
||||
<AppLayoutTemplate breadcrumbs={breadcrumbs} {...props}>
|
||||
{children}
|
||||
<Toaster
|
||||
closeButton
|
||||
richColors
|
||||
position="bottom-right"
|
||||
duration={4000}
|
||||
visibleToasts={3}
|
||||
toastOptions={{
|
||||
style: {
|
||||
fontFamily: 'inherit',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</AppLayoutTemplate>
|
||||
);
|
14
resources/js/layouts/app/app-header-layout.tsx
Normal file
14
resources/js/layouts/app/app-header-layout.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
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';
|
||||
|
||||
export default function AppHeaderLayout({ children, breadcrumbs }: PropsWithChildren<{ breadcrumbs?: BreadcrumbItem[] }>) {
|
||||
return (
|
||||
<AppShell>
|
||||
<AppHeader breadcrumbs={breadcrumbs} />
|
||||
<AppContent>{children}</AppContent>
|
||||
</AppShell>
|
||||
);
|
||||
}
|
18
resources/js/layouts/app/app-sidebar-layout.tsx
Normal file
18
resources/js/layouts/app/app-sidebar-layout.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
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>
|
||||
);
|
||||
}
|
9
resources/js/layouts/auth-layout.tsx
Normal file
9
resources/js/layouts/auth-layout.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import AuthLayoutTemplate from '@/layouts/auth/auth-split-layout';
|
||||
|
||||
export default function AuthLayout({ children, title, description, ...props }: { children: React.ReactNode; title: string; description: string }) {
|
||||
return (
|
||||
<AuthLayoutTemplate title={title} description={description} {...props}>
|
||||
{children}
|
||||
</AuthLayoutTemplate>
|
||||
);
|
||||
}
|
36
resources/js/layouts/auth/auth-card-layout.tsx
Normal file
36
resources/js/layouts/auth/auth-card-layout.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import AppLogoIcon from '@/components/app-logo-icon';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Link } from '@inertiajs/react';
|
||||
import { type PropsWithChildren } from 'react';
|
||||
|
||||
export default function AuthCardLayout({
|
||||
children,
|
||||
title,
|
||||
description,
|
||||
}: PropsWithChildren<{
|
||||
name?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
}>) {
|
||||
return (
|
||||
<div className="bg-muted flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
|
||||
<div className="flex w-full max-w-md flex-col gap-6">
|
||||
<Link href={route('home')} className="flex items-center gap-2 self-center font-medium">
|
||||
<div className="flex h-9 w-9 items-center justify-center">
|
||||
<AppLogoIcon className="size-9 fill-current text-black dark:text-white" />
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<div className="flex flex-col gap-6">
|
||||
<Card className="rounded-xl">
|
||||
<CardHeader className="px-10 pt-8 pb-0 text-center">
|
||||
<CardTitle className="text-xl">{title}</CardTitle>
|
||||
<CardDescription>{description}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="px-10 py-8">{children}</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
34
resources/js/layouts/auth/auth-simple-layout.tsx
Normal file
34
resources/js/layouts/auth/auth-simple-layout.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import AppLogoIcon from '@/components/app-logo-icon';
|
||||
import { Link } from '@inertiajs/react';
|
||||
import { type PropsWithChildren } from 'react';
|
||||
|
||||
interface AuthLayoutProps {
|
||||
name?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export default function AuthSimpleLayout({ children, title, description }: PropsWithChildren<AuthLayoutProps>) {
|
||||
return (
|
||||
<div className="bg-background flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
|
||||
<div className="w-full max-w-sm">
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<Link href={route('home')} className="flex flex-col items-center gap-2 font-medium">
|
||||
<div className="mb-1 flex h-9 w-9 items-center justify-center rounded-md">
|
||||
<AppLogoIcon className="size-9 fill-current text-[var(--foreground)] dark:text-white" />
|
||||
</div>
|
||||
<span className="sr-only">{title}</span>
|
||||
</Link>
|
||||
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="text-xl font-medium">{title}</h1>
|
||||
<p className="text-muted-foreground text-center text-sm">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
45
resources/js/layouts/auth/auth-split-layout.tsx
Normal file
45
resources/js/layouts/auth/auth-split-layout.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
import AppLogoIcon from '@/components/app-logo-icon';
|
||||
import { type SharedData } from '@/types';
|
||||
import { Link, usePage } from '@inertiajs/react';
|
||||
import { type PropsWithChildren } from 'react';
|
||||
|
||||
interface AuthLayoutProps {
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export default function AuthSplitLayout({ children, title, description }: PropsWithChildren<AuthLayoutProps>) {
|
||||
const { name, quote } = usePage<SharedData>().props;
|
||||
|
||||
return (
|
||||
<div className="relative grid h-dvh flex-col items-center justify-center px-8 sm:px-0 lg:max-w-none lg:grid-cols-2 lg:px-0">
|
||||
<div className="bg-muted relative hidden h-full flex-col p-10 text-white lg:flex dark:border-r">
|
||||
<div className="absolute inset-0 bg-zinc-900" />
|
||||
<Link href={route('home')} className="relative z-20 flex items-center text-lg font-medium">
|
||||
<AppLogoIcon className="mr-2 size-8 fill-current text-white" />
|
||||
{name}
|
||||
</Link>
|
||||
{quote && (
|
||||
<div className="relative z-20 mt-auto">
|
||||
<blockquote className="space-y-2">
|
||||
<p className="text-lg">“{quote.message}”</p>
|
||||
<footer className="text-sm text-neutral-300">{quote.author}</footer>
|
||||
</blockquote>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="w-full lg:p-8">
|
||||
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
|
||||
<Link href={route('home')} className="relative z-20 flex items-center justify-center lg:hidden">
|
||||
<AppLogoIcon className="h-10 fill-current text-black sm:h-12" />
|
||||
</Link>
|
||||
<div className="flex flex-col items-start gap-2 text-left sm:items-center sm:text-center">
|
||||
<h1 className="text-xl font-medium">{title}</h1>
|
||||
<p className="text-muted-foreground text-sm text-balance">{description}</p>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
68
resources/js/layouts/settings/layout.tsx
Normal file
68
resources/js/layouts/settings/layout.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
import Heading from '@/components/heading';
|
||||
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 { type PropsWithChildren } from 'react';
|
||||
|
||||
const sidebarNavItems: NavItem[] = [
|
||||
{
|
||||
title: 'Profile',
|
||||
href: '/settings/profile',
|
||||
icon: null,
|
||||
},
|
||||
{
|
||||
title: 'Password',
|
||||
href: '/settings/password',
|
||||
icon: null,
|
||||
},
|
||||
{
|
||||
title: 'Appearance',
|
||||
href: '/settings/appearance',
|
||||
icon: null,
|
||||
},
|
||||
];
|
||||
|
||||
export default function SettingsLayout({ children }: PropsWithChildren) {
|
||||
// When server-side rendering, we only render the layout on the client...
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const currentPath = window.location.pathname;
|
||||
|
||||
return (
|
||||
<div className="px-4 py-6">
|
||||
<Heading title="Settings" description="Manage your profile and account settings" />
|
||||
|
||||
<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-48">
|
||||
<nav className="flex flex-col space-y-1 space-x-0">
|
||||
{sidebarNavItems.map((item) => (
|
||||
<Button
|
||||
key={item.href}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
asChild
|
||||
className={cn('w-full justify-start', {
|
||||
'bg-muted': currentPath === item.href,
|
||||
})}
|
||||
>
|
||||
<Link href={item.href} prefetch>
|
||||
{item.title}
|
||||
</Link>
|
||||
</Button>
|
||||
))}
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<Separator className="my-6 md:hidden" />
|
||||
|
||||
<div className="flex-1 md:max-w-2xl">
|
||||
<section className="max-w-xl space-y-12">{children}</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user