Skip to main content

Overview

VizBoard follows a modular, feature-based directory structure that separates concerns and promotes code reusability.

Root Directory

vizboard/
├── src/                    # Application source code
├── prisma/                 # Database schema and migrations
├── public/                 # Static assets
├── scripts/                # Database seeding scripts
├── docker-compose.yml      # Local database setup
├── package.json            # Dependencies and scripts
├── tsconfig.json           # TypeScript configuration
├── tailwind.config.ts      # Tailwind CSS configuration
├── next.config.js          # Next.js configuration
└── .env                    # Environment variables

Source Directory (src/)

src/
├── app/                    # Next.js App Router
├── components/             # React components
├── lib/                    # Utilities and helpers
├── hooks/                  # Custom React hooks
├── store/                  # Zustand state stores
├── types/                  # TypeScript type definitions
├── contexts/               # React contexts
└── pages/                  # Special Next.js pages

App Directory (src/app/)

Route Structure

The app/ directory uses Next.js 15 App Router with file-based routing:
app/(auth)/
├── layout.tsx              # Auth layout (centered forms)
├── login/
│   └── page.tsx           # Login page
└── signup/
    └── page.tsx           # Signup page
Features:
  • Route group (auth) excludes path from URL
  • Shared layout for login/signup
  • Server-side form validation
  • Redirect after successful auth
app/(homepage)/
├── layout.tsx              # Public layout
└── page.tsx               # Landing page
Features:
  • Public-facing homepage
  • Marketing content
  • Call-to-action for signup
app/projects/
├── page.tsx                          # Projects list (Server Component)
├── ProjectsClientPage.tsx            # Client-side interactions
└── [projectId]/                      # Dynamic project routes
    └── dashboard/
        ├── page.tsx                  # Dashboard (Server Component)
        ├── pageClient.tsx            # Dashboard client logic
        ├── DashboardPageWrapper.tsx  # Layout wrapper
        └── not-found.tsx            # 404 handler
Features:
  • Dynamic routes with [projectId]
  • Server Components for data fetching
  • Client Components for interactivity
  • Nested layouts
app/public/
└── [idPublic]/
    ├── page.tsx           # Public dashboard view
    └── not-found.tsx      # Invalid share link
Features:
  • Share dashboards with public URL
  • No authentication required
  • Read-only view
app/settings/
└── page.tsx               # User settings
Features:
  • Profile management
  • Password updates
  • Account preferences

Server Actions

Server Actions are organized by domain:

Auth Actions

app/actions/auth/
└── signUp.ts
  • User registration
  • Password hashing
  • Input validation

Project Actions

app/actions/project/
├── crud.ts               # CRUD operations
├── connections.ts        # Connection management
├── database.ts          # Schema introspection
├── validation.ts        # Connection validation
├── revalidation.ts      # Cache invalidation
└── index.ts             # Exports
  • Create/update/delete projects
  • Manage connections
  • Validate credentials
  • Introspect schemas

Dashboard Actions

app/actions/dashboard/
├── crudWidgets.ts       # Widget CRUD
├── deleteWidget.ts      # Delete handler
├── getTableData.ts      # Data fetching
├── updateWidgetOrder.ts # Reordering
├── upsertWidget.ts      # Create/update
└── index.ts             # Exports
  • Widget operations
  • Data queries
  • Order management

User Actions

app/actions/user/
└── user.ts
  • Profile updates
  • Password changes
  • Settings

API Routes

API routes for client-side data fetching:
app/api/
├── auth/
│   └── [...nextauth]/
│       └── route.ts                 # NextAuth.js handler
├── projects/
│   └── [projectId]/
│       ├── dashboard/
│       │   └── route.ts            # GET dashboard data
│       ├── public/
│       │   └── route.ts            # GET public dashboard
│       └── widgets/
│           └── order/
│               └── route.ts        # PATCH widget order
├── widgets/
│   └── [widgetId]/
│       └── route.ts                # GET/PATCH/DELETE widget
├── userprojects/
│   └── route.ts                    # GET user's projects
├── listconnections/
│   └── route.ts                    # GET project connections
└── compatible-tables/
    └── route.ts                    # GET compatible table schemas
Route Handlers:
// GET /api/projects/[projectId]/dashboard
export async function GET(request: Request, { params })

// PATCH /api/widgets/[widgetId]
export async function PATCH(request: Request, { params })

