Revert Zustand persist middleware approach and restore AuthInitializer

- Remove persist middleware from authStore (causing hooks timing issues)
- Restore original AuthInitializer component pattern
- Keep good Phase 3 optimizations:
  - Theme FOUC fix (inline script)
  - React Query refetchOnWindowFocus disabled
  - Code splitting for dev/auth components
  - Shared form components (FormField, useFormError)
  - Store location in lib/stores
This commit is contained in:
2025-11-02 14:52:12 +01:00
parent 6d1b730ae7
commit 68e28e4c76
22 changed files with 2822 additions and 398 deletions

346
ANALYSIS_SUMMARY.md Normal file
View File

@@ -0,0 +1,346 @@
# /dev/ Pages Structure Analysis - Summary
## Files Generated
This analysis has produced **3 comprehensive documents**:
1. **DEV_PAGES_STRUCTURE_ANALYSIS.md** (758 lines)
- Complete technical analysis of all 7 pages
- Detailed structure for each page
- 8 identified inconsistencies
- 8 specific recommendations
- Code duplication analysis
- Complete refactor proposal
2. **DEV_PAGES_QUICK_REFERENCE.md** (400+ lines)
- Visual ASCII diagrams of page structures
- Quick-find tables and matrices
- Implementation roadmap
- Before/after code examples
- Testing checklist
- File modification guide
3. **COMPONENT_IMPLEMENTATION_GUIDE.md** (600+ lines)
- Complete code for 5 new components
- Detailed usage examples
- Step-by-step implementation checklist
- Migration instructions for each page
- Testing commands
---
## Key Findings at a Glance
### Problem Statement
The `/dev/` pages exhibit **significant structural inconsistencies** that result in:
- 144 lines of duplicated code
- Inconsistent header implementations (3 pages)
- Inconsistent footer implementations (3 pages)
- No breadcrumb navigation
- Inconsistent page title display
### Scope
- **Pages Analyzed:** 7
- **Components Analyzed:** 6
- **Lines of Code Reviewed:** 2,500+
- **Inconsistencies Found:** 8
- **Code Duplication:** 144 lines
- **Duplicated Components:** Headers (3) + Footers (3)
---
## What's Wrong (The 8 Inconsistencies)
1. **Header Implementation (CRITICAL)**
- Forms, Layouts, Spacing: Custom sticky headers
- Hub, Components, Docs: Inherited from DevLayout
- Problem: 40 lines × 3 pages = 120 lines duplicated
2. **Page Title/Breadcrumbs (CRITICAL)**
- No breadcrumb navigation anywhere
- Page titles shown in: Hero sections, Headers, Frontmatter
- Missing: Consistent breadcrumb pattern
3. **Footer Implementation (INCONSISTENT)**
- 3 pages have footers (Forms, Layouts, Spacing)
- 4 pages have no footers (Hub, Components, Docs, Dynamic)
- Problem: 8 lines × 3 pages = 24 lines duplicated
4. **Page Layout Wrapper (INCONSISTENT)**
- Forms/Layouts/Spacing: min-h-screen wrapper
- Hub/Docs: No wrapper or different structure
- Components: No wrapper at all
5. **Content Container Padding (MINOR)**
- py-12 on some pages
- py-8 on other pages
- Should be standardized
6. **'use client' Directive (INCONSISTENT)**
- Forms: Uses 'use client'
- Others: Server components (inconsistent pattern)
7. **Component Loading/Code Splitting (INCONSISTENT)**
- Components: Dynamic with loading state
- Spacing: Dynamic with skeleton loading
- Forms/Layouts: No dynamic imports
- Should use consistent pattern
8. **ExampleSection Organization (INCONSISTENT)**
- Forms/Layouts/Spacing: Use ExampleSection
- Hub/Components/Docs: Don't use ExampleSection
---
## Proposed Solution
Create **5 new reusable components** to replace the duplicated code:
```
NEW COMPONENTS:
├── DevPageHeader.tsx - Shared header (replaces 120 lines)
├── DevPageFooter.tsx - Shared footer (replaces 24 lines)
├── DevBreadcrumbs.tsx - Navigation breadcrumbs (new feature)
├── DevPageLayout.tsx - Complete wrapper (combines above)
└── ExampleLoadingSkeleton.tsx - Consistent loading states (new feature)
```
**Impact:**
- Eliminates 144 lines of code duplication
- Adds breadcrumb navigation to all pages
- Standardizes page structure
- Improves maintainability
- Better UX consistency
---
## Implementation Plan
### Phase 1: Extract Components (1-2 hours)
Create the 5 new components with full TypeScript types and documentation.
### Phase 2: Refactor Pages (2-3 hours)
Update the 7 pages to use new components:
- Forms page: Remove 40 lines of header/footer code
- Layouts page: Remove 40 lines of header/footer code
- Spacing page: Remove 40 lines of header/footer code
- Components page: Add breadcrumbs & footer
- Hub page: Add footer
- Docs page: Add footer
- Docs Dynamic: Add breadcrumbs & footer
### Phase 3: Enhance (1-2 hours)
- Add breadcrumbs to all detail pages
- Standardize dynamic imports
- Update loading states
### Phase 4: Polish (30-60 min)
- Test all navigation flows
- Verify mobile responsiveness
- Check accessibility
**Total Time Estimate: 4-8 hours**
---
## Page Comparison Matrix
### Current State
```
Feature Hub Comp Forms Layout Spacing Docs DocsDyn
Custom Header · · ✓ ✓ ✓ · ·
Back Button · · ✓ ✓ ✓ · ·
Page Title H · H H H H FM
Breadcrumbs · · · · · · ·
Footer · · ✓ ✓ ✓ · ·
Hero Section ✓ · · · · ✓ ·
Code Duplication Low Low HIGH HIGH HIGH Low Low
```
### Target State
```
Feature Hub Comp Forms Layout Spacing Docs DocsDyn
Custom Header · · ✗ ✗ ✗ · ·
Back Button · ✓ ✓ ✓ ✓ · ✓
Page Title H ✓ ✓ ✓ ✓ H FM
Breadcrumbs · ✓ ✓ ✓ ✓ · ✓
Footer FTR FTR ✓ ✓ ✓ FTR FTR
Hero Section ✓ · · · · ✓ ·
Code Duplication - - - - - - -
```
---
## Code Metrics
### Before Implementation
- Total components: 6
- Duplicated lines: 144
- Inconsistent patterns: 8
- Pages without footers: 4
- Pages without breadcrumbs: 7
### After Implementation
- Total components: 10 (adding 4 reusable components)
- Duplicated lines: 0
- Inconsistent patterns: 0
- Pages without footers: 0
- Pages without breadcrumbs: 0
### Bundle Impact
- New code: ~2-3 KB
- Removed code: ~6 KB
- Net savings: ~3-4 KB
---
## File Changes Summary
### Files to Create
1. `src/components/dev/DevPageHeader.tsx`
2. `src/components/dev/DevBreadcrumbs.tsx`
3. `src/components/dev/DevPageFooter.tsx`
4. `src/components/dev/DevPageLayout.tsx`
5. `src/components/dev/ExampleLoadingSkeleton.tsx`
### Files to Modify
1. `src/app/dev/page.tsx` - Add footer
2. `src/app/dev/components/page.tsx` - Add breadcrumbs/footer
3. `src/app/dev/forms/page.tsx` - Refactor with DevPageLayout
4. `src/app/dev/layouts/page.tsx` - Refactor with DevPageLayout
5. `src/app/dev/spacing/page.tsx` - Refactor with DevPageLayout
6. `src/app/dev/docs/page.tsx` - Add footer
7. `src/app/dev/docs/design-system/[...slug]/page.tsx` - Add breadcrumbs/footer
### Files to Keep As-Is
1. `src/app/dev/layout.tsx`
2. `src/components/dev/DevLayout.tsx`
3. `src/components/dev/Example.tsx`
4. `src/components/dev/BeforeAfter.tsx`
5. `src/components/dev/CodeSnippet.tsx`
6. `src/components/dev/ComponentShowcase.tsx`
---
## How to Use These Documents
### For Quick Overview
→ Read **DEV_PAGES_QUICK_REFERENCE.md**
- Visual diagrams
- Quick matrices
- Implementation roadmap
### For Complete Details
→ Read **DEV_PAGES_STRUCTURE_ANALYSIS.md**
- Deep technical analysis
- Detailed comparison tables
- Complete recommendations
### For Implementation
→ Read **COMPONENT_IMPLEMENTATION_GUIDE.md**
- Copy-paste ready code
- Step-by-step checklist
- Usage examples
---
## Pages Analyzed
1. `/dev/page.tsx` (Design System Hub)
- Server component
- Hero section with 3 content areas
- No header, footer, or breadcrumbs
- Status: Add footer
2. `/dev/components/page.tsx` (Component Showcase)
- Server component
- Minimal structure (just ComponentShowcase wrapper)
- No header, footer, breadcrumbs, or title
- Status: Add all
3. `/dev/forms/page.tsx` (Form Patterns)
- Client component
- Custom sticky header + footer
- 6 ExampleSections
- Status: Refactor to use DevPageLayout
4. `/dev/layouts/page.tsx` (Layout Patterns)
- Server component
- Custom sticky header + footer
- 7 ExampleSections
- Status: Refactor to use DevPageLayout
5. `/dev/spacing/page.tsx` (Spacing Patterns)
- Server component
- Custom sticky header + footer
- 7 ExampleSections
- Dynamic imports with skeleton loading
- Status: Refactor to use DevPageLayout
6. `/dev/docs/page.tsx` (Documentation Hub)
- Server component
- Hero section with 3 doc sections
- No header, footer, or breadcrumbs
- Status: Add footer
7. `/dev/docs/design-system/[...slug]/page.tsx` (Dynamic Docs)
- Server component
- Reads markdown files
- Minimal structure
- No breadcrumbs or footer
- Status: Add both
---
## Success Criteria
After implementation, all pages should:
- [ ] Have consistent header styling
- [ ] Show breadcrumb navigation (except Hub)
- [ ] Have page titles visible
- [ ] Have footers with documentation links
- [ ] Back buttons work on detail pages
- [ ] Global navigation is accessible
- [ ] Mobile layout is responsive
- [ ] No console errors
- [ ] Build passes without warnings
- [ ] E2E tests pass
---
## Next Steps
1. Review these documents with your team
2. Prioritize implementation phases
3. Create the 5 new components
4. Refactor the 7 pages
5. Test thoroughly
6. Commit changes with detailed commit message
---
## Related Resources
- **CLAUDE.md** - Project guidelines and conventions
- **frontend/docs/design-system/** - Design system documentation
- **frontend/src/components/dev/** - Current dev components
- **frontend/src/app/dev/** - All dev pages
---
## Questions?
Key questions answered in the detailed documents:
- **Why create new components?** DRY principle - eliminate 144 lines of duplication
- **Why breadcrumbs?** Better navigation for nested routes
- **Why standardize footers?** Consistent UX across all pages
- **How long will this take?** 4-8 hours total (4 phases)
- **Will this break anything?** No - only adding/refactoring, not removing features
- **Should I do all pages at once?** Recommended: Phase 1 (components) + Phase 2 (pages) = 3-5 hours
---
Generated: November 2, 2025
Analysis Type: Very Thorough (Complete structure analysis)
Scope: All 7 pages under /dev/ route

View File

@@ -0,0 +1,629 @@
# Component Implementation Guide
This document provides detailed code for implementing the proposed dev page components.
## 1. DevPageHeader Component
**File:** `src/components/dev/DevPageHeader.tsx`
```typescript
'use client';
import Link from 'next/link';
import { ArrowLeft } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
interface DevPageHeaderProps {
title: string;
subtitle?: string;
showBackButton?: boolean;
backHref?: string;
}
/**
* DevPageHeader
* Shared sticky header for detail pages with optional back button
*
* Replaces duplicated headers in forms, layouts, and spacing pages
*
* @example
* <DevPageHeader
* title="Form Patterns"
* subtitle="react-hook-form + Zod examples"
* showBackButton
* backHref="/dev"
* />
*/
export function DevPageHeader({
title,
subtitle,
showBackButton = false,
backHref = '/dev',
}: DevPageHeaderProps) {
return (
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container mx-auto flex h-16 items-center gap-4 px-4">
{showBackButton && (
<Link href={backHref}>
<Button
variant="ghost"
size="icon"
aria-label="Back to previous page"
>
<ArrowLeft className="h-5 w-5" />
</Button>
</Link>
)}
<div className="flex-1">
<h1 className="text-xl font-bold">{title}</h1>
{subtitle && (
<p className="text-sm text-muted-foreground">{subtitle}</p>
)}
</div>
</div>
</header>
);
}
```
---
## 2. DevBreadcrumbs Component
**File:** `src/components/dev/DevBreadcrumbs.tsx`
```typescript
'use client';
import Link from 'next/link';
import { ChevronRight, Home } from 'lucide-react';
import { cn } from '@/lib/utils';
interface Breadcrumb {
label: string;
href?: string;
}
interface DevBreadcrumbsProps {
items: Breadcrumb[];
className?: string;
}
/**
* DevBreadcrumbs
* Breadcrumb navigation for showing page hierarchy
*
* @example
* <DevBreadcrumbs
* items={[
* { label: 'Hub', href: '/dev' },
* { label: 'Forms' }
* ]}
* />
*/
export function DevBreadcrumbs({ items, className }: DevBreadcrumbsProps) {
return (
<nav
className={cn('bg-muted/30 border-b px-4 py-2', className)}
aria-label="Breadcrumb"
>
<ol className="container mx-auto flex items-center gap-2 text-sm">
{/* Home link */}
<li>
<Link
href="/dev"
className="inline-flex items-center gap-1 text-muted-foreground hover:text-foreground transition-colors"
>
<Home className="h-4 w-4" />
<span className="sr-only">Home</span>
</Link>
</li>
{/* Breadcrumb items */}
{items.map((item, index) => (
<li key={index} className="flex items-center gap-2">
<ChevronRight className="h-4 w-4 text-muted-foreground" />
{item.href ? (
<Link
href={item.href}
className="text-muted-foreground hover:text-foreground transition-colors"
>
{item.label}
</Link>
) : (
<span className="text-foreground font-medium">{item.label}</span>
)}
</li>
))}
</ol>
</nav>
);
}
```
---
## 3. DevPageFooter Component
**File:** `src/components/dev/DevPageFooter.tsx`
```typescript
import Link from 'next/link';
interface DevPageFooterProps {
documentationLink?: {
label: string;
href: string;
};
}
/**
* DevPageFooter
* Shared footer for dev pages with optional documentation link
*
* @example
* <DevPageFooter
* documentationLink={{
* label: 'Forms Documentation',
* href: '/dev/docs/design-system/06-forms'
* }}
* />
*/
export function DevPageFooter({ documentationLink }: DevPageFooterProps) {
return (
<footer className="mt-16 border-t py-6">
<div className="container mx-auto px-4">
{documentationLink ? (
<p className="text-center text-sm text-muted-foreground">
Learn more:{' '}
<Link
href={documentationLink.href}
className="font-medium hover:text-foreground transition-colors"
>
{documentationLink.label}
</Link>
</p>
) : (
<p className="text-center text-sm text-muted-foreground">
Part of the FastNext Design System
</p>
)}
</div>
</footer>
);
}
```
---
## 4. DevPageLayout Component
**File:** `src/components/dev/DevPageLayout.tsx`
```typescript
import { ReactNode } from 'react';
import { DevPageHeader } from './DevPageHeader';
import { DevBreadcrumbs } from './DevBreadcrumbs';
import { DevPageFooter } from './DevPageFooter';
import { cn } from '@/lib/utils';
interface Breadcrumb {
label: string;
href?: string;
}
interface DocumentationLink {
label: string;
href: string;
}
interface DevPageLayoutProps {
title?: string;
subtitle?: string;
breadcrumbs?: Breadcrumb[];
showBackButton?: boolean;
backHref?: string;
footer?: DocumentationLink | boolean;
children: ReactNode;
className?: string;
}
/**
* DevPageLayout
* Complete page layout wrapper for dev pages
*
* Combines header, breadcrumbs, main content, and footer
*
* @example
* <DevPageLayout
* title="Form Patterns"
* subtitle="Complete form examples"
* breadcrumbs={[
* { label: 'Hub', href: '/dev' },
* { label: 'Forms' }
* ]}
* footer={{
* label: 'Forms Documentation',
* href: '/dev/docs/design-system/06-forms'
* }}
* showBackButton
* >
* {/* Page content */}
* </DevPageLayout>
*/
export function DevPageLayout({
title,
subtitle,
breadcrumbs,
showBackButton = false,
backHref = '/dev',
footer,
children,
className,
}: DevPageLayoutProps) {
// Determine footer configuration
let footerLink: DocumentationLink | undefined;
if (typeof footer === 'object' && footer !== null) {
footerLink = footer;
}
return (
<div className="min-h-screen bg-background">
{/* Breadcrumbs (optional) */}
{breadcrumbs && breadcrumbs.length > 0 && (
<DevBreadcrumbs items={breadcrumbs} />
)}
{/* Page Header (optional) */}
{title && (
<DevPageHeader
title={title}
subtitle={subtitle}
showBackButton={showBackButton}
backHref={backHref}
/>
)}
{/* Main Content */}
<main className={cn('container mx-auto px-4 py-8', className)}>
{children}
</main>
{/* Footer */}
{footer !== false && <DevPageFooter documentationLink={footerLink} />}
</div>
);
}
```
---
## 5. ExampleLoadingSkeleton Component
**File:** `src/components/dev/ExampleLoadingSkeleton.tsx`
```typescript
import { cn } from '@/lib/utils';
interface ExampleLoadingSkeletonProps {
count?: number;
className?: string;
}
/**
* ExampleLoadingSkeleton
* Loading placeholder for dynamically imported example components
*
* @example
* const Example = dynamic(() => import('@/components/dev/Example'), {
* loading: () => <ExampleLoadingSkeleton />,
* });
*/
export function ExampleLoadingSkeleton({
count = 1,
className,
}: ExampleLoadingSkeletonProps) {
return (
<div className={cn('space-y-4', className)}>
{Array.from({ length: count }).map((_, i) => (
<div
key={i}
className="animate-pulse space-y-4 rounded-lg border p-6"
>
{/* Title skeleton */}
<div className="h-6 w-48 rounded bg-muted" />
{/* Description skeleton */}
<div className="space-y-2">
<div className="h-4 w-full rounded bg-muted" />
<div className="h-4 w-3/4 rounded bg-muted" />
</div>
{/* Content skeleton */}
<div className="h-32 rounded bg-muted" />
</div>
))}
</div>
);
}
```
---
## 6. Example Page Refactoring
### Forms Page (BEFORE)
```typescript
export default function FormsPage() {
const [isSubmitting, setIsSubmitting] = useState(false);
// ... rest of state
return (
<div className="min-h-screen bg-background">
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container mx-auto flex h-16 items-center gap-4 px-4">
<Link href="/dev">
<Button variant="ghost" size="icon" aria-label="Back to hub">
<ArrowLeft className="h-5 w-5" />
</Button>
</Link>
<div>
<h1 className="text-xl font-bold">Form Patterns</h1>
<p className="text-sm text-muted-foreground">
react-hook-form + Zod validation examples
</p>
</div>
</div>
</header>
<main className="container mx-auto px-4 py-8">
{/* Content sections */}
</main>
<footer className="mt-16 border-t py-6">
<div className="container mx-auto px-4 text-center">
<p className="text-sm text-muted-foreground">
Learn more:{' '}
<Link
href="/dev/docs/design-system/06-forms"
className="font-medium hover:text-foreground"
>
Forms Documentation
</Link>
</p>
</div>
</footer>
</div>
);
}
```
### Forms Page (AFTER)
```typescript
'use client';
import { useState } from 'react';
import { DevPageLayout } from '@/components/dev/DevPageLayout';
import { ExampleSection } from '@/components/dev/Example';
import { BeforeAfter } from '@/components/dev/BeforeAfter';
// ... other imports
export default function FormsPage() {
const [isSubmitting, setIsSubmitting] = useState(false);
// ... rest of state
return (
<DevPageLayout
title="Form Patterns"
subtitle="react-hook-form + Zod validation examples"
breadcrumbs={[
{ label: 'Hub', href: '/dev' },
{ label: 'Forms' }
]}
footer={{
label: 'Forms Documentation',
href: '/dev/docs/design-system/06-forms'
}}
showBackButton
>
<div className="space-y-12">
{/* Content sections remain the same */}
</div>
</DevPageLayout>
);
}
```
---
## 7. Updated Component Imports
### Update Example.tsx exports (if extracting ExampleSection)
```typescript
// src/components/dev/Example.tsx
export function Example({ ... }) { ... }
export function ExampleGrid({ ... }) { ... }
export function ExampleSection({ ... }) { ... }
export { ExampleLoadingSkeleton } from './ExampleLoadingSkeleton';
```
### Update spacing/page.tsx dynamic imports
```typescript
import dynamic from 'next/dynamic';
import { ExampleLoadingSkeleton } from '@/components/dev/ExampleLoadingSkeleton';
const Example = dynamic(
() => import('@/components/dev/Example').then((mod) => ({
default: mod.Example
})),
{ loading: () => <ExampleLoadingSkeleton /> }
);
const ExampleSection = dynamic(
() => import('@/components/dev/Example').then((mod) => ({
default: mod.ExampleSection
})),
{ loading: () => <ExampleLoadingSkeleton /> }
);
const BeforeAfter = dynamic(
() => import('@/components/dev/BeforeAfter').then((mod) => ({
default: mod.BeforeAfter
})),
{ loading: () => <ExampleLoadingSkeleton /> }
);
```
---
## 8. Implementation Checklist
### Step 1: Create New Components
- [ ] Create `DevPageHeader.tsx`
- [ ] Create `DevBreadcrumbs.tsx`
- [ ] Create `DevPageFooter.tsx`
- [ ] Create `DevPageLayout.tsx`
- [ ] Create `ExampleLoadingSkeleton.tsx`
### Step 2: Update Forms Page
- [ ] Import `DevPageLayout`
- [ ] Remove custom header
- [ ] Remove custom footer
- [ ] Wrap content with `DevPageLayout`
- [ ] Add breadcrumbs config
- [ ] Test navigation and styling
### Step 3: Update Layouts Page
- [ ] Import `DevPageLayout`
- [ ] Remove custom header
- [ ] Remove custom footer
- [ ] Wrap content with `DevPageLayout`
- [ ] Add breadcrumbs config
- [ ] Test navigation and styling
### Step 4: Update Spacing Page
- [ ] Import `DevPageLayout`
- [ ] Remove custom header
- [ ] Remove custom footer
- [ ] Update dynamic imports to use `ExampleLoadingSkeleton`
- [ ] Wrap content with `DevPageLayout`
- [ ] Add breadcrumbs config
- [ ] Test navigation and styling
### Step 5: Update Components Page
- [ ] Import `DevPageLayout` and `DevBreadcrumbs`
- [ ] Add breadcrumbs
- [ ] Add footer with doc link
- [ ] Update dynamic import to use `ExampleLoadingSkeleton`
- [ ] Test navigation and styling
### Step 6: Update Hub Page
- [ ] Import `DevPageFooter`
- [ ] Add footer component
- [ ] Test styling
### Step 7: Update Docs Page
- [ ] Import `DevPageFooter`
- [ ] Add footer component
- [ ] Test styling
### Step 8: Update Dynamic Docs Page
- [ ] Import `DevPageLayout` or `DevPageFooter`
- [ ] Add breadcrumbs
- [ ] Add footer
- [ ] Test navigation
### Step 9: Testing
- [ ] All pages have consistent headers
- [ ] Breadcrumbs display correctly
- [ ] Back buttons work
- [ ] Footers are present and styled
- [ ] Mobile responsiveness intact
- [ ] No console errors
---
## Usage Examples
### Simple Detail Page (Forms/Layouts/Spacing)
```typescript
<DevPageLayout
title="Form Patterns"
subtitle="Complete form examples"
breadcrumbs={[
{ label: 'Hub', href: '/dev' },
{ label: 'Forms' }
]}
footer={{
label: 'Forms Documentation',
href: '/dev/docs/design-system/06-forms'
}}
showBackButton
>
<div className="space-y-12">
{/* Your content */}
</div>
</DevPageLayout>
```
### Hub Page (with footer only)
```typescript
<DevPageLayout footer={false}>
{/* Hero section */}
{/* Content sections */}
</DevPageLayout>
```
### With Breadcrumbs Only
```typescript
<DevPageLayout
breadcrumbs={[
{ label: 'Docs', href: '/dev/docs' },
{ label: 'Forms' }
]}
>
{/* Content */}
</DevPageLayout>
```
---
## Migration Summary
| Page | Changes |
|------|---------|
| Forms | Remove header/footer, add DevPageLayout |
| Layouts | Remove header/footer, add DevPageLayout |
| Spacing | Remove header/footer, add DevPageLayout, update imports |
| Components | Add breadcrumbs/footer, update imports |
| Hub | Add DevPageFooter |
| Docs | Add DevPageFooter |
| Docs Dynamic | Add DevPageLayout with breadcrumbs |
---
## Testing Commands
```bash
# Type check
npm run type-check
# Lint
npm run lint
# E2E tests
npm run test:e2e
# Build
npm run build
```

View File

@@ -0,0 +1,352 @@
# Dev Pages Quick Reference
## Visual Structure Comparison
### Hub & Docs Pages (Hero Pattern)
```
┌─────────────────────────────────────────┐
│ DevLayout Global Nav (sticky) │
├─────────────────────────────────────────┤
│ ┌───────────────────────────────────────┐│
│ │ HERO SECTION (gradient bg) ││
│ │ - Title ││
│ │ - Description ││
│ │ - CTA Buttons ││
│ └───────────────────────────────────────┘│
├─────────────────────────────────────────┤
│ MAIN CONTENT │
│ ┌─────────────────────────────────────┐ │
│ │ Section 1 (Grid Layout) │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ Section 2 (Grid Layout) │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ Section 3 (Grid Layout) │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────┘
```
### Forms/Layouts/Spacing Pages (Header Pattern)
```
┌─────────────────────────────────────────┐
│ CUSTOM STICKY HEADER (hides global nav) │
│ [Back] | Title + Subtitle │
├─────────────────────────────────────────┤
│ MAIN CONTENT │
│ ┌─────────────────────────────────────┐ │
│ │ ExampleSection 1 │ │
│ │ - Title + Description │ │
│ │ - Example + Code tabs │ │
│ │ - BeforeAfter if needed │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ ExampleSection 2 │ │
│ └─────────────────────────────────────┘ │
│ ... more sections ... │
├─────────────────────────────────────────┤
│ FOOTER │
│ [Learn more: Link to Doc] │
└─────────────────────────────────────────┘
```
### Components Page (Minimal Pattern)
```
┌─────────────────────────────────────────┐
│ DevLayout Global Nav (sticky) │
├─────────────────────────────────────────┤
│ ComponentShowcase (dynamically loaded) │
│ 787 lines of component examples │
└─────────────────────────────────────────┘
```
---
## Page Inventory
### Pages Analyzed
1. `/dev/page.tsx` - Design System Hub
2. `/dev/components/page.tsx` - Component Showcase
3. `/dev/forms/page.tsx` - Form Patterns
4. `/dev/layouts/page.tsx` - Layout Patterns
5. `/dev/spacing/page.tsx` - Spacing Patterns
6. `/dev/docs/page.tsx` - Documentation Hub
7. `/dev/docs/design-system/[...slug]/page.tsx` - Dynamic Doc Pages
---
## Key Findings
### What's Duplicated
- **Custom Sticky Headers** (40 lines × 3 pages = 120 lines)
- Forms page header
- Layouts page header
- Spacing page header
- **Custom Footers** (8 lines × 3 pages = 24 lines)
- Forms page footer
- Layouts page footer
- Spacing page footer
**Total Duplication: 144 lines**
### What's Missing
- Breadcrumb navigation (all pages)
- Consistent page titles (only hero/header based)
- Footers on half the pages
- Consistent component loading patterns
### What's Inconsistent
1. Header implementation (custom vs inherited)
2. Page title display (hero vs header vs frontmatter)
3. Footer presence (3 pages yes, 4 pages no)
4. Page wrapper structure (min-h-screen vs not)
5. Content padding (py-12 vs py-8)
6. 'use client' directive (Forms only)
7. Dynamic imports (Components & Spacing only)
---
## Component Architecture
### Current Dev Components
```
/src/components/dev/
├── DevLayout.tsx - Global layout wrapper (14KB)
├── Example.tsx - Example container with tabs (5KB)
├── BeforeAfter.tsx - Before/after comparison
├── CodeSnippet.tsx - Code syntax highlighting
└── ComponentShowcase.tsx - 787-line component gallery
```
### Proposed New Components
```
/src/components/dev/
├── DevPageHeader.tsx - Shared page header with back button
├── DevBreadcrumbs.tsx - Navigation breadcrumbs
├── DevPageFooter.tsx - Shared footer with doc links
├── DevPageLayout.tsx - Complete page wrapper
└── ExampleLoadingSkeleton.tsx - Consistent loading state
```
---
## Page Usage Matrix
### What Each Page Actually Has
```
Hub Comp Forms Layout Spacing Docs DocsDyn
'use client' · · ✓ · · · ·
Custom Header · · ✓ ✓ ✓ · ·
Back Button · · ✓ ✓ ✓ · ·
Page Title H · H H H H FM
Breadcrumbs · · · · · · ·
Hero Section ✓ · · · · ✓ ·
Footer · · ✓ ✓ ✓ · ·
ExampleSection · · ✓ ✓ ✓ · ·
BeforeAfter · · ✓ ✓ ✓ · ·
Dynamic Imports · ✓ · · ✓ · ·
Code Duplication Low Low High High High Low Low
Legend:
✓ = Has it
· = Doesn't have it
H = Hero section
FM = Frontmatter
```
### What Each Page Should Have (Target)
```
Hub Comp Forms Layout Spacing Docs DocsDyn
'use client' · · ✓ · · · ·
Custom Header · · ✗ ✗ ✗ · ·
Back Button · ✓ ✓ ✓ ✓ · ✓
Page Title H ✓ ✓ ✓ ✓ H FM
Breadcrumbs · ✓ ✓ ✓ ✓ · ✓
Hero Section ✓ · · · · ✓ ·
Footer FTR FTR ✓ ✓ ✓ FTR FTR
ExampleSection · · ✓ ✓ ✓ · ·
BeforeAfter · · ✓ ✓ ✓ · ·
Dynamic Imports · ✓ · · ✓ · ·
Code Duplication Red Red Green Green Green Red Red
Legend:
✓ = Add/Keep
✗ = Remove
FTR = Use footer component
Red = Needs attention
Green = Ready
```
---
## Implementation Roadmap
### Phase 1: Extract Components (1-2 hours)
1. Create `DevPageHeader.tsx` - shared header
2. Create `DevPageFooter.tsx` - shared footer
3. Create `DevPageLayout.tsx` - wrapper component
4. Create `DevBreadcrumbs.tsx` - breadcrumb nav
### Phase 2: Refactor Pages (2-3 hours)
1. Update Forms page - remove custom header, use DevPageLayout
2. Update Layouts page - remove custom header, use DevPageLayout
3. Update Spacing page - remove custom header, use DevPageLayout
4. Update Components page - add breadcrumbs & footer
### Phase 3: Enhance (1-2 hours)
1. Add breadcrumbs to all non-hub pages
2. Standardize dynamic imports
3. Add consistent loading skeletons
4. Update Hub/Docs footers
### Phase 4: Polish (30-60 min)
1. Add Table of Contents to long pages
2. Improve mobile responsiveness
3. Test navigation flow
4. Verify accessibility
**Total Effort: 4-8 hours**
---
## Code Examples
### Before (Duplicated Header)
```typescript
// /dev/forms/page.tsx
export default function FormsPage() {
return (
<div className="min-h-screen bg-background">
<header className="sticky top-0 z-50 w-full border-b bg-background/95">
<div className="container mx-auto flex h-16 items-center gap-4 px-4">
<Link href="/dev">
<Button variant="ghost" size="icon">
<ArrowLeft />
</Button>
</Link>
<div>
<h1 className="text-xl font-bold">Form Patterns</h1>
<p className="text-sm text-muted-foreground">
react-hook-form + Zod examples
</p>
</div>
</div>
</header>
<main>...</main>
<footer className="mt-16 border-t py-6">
<Link href="/dev/docs/design-system/06-forms">
Forms Documentation
</Link>
</footer>
</div>
);
}
```
### After (Using Components)
```typescript
// /dev/forms/page.tsx
import { DevPageLayout } from '@/components/dev/DevPageLayout';
import { ExampleSection } from '@/components/dev/Example';
export default function FormsPage() {
return (
<DevPageLayout
title="Form Patterns"
subtitle="react-hook-form + Zod examples"
breadcrumbs={[
{ label: 'Hub', href: '/dev' },
{ label: 'Forms' }
]}
footer={{
label: 'Forms Documentation',
href: '/dev/docs/design-system/06-forms'
}}
showBackButton
>
<div className="space-y-12">
<ExampleSection
title="Basic Form"
description="..."
>
{/* Content */}
</ExampleSection>
</div>
</DevPageLayout>
);
}
```
**Savings: 40 lines of code per page × 3 pages = 120 lines**
---
## Testing Checklist
After implementation:
- [ ] All pages display breadcrumbs (except Hub)
- [ ] All pages have page titles visible
- [ ] All pages have footers with doc links (or no links for hubs)
- [ ] Back button works on all detail pages
- [ ] Global nav is consistent across pages
- [ ] Mobile navigation works properly
- [ ] Dynamic imports show loading states
- [ ] No console errors or warnings
---
## Files to Create/Modify
### Create
- `src/components/dev/DevPageHeader.tsx` (NEW)
- `src/components/dev/DevBreadcrumbs.tsx` (NEW)
- `src/components/dev/DevPageFooter.tsx` (NEW)
- `src/components/dev/DevPageLayout.tsx` (NEW)
### Modify
- `src/app/dev/components/page.tsx`
- `src/app/dev/forms/page.tsx`
- `src/app/dev/layouts/page.tsx`
- `src/app/dev/spacing/page.tsx`
- `src/app/dev/page.tsx` (footer)
- `src/app/dev/docs/page.tsx` (footer)
- `src/app/dev/docs/design-system/[...slug]/page.tsx` (footer)
### Keep As-Is
- `src/app/dev/layout.tsx`
- `src/components/dev/DevLayout.tsx`
- `src/components/dev/Example.tsx`
- `src/components/dev/BeforeAfter.tsx`
- `src/components/dev/CodeSnippet.tsx`
- `src/components/dev/ComponentShowcase.tsx`
---
## Metrics
### Code Duplication
- **Current:** 144 lines duplicated
- **After:** 0 lines duplicated
### File Complexity
- **Current:** 7 pages with inconsistent patterns
- **After:** 7 pages with consistent patterns
### Component Count
- **Current:** 6 components
- **After:** 10 components (adding 4 reusable pieces)
### Bundle Size Impact
- **Estimate:** +2-3KB for new components
- **Offset by:** Removing 144 lines of duplicated code
---
## Related Documentation
For more details, see:
- `/workdrive/workspace/Projects/HomeLab/fast-next-template/DEV_PAGES_STRUCTURE_ANALYSIS.md` - Full technical analysis
- `/workdrive/workspace/Projects/HomeLab/fast-next-template/frontend/docs/design-system/` - Design system docs
- `/workdrive/workspace/Projects/HomeLab/fast-next-template/CLAUDE.md` - Project guidelines

View File

@@ -0,0 +1,758 @@
# Page Structure Analysis: /dev/ Routes
## Executive Summary
The `/dev/` pages show **significant inconsistencies** in structure, header implementation, and layout patterns. While they share a common `DevLayout` wrapper through the parent `layout.tsx`, individual pages implement their own headers and footers inconsistently, leading to duplicated code and potential maintenance issues.
---
## Detailed Page Analysis
### 1. `/dev/page.tsx` (Design System Hub)
**Path:** `/workdrive/workspace/Projects/HomeLab/fast-next-template/frontend/src/app/dev/page.tsx`
**Structure:**
- No `'use client'` directive (Server Component)
- No custom header (relies entirely on shared `DevLayout`)
- No footer
- Hero section with gradient background
- Multiple content sections separated by `<Separator />`
- 3 main content areas: Demo Pages Grid, Documentation Links, Key Features
**Breadcrumbs:** None
**Page Title:** None (relies on h1 in hero section: "Design System Hub")
**Footer:** None
**Header/Nav:** Inherited from `DevLayout` only
**Components Used:**
- Icon components (Palette, Layout, Ruler, FileText, BookOpen, ArrowRight, Sparkles)
- Card, CardContent, CardDescription, CardHeader, CardTitle
- Badge, Separator, Button
**Layout Pattern:**
```
DevLayout (sticky header with global nav)
├── Hero Section (gradient, py-12)
├── Main Content (container, px-4, py-12)
│ ├── Demo Pages Section (space-y-12)
│ │ └── Grid (md:grid-cols-2)
│ ├── Separator
│ ├── Documentation Links Section
│ │ └── Grid (sm:grid-cols-2, lg:grid-cols-4)
│ ├── Separator
│ └── Key Features Section
│ └── Grid (sm:grid-cols-2, lg:grid-cols-3)
```
---
### 2. `/dev/components/page.tsx` (Component Showcase)
**Path:** `/workdrive/workspace/Projects/HomeLab/fast-next-template/frontend/src/app/dev/components/page.tsx`
**Structure:**
- No `'use client'` directive (Server Component)
- Dynamic imports with loading state
- No custom header
- No footer
- Minimal page structure - just renders `<ComponentShowcase />`
**Breadcrumbs:** None
**Page Title:** None
**Footer:** None
**Header/Nav:** Inherited from `DevLayout` only
**Components Used:**
- Dynamic `ComponentShowcase` (787 lines, code-split)
**Layout Pattern:**
```
DevLayout (sticky header with global nav)
├── Main Content
│ └── ComponentShowcase (dynamically loaded)
│ └── (787 lines of component examples)
```
**Issues:**
- Minimal structure - just a wrapper around ComponentShowcase
- No breadcrumbs to show navigation depth
- No page title/description
---
### 3. `/dev/forms/page.tsx` (Form Patterns Demo)
**Path:** `/workdrive/workspace/Projects/HomeLab/fast-next-template/frontend/src/app/dev/forms/page.tsx`
**Structure:**
- `'use client'` directive (Client Component)
- Custom sticky header with back button and title
- Content organized in `ExampleSection` components
- Custom footer with documentation link
**Breadcrumbs:** Implicit (back button in header)
**Page Title:** "Form Patterns" (in sticky header)
**Footer:** Yes (mt-16, border-t, py-6)
**Header/Nav:** Custom implementation (does NOT use `DevLayout`)
**Components Used:**
- Custom sticky header with back button
- Example, ExampleSection, BeforeAfter
- Form components: Input, Label, Textarea, Select
- react-hook-form, Zod validation
- Card, Button, Badge, Alert
**Layout Pattern:**
```
Custom Sticky Header
├── Back button → /dev
├── Title: "Form Patterns"
├── Subtitle: "react-hook-form + Zod validation examples"
Main Content (container, mx-auto, px-4, py-8)
├── Introduction (max-w-3xl)
├── ExampleSection × 6
│ ├── Basic Form
│ ├── Complete Form
│ ├── Error States
│ ├── Loading States
│ ├── Zod Patterns
└── Footer (mt-16, border-t)
└── Link to /dev/docs/design-system/06-forms
```
**Unique Features:**
- Back button to return to hub
- Two-tab Example components (Preview/Code)
- BeforeAfter comparisons for accessibility patterns
- Custom state management (isSubmitting, submitSuccess)
---
### 4. `/dev/layouts/page.tsx` (Layout Patterns Demo)
**Path:** `/workdrive/workspace/Projects/HomeLab/fast-next-template/frontend/src/app/dev/layouts/page.tsx`
**Structure:**
- Server Component (no `'use client'`)
- Custom sticky header with back button and title
- Content organized in `ExampleSection` components
- Custom footer with documentation link
**Breadcrumbs:** Implicit (back button in header)
**Page Title:** "Layout Patterns" (in sticky header)
**Footer:** Yes (mt-16, border-t, py-6)
**Header/Nav:** Custom implementation (NOT `DevLayout`)
**Components Used:**
- Custom sticky header (identical structure to forms page)
- Example, ExampleSection, BeforeAfter
- Card components, Badge, Button
- Grid3x3 icon
**Layout Pattern:**
```
Custom Sticky Header
├── Back button → /dev
├── Title: "Layout Patterns"
├── Subtitle: "Essential patterns for pages, dashboards, and forms"
Main Content (container, mx-auto, px-4, py-8)
├── Introduction (max-w-3xl)
├── ExampleSection × 7
│ ├── 1. Page Container
│ ├── 2. Dashboard Grid
│ ├── 3. Form Layout
│ ├── 4. Sidebar Layout
│ ├── 5. Centered Content
│ ├── Decision Tree (Grid vs Flex)
│ └── Responsive Patterns
└── Footer (mt-16, border-t)
└── Link to /dev/docs/design-system/03-layouts
```
**Issues:**
- Duplicates same header pattern as `/dev/forms/page.tsx`
- Both implement nearly identical sticky headers
---
### 5. `/dev/spacing/page.tsx` (Spacing Patterns Demo)
**Path:** `/workdrive/workspace/Projects/HomeLab/fast-next-template/frontend/src/app/dev/spacing/page.tsx`
**Structure:**
- Server Component (no `'use client'`)
- Dynamic imports for heavy components (Example, ExampleSection, BeforeAfter)
- Custom sticky header with back button and title
- Content organized in `ExampleSection` components
- Custom footer with documentation link
**Breadcrumbs:** Implicit (back button in header)
**Page Title:** "Spacing Patterns" (in sticky header)
**Footer:** Yes (mt-16, border-t, py-6)
**Header/Nav:** Custom implementation (NOT `DevLayout`)
**Components Used:**
- Custom sticky header
- Dynamically loaded: Example, ExampleSection, BeforeAfter
- Card, Badge, Button
- Ruler icon
**Layout Pattern:**
```
Custom Sticky Header
├── Back button → /dev
├── Title: "Spacing Patterns"
├── Subtitle: "Parent-controlled spacing philosophy"
Main Content (container, mx-auto, px-4, py-8)
├── Introduction (max-w-3xl)
├── ExampleSection × 7
│ ├── Spacing Scale
│ ├── Gap for Flex/Grid
│ ├── Space-y for Vertical Stacks
│ ├── Anti-patterns
│ ├── Decision Tree
│ └── Common Patterns
└── Footer (mt-16, border-t)
└── Link to /dev/docs/design-system/04-spacing-philosophy
```
**Notable:**
- Uses dynamic imports with loading skeletons (performance optimization)
- Spacing page doesn't use `'use client'` but Example/ExampleSection ARE client components
---
### 6. `/dev/docs/page.tsx` (Documentation Hub)
**Path:** `/workdrive/workspace/Projects/HomeLab/fast-next-template/frontend/src/app/dev/docs/page.tsx`
**Structure:**
- Server Component (no `'use client'`)
- No custom header (relies on `DevLayout`)
- No footer
- Hero section with gradient background
- Multiple documentation sections
**Breadcrumbs:** None
**Page Title:** None (relies on h2 in hero: "Design System Documentation")
**Footer:** None
**Header/Nav:** Inherited from `DevLayout` only
**Components Used:**
- Icon components (BookOpen, Sparkles, Layout, Palette, Code2, FileCode, Accessibility, Lightbulb, Search)
- Card, CardContent, CardDescription, CardHeader, CardTitle
- Badge, Button
**Layout Pattern:**
```
DevLayout (sticky header with global nav)
├── Hero Section (gradient, py-16)
│ ├── Title: "Design System Documentation"
│ ├── Description
│ └── CTA Buttons (3 variants)
├── Main Content (container, mx-auto, px-4, py-12)
│ └── Max-w-6xl
│ ├── Getting Started Section
│ │ └── Grid (md:grid-cols-2)
│ ├── Core Concepts Section
│ │ └── Grid (md:grid-cols-2, lg:grid-cols-3)
│ └── References Section
│ └── Grid (md:grid-cols-2)
```
---
### 7. `/dev/docs/design-system/[...slug]/page.tsx` (Dynamic Documentation)
**Path:** `/workdrive/workspace/Projects/HomeLab/fast-next-template/frontend/src/app/dev/docs/design-system/[...slug]/page.tsx`
**Structure:**
- Server Component with async rendering
- Reads markdown files from disk
- No custom header (relies on `DevLayout`)
- No footer
- Minimal wrapper structure
**Breadcrumbs:** None
**Page Title:** Extracted from markdown frontmatter
**Footer:** None
**Header/Nav:** Inherited from `DevLayout` only
**Components Used:**
- MarkdownContent (renders parsed markdown)
- gray-matter (for frontmatter parsing)
**Layout Pattern:**
```
DevLayout (sticky header with global nav)
├── Main Content (container, mx-auto, px-4, py-8)
│ └── Max-w-4xl
│ └── MarkdownContent
│ └── Rendered markdown from file system
```
**Notable:**
- Uses `generateStaticParams()` for static generation
- Minimal structure - just content wrapper
- No breadcrumbs despite being nested route
---
## Structure Comparison Table
| Aspect | Hub | Components | Forms | Layouts | Spacing | Docs | Docs Dynamic |
|--------|-----|-----------|-------|---------|---------|------|------|
| `'use client'` | No | No | YES | No | No | No | No |
| Custom Header | No | No | YES | YES | YES | No | No |
| Sticky Header | N/A | N/A | YES | YES | YES | N/A | N/A |
| Back Button | No | No | YES | YES | YES | No | No |
| Page Title Display | Hero only | None | Header | Header | Header | Hero only | Frontmatter |
| Breadcrumbs | None | None | Implicit | Implicit | Implicit | None | None |
| Custom Footer | No | No | YES | YES | YES | No | No |
| Hero Section | YES | No | No | No | No | YES | No |
| Footer Documentation Link | No | No | YES | YES | YES | No | No |
| ExampleSection Usage | No | No | YES | YES | YES | No | No |
| BeforeAfter Usage | No | No | YES | YES | YES | No | No |
| Dynamic Imports | No | YES | No | No | YES | No | No |
| Content Sections | 3 | 1 | 6 | 7 | 7 | 3 | 1 |
---
## Identified Inconsistencies
### 1. Header Implementation (CRITICAL)
**Problem:** Forms, Layouts, and Spacing pages implement duplicate custom sticky headers instead of using `DevLayout`.
**Current Pattern (Forms/Layouts/Spacing):**
```typescript
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur">
<div className="container mx-auto flex h-16 items-center gap-4 px-4">
<Link href="/dev">
<Button variant="ghost" size="icon" aria-label="Back to hub">
<ArrowLeft className="h-5 w-5" />
</Button>
</Link>
<div>
<h1 className="text-xl font-bold">{title}</h1>
<p className="text-sm text-muted-foreground">{subtitle}</p>
</div>
</div>
</header>
```
**Problem:**
- Duplicated across 3 pages (DRY violation)
- DevLayout is ignored on these pages
- Breaking point: Sticky headers override global nav
**Expected:** Should use a shared pattern or component
---
### 2. Page Title/Breadcrumbs (CRITICAL)
**Problem:** No consistent pattern for breadcrumbs or page titles.
- Hub, Docs: Hero section titles
- Components: No title at all
- Forms, Layouts, Spacing: Header titles only
- Docs Dynamic: Frontmatter (if present)
**Missing:** True breadcrumb navigation for nested routes
---
### 3. Footer Implementation (INCONSISTENT)
**Problem:** Some pages have footers, others don't.
**Pages with footers:**
- Forms: `<footer className="mt-16 border-t py-6">`
- Layouts: `<footer className="mt-16 border-t py-6">`
- Spacing: `<footer className="mt-16 border-t py-6">`
**Pages without footers:**
- Hub: None
- Components: None
- Docs: None
- Docs Dynamic: None
**Issue:** Documentation link footers only on 3 pages creates inconsistent UX
---
### 4. Page Layout Wrapper (INCONSISTENT)
**Problem:** Inconsistent use of wrapper divs.
**Forms/Layouts/Spacing:**
```typescript
<div className="min-h-screen bg-background">
<header>...</header>
<main>...</main>
<footer>...</footer>
</div>
```
**Hub/Docs:**
```typescript
<div className="bg-background">
{/* Hero Section */}
<section>...</section>
<main>...</main>
</div>
```
**Components:**
No wrapper at all - just `<ComponentShowcase />`
---
### 5. Content Container Padding (MINOR)
**Problem:** Inconsistent container and padding patterns.
```
Hub/Docs: container mx-auto px-4 py-12
Forms/Layouts/Spacing: container mx-auto px-4 py-8
Docs Dynamic: container mx-auto px-4 py-8
```
**Expected:** Standardized to `py-8` or `py-12` across all
---
### 6. 'use client' Directive (INCONSISTENT)
**Problem:** Inconsistent use of client vs server components.
- Forms: `'use client'` (needs interactivity)
- Others: Server components (except those with dynamic imports)
- Spacing: Server component with dynamic client imports
**Question:** Should all pages be consistent?
---
### 7. Component Loading/Code Splitting (INCONSISTENT)
**Problem:** Inconsistent approach to component loading.
**Components Page:**
- Uses dynamic imports with loading state
**Spacing Page:**
- Uses dynamic imports with skeleton loading
**Forms/Layouts Pages:**
- No dynamic imports, inline all components
**Expected:** Consistent strategy across demo pages
---
### 8. ExampleSection Organization (INCONSISTENT)
**Problem:** Not all pages use `ExampleSection`.
- Hub: No ExampleSection (custom layout)
- Components: No ExampleSection (uses ComponentShowcase)
- Forms/Layouts/Spacing: All use ExampleSection
- Docs: No ExampleSection
- Docs Dynamic: No ExampleSection
---
## Common Patterns Found
### Pattern 1: Hero Section + Grid Layout (Hub, Docs)
```typescript
<section className="border-b bg-gradient-to-b from-background to-muted/20 py-12">
<div className="container mx-auto px-4">
{/* Content */}
</div>
</section>
<main className="container mx-auto px-4 py-12">
<div className="space-y-12">
{/* Multiple sections with grids */}
</div>
</main>
```
### Pattern 2: Sticky Header + Sections (Forms, Layouts, Spacing)
```typescript
<header className="sticky top-0 z-50...">
{/* Custom header with back button */}
</header>
<main className="container mx-auto px-4 py-8">
<div className="space-y-12">
<ExampleSection>{...}</ExampleSection>
</div>
</main>
<footer className="mt-16 border-t py-6">{...}</footer>
```
---
## Recommendations
### 1. CREATE SHARED HEADER COMPONENT (HIGH PRIORITY)
Create `/src/components/dev/DevPageHeader.tsx`:
```typescript
interface DevPageHeaderProps {
title: string;
subtitle?: string;
showBackButton?: boolean;
breadcrumbs?: Array<{ label: string; href: string }>;
}
export function DevPageHeader({ title, subtitle, showBackButton, breadcrumbs }: DevPageHeaderProps) {
// Replaces duplicated headers in forms, layouts, spacing pages
}
```
**Impact:** Reduces code duplication, standardizes header UX
---
### 2. ADD BREADCRUMBS COMPONENT (MEDIUM PRIORITY)
Create `/src/components/dev/DevBreadcrumbs.tsx`:
```typescript
interface Breadcrumb {
label: string;
href: string;
}
export function DevBreadcrumbs({ items }: { items: Breadcrumb[] }) {
// Renders: Home > Section > Current Page
}
```
**Usage:**
- Hub: None
- Components: `[{ label: 'Hub', href: '/dev' }, { label: 'Components' }]`
- Forms: `[{ label: 'Hub', href: '/dev' }, { label: 'Forms' }]`
- Layouts: `[{ label: 'Hub', href: '/dev' }, { label: 'Layouts' }]`
- Spacing: `[{ label: 'Hub', href: '/dev' }, { label: 'Spacing' }]`
- Docs: `[{ label: 'Hub', href: '/dev' }, { label: 'Docs' }]`
- Docs Dynamic: `[{ label: 'Docs', href: '/dev/docs' }, { label: currentTitle }]`
---
### 3. STANDARDIZE FOOTER (MEDIUM PRIORITY)
Create `/src/components/dev/DevPageFooter.tsx`:
```typescript
interface DevPageFooterProps {
documentationLink?: {
label: string;
href: string;
};
}
export function DevPageFooter({ documentationLink }: DevPageFooterProps) {
return (
<footer className="mt-16 border-t py-6">
{documentationLink && (
<div className="container mx-auto px-4 text-center">
<p className="text-sm text-muted-foreground">
Learn more:{' '}
<Link href={documentationLink.href} className="font-medium hover:text-foreground">
{documentationLink.label}
</Link>
</p>
</div>
)}
</footer>
);
}
```
**Add to ALL pages:** Hub, Components, Forms, Layouts, Spacing, Docs
---
### 4. CREATE DEVPAGE LAYOUT WRAPPER (HIGH PRIORITY)
Create `/src/components/dev/DevPageLayout.tsx`:
```typescript
interface DevPageLayoutProps {
title?: string;
subtitle?: string;
breadcrumbs?: Breadcrumb[];
showBackButton?: boolean;
footer?: { label: string; href: string };
children: React.ReactNode;
}
export function DevPageLayout({
title,
subtitle,
breadcrumbs,
showBackButton,
footer,
children,
}: DevPageLayoutProps) {
return (
<div className="min-h-screen bg-background">
{breadcrumbs && <DevBreadcrumbs items={breadcrumbs} />}
{title && (
<DevPageHeader
title={title}
subtitle={subtitle}
showBackButton={showBackButton}
breadcrumbs={breadcrumbs}
/>
)}
<main className="container mx-auto px-4 py-8">{children}</main>
{footer && <DevPageFooter documentationLink={footer} />}
</div>
);
}
```
---
### 5. STANDARDIZE COMPONENT LOADING (MEDIUM PRIORITY)
Use consistent pattern for code-splitting:
**Create loading skeleton component:**
```typescript
export function ExampleLoadingSkeleton() {
return <div className="animate-pulse h-64 bg-muted rounded-lg" />;
}
```
**Use in all heavy components:**
```typescript
const ComponentShowcase = dynamic(() => import('@/components/dev/ComponentShowcase'), {
loading: () => <ExampleLoadingSkeleton />,
});
```
---
### 6. UPDATE DevLayout TO NOT OVERRIDE (MEDIUM PRIORITY)
Current issue: Forms, Layouts, Spacing pages implement custom headers that hide global nav.
**Solution:** Update those pages to use DevLayout properly OR explicitly extend it:
```typescript
// Option A: Remove custom headers and use DevLayout's nav + page title
// Option B: Make DevLayout flexible to show page-specific titles
```
---
### 7. AUDIT Components PAGE (LOW PRIORITY)
The Components page is minimal - just a wrapper around ComponentShowcase.
**Consider:**
- Add breadcrumbs
- Add footer
- Add page title/subtitle
- Show 787-line warning or lazy load sections
---
### 8. ADD TABLE OF CONTENTS (LOW PRIORITY)
For longer pages (Layouts, Spacing), add sidebar with section navigation:
```typescript
const sections = [
{ id: 'spacing-scale', label: 'Spacing Scale' },
{ id: 'gap', label: 'Gap for Flex/Grid' },
// ...
];
<TableOfContents sections={sections} />
```
---
## Implementation Priority
**Phase 1 (Critical - Fixes inconsistencies):**
1. Create `DevPageHeader` component
2. Create `DevPageLayout` wrapper
3. Update Forms/Layouts/Spacing pages to use new components
**Phase 2 (Important - UX improvements):**
1. Create `DevBreadcrumbs` component
2. Create `DevPageFooter` component
3. Add breadcrumbs to all pages
4. Standardize footers
**Phase 3 (Nice to have - Polish):**
1. Add Table of Contents to long pages
2. Audit Components page structure
3. Standardize dynamic imports
4. Add loading states to all dynamic components
---
## Code Duplication Analysis
### Header Duplication (3 pages - 40 lines × 3)
```
/dev/forms/page.tsx: Lines 99-113
/dev/layouts/page.tsx: Lines 31-45
/dev/spacing/page.tsx: Lines 46-60
Total duplicated: ~40 lines × 3 = 120 lines of nearly identical code
```
### Footer Duplication (3 pages - 8 lines × 3)
```
/dev/forms/page.tsx: Lines 572-584
/dev/layouts/page.tsx: Lines 507-519
/dev/spacing/page.tsx: Lines 520-532
Total duplicated: ~8 lines × 3 = 24 lines of identical code
```
**Total Duplication:** 144 lines of code that could be extracted into components
---
## File Structure Refactor Proposal
```
frontend/src/components/dev/
├── DevLayout.tsx (current - global layout)
├── DevPageHeader.tsx (NEW - shared page header)
├── DevBreadcrumbs.tsx (NEW - breadcrumb navigation)
├── DevPageFooter.tsx (NEW - shared page footer)
├── DevPageLayout.tsx (NEW - complete page wrapper)
├── Example.tsx (current - example container)
├── ExampleSection.tsx (extracted from Example.tsx)
├── ExampleGrid.tsx (extracted from Example.tsx)
├── BeforeAfter.tsx (current)
├── CodeSnippet.tsx (current)
└── ComponentShowcase.tsx (current)
```
---
## Summary Table: What Each Page Should Have
| Element | Hub | Components | Forms | Layouts | Spacing | Docs | Docs Dyn |
|---------|-----|-----------|-------|---------|---------|------|----------|
| Breadcrumbs | ✗ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ |
| Page Title | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Hero Section | ✓ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ |
| Sticky Header | ✗ | ✗ | ✓ | ✓ | ✓ | ✗ | ✗ |
| Footer | ✗ | ✗ | ✓ | ✓ | ✓ | ✗ | ✗ |
| Back Button | ✗ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ |
| Content Sections | 3 | 1 | 6 | 7 | 7 | 3 | 1 |
**Proposed Target:**
| Element | Hub | Components | Forms | Layouts | Spacing | Docs | Docs Dyn |
|---------|-----|-----------|-------|---------|---------|------|----------|
| Breadcrumbs | ✗ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ |
| Page Title | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Hero Section | ✓ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ |
| Sticky Header | ✗ | ✗ | REMOVE | REMOVE | REMOVE | ✗ | ✗ |
| Footer | UPGRADE | UPGRADE | ✓ | ✓ | ✓ | UPGRADE | UPGRADE |
| Back Button | ✗ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ |

401
README_ANALYSIS.md Normal file
View File

@@ -0,0 +1,401 @@
# Dev Pages Analysis - Complete Documentation Index
## Overview
This analysis provides a thorough examination of all pages under `/frontend/src/app/dev/` and their imported components, identifying structural inconsistencies and providing a complete implementation roadmap.
**Analysis Date:** November 2, 2025
**Scope:** 7 pages, 6 components, 2,500+ lines of code
**Total Documentation:** 2,085 lines across 4 files
---
## Quick Start
### For Managers/Decision Makers
Read: **ANALYSIS_SUMMARY.md** (5-10 min read)
- Problem statement
- Key findings
- Implementation effort estimate
- Success criteria
### For Developers (Implementation)
Read in order:
1. **DEV_PAGES_QUICK_REFERENCE.md** (10 min) - Overview and visual diagrams
2. **COMPONENT_IMPLEMENTATION_GUIDE.md** (20 min) - Code to implement
3. **DEV_PAGES_STRUCTURE_ANALYSIS.md** (reference) - Detailed analysis
### For Code Reviewers
Read: **DEV_PAGES_STRUCTURE_ANALYSIS.md** (30 min)
- Detailed before/after comparison
- All inconsistencies documented
- Rationale for each recommendation
---
## Document Breakdown
### 1. ANALYSIS_SUMMARY.md (346 lines)
**Purpose:** Executive summary and quick reference
**Contains:**
- Problem statement
- Key findings (8 inconsistencies)
- Proposed solution overview
- Implementation plan (4 phases)
- Page comparison matrix
- Code metrics
- File changes summary
- Success criteria
**Best for:** Quick understanding of the issues and proposed solution
---
### 2. DEV_PAGES_STRUCTURE_ANALYSIS.md (758 lines)
**Purpose:** Complete technical analysis with detailed structure
**Contains:**
- Detailed analysis of all 7 pages:
- `/dev/page.tsx` (Design System Hub)
- `/dev/components/page.tsx` (Component Showcase)
- `/dev/forms/page.tsx` (Form Patterns)
- `/dev/layouts/page.tsx` (Layout Patterns)
- `/dev/spacing/page.tsx` (Spacing Patterns)
- `/dev/docs/page.tsx` (Documentation Hub)
- `/dev/docs/design-system/[...slug]/page.tsx` (Dynamic Docs)
- For each page:
- File path
- Structure breakdown
- Breadcrumbs/titles/footers
- Components used
- Layout pattern (ASCII diagram)
- Comparison tables showing:
- What each page has
- What's inconsistent
- What's missing
- 8 Identified inconsistencies with details:
1. Header Implementation (CRITICAL)
2. Page Title/Breadcrumbs (CRITICAL)
3. Footer Implementation (INCONSISTENT)
4. Page Layout Wrapper (INCONSISTENT)
5. Content Container Padding (MINOR)
6. 'use client' Directive (INCONSISTENT)
7. Component Loading/Code Splitting (INCONSISTENT)
8. ExampleSection Organization (INCONSISTENT)
- 8 Specific recommendations
- Code duplication analysis (144 lines identified)
- File structure refactor proposal
- Target state comparison table
**Best for:** Understanding the complete picture and rationale
---
### 3. DEV_PAGES_QUICK_REFERENCE.md (352 lines)
**Purpose:** Quick lookup guide with visual diagrams and matrices
**Contains:**
- Visual ASCII diagrams of page structures
- Page inventory
- Key findings summary
- Component architecture
- Page usage matrix (current vs target)
- Implementation roadmap (4 phases)
- Before/after code examples
- Testing checklist
- File modification guide
- Metrics summary
**Best for:** Quick visual reference during implementation
---
### 4. COMPONENT_IMPLEMENTATION_GUIDE.md (629 lines)
**Purpose:** Ready-to-implement code and step-by-step instructions
**Contains:**
- Complete code for 5 new components:
1. `DevPageHeader.tsx` - Shared page header
2. `DevBreadcrumbs.tsx` - Breadcrumb navigation
3. `DevPageFooter.tsx` - Shared footer
4. `DevPageLayout.tsx` - Complete wrapper
5. `ExampleLoadingSkeleton.tsx` - Loading placeholder
- For each component:
- Full TypeScript code
- JSDoc documentation
- Usage examples
- Type definitions
- Page refactoring examples:
- Forms page (before/after)
- Usage patterns
- Migration instructions
- Step-by-step implementation checklist
- Testing commands
- Usage examples for different page types
**Best for:** Actual implementation work
---
## Key Findings Summary
### Problems Identified
1. **144 lines of duplicated code**
- 120 lines: Duplicated headers (3 pages × 40 lines)
- 24 lines: Duplicated footers (3 pages × 8 lines)
2. **Inconsistent header implementations**
- 3 pages (Forms, Layouts, Spacing) implement custom sticky headers
- 4 pages (Hub, Components, Docs, Dynamic) use or don't use DevLayout
3. **No breadcrumb navigation**
- All 7 pages missing breadcrumbs
- Only Back buttons on detail pages (4 pages)
4. **Inconsistent footer usage**
- 3 pages have footers with doc links
- 4 pages have no footers
5. **8 structural inconsistencies** in total
### Proposed Solution
Create 4 reusable components to:
- Eliminate 144 lines of duplication
- Add breadcrumb navigation
- Standardize page structure
- Improve maintainability
**Estimated effort:** 4-8 hours
**Code savings:** 6 KB (after removing duplication)
**New bundle:** +2-3 KB (net savings: 3-4 KB)
---
## Implementation Timeline
### Phase 1: Extract Components (1-2 hours)
- Create 5 new reusable components
- Full TypeScript types
- Documentation
### Phase 2: Refactor Pages (2-3 hours)
- Update 7 pages to use new components
- Remove 144 lines of duplicated code
- Add breadcrumbs and footers
### Phase 3: Enhance (1-2 hours)
- Standardize dynamic imports
- Add consistent loading states
- Update all loading skeletons
### Phase 4: Polish (30-60 min)
- Test navigation flows
- Verify mobile responsiveness
- Check accessibility
- Run full test suite
**Total: 4-8 hours of implementation work**
---
## File Changes Required
### Create (5 files)
```
src/components/dev/
├── DevPageHeader.tsx (NEW)
├── DevBreadcrumbs.tsx (NEW)
├── DevPageFooter.tsx (NEW)
├── DevPageLayout.tsx (NEW)
└── ExampleLoadingSkeleton.tsx (NEW)
```
### Modify (7 files)
```
src/app/dev/
├── page.tsx (add footer)
├── components/page.tsx (add breadcrumbs/footer)
├── forms/page.tsx (refactor with DevPageLayout)
├── layouts/page.tsx (refactor with DevPageLayout)
├── spacing/page.tsx (refactor with DevPageLayout)
├── docs/page.tsx (add footer)
└── docs/design-system/[...slug]/page.tsx (add breadcrumbs/footer)
```
### Keep As-Is (6 files)
- Layout wrapper
- DevLayout.tsx
- Example components
- CodeSnippet
- ComponentShowcase
---
## How to Use This Analysis
### Step 1: Review
1. Read ANALYSIS_SUMMARY.md to understand the issues
2. Review DEV_PAGES_QUICK_REFERENCE.md for visual overview
3. Get buy-in from team
### Step 2: Plan
1. Prioritize based on your schedule
2. Allocate 4-8 hours for implementation
3. Assign team members
### Step 3: Implement
1. Follow COMPONENT_IMPLEMENTATION_GUIDE.md
2. Create new components (Phase 1)
3. Refactor pages (Phase 2)
4. Enhance and test (Phases 3-4)
### Step 4: Review
1. Run full test suite
2. Check mobile responsiveness
3. Verify accessibility
4. Commit with detailed message
---
## Success Criteria
After implementation, verify:
- [ ] 144 lines of duplicated code removed
- [ ] All pages have consistent header styling
- [ ] Breadcrumbs visible on all detail pages
- [ ] Page titles visible on all pages
- [ ] Footers with doc links on all pages
- [ ] Back buttons work on detail pages
- [ ] Global navigation accessible
- [ ] Mobile layout responsive
- [ ] No console errors
- [ ] Build passes without warnings
- [ ] E2E tests pass
- [ ] Code follows project conventions
---
## Project Context
This analysis is part of the **FastNext** project:
- Full-stack FastAPI + Next.js 15 application
- Comprehensive design system documentation
- Interactive demo pages under `/dev/`
- Production-ready code with high test coverage
### Related Documentation
- `/CLAUDE.md` - Project guidelines and conventions
- `/frontend/docs/design-system/` - Design system documentation
- `/frontend/src/components/` - All component definitions
---
## Questions & Answers
**Q: Why create new components instead of just fixing the existing code?**
A: DRY principle - 144 lines of identical code should be extracted to reusable components
**Q: Will this refactoring break anything?**
A: No. We're only extracting and reorganizing existing code, not changing functionality
**Q: Should I do all pages at once?**
A: Recommended approach: Create components first (Phase 1), then refactor all pages (Phase 2)
**Q: How do I prioritize if I have limited time?**
A: Phase 1 (extract components) is highest priority as it unblocks all page refactoring
**Q: What if I have concerns about the approach?**
A: See DEV_PAGES_STRUCTURE_ANALYSIS.md for detailed rationale behind each recommendation
---
## Navigation Guide
### If you want to...
**Understand the problem quickly**
→ Read: ANALYSIS_SUMMARY.md (page 1-2)
**See visual diagrams of page structures**
→ Read: DEV_PAGES_QUICK_REFERENCE.md (page 1-2)
**Understand why these changes matter**
→ Read: DEV_PAGES_STRUCTURE_ANALYSIS.md (page 1-3, 22-25)
**Get started implementing**
→ Read: COMPONENT_IMPLEMENTATION_GUIDE.md (page 1-2)
**See before/after code examples**
→ Read: DEV_PAGES_QUICK_REFERENCE.md (page 12) or COMPONENT_IMPLEMENTATION_GUIDE.md (page 14)
**Understand all the inconsistencies**
→ Read: DEV_PAGES_STRUCTURE_ANALYSIS.md (page 18-27)
**Get a step-by-step checklist**
→ Read: COMPONENT_IMPLEMENTATION_GUIDE.md (page 20-21)
**Verify success after implementation**
→ Read: ANALYSIS_SUMMARY.md (page 18) or DEV_PAGES_QUICK_REFERENCE.md (page 13)
---
## File Locations
All analysis documents are in the project root:
```
/workdrive/workspace/Projects/HomeLab/fast-next-template/
├── README_ANALYSIS.md (this file)
├── ANALYSIS_SUMMARY.md
├── DEV_PAGES_STRUCTURE_ANALYSIS.md
├── DEV_PAGES_QUICK_REFERENCE.md
├── COMPONENT_IMPLEMENTATION_GUIDE.md
└── frontend/
├── src/app/dev/ (pages to refactor)
├── src/components/dev/ (components to create)
└── docs/design-system/ (related documentation)
```
---
## Contact & Support
For questions about this analysis:
1. Review the relevant document linked above
2. Check the "Questions & Answers" section
3. Refer to CLAUDE.md for project conventions
For implementation issues:
1. Consult COMPONENT_IMPLEMENTATION_GUIDE.md
2. Check DEV_PAGES_QUICK_REFERENCE.md for quick answers
3. Review existing dev components for patterns
---
## Document Statistics
| Document | Lines | Size | Focus |
|----------|-------|------|-------|
| ANALYSIS_SUMMARY.md | 346 | 10K | Overview |
| DEV_PAGES_STRUCTURE_ANALYSIS.md | 758 | 22K | Deep dive |
| DEV_PAGES_QUICK_REFERENCE.md | 352 | 13K | Quick lookup |
| COMPONENT_IMPLEMENTATION_GUIDE.md | 629 | 15K | Implementation |
| **Total** | **2,085** | **60K** | Complete analysis |
---
Generated: November 2, 2025
Analysis Type: Very Thorough (Complete structure & component analysis)
Analyzed Pages: 7 (all /dev/ routes)
Analyzed Components: 6 (all dev components)

View File

@@ -5,7 +5,15 @@
*/
import type { Metadata } from 'next';
import { ComponentShowcase } from '@/components/dev/ComponentShowcase';
import dynamic from 'next/dynamic';
// Code-split heavy dev component (787 lines)
const ComponentShowcase = dynamic(
() => import('@/components/dev/ComponentShowcase').then((mod) => mod.ComponentShowcase),
{
loading: () => <div className="p-8 text-center text-muted-foreground">Loading components...</div>,
}
);
export const metadata: Metadata = {
title: 'Component Showcase | Dev',

View File

@@ -0,0 +1,66 @@
/**
* Dynamic Documentation Route
* Renders markdown files from docs/ directory
* Access: /dev/docs/design-system/01-foundations, etc.
*/
import { notFound } from 'next/navigation';
import { promises as fs } from 'fs';
import path from 'path';
import matter from 'gray-matter';
import { MarkdownContent } from '@/components/docs/MarkdownContent';
interface DocPageProps {
params: Promise<{ slug: string[] }>;
}
// Generate static params for all documentation files
export async function generateStaticParams() {
const docsDir = path.join(process.cwd(), 'docs', 'design-system');
try {
const files = await fs.readdir(docsDir);
const mdFiles = files.filter(file => file.endsWith('.md'));
return mdFiles.map(file => ({
slug: [file.replace(/\.md$/, '')],
}));
} catch {
return [];
}
}
// Get markdown file content
async function getDocContent(slug: string[]) {
const filePath = path.join(process.cwd(), 'docs', 'design-system', ...slug) + '.md';
try {
const fileContent = await fs.readFile(filePath, 'utf-8');
const { data, content } = matter(fileContent);
return {
frontmatter: data,
content,
filePath: slug.join('/'),
};
} catch {
return null;
}
}
export default async function DocPage({ params }: DocPageProps) {
const { slug } = await params;
const doc = await getDocContent(slug);
if (!doc) {
notFound();
}
return (
<div className="container mx-auto px-4 py-8">
<div className="mx-auto max-w-4xl">
<MarkdownContent content={doc.content} />
</div>
</div>
);
}

View File

@@ -1,7 +1,7 @@
/**
* Documentation Hub
* Central hub for all design system documentation
* Access: /docs
* Access: /dev/docs
*/
import Link from 'next/link';
@@ -22,14 +22,14 @@ const gettingStartedDocs: DocItem[] = [
{
title: 'Quick Start',
description: '5-minute crash course to get up and running with the design system',
href: '/docs/design-system/00-quick-start',
href: '/dev/docs/design-system/00-quick-start',
icon: <Sparkles className="h-5 w-5" />,
badge: 'Start Here',
},
{
title: 'README',
description: 'Complete overview and learning paths for the design system',
href: '/docs/design-system/README',
href: '/dev/docs/design-system/README',
icon: <BookOpen className="h-5 w-5" />,
},
];
@@ -38,43 +38,43 @@ const coreConceptsDocs: DocItem[] = [
{
title: 'Foundations',
description: 'Colors (OKLCH), typography, spacing, and shadows',
href: '/docs/design-system/01-foundations',
href: '/dev/docs/design-system/01-foundations',
icon: <Palette className="h-5 w-5" />,
},
{
title: 'Components',
description: 'shadcn/ui component library guide and usage patterns',
href: '/docs/design-system/02-components',
href: '/dev/docs/design-system/02-components',
icon: <Code2 className="h-5 w-5" />,
},
{
title: 'Layouts',
description: 'Layout patterns with Grid vs Flex decision trees',
href: '/docs/design-system/03-layouts',
href: '/dev/docs/design-system/03-layouts',
icon: <Layout className="h-5 w-5" />,
},
{
title: 'Spacing Philosophy',
description: 'Parent-controlled spacing strategy and best practices',
href: '/docs/design-system/04-spacing-philosophy',
href: '/dev/docs/design-system/04-spacing-philosophy',
icon: <FileCode className="h-5 w-5" />,
},
{
title: 'Component Creation',
description: 'When to create vs compose components',
href: '/docs/design-system/05-component-creation',
href: '/dev/docs/design-system/05-component-creation',
icon: <Code2 className="h-5 w-5" />,
},
{
title: 'Forms',
description: 'Form patterns with react-hook-form and Zod validation',
href: '/docs/design-system/06-forms',
href: '/dev/docs/design-system/06-forms',
icon: <FileCode className="h-5 w-5" />,
},
{
title: 'Accessibility',
description: 'WCAG AA compliance, keyboard navigation, and screen readers',
href: '/docs/design-system/07-accessibility',
href: '/dev/docs/design-system/07-accessibility',
icon: <Accessibility className="h-5 w-5" />,
},
];
@@ -83,39 +83,21 @@ const referencesDocs: DocItem[] = [
{
title: 'AI Guidelines',
description: 'Rules and best practices for AI code generation',
href: '/docs/design-system/08-ai-guidelines',
href: '/dev/docs/design-system/08-ai-guidelines',
icon: <Lightbulb className="h-5 w-5" />,
badge: 'AI',
},
{
title: 'Quick Reference',
description: 'Cheat sheet for quick lookups and common patterns',
href: '/docs/design-system/99-reference',
href: '/dev/docs/design-system/99-reference',
icon: <Search className="h-5 w-5" />,
},
];
export default function DocsHub() {
return (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container mx-auto flex h-16 items-center justify-between px-4">
<div className="flex items-center gap-3">
<BookOpen className="h-6 w-6 text-primary" />
<div>
<h1 className="text-lg font-semibold">FastNext Documentation</h1>
<p className="text-xs text-muted-foreground">Design System & Component Library</p>
</div>
</div>
<Link href="/dev">
<Button variant="outline" size="sm">
Interactive Demos
</Button>
</Link>
</div>
</header>
<div className="bg-background">
{/* Hero Section */}
<section className="border-b bg-gradient-to-b from-background to-muted/20 py-16">
<div className="container mx-auto px-4">
@@ -128,19 +110,19 @@ export default function DocsHub() {
accessible, and maintainable user interfaces with the FastNext design system.
</p>
<div className="flex flex-wrap gap-3 justify-center">
<Link href="/docs/design-system/00-quick-start">
<Link href="/dev/docs/design-system/00-quick-start">
<Button size="lg" className="gap-2">
<Sparkles className="h-4 w-4" />
Get Started
</Button>
</Link>
<Link href="/docs/design-system/README">
<Link href="/dev/docs/design-system/README">
<Button variant="outline" size="lg" className="gap-2">
<BookOpen className="h-4 w-4" />
Full Documentation
</Button>
</Link>
<Link href="/dev">
<Link href="/dev/components">
<Button variant="outline" size="lg" className="gap-2">
<Code2 className="h-4 w-4" />
View Examples
@@ -269,35 +251,6 @@ export default function DocsHub() {
</div>
</main>
{/* Footer */}
<footer className="mt-16 border-t py-8">
<div className="container mx-auto px-4">
<div className="mx-auto max-w-6xl">
<div className="flex flex-col gap-6 sm:flex-row sm:items-center sm:justify-between">
<div>
<p className="text-sm font-medium text-foreground mb-1">
FastNext Design System
</p>
<p className="text-xs text-muted-foreground">
Built with shadcn/ui, Tailwind CSS, and OKLCH colors
</p>
</div>
<div className="flex gap-3">
<Link href="/dev">
<Button variant="ghost" size="sm">
Interactive Demos
</Button>
</Link>
<Link href="/docs/design-system/00-quick-start">
<Button variant="ghost" size="sm">
Quick Start
</Button>
</Link>
</div>
</div>
</div>
</div>
</footer>
</div>
);
}

View File

@@ -574,7 +574,7 @@ const { register, handleSubmit, formState: { errors } } = useForm({
<p className="text-sm text-muted-foreground">
Learn more:{' '}
<Link
href="/docs/design-system/06-forms"
href="/dev/docs/design-system/06-forms"
className="font-medium hover:text-foreground"
>
Forms Documentation

View File

@@ -0,0 +1,10 @@
/**
* Dev Layout
* Shared layout for all development routes
*/
import { DevLayout } from '@/components/dev/DevLayout';
export default function Layout({ children }: { children: React.ReactNode }) {
return <DevLayout>{children}</DevLayout>;
}

View File

@@ -509,7 +509,7 @@ export default function LayoutsPage() {
<p className="text-sm text-muted-foreground">
Learn more:{' '}
<Link
href="/docs/design-system/03-layouts"
href="/dev/docs/design-system/03-layouts"
className="font-medium hover:text-foreground"
>
Layout Documentation

View File

@@ -70,45 +70,45 @@ const documentationLinks = [
{
title: 'Quick Start',
description: '5-minute crash course',
href: '/docs/design-system/00-quick-start',
href: '/dev/docs/design-system/00-quick-start',
},
{
title: 'Complete Documentation',
description: 'Full design system guide',
href: '/docs/design-system/README',
href: '/dev/docs',
},
{
title: 'AI Guidelines',
description: 'Rules for AI code generation',
href: '/docs/design-system/08-ai-guidelines',
href: '/dev/docs/design-system/08-ai-guidelines',
},
{
title: 'Quick Reference',
description: 'Cheat sheet for lookups',
href: '/docs/design-system/99-reference',
href: '/dev/docs/design-system/99-reference',
},
];
export default function DesignSystemHub() {
return (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container mx-auto px-4 py-8">
<div className="space-y-4">
<div className="bg-background">
{/* Hero Section */}
<section className="border-b bg-gradient-to-b from-background to-muted/20 py-12">
<div className="container mx-auto px-4">
<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>
</div>
<p className="text-lg text-muted-foreground max-w-2xl">
Interactive demonstrations, live examples, and copy-paste code for
<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.
</p>
</div>
</div>
</header>
</section>
{/* Main Content */}
<main className="container mx-auto px-4 py-12">
@@ -276,32 +276,6 @@ export default function DesignSystemHub() {
</section>
</div>
</main>
{/* Footer */}
<footer className="mt-16 border-t py-8">
<div className="container mx-auto px-4 text-center text-sm text-muted-foreground">
<p>
FastNext Design System Built with{' '}
<a
href="https://ui.shadcn.com"
target="_blank"
rel="noopener noreferrer"
className="font-medium hover:text-foreground"
>
shadcn/ui
</a>
{' + '}
<a
href="https://tailwindcss.com"
target="_blank"
rel="noopener noreferrer"
className="font-medium hover:text-foreground"
>
Tailwind CSS 4
</a>
</p>
</div>
</footer>
</div>
);
}

View File

@@ -5,6 +5,7 @@
*/
import type { Metadata } from 'next';
import dynamic from 'next/dynamic';
import Link from 'next/link';
import { ArrowLeft, Ruler } from 'lucide-react';
import { Button } from '@/components/ui/button';
@@ -16,8 +17,22 @@ import {
CardTitle,
} from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Example, ExampleSection } from '@/components/dev/Example';
import { BeforeAfter } from '@/components/dev/BeforeAfter';
// Code-split heavy dev components
const Example = dynamic(
() => import('@/components/dev/Example').then((mod) => ({ default: mod.Example })),
{ loading: () => <div className="animate-pulse h-32 bg-muted rounded" /> }
);
const ExampleSection = dynamic(
() => import('@/components/dev/Example').then((mod) => ({ default: mod.ExampleSection })),
{ loading: () => <div className="animate-pulse h-24 bg-muted rounded" /> }
);
const BeforeAfter = dynamic(
() => import('@/components/dev/BeforeAfter').then((mod) => ({ default: mod.BeforeAfter })),
{ loading: () => <div className="animate-pulse h-48 bg-muted rounded" /> }
);
export const metadata: Metadata = {
title: 'Spacing Patterns | Dev',
@@ -507,7 +522,7 @@ export default function SpacingPage() {
<p className="text-sm text-muted-foreground">
Learn more:{' '}
<Link
href="/docs/design-system/04-spacing-philosophy"
href="/dev/docs/design-system/04-spacing-philosophy"
className="font-medium hover:text-foreground"
>
Spacing Philosophy Documentation

View File

@@ -1,122 +0,0 @@
/**
* Dynamic Documentation Route
* Renders markdown files from docs/ directory
* Access: /docs/design-system/01-foundations, etc.
*/
import { notFound } from 'next/navigation';
import { promises as fs } from 'fs';
import path from 'path';
import matter from 'gray-matter';
import Link from 'next/link';
import { ArrowLeft, FileText } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Separator } from '@/components/ui/separator';
import { MarkdownContent } from '@/components/docs/MarkdownContent';
interface DocPageProps {
params: Promise<{ slug: string[] }>;
}
// Generate static params for all documentation files
export async function generateStaticParams() {
const docsDir = path.join(process.cwd(), 'docs', 'design-system');
try {
const files = await fs.readdir(docsDir);
const mdFiles = files.filter(file => file.endsWith('.md'));
return mdFiles.map(file => ({
slug: ['design-system', file.replace(/\.md$/, '')],
}));
} catch {
return [];
}
}
// Get markdown file content
async function getDocContent(slug: string[]) {
const filePath = path.join(process.cwd(), 'docs', ...slug) + '.md';
try {
const fileContent = await fs.readFile(filePath, 'utf-8');
const { data, content } = matter(fileContent);
return {
frontmatter: data,
content,
filePath: slug.join('/'),
};
} catch {
return null;
}
}
export default async function DocPage({ params }: DocPageProps) {
const { slug } = await params;
const doc = await getDocContent(slug);
if (!doc) {
notFound();
}
// Extract title from first heading or use filename
const title = doc.content.match(/^#\s+(.+)$/m)?.[1] || slug[slug.length - 1];
return (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container mx-auto flex h-16 items-center gap-4 px-4">
<Link href="/dev">
<Button variant="ghost" size="icon" aria-label="Back to design system">
<ArrowLeft className="h-5 w-5" />
</Button>
</Link>
<div className="flex items-center gap-2">
<FileText className="h-5 w-5 text-muted-foreground" />
<div>
<h1 className="text-lg font-semibold">{title}</h1>
<p className="text-xs text-muted-foreground">
{doc.filePath}
</p>
</div>
</div>
</div>
</header>
{/* Content */}
<main className="container mx-auto px-4 py-8">
<div className="mx-auto max-w-4xl">
<MarkdownContent content={doc.content} />
</div>
</main>
{/* Footer */}
<footer className="mt-16 border-t py-6">
<div className="container mx-auto px-4">
<div className="mx-auto max-w-4xl">
<Separator className="mb-6" />
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<p className="text-sm text-muted-foreground">
FastNext Design System Documentation
</p>
<div className="flex gap-2">
<Link href="/dev">
<Button variant="outline" size="sm">
View Interactive Demos
</Button>
</Link>
<Link href="/docs/design-system/README">
<Button variant="outline" size="sm">
Documentation Home
</Button>
</Link>
</div>
</div>
</div>
</div>
</footer>
</div>
);
}

View File

@@ -4,6 +4,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { useState } from 'react';
import { ThemeProvider } from '@/components/theme';
import { AuthInitializer } from '@/components/auth';
export function Providers({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(
@@ -26,7 +27,7 @@ export function Providers({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider>
<QueryClientProvider client={queryClient}>
{/* AuthInitializer removed - Zustand persist middleware handles auto-hydration */}
<AuthInitializer />
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>

View File

@@ -63,16 +63,11 @@ function LoadingSpinner() {
export function AuthGuard({ children, requireAdmin = false, fallback }: AuthGuardProps) {
const router = useRouter();
const pathname = usePathname();
const { isAuthenticated, isLoading: authLoading, user, _hasHydrated } = useAuthStore();
const { isAuthenticated, isLoading: authLoading, user } = useAuthStore();
// Fetch user data if authenticated but user not loaded
const { isLoading: userLoading } = useMe();
// Wait for store to hydrate from localStorage to prevent hook order issues
if (!_hasHydrated) {
return fallback ? <>{fallback}</> : <LoadingSpinner />;
}
// Determine overall loading state
const isLoading = authLoading || (isAuthenticated && !user && userLoading);

View File

@@ -1,5 +1,8 @@
// Authentication components
// Auth initialization
export { AuthInitializer } from './AuthInitializer';
// Route protection
export { AuthGuard } from './AuthGuard';

View File

@@ -9,11 +9,10 @@
'use client';
import { useState } from 'react';
import Link from 'next/link';
import {
Moon, Sun, Mail, User,
Mail, User,
Settings, LogOut, Shield, AlertCircle, Info,
Trash2, ArrowLeft
Trash2
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
@@ -73,41 +72,10 @@ import { Example, ExampleGrid, ExampleSection } from './Example';
* Component showcase
*/
export function ComponentShowcase() {
const [isDark, setIsDark] = useState(false);
const [checked, setChecked] = useState(false);
const toggleTheme = () => {
setIsDark(!isDark);
document.documentElement.classList.toggle('dark');
};
return (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container mx-auto flex h-16 items-center justify-between px-4">
<div className="flex items-center gap-4">
<Link href="/dev">
<Button variant="ghost" size="icon" aria-label="Back to hub">
<ArrowLeft className="h-5 w-5" />
</Button>
</Link>
<div>
<h1 className="text-xl font-bold">Component Showcase</h1>
<p className="text-sm text-muted-foreground">All shadcn/ui components with code</p>
</div>
</div>
<Button
variant="outline"
size="icon"
onClick={toggleTheme}
aria-label="Toggle theme"
>
{isDark ? <Sun className="h-5 w-5" /> : <Moon className="h-5 w-5" />}
</Button>
</div>
</header>
<div className="bg-background">
{/* Content */}
<main className="container mx-auto px-4 py-8">
<div className="space-y-12">

View File

@@ -0,0 +1,119 @@
/* istanbul ignore file */
/**
* DevLayout Component
* Shared layout for all /dev routes with navigation and theme toggle
* This file is excluded from coverage as it's a development tool
*/
'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { Code2, Palette, LayoutDashboard, Box, FileText, BookOpen, Home } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { ThemeToggle } from '@/components/theme';
import { cn } from '@/lib/utils';
interface DevLayoutProps {
children: React.ReactNode;
}
const navItems = [
{
title: 'Hub',
href: '/dev',
icon: Home,
exact: true,
},
{
title: 'Components',
href: '/dev/components',
icon: Box,
},
{
title: 'Forms',
href: '/dev/forms',
icon: FileText,
},
{
title: 'Layouts',
href: '/dev/layouts',
icon: LayoutDashboard,
},
{
title: 'Spacing',
href: '/dev/spacing',
icon: Palette,
},
{
title: 'Docs',
href: '/dev/docs',
icon: BookOpen,
},
];
export function DevLayout({ children }: DevLayoutProps) {
const pathname = usePathname();
const isActive = (href: string, exact?: boolean) => {
if (exact) {
return pathname === href;
}
return pathname.startsWith(href);
};
return (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container mx-auto px-4">
{/* Single Row: Logo + Badge + Navigation + Theme Toggle */}
<div className="flex h-14 items-center justify-between gap-6">
{/* Left: Logo + Badge */}
<div className="flex items-center gap-3 shrink-0">
<Code2 className="h-5 w-5 text-primary" />
<h1 className="text-base font-semibold">FastNext</h1>
<Badge variant="secondary" className="text-xs">
Dev
</Badge>
</div>
{/* Center: Navigation */}
<nav className="flex gap-1 overflow-x-auto flex-1">
{navItems.map((item) => {
const Icon = item.icon;
const active = isActive(item.href, item.exact);
return (
<Link key={item.href} href={item.href}>
<Button
variant={active ? 'default' : 'ghost'}
size="sm"
className={cn(
'gap-2 whitespace-nowrap',
!active && 'text-muted-foreground hover:text-foreground'
)}
>
<Icon className="h-4 w-4" />
{item.title}
</Button>
</Link>
);
})}
</nav>
{/* Right: Theme Toggle */}
<div className="shrink-0">
<ThemeToggle />
</div>
</div>
</div>
</header>
{/* Main Content */}
<main>{children}</main>
</div>
);
}

View File

@@ -1,10 +1,9 @@
/**
* Authentication Store - Zustand with secure token storage
* Implements proper state management with validation and automatic persistence
* Implements proper state management with validation
*/
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import { saveTokens, getTokens, clearTokens } from '@/lib/auth/storage';
/**
@@ -31,7 +30,6 @@ interface AuthState {
isAuthenticated: boolean;
isLoading: boolean;
tokenExpiresAt: number | null; // Unix timestamp
_hasHydrated: boolean; // Internal flag for persist middleware
// Actions
setAuth: (user: User, accessToken: string, refreshToken: string, expiresIn?: number) => Promise<void>;
@@ -40,7 +38,6 @@ interface AuthState {
clearAuth: () => Promise<void>;
loadAuthFromStorage: () => Promise<void>;
isTokenExpired: () => boolean;
setHasHydrated: (hasHydrated: boolean) => void; // Internal method for persist
}
/**
@@ -71,63 +68,14 @@ function calculateExpiry(expiresIn?: number): number {
return Date.now() + seconds * 1000;
}
/**
* Custom storage adapter for Zustand persist
* Uses our encrypted token storage functions
*/
const authStorage = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getItem: async (_name: string): Promise<string | null> => {
try {
const tokens = await getTokens();
if (!tokens) return null;
// Return the tokens as a JSON string that persist middleware expects
return JSON.stringify({
state: {
accessToken: tokens.accessToken,
refreshToken: tokens.refreshToken,
isAuthenticated: !!(tokens.accessToken && tokens.refreshToken),
},
});
} catch (error) {
console.error('Failed to load auth from storage:', error);
return null;
}
},
setItem: async (_name: string, value: string): Promise<void> => {
try {
const parsed = JSON.parse(value);
const { accessToken, refreshToken } = parsed.state;
if (accessToken && refreshToken) {
await saveTokens({ accessToken, refreshToken });
}
} catch (error) {
console.error('Failed to save auth to storage:', error);
}
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
removeItem: async (_name: string): Promise<void> => {
try {
await clearTokens();
} catch (error) {
console.error('Failed to clear auth from storage:', error);
}
},
};
export const useAuthStore = create<AuthState>()(
persist(
(set, get) => ({
// Initial state
user: null,
accessToken: null,
refreshToken: null,
isAuthenticated: false,
isLoading: false, // No longer needed - persist handles hydration
tokenExpiresAt: null,
_hasHydrated: false,
export const useAuthStore = create<AuthState>((set, get) => ({
// Initial state
user: null,
accessToken: null,
refreshToken: null,
isAuthenticated: false,
isLoading: true, // Start as loading to check stored tokens
tokenExpiresAt: null,
// Set complete auth state (user + tokens)
setAuth: async (user, accessToken, refreshToken, expiresIn) => {
@@ -210,58 +158,50 @@ export const useAuthStore = create<AuthState>()(
});
},
/**
* @deprecated No longer needed with persist middleware
* The persist middleware automatically hydrates tokens on store initialization
* Kept for backward compatibility but does nothing
*/
loadAuthFromStorage: async () => {
// No-op: persist middleware handles this automatically
console.warn('loadAuthFromStorage() is deprecated and no longer necessary');
},
// Load auth from storage on app start
loadAuthFromStorage: async () => {
try {
const tokens = await getTokens();
// Check if current token is expired
isTokenExpired: () => {
const { tokenExpiresAt } = get();
if (!tokenExpiresAt) return true;
return Date.now() >= tokenExpiresAt;
},
// Internal method for persist middleware
setHasHydrated: (hasHydrated) => {
set({ _hasHydrated: hasHydrated });
},
}),
{
name: 'auth_store', // Storage key
storage: createJSONStorage(() => authStorage),
partialize: (state) => ({
// Only persist tokens and auth status, not user or computed values
accessToken: state.accessToken,
refreshToken: state.refreshToken,
isAuthenticated: state.isAuthenticated,
}),
onRehydrateStorage: () => {
return (state, error) => {
if (error) {
console.error('Failed to rehydrate auth store:', error);
}
// Mark store as hydrated to prevent rendering issues
if (state) {
state.setHasHydrated(true);
}
};
},
if (tokens?.accessToken && tokens?.refreshToken) {
// Validate token format
if (isValidToken(tokens.accessToken) && isValidToken(tokens.refreshToken)) {
set({
accessToken: tokens.accessToken,
refreshToken: tokens.refreshToken,
isAuthenticated: true,
isLoading: false,
// User will be loaded separately via API call
});
return;
}
}
} catch (error) {
console.error('Failed to load auth from storage:', error);
}
)
);
// No valid tokens found
set({ isLoading: false });
},
// Check if current token is expired
isTokenExpired: () => {
const { tokenExpiresAt } = get();
if (!tokenExpiresAt) return true;
return Date.now() >= tokenExpiresAt;
},
}));
/**
* @deprecated No longer needed with persist middleware
* The persist middleware automatically hydrates the store on initialization
* Kept for backward compatibility but does nothing
* Initialize auth store from storage
* Call this on app startup
* Errors are logged but don't throw to prevent app crashes
*/
export async function initializeAuth(): Promise<void> {
// No-op: persist middleware handles initialization automatically
console.warn('initializeAuth() is deprecated and no longer necessary');
try {
await useAuthStore.getState().loadAuthFromStorage();
} catch (error) {
// Log error but don't throw - app should continue even if auth init fails
console.error('Failed to initialize auth:', error);
}
}

View File

@@ -0,0 +1,22 @@
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Block access to /dev routes in production
if (pathname.startsWith('/dev')) {
const isProduction = process.env.NODE_ENV === 'production';
if (isProduction) {
// Return 404 in production
return new NextResponse(null, { status: 404 });
}
}
return NextResponse.next();
}
export const config = {
matcher: '/dev/:path*',
};

View File

@@ -23,12 +23,10 @@ let mockAuthState: {
isAuthenticated: boolean;
isLoading: boolean;
user: any;
_hasHydrated: boolean;
} = {
isAuthenticated: false,
isLoading: false,
user: null,
_hasHydrated: true, // In tests, assume store is always hydrated
};
jest.mock('@/lib/stores/authStore', () => ({
@@ -71,7 +69,6 @@ describe('AuthGuard', () => {
isAuthenticated: false,
isLoading: false,
user: null,
_hasHydrated: true, // In tests, assume store is always hydrated
};
mockMeState = {
isLoading: false,
@@ -85,7 +82,6 @@ describe('AuthGuard', () => {
isAuthenticated: false,
isLoading: true,
user: null,
_hasHydrated: true,
};
render(
@@ -104,7 +100,6 @@ describe('AuthGuard', () => {
isAuthenticated: true,
isLoading: false,
user: null,
_hasHydrated: true,
};
mockMeState = {
isLoading: true,
@@ -126,7 +121,6 @@ describe('AuthGuard', () => {
isAuthenticated: false,
isLoading: true,
user: null,
_hasHydrated: true,
};
render(
@@ -148,7 +142,6 @@ describe('AuthGuard', () => {
isAuthenticated: false,
isLoading: false,
user: null,
_hasHydrated: true,
};
render(
@@ -179,7 +172,6 @@ describe('AuthGuard', () => {
created_at: '2024-01-01',
updated_at: '2024-01-01',
},
_hasHydrated: true,
};
render(
@@ -205,11 +197,10 @@ describe('AuthGuard', () => {
first_name: 'Admin',
last_name: 'User',
is_active: true,
is_superuser: true, // Admin user must have is_superuser: true
is_superuser: true,
created_at: '2024-01-01',
updated_at: '2024-01-01',
},
_hasHydrated: true,
};
render(
@@ -228,7 +219,7 @@ describe('AuthGuard', () => {
isAuthenticated: true,
isLoading: false,
user: {
id: '1',
id: '1',
email: 'user@example.com',
first_name: 'Regular',
last_name: 'User',
@@ -237,7 +228,6 @@ describe('AuthGuard', () => {
created_at: '2024-01-01',
updated_at: '2024-01-01',
},
_hasHydrated: true,
};
render(
@@ -259,7 +249,7 @@ describe('AuthGuard', () => {
isAuthenticated: true,
isLoading: false,
user: {
id: '1',
id: '1',
email: 'user@example.com',
first_name: 'Regular',
last_name: 'User',
@@ -268,7 +258,6 @@ describe('AuthGuard', () => {
created_at: '2024-01-01',
updated_at: '2024-01-01',
},
_hasHydrated: true,
};
render(
@@ -289,7 +278,6 @@ describe('AuthGuard', () => {
isAuthenticated: false,
isLoading: false,
user: null,
_hasHydrated: true,
};
render(
@@ -313,7 +301,6 @@ describe('AuthGuard', () => {
isAuthenticated: true,
isLoading: false,
user: null,
_hasHydrated: true,
};
mockMeState = {
isLoading: true,
@@ -335,7 +322,7 @@ describe('AuthGuard', () => {
isAuthenticated: true,
isLoading: false,
user: {
id: '1',
id: '1',
email: 'user@example.com',
first_name: 'Test',
last_name: 'User',
@@ -344,7 +331,6 @@ describe('AuthGuard', () => {
created_at: '2024-01-01',
updated_at: '2024-01-01',
},
_hasHydrated: true,
};
mockMeState = {
isLoading: false,