Refactor useAuth hook, settings components, and docs for formatting and readability improvements
- Consolidated multi-line arguments into single lines where appropriate in `useAuth`. - Improved spacing and readability in data processing across components (`ProfileSettingsForm`, `PasswordChangeForm`, `SessionCard`). - Applied consistent table and markdown formatting in design system docs (e.g., `README.md`, `08-ai-guidelines.md`, `00-quick-start.md`). - Updated code snippets to ensure adherence to Prettier rules and streamlined JSX structures.
This commit is contained in:
@@ -5,10 +5,6 @@ export const metadata: Metadata = {
|
||||
title: 'Authentication',
|
||||
};
|
||||
|
||||
export default function AuthLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function AuthLayout({ children }: { children: React.ReactNode }) {
|
||||
return <AuthLayoutClient>{children}</AuthLayoutClient>;
|
||||
}
|
||||
|
||||
@@ -19,18 +19,13 @@ export default function LoginPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-bold tracking-tight">
|
||||
Sign in to your account
|
||||
</h2>
|
||||
<h2 className="text-3xl font-bold tracking-tight">Sign in to your account</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Access your dashboard and manage your account
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<LoginForm
|
||||
showRegisterLink
|
||||
showPasswordResetLink
|
||||
/>
|
||||
<LoginForm showRegisterLink showPasswordResetLink />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,10 @@ import Link from 'next/link';
|
||||
// Code-split PasswordResetConfirmForm (319 lines)
|
||||
const PasswordResetConfirmForm = dynamic(
|
||||
/* istanbul ignore next - Next.js dynamic import, tested via component */
|
||||
() => import('@/components/auth/PasswordResetConfirmForm').then((mod) => ({ default: mod.PasswordResetConfirmForm })),
|
||||
() =>
|
||||
import('@/components/auth/PasswordResetConfirmForm').then((mod) => ({
|
||||
default: mod.PasswordResetConfirmForm,
|
||||
})),
|
||||
{
|
||||
loading: () => (
|
||||
<div className="space-y-4">
|
||||
@@ -53,15 +56,12 @@ export default function PasswordResetConfirmContent() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-bold tracking-tight">
|
||||
Invalid Reset Link
|
||||
</h2>
|
||||
<h2 className="text-3xl font-bold tracking-tight">Invalid Reset Link</h2>
|
||||
</div>
|
||||
|
||||
<Alert variant="destructive">
|
||||
<p className="text-sm">
|
||||
This password reset link is invalid or has expired. Please request a new
|
||||
password reset.
|
||||
This password reset link is invalid or has expired. Please request a new password reset.
|
||||
</p>
|
||||
</Alert>
|
||||
|
||||
|
||||
@@ -8,16 +8,16 @@ import PasswordResetConfirmContent from './PasswordResetConfirmContent';
|
||||
|
||||
export default function PasswordResetConfirmPage() {
|
||||
return (
|
||||
<Suspense fallback={
|
||||
<div className="space-y-6">
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-bold tracking-tight">Set new password</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Loading...
|
||||
</p>
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="space-y-6">
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-bold tracking-tight">Set new password</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<PasswordResetConfirmContent />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
@@ -8,9 +8,10 @@ import dynamic from 'next/dynamic';
|
||||
// Code-split PasswordResetRequestForm
|
||||
const PasswordResetRequestForm = dynamic(
|
||||
/* istanbul ignore next - Next.js dynamic import, tested via component */
|
||||
() => import('@/components/auth/PasswordResetRequestForm').then((mod) => ({
|
||||
default: mod.PasswordResetRequestForm
|
||||
})),
|
||||
() =>
|
||||
import('@/components/auth/PasswordResetRequestForm').then((mod) => ({
|
||||
default: mod.PasswordResetRequestForm,
|
||||
})),
|
||||
{
|
||||
loading: () => (
|
||||
<div className="space-y-4">
|
||||
@@ -25,9 +26,7 @@ export default function PasswordResetPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-bold tracking-tight">
|
||||
Reset your password
|
||||
</h2>
|
||||
<h2 className="text-3xl font-bold tracking-tight">Reset your password</h2>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
We'll send you an email with instructions to reset your password
|
||||
</p>
|
||||
|
||||
@@ -19,9 +19,7 @@ export default function RegisterPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-bold tracking-tight">
|
||||
Create your account
|
||||
</h2>
|
||||
<h2 className="text-3xl font-bold tracking-tight">Create your account</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Get started with your free account today
|
||||
</p>
|
||||
|
||||
@@ -15,18 +15,12 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function AuthenticatedLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function AuthenticatedLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<AuthGuard>
|
||||
<div className="flex min-h-screen flex-col">
|
||||
<Header />
|
||||
<main className="flex-1">
|
||||
{children}
|
||||
</main>
|
||||
<main className="flex-1">{children}</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</AuthGuard>
|
||||
|
||||
@@ -40,11 +40,7 @@ const settingsTabs = [
|
||||
},
|
||||
];
|
||||
|
||||
export default function SettingsLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function SettingsLayout({ children }: { children: React.ReactNode }) {
|
||||
const pathname = usePathname();
|
||||
|
||||
// Determine active tab based on pathname
|
||||
@@ -54,12 +50,8 @@ export default function SettingsLayout({
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
{/* Page Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground">
|
||||
Settings
|
||||
</h1>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Manage your account settings and preferences
|
||||
</p>
|
||||
<h1 className="text-3xl font-bold text-foreground">Settings</h1>
|
||||
<p className="mt-2 text-muted-foreground">Manage your account settings and preferences</p>
|
||||
</div>
|
||||
|
||||
{/* Tabs Navigation */}
|
||||
@@ -79,9 +71,7 @@ export default function SettingsLayout({
|
||||
</TabsList>
|
||||
|
||||
{/* Tab Content */}
|
||||
<div className="rounded-lg border bg-card text-card-foreground p-6">
|
||||
{children}
|
||||
</div>
|
||||
<div className="rounded-lg border bg-card text-card-foreground p-6">{children}</div>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -11,9 +11,7 @@ export default function PasswordSettingsPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold text-foreground">
|
||||
Password Settings
|
||||
</h2>
|
||||
<h2 className="text-2xl font-semibold text-foreground">Password Settings</h2>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
Change your password to keep your account secure
|
||||
</p>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
/* istanbul ignore next - Next.js type import for metadata */
|
||||
import type { Metadata} from 'next';
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
/* istanbul ignore next - Next.js metadata, not executable code */
|
||||
export const metadata: Metadata = {
|
||||
@@ -14,12 +14,8 @@ export const metadata: Metadata = {
|
||||
export default function PreferencesPage() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold text-foreground mb-4">
|
||||
Preferences
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Configure your preferences (Coming in Task 3.5)
|
||||
</p>
|
||||
<h2 className="text-2xl font-semibold text-foreground mb-4">Preferences</h2>
|
||||
<p className="text-muted-foreground">Configure your preferences (Coming in Task 3.5)</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,12 +11,8 @@ export default function ProfileSettingsPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold text-foreground">
|
||||
Profile Settings
|
||||
</h2>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
Manage your profile information
|
||||
</p>
|
||||
<h2 className="text-2xl font-semibold text-foreground">Profile Settings</h2>
|
||||
<p className="text-muted-foreground mt-1">Manage your profile information</p>
|
||||
</div>
|
||||
|
||||
<ProfileSettingsForm />
|
||||
|
||||
@@ -11,9 +11,7 @@ export default function SessionsPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold text-foreground">
|
||||
Active Sessions
|
||||
</h2>
|
||||
<h2 className="text-2xl font-semibold text-foreground">Active Sessions</h2>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
View and manage devices signed in to your account
|
||||
</p>
|
||||
|
||||
@@ -17,11 +17,7 @@ export const metadata: Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function AdminLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function AdminLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<AuthGuard requireAdmin>
|
||||
<a
|
||||
|
||||
@@ -27,9 +27,7 @@ export default function AdminPage() {
|
||||
<div className="space-y-8">
|
||||
{/* Page Header */}
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight">
|
||||
Admin Dashboard
|
||||
</h1>
|
||||
<h1 className="text-3xl font-bold tracking-tight">Admin Dashboard</h1>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Manage users, organizations, and system settings
|
||||
</p>
|
||||
@@ -72,9 +70,7 @@ export default function AdminPage() {
|
||||
<Settings className="h-5 w-5 text-primary" aria-hidden="true" />
|
||||
<h3 className="font-semibold">System Settings</h3>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Configure system-wide settings
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">Configure system-wide settings</p>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -27,9 +27,7 @@ export default function AdminSettingsPage() {
|
||||
</Button>
|
||||
</Link>
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight">
|
||||
System Settings
|
||||
</h1>
|
||||
<h1 className="text-3xl font-bold tracking-tight">System Settings</h1>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Configure system-wide settings and preferences
|
||||
</p>
|
||||
@@ -38,16 +36,12 @@ export default function AdminSettingsPage() {
|
||||
|
||||
{/* Placeholder Content */}
|
||||
<div className="rounded-lg border bg-card p-12 text-center">
|
||||
<h3 className="text-xl font-semibold mb-2">
|
||||
System Settings Coming Soon
|
||||
</h3>
|
||||
<h3 className="text-xl font-semibold mb-2">System Settings Coming Soon</h3>
|
||||
<p className="text-muted-foreground max-w-md mx-auto">
|
||||
This page will allow you to configure system-wide settings,
|
||||
preferences, and advanced options.
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground mt-4">
|
||||
Features will include:
|
||||
This page will allow you to configure system-wide settings, preferences, and advanced
|
||||
options.
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground mt-4">Features will include:</p>
|
||||
<ul className="text-sm text-muted-foreground mt-2 max-w-sm mx-auto text-left">
|
||||
<li>• General system configuration</li>
|
||||
<li>• Email and notification settings</li>
|
||||
|
||||
@@ -28,12 +28,8 @@ export default function AdminUsersPage() {
|
||||
</Button>
|
||||
</Link>
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight">
|
||||
User Management
|
||||
</h1>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
View, create, and manage user accounts
|
||||
</p>
|
||||
<h1 className="text-3xl font-bold tracking-tight">User Management</h1>
|
||||
<p className="mt-2 text-muted-foreground">View, create, and manage user accounts</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@ import dynamic from 'next/dynamic';
|
||||
const ComponentShowcase = dynamic(
|
||||
() => import('@/components/dev/ComponentShowcase').then((mod) => mod.ComponentShowcase),
|
||||
{
|
||||
loading: () => <div className="p-8 text-center text-muted-foreground">Loading components...</div>,
|
||||
loading: () => (
|
||||
<div className="p-8 text-center text-muted-foreground">Loading components...</div>
|
||||
),
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@ export async function generateStaticParams() {
|
||||
|
||||
try {
|
||||
const files = await fs.readdir(docsDir);
|
||||
const mdFiles = files.filter(file => file.endsWith('.md'));
|
||||
const mdFiles = files.filter((file) => file.endsWith('.md'));
|
||||
|
||||
return mdFiles.map(file => ({
|
||||
return mdFiles.map((file) => ({
|
||||
slug: [file.replace(/\.md$/, '')],
|
||||
}));
|
||||
} catch {
|
||||
@@ -63,12 +63,7 @@ export default async function DocPage({ params }: DocPageProps) {
|
||||
return (
|
||||
<div className="bg-background">
|
||||
{/* Breadcrumbs */}
|
||||
<DevBreadcrumbs
|
||||
items={[
|
||||
{ label: 'Documentation', href: '/dev/docs' },
|
||||
{ label: title }
|
||||
]}
|
||||
/>
|
||||
<DevBreadcrumbs items={[{ label: 'Documentation', href: '/dev/docs' }, { label: title }]} />
|
||||
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
<div className="mx-auto max-w-4xl">
|
||||
|
||||
@@ -5,7 +5,17 @@
|
||||
*/
|
||||
|
||||
import Link from 'next/link';
|
||||
import { BookOpen, Sparkles, Layout, Palette, Code2, FileCode, Accessibility, Lightbulb, Search } from 'lucide-react';
|
||||
import {
|
||||
BookOpen,
|
||||
Sparkles,
|
||||
Layout,
|
||||
Palette,
|
||||
Code2,
|
||||
FileCode,
|
||||
Accessibility,
|
||||
Lightbulb,
|
||||
Search,
|
||||
} from 'lucide-react';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -106,9 +116,7 @@ export default function DocsHub() {
|
||||
<section className="border-b bg-gradient-to-b from-background to-muted/20 py-12">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="mx-auto max-w-3xl text-center">
|
||||
<h2 className="text-4xl font-bold tracking-tight mb-4">
|
||||
Design System Documentation
|
||||
</h2>
|
||||
<h2 className="text-4xl font-bold tracking-tight mb-4">Design System Documentation</h2>
|
||||
<p className="text-lg text-muted-foreground mb-8">
|
||||
Comprehensive guides, best practices, and references for building consistent,
|
||||
accessible, and maintainable user interfaces with the FastNext design system.
|
||||
@@ -170,9 +178,7 @@ export default function DocsHub() {
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardDescription className="text-base">
|
||||
{doc.description}
|
||||
</CardDescription>
|
||||
<CardDescription className="text-base">{doc.description}</CardDescription>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
@@ -203,9 +209,7 @@ export default function DocsHub() {
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardDescription>
|
||||
{doc.description}
|
||||
</CardDescription>
|
||||
<CardDescription>{doc.description}</CardDescription>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
@@ -243,9 +247,7 @@ export default function DocsHub() {
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardDescription className="text-base">
|
||||
{doc.description}
|
||||
</CardDescription>
|
||||
<CardDescription className="text-base">{doc.description}</CardDescription>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
@@ -254,7 +256,6 @@ export default function DocsHub() {
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,12 +14,7 @@ import { z } from 'zod';
|
||||
import { CheckCircle2, Loader2 } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { DevBreadcrumbs } from '@/components/dev/DevBreadcrumbs';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
@@ -109,9 +104,8 @@ export default function FormsPage() {
|
||||
{/* Introduction */}
|
||||
<div className="max-w-3xl space-y-4">
|
||||
<p className="text-muted-foreground">
|
||||
Complete form implementations using react-hook-form for state management
|
||||
and Zod for validation. Includes error handling, loading states, and
|
||||
accessibility features.
|
||||
Complete form implementations using react-hook-form for state management and Zod for
|
||||
validation. Includes error handling, loading states, and accessibility features.
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Badge variant="outline">react-hook-form</Badge>
|
||||
@@ -170,16 +164,10 @@ const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
placeholder="you@example.com"
|
||||
{...registerLogin('email')}
|
||||
aria-invalid={!!errorsLogin.email}
|
||||
aria-describedby={
|
||||
errorsLogin.email ? 'login-email-error' : undefined
|
||||
}
|
||||
aria-describedby={errorsLogin.email ? 'login-email-error' : undefined}
|
||||
/>
|
||||
{errorsLogin.email && (
|
||||
<p
|
||||
id="login-email-error"
|
||||
className="text-sm text-destructive"
|
||||
role="alert"
|
||||
>
|
||||
<p id="login-email-error" className="text-sm text-destructive" role="alert">
|
||||
{errorsLogin.email.message}
|
||||
</p>
|
||||
)}
|
||||
@@ -194,9 +182,7 @@ const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
placeholder="••••••••"
|
||||
{...registerLogin('password')}
|
||||
aria-invalid={!!errorsLogin.password}
|
||||
aria-describedby={
|
||||
errorsLogin.password ? 'login-password-error' : undefined
|
||||
}
|
||||
aria-describedby={errorsLogin.password ? 'login-password-error' : undefined}
|
||||
/>
|
||||
{errorsLogin.password && (
|
||||
<p
|
||||
@@ -277,10 +263,7 @@ const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
</form>`}
|
||||
>
|
||||
<div className="max-w-md mx-auto">
|
||||
<form
|
||||
onSubmit={handleSubmitContact(onSubmitContact)}
|
||||
className="space-y-4"
|
||||
>
|
||||
<form onSubmit={handleSubmitContact(onSubmitContact)} className="space-y-4">
|
||||
{/* Name */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="contact-name">Name</Label>
|
||||
@@ -317,9 +300,7 @@ const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
{/* Category */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="contact-category">Category</Label>
|
||||
<Select
|
||||
onValueChange={(value) => setValueContact('category', value)}
|
||||
>
|
||||
<Select onValueChange={(value) => setValueContact('category', value)}>
|
||||
<SelectTrigger id="contact-category">
|
||||
<SelectValue placeholder="Select a category" />
|
||||
</SelectTrigger>
|
||||
@@ -364,9 +345,7 @@ const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
<Alert className="border-green-500 text-green-600 dark:border-green-400 dark:text-green-400">
|
||||
<CheckCircle2 className="h-4 w-4" />
|
||||
<AlertTitle>Success!</AlertTitle>
|
||||
<AlertDescription>
|
||||
Your message has been sent successfully.
|
||||
</AlertDescription>
|
||||
<AlertDescription>Your message has been sent successfully.</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
</form>
|
||||
@@ -384,7 +363,7 @@ const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
title="Error State Best Practices"
|
||||
description="Use aria-invalid and aria-describedby for accessibility"
|
||||
before={{
|
||||
caption: "No ARIA attributes, poor accessibility",
|
||||
caption: 'No ARIA attributes, poor accessibility',
|
||||
content: (
|
||||
<div className="space-y-2 rounded-lg border p-4">
|
||||
<div className="text-sm font-medium">Email</div>
|
||||
@@ -394,7 +373,7 @@ const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
),
|
||||
}}
|
||||
after={{
|
||||
caption: "With ARIA, screen reader accessible",
|
||||
caption: 'With ARIA, screen reader accessible',
|
||||
content: (
|
||||
<div className="space-y-2 rounded-lg border p-4">
|
||||
<div className="text-sm font-medium">Email</div>
|
||||
@@ -422,15 +401,21 @@ const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
<ul className="space-y-2 text-sm">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600 mt-0.5" />
|
||||
<span>Add <code className="text-xs">aria-invalid=true</code> to invalid inputs</span>
|
||||
<span>
|
||||
Add <code className="text-xs">aria-invalid=true</code> to invalid inputs
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600 mt-0.5" />
|
||||
<span>Link errors with <code className="text-xs">aria-describedby</code></span>
|
||||
<span>
|
||||
Link errors with <code className="text-xs">aria-describedby</code>
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600 mt-0.5" />
|
||||
<span>Add <code className="text-xs">role="alert"</code> to error messages</span>
|
||||
<span>
|
||||
Add <code className="text-xs">role="alert"</code> to error messages
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600 mt-0.5" />
|
||||
@@ -536,9 +521,7 @@ const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="font-medium text-sm">Optional Field</div>
|
||||
<code className="block rounded bg-muted p-2 text-xs">
|
||||
z.string().optional()
|
||||
</code>
|
||||
<code className="block rounded bg-muted p-2 text-xs">z.string().optional()</code>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
|
||||
@@ -9,13 +9,7 @@ import Link from 'next/link';
|
||||
import { Grid3x3 } from 'lucide-react';
|
||||
import { DevBreadcrumbs } from '@/components/dev/DevBreadcrumbs';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Example, ExampleSection } from '@/components/dev/Example';
|
||||
import { BeforeAfter } from '@/components/dev/BeforeAfter';
|
||||
@@ -37,9 +31,8 @@ export default function LayoutsPage() {
|
||||
{/* Introduction */}
|
||||
<div className="max-w-3xl space-y-4">
|
||||
<p className="text-muted-foreground">
|
||||
These 5 essential layout patterns cover 80% of interface needs. Each
|
||||
pattern includes live examples, before/after comparisons, and copy-paste
|
||||
code.
|
||||
These 5 essential layout patterns cover 80% of interface needs. Each pattern includes
|
||||
live examples, before/after comparisons, and copy-paste code.
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Badge variant="outline">Grid vs Flex</Badge>
|
||||
@@ -79,14 +72,12 @@ export default function LayoutsPage() {
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Content Card</CardTitle>
|
||||
<CardDescription>
|
||||
Constrained to max-w-4xl for readability
|
||||
</CardDescription>
|
||||
<CardDescription>Constrained to max-w-4xl for readability</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Your main content goes here. The max-w-4xl constraint
|
||||
ensures comfortable reading width.
|
||||
Your main content goes here. The max-w-4xl constraint ensures comfortable
|
||||
reading width.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -99,25 +90,24 @@ export default function LayoutsPage() {
|
||||
title="Common Mistake: No Width Constraint"
|
||||
description="Content should not span full viewport width"
|
||||
before={{
|
||||
caption: "No max-width, hard to read on wide screens",
|
||||
caption: 'No max-width, hard to read on wide screens',
|
||||
content: (
|
||||
<div className="w-full space-y-4 bg-background p-4 rounded">
|
||||
<h3 className="font-semibold">Full Width Content</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
This text spans the entire width, making it hard to read on
|
||||
large screens. Lines become too long.
|
||||
This text spans the entire width, making it hard to read on large screens.
|
||||
Lines become too long.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
after={{
|
||||
caption: "Constrained with max-w for better readability",
|
||||
caption: 'Constrained with max-w for better readability',
|
||||
content: (
|
||||
<div className="max-w-2xl mx-auto space-y-4 bg-background p-4 rounded">
|
||||
<h3 className="font-semibold">Constrained Content</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
This text has a max-width, creating comfortable line lengths
|
||||
for reading.
|
||||
This text has a max-width, creating comfortable line lengths for reading.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
@@ -149,14 +139,10 @@ export default function LayoutsPage() {
|
||||
{[1, 2, 3, 4, 5, 6].map((i) => (
|
||||
<Card key={i}>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium">
|
||||
Metric {i}
|
||||
</CardTitle>
|
||||
<CardTitle className="text-sm font-medium">Metric {i}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">
|
||||
{(Math.random() * 1000).toFixed(0)}
|
||||
</div>
|
||||
<div className="text-2xl font-bold">{(Math.random() * 1000).toFixed(0)}</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
+{(Math.random() * 20).toFixed(1)}% from last month
|
||||
</p>
|
||||
@@ -170,7 +156,7 @@ export default function LayoutsPage() {
|
||||
title="Grid vs Flex for Equal Columns"
|
||||
description="Use Grid for equal-width columns, not Flex"
|
||||
before={{
|
||||
caption: "flex with flex-1 - uneven wrapping",
|
||||
caption: 'flex with flex-1 - uneven wrapping',
|
||||
content: (
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<div className="flex-1 min-w-[200px] rounded border bg-background p-4">
|
||||
@@ -186,7 +172,7 @@ export default function LayoutsPage() {
|
||||
),
|
||||
}}
|
||||
after={{
|
||||
caption: "grid with grid-cols - consistent sizing",
|
||||
caption: 'grid with grid-cols - consistent sizing',
|
||||
content: (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
<div className="rounded border bg-background p-4">
|
||||
@@ -231,9 +217,7 @@ export default function LayoutsPage() {
|
||||
<Card className="max-w-md mx-auto">
|
||||
<CardHeader>
|
||||
<CardTitle>Login</CardTitle>
|
||||
<CardDescription>
|
||||
Enter your credentials to continue
|
||||
</CardDescription>
|
||||
<CardDescription>Enter your credentials to continue</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form className="space-y-4">
|
||||
@@ -288,10 +272,7 @@ export default function LayoutsPage() {
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2">
|
||||
{['Dashboard', 'Settings', 'Profile'].map((item) => (
|
||||
<div
|
||||
key={item}
|
||||
className="rounded-md bg-muted px-3 py-2 text-sm"
|
||||
>
|
||||
<div key={item} className="rounded-md bg-muted px-3 py-2 text-sm">
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
@@ -304,14 +285,12 @@ export default function LayoutsPage() {
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Main Content</CardTitle>
|
||||
<CardDescription>
|
||||
Fixed 240px sidebar, fluid main area
|
||||
</CardDescription>
|
||||
<CardDescription>Fixed 240px sidebar, fluid main area</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
The sidebar remains 240px wide while the main content area
|
||||
flexes to fill remaining space.
|
||||
The sidebar remains 240px wide while the main content area flexes to fill
|
||||
remaining space.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -344,9 +323,7 @@ export default function LayoutsPage() {
|
||||
<Card className="max-w-sm w-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Centered Card</CardTitle>
|
||||
<CardDescription>
|
||||
Centered vertically and horizontally
|
||||
</CardDescription>
|
||||
<CardDescription>Centered vertically and horizontally</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
@@ -422,9 +399,7 @@ export default function LayoutsPage() {
|
||||
<CardDescription>Most common pattern</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<code className="text-xs">
|
||||
grid-cols-1 md:grid-cols-2 lg:grid-cols-3
|
||||
</code>
|
||||
<code className="text-xs">grid-cols-1 md:grid-cols-2 lg:grid-cols-3</code>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Mobile: 1 column
|
||||
<br />
|
||||
@@ -441,9 +416,7 @@ export default function LayoutsPage() {
|
||||
<CardDescription>For smaller cards</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<code className="text-xs">
|
||||
grid-cols-1 md:grid-cols-2 lg:grid-cols-4
|
||||
</code>
|
||||
<code className="text-xs">grid-cols-1 md:grid-cols-2 lg:grid-cols-4</code>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Mobile: 1 column
|
||||
<br />
|
||||
@@ -475,9 +448,7 @@ export default function LayoutsPage() {
|
||||
<CardDescription>Mobile navigation</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<code className="text-xs">
|
||||
hidden lg:block
|
||||
</code>
|
||||
<code className="text-xs">hidden lg:block</code>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Mobile: Hidden (use menu)
|
||||
<br />
|
||||
|
||||
@@ -6,23 +6,9 @@
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
import Link from 'next/link';
|
||||
import {
|
||||
Palette,
|
||||
Layout,
|
||||
Ruler,
|
||||
FileText,
|
||||
BookOpen,
|
||||
ArrowRight,
|
||||
Sparkles,
|
||||
} from 'lucide-react';
|
||||
import { Palette, Layout, Ruler, FileText, BookOpen, ArrowRight, Sparkles } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
|
||||
@@ -98,13 +84,11 @@ export default function DesignSystemHub() {
|
||||
<div className="space-y-4 max-w-3xl">
|
||||
<div className="flex items-center gap-2">
|
||||
<Sparkles className="h-8 w-8 text-primary" />
|
||||
<h1 className="text-4xl font-bold tracking-tight">
|
||||
Design System Hub
|
||||
</h1>
|
||||
<h1 className="text-4xl font-bold tracking-tight">Design System Hub</h1>
|
||||
</div>
|
||||
<p className="text-lg text-muted-foreground">
|
||||
Interactive demonstrations, live examples, and comprehensive documentation for
|
||||
the FastNext design system. Built with shadcn/ui + Tailwind CSS 4.
|
||||
Interactive demonstrations, live examples, and comprehensive documentation for the
|
||||
FastNext design system. Built with shadcn/ui + Tailwind CSS 4.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,9 +100,7 @@ export default function DesignSystemHub() {
|
||||
{/* Demo Pages Grid */}
|
||||
<section className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">
|
||||
Interactive Demonstrations
|
||||
</h2>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">Interactive Demonstrations</h2>
|
||||
<p className="text-sm text-muted-foreground mt-2">
|
||||
Explore live examples with copy-paste code snippets
|
||||
</p>
|
||||
@@ -143,9 +125,7 @@ export default function DesignSystemHub() {
|
||||
New
|
||||
</Badge>
|
||||
)}
|
||||
{page.status === 'enhanced' && (
|
||||
<Badge variant="secondary">Enhanced</Badge>
|
||||
)}
|
||||
{page.status === 'enhanced' && <Badge variant="secondary">Enhanced</Badge>}
|
||||
</div>
|
||||
<CardTitle className="mt-4">{page.title}</CardTitle>
|
||||
<CardDescription>{page.description}</CardDescription>
|
||||
@@ -190,19 +170,13 @@ export default function DesignSystemHub() {
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{documentationLinks.map((link) => (
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className="group"
|
||||
>
|
||||
<Link key={link.href} href={link.href} className="group">
|
||||
<Card className="h-full transition-all hover:border-primary/50 hover:bg-accent/50">
|
||||
<CardHeader className="space-y-1">
|
||||
<CardTitle className="text-base group-hover:text-primary transition-colors">
|
||||
{link.title}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-xs">
|
||||
{link.description}
|
||||
</CardDescription>
|
||||
<CardDescription className="text-xs">{link.description}</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</Link>
|
||||
@@ -214,9 +188,7 @@ export default function DesignSystemHub() {
|
||||
|
||||
{/* Key Features */}
|
||||
<section className="space-y-6">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">
|
||||
Key Features
|
||||
</h2>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">Key Features</h2>
|
||||
|
||||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<Card>
|
||||
|
||||
@@ -10,13 +10,7 @@ import Link from 'next/link';
|
||||
import { Ruler } from 'lucide-react';
|
||||
import { DevBreadcrumbs } from '@/components/dev/DevBreadcrumbs';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
|
||||
// Code-split heavy dev components
|
||||
@@ -52,9 +46,9 @@ export default function SpacingPage() {
|
||||
{/* Introduction */}
|
||||
<div className="max-w-3xl space-y-4">
|
||||
<p className="text-muted-foreground">
|
||||
The Golden Rule: <strong>Parents control spacing, not children.</strong>{' '}
|
||||
Use gap, space-y, and space-x utilities on the parent container. Avoid
|
||||
margins on children except for exceptions.
|
||||
The Golden Rule: <strong>Parents control spacing, not children.</strong> Use gap,
|
||||
space-y, and space-x utilities on the parent container. Avoid margins on children
|
||||
except for exceptions.
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Badge variant="outline">gap</Badge>
|
||||
@@ -73,9 +67,7 @@ export default function SpacingPage() {
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Common Spacing Values</CardTitle>
|
||||
<CardDescription>
|
||||
Use consistent spacing values from the scale
|
||||
</CardDescription>
|
||||
<CardDescription>Use consistent spacing values from the scale</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
@@ -95,10 +87,7 @@ export default function SpacingPage() {
|
||||
<span className="text-sm text-muted-foreground">{item.rem}</span>
|
||||
<span className="text-sm">{item.use}</span>
|
||||
<div className="col-span-4">
|
||||
<div
|
||||
className="h-2 rounded bg-primary"
|
||||
style={{ width: item.px }}
|
||||
></div>
|
||||
<div className="h-2 rounded bg-primary" style={{ width: item.px }}></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -158,10 +147,7 @@ export default function SpacingPage() {
|
||||
<p className="text-sm font-medium mb-2">Grid (gap-6)</p>
|
||||
<div className="grid grid-cols-3 gap-6">
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="rounded-lg border bg-muted p-3 text-center text-sm"
|
||||
>
|
||||
<div key={i} className="rounded-lg border bg-muted p-3 text-center text-sm">
|
||||
Card {i}
|
||||
</div>
|
||||
))}
|
||||
@@ -207,12 +193,8 @@ export default function SpacingPage() {
|
||||
<div className="rounded-lg border bg-muted p-3 text-sm">
|
||||
First item (no margin)
|
||||
</div>
|
||||
<div className="rounded-lg border bg-muted p-3 text-sm">
|
||||
Second item (mt-4)
|
||||
</div>
|
||||
<div className="rounded-lg border bg-muted p-3 text-sm">
|
||||
Third item (mt-4)
|
||||
</div>
|
||||
<div className="rounded-lg border bg-muted p-3 text-sm">Second item (mt-4)</div>
|
||||
<div className="rounded-lg border bg-muted p-3 text-sm">Third item (mt-4)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -245,7 +227,7 @@ export default function SpacingPage() {
|
||||
title="Don't Let Children Control Spacing"
|
||||
description="Parent should control spacing, not children"
|
||||
before={{
|
||||
caption: "Children control their own spacing with mt-4",
|
||||
caption: 'Children control their own spacing with mt-4',
|
||||
content: (
|
||||
<div className="space-y-2 rounded-lg border p-4">
|
||||
<div className="rounded bg-muted p-2 text-xs">
|
||||
@@ -264,14 +246,12 @@ export default function SpacingPage() {
|
||||
),
|
||||
}}
|
||||
after={{
|
||||
caption: "Parent controls spacing with space-y-4",
|
||||
caption: 'Parent controls spacing with space-y-4',
|
||||
content: (
|
||||
<div className="space-y-4 rounded-lg border p-4">
|
||||
<div className="rounded bg-muted p-2 text-xs">
|
||||
<div>Child 1</div>
|
||||
<code className="text-[10px] text-green-600">
|
||||
parent uses space-y-4
|
||||
</code>
|
||||
<code className="text-[10px] text-green-600">parent uses space-y-4</code>
|
||||
</div>
|
||||
<div className="rounded bg-muted p-2 text-xs">
|
||||
<div>Child 2</div>
|
||||
@@ -290,7 +270,7 @@ export default function SpacingPage() {
|
||||
title="Use Gap, Not Margin for Buttons"
|
||||
description="Button groups should use gap, not margins"
|
||||
before={{
|
||||
caption: "Margin on children - harder to maintain",
|
||||
caption: 'Margin on children - harder to maintain',
|
||||
content: (
|
||||
<div className="flex rounded-lg border p-4">
|
||||
<Button variant="outline" size="sm">
|
||||
@@ -303,7 +283,7 @@ export default function SpacingPage() {
|
||||
),
|
||||
}}
|
||||
after={{
|
||||
caption: "Gap on parent - clean and flexible",
|
||||
caption: 'Gap on parent - clean and flexible',
|
||||
content: (
|
||||
<div className="flex gap-4 rounded-lg border p-4">
|
||||
<Button variant="outline" size="sm">
|
||||
|
||||
@@ -20,23 +20,18 @@ export default function ForbiddenPage() {
|
||||
<div className="container mx-auto px-6 py-16">
|
||||
<div className="flex min-h-[60vh] flex-col items-center justify-center text-center">
|
||||
<div className="mb-8 rounded-full bg-destructive/10 p-6">
|
||||
<ShieldAlert
|
||||
className="h-16 w-16 text-destructive"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<ShieldAlert className="h-16 w-16 text-destructive" aria-hidden="true" />
|
||||
</div>
|
||||
|
||||
<h1 className="mb-4 text-4xl font-bold tracking-tight">
|
||||
403 - Access Forbidden
|
||||
</h1>
|
||||
<h1 className="mb-4 text-4xl font-bold tracking-tight">403 - Access Forbidden</h1>
|
||||
|
||||
<p className="mb-2 text-lg text-muted-foreground max-w-md">
|
||||
You don't have permission to access this resource.
|
||||
</p>
|
||||
|
||||
<p className="mb-8 text-sm text-muted-foreground max-w-md">
|
||||
This page requires administrator privileges. If you believe you should
|
||||
have access, please contact your system administrator.
|
||||
This page requires administrator privileges. If you believe you should have access, please
|
||||
contact your system administrator.
|
||||
</p>
|
||||
|
||||
<div className="flex gap-4">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@import "tailwindcss";
|
||||
@import 'tailwindcss';
|
||||
|
||||
/**
|
||||
* FastNext Template Design System
|
||||
@@ -11,38 +11,38 @@
|
||||
|
||||
:root {
|
||||
/* Colors */
|
||||
--background: oklch(1.0000 0 0);
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.3211 0 0);
|
||||
--card: oklch(1.0000 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.3211 0 0);
|
||||
--popover: oklch(1.0000 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.3211 0 0);
|
||||
--primary: oklch(0.6231 0.1880 259.8145);
|
||||
--primary-foreground: oklch(1.0000 0 0);
|
||||
--secondary: oklch(0.9670 0.0029 264.5419);
|
||||
--primary: oklch(0.6231 0.188 259.8145);
|
||||
--primary-foreground: oklch(1 0 0);
|
||||
--secondary: oklch(0.967 0.0029 264.5419);
|
||||
--secondary-foreground: oklch(0.4461 0.0263 256.8018);
|
||||
--muted: oklch(0.9846 0.0017 247.8389);
|
||||
--muted-foreground: oklch(0.5510 0.0234 264.3637);
|
||||
--accent: oklch(0.9514 0.0250 236.8242);
|
||||
--muted-foreground: oklch(0.551 0.0234 264.3637);
|
||||
--accent: oklch(0.9514 0.025 236.8242);
|
||||
--accent-foreground: oklch(0.3791 0.1378 265.5222);
|
||||
--destructive: oklch(0.6368 0.2078 25.3313);
|
||||
--destructive-foreground: oklch(1.0000 0 0);
|
||||
--destructive-foreground: oklch(1 0 0);
|
||||
--border: oklch(0.9276 0.0058 264.5313);
|
||||
--input: oklch(0.9276 0.0058 264.5313);
|
||||
--ring: oklch(0.6231 0.1880 259.8145);
|
||||
--chart-1: oklch(0.6231 0.1880 259.8145);
|
||||
--ring: oklch(0.6231 0.188 259.8145);
|
||||
--chart-1: oklch(0.6231 0.188 259.8145);
|
||||
--chart-2: oklch(0.5461 0.2152 262.8809);
|
||||
--chart-3: oklch(0.4882 0.2172 264.3763);
|
||||
--chart-4: oklch(0.4244 0.1809 265.6377);
|
||||
--chart-5: oklch(0.3791 0.1378 265.5222);
|
||||
--sidebar: oklch(0.9846 0.0017 247.8389);
|
||||
--sidebar-foreground: oklch(0.3211 0 0);
|
||||
--sidebar-primary: oklch(0.6231 0.1880 259.8145);
|
||||
--sidebar-primary-foreground: oklch(1.0000 0 0);
|
||||
--sidebar-accent: oklch(0.9514 0.0250 236.8242);
|
||||
--sidebar-primary: oklch(0.6231 0.188 259.8145);
|
||||
--sidebar-primary-foreground: oklch(1 0 0);
|
||||
--sidebar-accent: oklch(0.9514 0.025 236.8242);
|
||||
--sidebar-accent-foreground: oklch(0.3791 0.1378 265.5222);
|
||||
--sidebar-border: oklch(0.9276 0.0058 264.5313);
|
||||
--sidebar-ring: oklch(0.6231 0.1880 259.8145);
|
||||
--sidebar-ring: oklch(0.6231 0.188 259.8145);
|
||||
|
||||
/* Typography - Use Geist fonts from Next.js */
|
||||
--font-sans: var(--font-geist-sans), system-ui, -apple-system, sans-serif;
|
||||
@@ -61,11 +61,11 @@
|
||||
--shadow-color: oklch(0 0 0);
|
||||
--shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);
|
||||
--shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);
|
||||
--shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10);
|
||||
--shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10);
|
||||
--shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10);
|
||||
--shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10);
|
||||
--shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10);
|
||||
--shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 2px 4px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 4px 6px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 8px 10px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25);
|
||||
|
||||
/* Spacing */
|
||||
@@ -81,8 +81,8 @@
|
||||
--card-foreground: oklch(0.9219 0 0);
|
||||
--popover: oklch(0.2686 0 0);
|
||||
--popover-foreground: oklch(0.9219 0 0);
|
||||
--primary: oklch(0.6231 0.1880 259.8145);
|
||||
--primary-foreground: oklch(1.0000 0 0);
|
||||
--primary: oklch(0.6231 0.188 259.8145);
|
||||
--primary-foreground: oklch(1 0 0);
|
||||
--secondary: oklch(0.2686 0 0);
|
||||
--secondary-foreground: oklch(0.9219 0 0);
|
||||
--muted: oklch(0.2393 0 0);
|
||||
@@ -90,23 +90,23 @@
|
||||
--accent: oklch(0.3791 0.1378 265.5222);
|
||||
--accent-foreground: oklch(0.8823 0.0571 254.1284);
|
||||
--destructive: oklch(0.6368 0.2078 25.3313);
|
||||
--destructive-foreground: oklch(1.0000 0 0);
|
||||
--destructive-foreground: oklch(1 0 0);
|
||||
--border: oklch(0.3715 0 0);
|
||||
--input: oklch(0.3715 0 0);
|
||||
--ring: oklch(0.6231 0.1880 259.8145);
|
||||
--chart-1: oklch(0.7137 0.1434 254.6240);
|
||||
--chart-2: oklch(0.6231 0.1880 259.8145);
|
||||
--ring: oklch(0.6231 0.188 259.8145);
|
||||
--chart-1: oklch(0.7137 0.1434 254.624);
|
||||
--chart-2: oklch(0.6231 0.188 259.8145);
|
||||
--chart-3: oklch(0.5461 0.2152 262.8809);
|
||||
--chart-4: oklch(0.4882 0.2172 264.3763);
|
||||
--chart-5: oklch(0.4244 0.1809 265.6377);
|
||||
--sidebar: oklch(0.2046 0 0);
|
||||
--sidebar-foreground: oklch(0.9219 0 0);
|
||||
--sidebar-primary: oklch(0.6231 0.1880 259.8145);
|
||||
--sidebar-primary-foreground: oklch(1.0000 0 0);
|
||||
--sidebar-primary: oklch(0.6231 0.188 259.8145);
|
||||
--sidebar-primary-foreground: oklch(1 0 0);
|
||||
--sidebar-accent: oklch(0.3791 0.1378 265.5222);
|
||||
--sidebar-accent-foreground: oklch(0.8823 0.0571 254.1284);
|
||||
--sidebar-border: oklch(0.3715 0 0);
|
||||
--sidebar-ring: oklch(0.6231 0.1880 259.8145);
|
||||
--sidebar-ring: oklch(0.6231 0.188 259.8145);
|
||||
}
|
||||
|
||||
/* Make CSS variables available to Tailwind utilities */
|
||||
@@ -186,24 +186,24 @@ html.dark {
|
||||
|
||||
/* Cursor pointer for all clickable elements */
|
||||
button,
|
||||
[role="button"],
|
||||
[type="button"],
|
||||
[type="submit"],
|
||||
[type="reset"],
|
||||
[role='button'],
|
||||
[type='button'],
|
||||
[type='submit'],
|
||||
[type='reset'],
|
||||
a,
|
||||
label[for],
|
||||
select,
|
||||
[tabindex]:not([tabindex="-1"]) {
|
||||
[tabindex]:not([tabindex='-1']) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Exception: disabled elements should not have pointer cursor */
|
||||
button:disabled,
|
||||
[role="button"][aria-disabled="true"],
|
||||
[type="button"]:disabled,
|
||||
[type="submit"]:disabled,
|
||||
[type="reset"]:disabled,
|
||||
a[aria-disabled="true"],
|
||||
[role='button'][aria-disabled='true'],
|
||||
[type='button']:disabled,
|
||||
[type='submit']:disabled,
|
||||
[type='reset']:disabled,
|
||||
a[aria-disabled='true'],
|
||||
select:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { Providers } from "./providers";
|
||||
import { AuthProvider } from "@/lib/auth/AuthContext";
|
||||
import { AuthInitializer } from "@/components/auth";
|
||||
import type { Metadata } from 'next';
|
||||
import { Geist, Geist_Mono } from 'next/font/google';
|
||||
import './globals.css';
|
||||
import { Providers } from './providers';
|
||||
import { AuthProvider } from '@/lib/auth/AuthContext';
|
||||
import { AuthInitializer } from '@/components/auth';
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
display: "swap", // Prevent font from blocking render
|
||||
variable: '--font-geist-sans',
|
||||
subsets: ['latin'],
|
||||
display: 'swap', // Prevent font from blocking render
|
||||
preload: true,
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
display: "swap", // Prevent font from blocking render
|
||||
variable: '--font-geist-mono',
|
||||
subsets: ['latin'],
|
||||
display: 'swap', // Prevent font from blocking render
|
||||
preload: false, // Only preload primary font
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "FastNext Template",
|
||||
description: "FastAPI + Next.js Template",
|
||||
title: 'FastNext Template',
|
||||
description: 'FastAPI + Next.js Template',
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
@@ -57,9 +57,7 @@ export default function RootLayout({
|
||||
}}
|
||||
/>
|
||||
</head>
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
|
||||
<AuthProvider>
|
||||
<AuthInitializer />
|
||||
<Providers>{children}</Providers>
|
||||
|
||||
@@ -99,10 +99,7 @@ export default function Home() {
|
||||
</footer>
|
||||
|
||||
{/* Shared Demo Credentials Modal */}
|
||||
<DemoCredentialsModal
|
||||
open={demoModalOpen}
|
||||
onClose={() => setDemoModalOpen(false)}
|
||||
/>
|
||||
<DemoCredentialsModal open={demoModalOpen} onClose={() => setDemoModalOpen(false)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ import { ThemeProvider } from '@/components/theme';
|
||||
// Set NEXT_PUBLIC_ENABLE_DEVTOOLS=true in .env.local to enable
|
||||
/* istanbul ignore next - Dev-only devtools, not tested in production */
|
||||
const ReactQueryDevtools =
|
||||
process.env.NODE_ENV === 'development' &&
|
||||
process.env.NEXT_PUBLIC_ENABLE_DEVTOOLS === 'true'
|
||||
process.env.NODE_ENV === 'development' && process.env.NEXT_PUBLIC_ENABLE_DEVTOOLS === 'true'
|
||||
? lazy(() =>
|
||||
import('@tanstack/react-query-devtools').then((mod) => ({
|
||||
default: mod.ReactQueryDevtools,
|
||||
|
||||
Reference in New Issue
Block a user