// DELETE /api/widgets/[widgetId]
export async function DELETE(request: Request, { params })

Root Layout

import { Inter } from "next/font/google"
import { ThemeProvider } from "@/components/theme-provider"
import { Toaster } from "@/components/ui/sonner"
import { SessionProviderContext } from "@/components/providers/session-provider"

export default function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body className={inter.className}>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
        >
          <LanguageProvider>
            <SessionProviderContext>
              {children}
              <Toaster />
            </SessionProviderContext>
          </LanguageProvider>
        </ThemeProvider>
      </body>
    </html>
  )
}
Providers:
  • Theme (light/dark mode)
  • Session (authentication)
  • Language (i18n)
  • Toast notifications

Components Directory (src/components/)

Component Organization

Auth Components

components/auth/
├── cardWrapper.tsx
├── login-form.tsx
├── signup-form.tsx
└── oAuths.tsx
  • Form wrappers
  • OAuth buttons
  • Input fields

Project Components

components/projects/
├── cards/              # Project cards
├── dialogs/            # Create/edit dialogs
├── forms/              # Project forms
└── lists/              # Project lists
  • Project cards
  • Creation forms
  • Connection forms
  • Project lists

Dashboard Components

components/dashboard/
├── layouts/
│   ├── dashboardLayout.tsx
│   └── PublicDashboardLayout.tsx
├── navigation/
│   ├── appSidebar.tsx
│   └── addWidget.tsx
└── widgets/
    ├── charts/
    ├── datatable/
    ├── integratedDatatable/
    ├── textblocks/
    └── shared/
  • Dashboard layouts
  • Sidebar navigation
  • Widget components

UI Components

components/ui/
├── button.tsx
├── dialog.tsx
├── input.tsx
├── select.tsx
├── table.tsx
├── card.tsx
├── tabs.tsx
└── ... (40+ components)
  • Radix UI wrappers
  • Styled with Tailwind
  • Reusable primitives

Widget Components

components/dashboard/widgets/charts/
├── barChart.tsx           # Bar chart widget
├── lineChart.tsx          # Line chart widget
├── areaChart.tsx          # Area chart widget
├── chartConfig.tsx        # Configuration dialog
└── utils/
    ├── aggregation.ts     # Data aggregation
    ├── grouping.ts        # Data grouping
    └── formatting.ts      # Value formatting
Features:
  • Recharts integration
  • Data aggregation (sum, avg)
  • Grouping by columns
  • Responsive sizing
components/dashboard/widgets/datatable/
├── dataTable.tsx          # Simple table widget
├── columns.tsx            # Column definitions
├── dataTableConfig.tsx    # Configuration dialog
└── filters/
    ├── searchFilter.tsx   # Search functionality
    ├── columnFilter.tsx   # Column filtering
    └── pagination.tsx     # Pagination controls
Features:
  • TanStack Table integration
  • Sorting (multi-column)
  • Filtering
  • Pagination
  • Column visibility
components/dashboard/widgets/integratedDatatable/
├── integratedTable.tsx           # Multi-connection table
├── integratedTableConfig.tsx     # Configuration
└── utils/
    └── dataFetching.ts          # Parallel data fetching
Features:
  • Compare data from multiple connections
  • Join data client-side
  • Unified column mapping

Library Directory (src/lib/)

Authentication

lib/auth/
├── auth.ts               # NextAuth.js configuration
└── userSchema.ts         # Zod validation schemas
auth.ts
export const { handlers, signIn, signOut, auth } = NextAuth({
  adapter: PrismaAdapter(prisma),
  session: { strategy: "jwt" },
  providers: [Google, GitHub, Credentials],
  callbacks: { ... },
})

Database

lib/db/
└── prisma.ts             # Prisma client singleton
prisma.ts
import { PrismaClient } from '@prisma/client'

const globalForPrisma = global as unknown as { prisma: PrismaClient }

const prisma = globalForPrisma.prisma || new PrismaClient()

if (process.env.NODE_ENV !== 'production') {
  globalForPrisma.prisma = prisma
}

export default prisma
Purpose:
  • Singleton pattern
  • Prevents multiple instances in development
  • Connection pooling

Encryption

lib/crypto/
└── crypto.ts             # AES-256-GCM encryption
crypto.ts
export function encrypt(text: string): string
export function decrypt(encryptedData: string): string
Used for:
  • Database connection credentials
  • Sensitive configuration
  • AES-256-GCM with authentication tags

