Skip to main content

Frontend App Structure

Quality Hub GINEZ uses Next.js 14 App Router for a modern, file-system based routing approach with React Server and Client Components.

Directory Structure

QualityHub/
├── app/                          # Next.js App Router
│   ├── (auth)/                   # Route group: Authentication
│   │   └── login/
│   │       └── page.tsx          # Login page
│   ├── bitacora/                 # Production log module
│   │   └── page.tsx
│   ├── calidad/                  # Quality control module
│   │   ├── page.tsx
│   │   ├── ncr/
│   │   │   └── [id]/
│   │   │       └── page.tsx      # NCR detail page
│   │   └── components/
│   │       └── NCRManager.tsx
│   ├── catalog/                  # Product catalog module
│   │   ├── page.tsx              # Catalog home
│   │   ├── raw-materials/
│   │   │   ├── page.tsx
│   │   │   └── [code]/
│   │   │       └── page.tsx      # Material detail
│   │   └── finished-products/
│   │       ├── page.tsx
│   │       └── [family]/
│   │           ├── page.tsx
│   │           ├── FamilyDetailView.tsx
│   │           └── [category]/
│   │               ├── page.tsx
│   │               ├── CategoryDetailView.tsx
│   │               └── [sku]/
│   │                   └── page.tsx  # Product detail
│   ├── reportes/                 # Reports & analytics module
│   │   ├── page.tsx
│   │   └── spy/
│   │       └── page.tsx          # Debug page
│   ├── configuracion/            # Settings module
│   │   ├── page.tsx              # User profile
│   │   └── usuarios/
│   │       └── page.tsx          # User management (admin)
│   ├── layout.tsx                # Root layout
│   └── page.tsx                  # Dashboard (home)
├── components/                   # Reusable components
│   ├── ui/                       # shadcn/ui components
│   │   ├── button.tsx
│   │   ├── card.tsx
│   │   ├── dialog.tsx
│   │   ├── input.tsx
│   │   ├── select.tsx
│   │   ├── table.tsx
│   │   ├── tabs.tsx
│   │   └── ... (16 UI components)
│   ├── AppShell.tsx              # Application shell/layout
│   ├── AuthProvider.tsx          # Authentication context
│   ├── Breadcrumbs.tsx           # Breadcrumb navigation
│   ├── DataTable.tsx             # Data table component
│   ├── DashboardBanner.tsx       # Banner carousel
│   ├── Filters.tsx               # Filter component
│   ├── ModuleCard.tsx            # Dashboard module cards
│   ├── NotificationBell.tsx      # Notification icon
│   ├── SearchInput.tsx           # Search component
│   └── ... (more components)
├── lib/                          # Utilities and logic
│   ├── analysis-utils.ts         # Conformity analysis
│   ├── production-constants.ts   # Product standards
│   ├── supabase.ts               # Supabase client
│   ├── utils.ts                  # General utilities
│   ├── validations.ts            # Zod schemas
│   ├── types.ts                  # TypeScript types
│   ├── usePermissions.ts         # Permissions hook
│   ├── sanitize.ts               # Input sanitization
│   ├── rate-limit.ts             # Rate limiting
│   ├── logger.ts                 # Logging utility
│   └── audit.ts                  # Audit logging
├── public/                       # Static assets
│   ├── logo.png
│   ├── logo-small.png
│   └── capturas_proyecto_quality_hub/
├── .env.local                    # Environment variables (gitignored)
├── next.config.js                # Next.js configuration
├── tailwind.config.ts            # Tailwind configuration
├── tsconfig.json                 # TypeScript configuration
└── package.json                  # Dependencies

Next.js App Router

File-Based Routing

The App Router uses file conventions for routing:
  • page.tsx - Defines a route’s UI
  • layout.tsx - Shared UI for a segment and its children
  • loading.tsx - Loading UI for a segment
  • error.tsx - Error UI for a segment
  • not-found.tsx - 404 UI

Route Groups

Parentheses create route groups without affecting URL:
app/
  (auth)/          # Group for authentication routes
    login/
      page.tsx     # URL: /login

Dynamic Routes

Brackets create dynamic segments:
app/
  catalog/
    finished-products/
      [family]/          # Dynamic: product family
        [category]/      # Dynamic: product category
          [sku]/         # Dynamic: product SKU
            page.tsx     # URL: /catalog/finished-products/laundry/detergent/COLGIN

Root Layout

The root layout wraps all pages:
// app/layout.tsx
import { AuthProvider } from '@/components/AuthProvider'
import { AppShell } from '@/components/AppShell'
import { ThemeProvider } from '@/components/theme-provider'
import { Toaster } from 'sonner'
import './globals.css'

export default function RootLayout({ children }) {
  return (
    <html lang="es" suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        >
          <AuthProvider>
            <AppShell>
              {children}
            </AppShell>
            <Toaster position="top-right" richColors />
          </AuthProvider>
        </ThemeProvider>
      </body>
    </html>
  )
}

Application Shell

