Frontend

The dashboard is a Next.js App Router application. Server components render the shell; client components own interactivity and all server state.

Rendering model

Layer Role

Server components

Layouts, page shells, static structure. The default — no "use client"

Client components

Anything with state, effects or event handlers. Marked "use client"

React Query

All server state. Client components fetch through hooks in lib/hooks/

Pages never query Prisma directly. Everything goes through the application’s own HTTP API, so the dashboard and any external consumer see the same contract — and a schema change breaks loudly in one place rather than silently in two.

WorkspaceGate

The dashboard layout wraps its children in WorkspaceGate: nothing renders until GET /api/me has resolved and the user has at least one workspace with an active selection. A loading screen is shown until then.

Without the gate, components would render against an unresolved workspace and issue queries that either fail or return another tenant’s default. The gate makes "the workspace is known" an invariant for every component below it.

The sidebar workspace switcher posts to POST /api/subscriptions/active and then invalidates the React Query cache, so a switch re-fetches everything rather than mixing two workspaces' data.

Component organisation

Directory Contents

components/ui/

shadcn/ui primitives — generated, never edited directly

components/activity/

Activity feed

components/ai-agents/

Agent editor, version history, analysis views

components/auth/

Login and signup forms

components/calls/

Call tables and filters

components/campaign/

Campaign detail tabs, targets, calls, the batch re-analyse dialog

components/contacts/

Contact list and detail

components/email-campaign/

Email campaign detail, recipients, variations

components/email-domains/

Domains and sending accounts

components/emails/

Inbox

components/follow-up-tasks/

Task board

components/reports/

Charts

components/sales-reps/

Rep management

components/sellers/

Seller list

components/suppliers/

Payability supplier list and status badges

components/sequences/

Sequence builder and enrollment views

components/targets/

Target registries

components/test-cases/

Test case management

components/mdx/

MDX renderers for the in-app docs site

components/ui/ is off-limits because it is regenerated by the shadcn CLI. Extend by composition — wrap the primitive in a feature component rather than editing it.

Standard client component

"use client"

interface Props { campaignId: string }

export function CampaignTargetsTable({ campaignId }: Props) {
  // 1. Local state
  const [page, setPage] = useState(1)
  const [pageSize, setPageSize] = useState(25)
  const [filters, setFilters] = useState<FilterState>({})

  // 2. Server state
  const { data, isLoading } = useCampaignTargets(campaignId, { page, pageSize, ...filters })

  // 3. Handlers
  // 4. JSX
}

Order is consistent across the codebase: local state, server state, handlers, JSX.

Naming

Kind Convention

Files

kebab-case — campaign-targets-table.tsx

Components

PascalCase — CampaignTargetsTable

Hooks

use* — useCampaigns, useCampaignTargets

Types

PascalCase, derived via z.infer<>

Import order

  1. React / Next.js

  2. Third-party libraries

  3. Internal components (@/components/…​)

  4. Internal utilities (@/lib/…​)

  5. Types

  6. Relative imports

Forms

React Hook Form with zodResolver, always. A dialog form resets and closes on mutation success; errors surface through form state or a sonner toast.

The Zod schema that validates the form is the same one that types the API payload, so a client-side validation rule cannot drift from the server’s.

Key libraries

Library Use

@tanstack/react-query

All server state

react-hook-form + @hookform/resolvers

Forms

recharts

Report charts

lexical + @lexical/*

Rich-text email editor

mermaid

In-app diagrams

xlsx

Spreadsheet import and export

sonner

Toasts

date-fns

Date formatting

lucide-react

Icons

next-themes

Light/dark switching

react-diff-viewer-continued

Prompt version diffs

json-edit-react

JSON payload editing

html-to-image

Chart export

react-resizable-panels, vaul, cmdk, embla-carousel-react

Layout and interaction primitives