Project Utilities

lib/projects/
├── schemas/              # Zod validation schemas
│   ├── index.ts
│   ├── project.ts
│   └── connection.ts
└── queries/              # Reusable database queries
Schemas:
  • Project creation/update validation
  • Connection validation
  • Type inference

Adapters

lib/adapters/
└── prisma.ts             # Custom Prisma adapter extensions

Other Utilities

lib/
├── widget.ts            # Widget type helpers
├── extractWidgetTypeInfo.ts
└── revalidate.ts        # Cache revalidation

Hooks Directory (src/hooks/)

Custom React Hooks

Project Hooks

hooks/projects/
├── useProjectsWithStore.ts
├── useProjectConnections.ts
└── index.ts
  • Fetch projects with SWR
  • Manage connections
  • Cache management

Widget Hooks

hooks/
├── useWidget.ts
├── useTableDataWithSchema.ts
└── useIntegratedTableData.ts
  • Widget data fetching
  • Table data with caching
  • Multi-connection queries

UI Hooks

hooks/
├── use-mobile.ts
└── useOnlineStatus.ts
  • Mobile detection
  • Network status
  • Responsive behavior
import useSWR from 'swr'
import { useProjectsStore } from '@/store/projects'

export function useProjectsWithStore() {
  const { data, error, isLoading, mutate } = useSWR(
    '/api/userprojects',
    fetcher
  )
  
  const { setProjects } = useProjectsStore()
  
  useEffect(() => {
    if (data?.projects) {
      setProjects(data.projects)
    }
  }, [data])
  
  return {
    projects: data?.projects || [],
    isLoading,
    error,
    refresh: mutate,
  }
}

Store Directory (src/store/)

Zustand State Stores

Dashboard Stores

store/dashboard/
├── widgetsStore.ts
├── widgetDialogStore.ts
└── index.ts
  • Widget list state
  • Dialog open/close
  • Editing widget

Project Stores

store/projects/
├── projectsStore.ts
├── projectDraftStore.ts
└── index.ts
  • Projects list
  • Draft persistence
  • Form state
import { create } from 'zustand'
import type { Widget } from '@/types/widget'

interface WidgetDialogStore {
  isOpen: boolean
  widgetType: string | null
  editingWidget: Widget | null
  openDialog: (type: string) => void
  openEditDialog: (widget: Widget) => void
  closeDialog: () => void
}

export const useWidgetDialog = create<WidgetDialogStore>((set) => ({
  isOpen: false,
  widgetType: null,
  editingWidget: null,
  
  openDialog: (type) => set({
    isOpen: true,
    widgetType: type,
    editingWidget: null,
  }),
  
  openEditDialog: (widget) => set({
    isOpen: true,
    widgetType: widget.type,
    editingWidget: widget,
  }),
  
  closeDialog: () => set({
    isOpen: false,
    widgetType: null,
    editingWidget: null,
  }),
}))

Types Directory (src/types/)

TypeScript Definitions

types/
├── auth.d.ts             # NextAuth.js type extensions
├── widget.ts             # Widget type definitions
├── database.ts           # Database schema types
└── api.ts                # API response types
auth.d.ts
import 'next-auth'

declare module 'next-auth' {
  interface User {
    id: string
    firstName?: string
    lastName?: string
    password?: string | null
  }
  
  interface Session {
    user: {
      id: string
      email?: string
      name?: string
      firstName?: string
      lastName?: string
    }
  }
}

declare module 'next-auth/jwt' {
  interface JWT {
    id?: string
    firstName?: string
    lastName?: string
  }
}

Prisma Directory

Database Schema & Migrations

prisma/
├── schema.prisma         # Database schema definition
├── migrations/           # Migration history
│   ├── 20240101_init/
│   ├── 20240105_add_widgets/
│   └── migration_lock.toml
└── seed.ts              # Database seeding script
schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id            String    @id @default(uuid())
  firstName     String?
  lastName      String?
  email         String    @unique
  password      String?
  projects      Project[]
  accounts      Account[]
  @@map("users")
}

model Project {
  id                  String         @id @default(uuid())
  userId              String
  title               String
  description         String?
  isPublic            Boolean        @default(false)
  idPublic            String?        @unique
  dbconnections       DbConnection[]
  widgets             Widget[]
  orderedWidgetIds    String[]
  user                User           @relation(...)
  @@map("projects")
}