The AppShell component provides the main application structure:
// components/AppShell.tsx
export function AppShell({ children }: { children: React.ReactNode }) {
  const pathname = usePathname()
  const { user, profile, loading, signOut } = useAuth()
  const { setTheme, theme } = useTheme()
  const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false)
  
  // Skip shell for login page
  if (pathname === '/login') {
    return <div className="min-h-screen">{children}</div>
  }
  
  return (
    <div className="flex min-h-screen">
      {/* Desktop Sidebar */}
      <aside className={cn(
        "hidden md:flex flex-col",
        isSidebarCollapsed ? "w-20" : "w-64"
      )}>
        {/* Logo */}
        {/* Navigation */}
        {/* Theme toggle */}
      </aside>
      
      {/* Mobile Header */}
      <div className="md:hidden fixed top-0">
        {/* Mobile menu */}
      </div>
      
      {/* Main Content */}
      <div className="flex-1">
        <header className="hidden md:flex">
          {/* User profile */}
        </header>
        <main className="p-6 md:p-10">
          {loading ? <LoadingState /> : children}
        </main>
      </div>
    </div>
  )
}

Page Structure Pattern

Typical page component structure:
"use client"  // Client Component for interactivity

import { useState, useEffect } from 'react'
import { useAuth } from '@/components/AuthProvider'
import { supabase } from '@/lib/supabase'
import { Button } from '@/components/ui/button'

export default function PageName() {
  // 1. Hooks
  const { user, profile } = useAuth()
  const [data, setData] = useState([])
  const [loading, setLoading] = useState(true)
  
  // 2. Data Fetching
  useEffect(() => {
    fetchData()
  }, [])
  
  const fetchData = async () => {
    const { data, error } = await supabase
      .from('table_name')
      .select('*')
    
    if (data) setData(data)
    setLoading(false)
  }
  
  // 3. Event Handlers
  const handleAction = async () => {
    // Handle user action
  }
  
  // 4. Render
  if (loading) return <LoadingSpinner />
  
  return (
    <div className="space-y-6">
      <h1 className="text-3xl font-bold">Page Title</h1>
      {/* Content */}
    </div>
  )
}
Navigation is defined in AppShell.tsx:
const navigation = [
  {
    section: "General",
    items: [
      { href: "/", icon: LayoutDashboard, label: "Panel Principal" }
    ]
  },
  {
    section: "Producción",
    items: [
      { href: "/bitacora", icon: ClipboardList, label: "Bitácora" },
      { href: "/calidad", icon: Microscope, label: "Control Calidad" },
      { href: "/reportes", icon: BarChart3, label: "Reportes" }
    ]
  },
  {
    section: "Soporte",
    items: [
      { href: "/catalog", icon: BookOpen, label: "Catálogo" },
      { href: "/configuracion", icon: Settings, label: "Configuración" }
    ]
  }
]

Responsive Design

The app uses a mobile-first approach:

Breakpoints

/* Tailwind breakpoints */
sm: 640px   /* Small devices */
md: 768px   /* Tablets */
lg: 1024px  /* Laptops */
xl: 1280px  /* Desktops */
2xl: 1536px /* Large screens */

Mobile Adaptations

  • Collapsible sidebar on desktop
  • Hamburger menu on mobile
  • Responsive tables (horizontal scroll)
  • Touch-friendly buttons and inputs
  • Mobile-optimized forms

Client vs Server Components

Client Components ("use client")

Required for:
  • State management (useState, useEffect)
  • Event handlers (onClick, onChange)
  • Browser APIs (localStorage, window)
  • Context consumers (useAuth, useTheme)
  • Third-party libraries with browser dependencies
Most pages in Quality Hub are Client Components because they require interactivity.

Server Components (default)

Used for:
  • Static content
  • SEO-critical pages
  • Data fetching without client state
Limited use in Quality Hub due to interactive nature of the app.

Code Splitting

Next.js automatically code-splits by route:
// Automatic code splitting
import DashboardPage from './app/page'           // Chunk: page
import BitacoraPage from './app/bitacora/page'   // Chunk: bitacora-page
import CalidadPage from './app/calidad/page'     // Chunk: calidad-page

Dynamic Imports

For heavy components:
import dynamic from 'next/dynamic'

const HeavyChart = dynamic(
  () => import('@/components/HeavyChart'),
  { loading: () => <Skeleton /> }
)

Performance Optimizations

Image Optimization

import Image from 'next/image'

<Image
  src="/logo.png"
  alt="Logo"
  width={200}
  height={50}
  priority  // For above-the-fold images
/>

Font Optimization

import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

export default function RootLayout({ children }) {
  return (
    <html className={inter.className}>
      {children}
    </html>
  )
}

Memoization

const expensiveCalculation = useMemo(() => {
  return data.map(item => analyzeConformity(item))
}, [data])

const handleCallback = useCallback(() => {
  doSomething()
}, [dependency])

Error Handling

Error Boundaries

// app/error.tsx
'use client'

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <div className="flex flex-col items-center justify-center min-h-screen">
      <h2 className="text-2xl font-bold mb-4">Algo salió mal</h2>
      <button onClick={reset}>Intentar de nuevo</button>
    </div>
  )
}

404 Handling

// app/not-found.tsx
import Link from 'next/link'

export default function NotFound() {
  return (
    <div className="text-center">
      <h2>Página no encontrada</h2>
      <Link href="/">Volver al inicio</Link>
    </div>
  )
}

Styling Architecture

See Components for detailed component styling patterns.

Next Steps

Build docs developers (and LLMs) love