model DbConnection {
  id                  String    @id @default(uuid())
  projectId           String
  title               String
  dbAccess            Json      # Encrypted credentials
  dbSchema            Json?     # Cached schema
  isValid             Boolean?
  validationError     String?
  lastIntrospectionAt DateTime?
  project             Project   @relation(...)
  @@map("dbconnections")
}

model Widget {
  id          String   @id @default(uuid())
  projectId   String
  title       String
  description String?
  type        String   # bar, line, area, datatable, etc.
  subtype     String?  # Simple, integrated, etc.
  configs     Json?    # Widget-specific config
  project     Project  @relation(...)
  @@map("widgets")
}
Key Features:
  • UUID primary keys
  • Cascade deletes
  • JSON for flexible configs
  • Encrypted credentials
  • Schema caching

Scripts Directory

Database Seeding Scripts

scripts/
├── seed_store.js         # Seed store data (test DB 1 & 2)
├── seed_clients.js       # Seed clients (test DB 3)
├── seed_commandes.js     # Seed orders (test DB 3)
└── seed_apirequestevent.js # Seed API events (test DB 3)
These scripts populate external PostgreSQL test databases with sample data for widget testing:
# Seed all test databases
npm run seed:test-dbs

# Or individual databases
npm run seed:store       # Products, users, sales, reviews
npm run seed:clients     # Client records
npm run seed:commandes   # Order records
Generated Data:
  • 100-1000 realistic records
  • Uses Faker.js for names, dates, etc.
  • Relational data (foreign keys)
  • Time-series data for charts

Configuration Files

Next.js Configuration

module.exports = {
  reactStrictMode: true,
  experimental: {
    serverActions: true,
  },
  images: {
    domains: ['lh3.googleusercontent.com', 'avatars.githubusercontent.com'],
  },
}

Import Aliases

VizBoard uses TypeScript path aliases for cleaner imports:
// Instead of:
import { Button } from '../../../components/ui/button'

// Use:
import { Button } from '@/components/ui/button'
Configuration:
tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

File Naming Conventions

React Components

PascalCase
  • Button.tsx
  • DashboardLayout.tsx
  • ProjectCard.tsx

Utilities & Hooks

camelCase
  • utils.ts
  • useProjects.ts
  • swrConfig.ts

Server Actions

camelCase
  • crud.ts
  • signUp.ts
  • getTableData.ts

Route Files

Next.js Convention
  • page.tsx (route)
  • layout.tsx (layout)
  • route.ts (API route)
  • not-found.tsx (404)

Code Organization Principles

  1. Server Components - Data fetching and rendering
  2. Client Components - Interactivity and state
  3. Server Actions - Data mutations
  4. API Routes - Client-side data fetching
  5. Hooks - Reusable logic
  6. Stores - Global state
  7. Components - Reusable UI
projects/
├── components/projects/   # Project-specific UI
├── actions/project/       # Project mutations
├── hooks/projects/        # Project data hooks
└── store/projects/        # Project state
Benefits:
  • Easy to find related code
  • Clear dependencies
  • Better modularity
  • Scalable structure
Keep related files close together:
widgets/
├── charts/
│   ├── barChart.tsx       # Component
│   ├── chartConfig.tsx    # Config dialog
│   └── utils/             # Chart-specific utils
│       ├── aggregation.ts
│       └── formatting.ts

Best Practices

Key Guidelines:
  1. Server Components by default - Only add "use client" when needed
  2. Colocation - Keep related files together
  3. Type safety - Use TypeScript everywhere
  4. Validation - Zod schemas for all inputs
  5. Error handling - Try-catch in Server Actions
  6. Revalidation - Invalidate cache after mutations
  7. Security - Encrypt sensitive data
  8. Performance - Optimize database queries

Growing the Codebase

When adding new features:
  1. New widget type? → Add to components/dashboard/widgets/
  2. New data operation? → Add Server Action to app/actions/
  3. New route? → Add to app/ with appropriate route group
  4. Reusable logic? → Create custom hook in hooks/
  5. Global state? → Add Zustand store in store/
  6. New utility? → Add to lib/
  7. Database change? → Update Prisma schema and migrate
The structure is designed to scale from small teams to large applications while maintaining clarity and organization.

Build docs developers (and LLMs) love