# Structure

_Last updated: 2026-05-14_

Directory layout, conventions, and where to look for things in the `xtablo-source` monorepo.

## Top-Level Layout

```
xtablo-source/
├── apps/              # Deployable applications
│   ├── main/          # Authenticated dashboard SPA (Vite + CF Workers)
│   ├── external/      # Public booking widget (Vite)
│   ├── clients/       # Client portal (Vite + CF Workers)
│   ├── admin/         # Internal admin tools (Vite + CF Workers)
│   ├── api/           # Hono REST API (Node, GCP Cloud Run)
│   └── chat-worker/   # Cloudflare Durable Object worker for chat
├── packages/          # Shared workspace packages (source-only)
│   ├── shared/        # Contexts, hooks, API client, toast, supabase client
│   ├── ui/            # Radix + Tailwind component library
│   ├── shared-types/  # Pure TypeScript types (zero runtime deps)
│   ├── auth-ui/       # Auth screens (login/register shells)
│   ├── chat-ui/       # Chat-specific UI components
│   └── tablo-views/   # Tablo detail / sections components
├── backend/           # Legacy / auxiliary backend code
├── go-backend/        # Go services (separate stack)
├── frontend_v2/       # Frontend v2 prototype
├── xtablo-expo/       # React Native (Expo) app
├── supabase/          # Supabase project (migrations, config)
├── infra/             # Infrastructure-as-code
├── docs/              # Architecture, deployment, integration docs
├── prompts/           # Prompt templates
├── scripts/           # Repo scripts
├── CLAUDE.md          # Claude Code guidance (root)
├── DEVELOPMENT.md     # Dev guide
├── turbo.json         # Turborepo pipeline config
├── pnpm-workspace.yaml
└── package.json
```

## Apps

### `apps/main` (`@xtablo/main`)

Primary dashboard. Internal layout:

```
apps/main/src/
├── main.tsx                 # Vite entry
├── App.tsx                  # Root component, providers
├── i18n.ts                  # i18next setup
├── main.css                 # Tailwind entry
├── components/              # UI components (Modals, Sections, Cards, ...)
├── contexts/                # App-local React contexts (UpgradeBlockContext, ...)
├── hooks/                   # Feature hooks (tablos, events, tasks, stripe, ...)
├── lib/
│   ├── api.ts               # API client wrapper
│   ├── supabase.ts          # Supabase client re-export
│   ├── routes.tsx           # Protected routes
│   ├── publicRoutes.tsx     # Public/auth routes
│   ├── billing.ts           # Stripe-related helpers
│   ├── env.ts               # Env validation
│   └── rum.ts               # Datadog RUM init
├── pages/                   # Route page components
├── providers/
│   └── UserStoreProvider.tsx # Zustand user store
├── locales/                 # i18n JSON
├── utils/
└── assets/
```

### `apps/external` (`@xtablo/external`)

Embeddable booking widget. Internal layout:

```
apps/external/src/
├── main.tsx
├── routes.tsx
├── EmbeddedBookingPage.tsx
├── FloatingBookingWidget.tsx
├── CustomModal.tsx
├── UserStoreProvider.tsx
├── lib/
└── locales/
```

Mode is driven by query string: `?mode=embed&eventTypeId=...` or `?mode=floating`.

### `apps/clients` (`@xtablo/clients`)

Public-facing client portal.

```
apps/clients/src/
├── main.tsx
├── App.tsx
├── routes.tsx
├── components/
├── hooks/
├── lib/
├── pages/
├── locales/
└── test/
```

### `apps/admin` (`@xtablo/admin`)

Internal admin app.

```
apps/admin/src/
├── main.tsx
├── App.tsx
├── routes.tsx
├── components/
├── hooks/
├── lib/
├── pages/
└── registry/
```

### `apps/api` (`@xtablo/api`)

Hono REST API. Internal layout:

```
apps/api/src/
├── index.ts                # Entry: tracer, secrets, server start
├── config.ts               # createConfig(secrets) -> AppConfig
├── secrets.ts              # loadSecrets() (env or GCP Secret Manager)
├── client.ts               # Supabase admin client factory
├── middlewares/
│   ├── middleware.ts       # MiddlewareManager singleton + supabase/r2/stripe
│   ├── stripeSync.ts       # Stripe<->Supabase sync engine
│   └── transporter.ts      # Email transport middleware
├── routers/
│   ├── index.ts            # getMainRouter — composition + ordering
│   ├── public.ts           # Unauthenticated endpoints
│   ├── authRouter.ts       # Requires JWT
│   ├── maybeAuthRouter.ts  # Optional auth (booking-aware)
│   ├── tablo.ts            # Tablo CRUD
│   ├── tablo_data.ts       # Tablo content blocks
│   ├── tasks.ts            # Tasks
│   ├── notes.ts            # Notes
│   ├── events.ts (in user.ts/tablo_data.ts)
│   ├── stripe.ts           # Stripe webhook + ops
│   ├── revenuecat.ts       # RevenueCat webhook
│   ├── invite.ts           # Tablo invites
│   ├── user.ts             # User profile / settings
│   ├── admin*.ts           # admin.ts, adminActions.ts, adminAuth.ts,
│   │                       # adminDatasets.ts, adminOverview.ts, adminTables.ts
│   ├── clientAuth.ts       # Client portal magic-link auth
│   ├── clientPortal.ts     # Client portal endpoints
│   └── clientInvites.ts    # Public client invites
├── helpers/
├── types/                  # BaseEnv, app.types.ts
└── __tests__/
```

### `apps/chat-worker`

```
apps/chat-worker/src/
├── index.ts                # Worker entry
├── durable-objects/        # Durable Object classes
└── lib/
```

## Packages

### `packages/shared` (`@xtablo/shared`)

Public surface via `packages/shared/src/index.ts`.

```
packages/shared/src/
├── index.ts                # Barrel
├── contexts/
│   ├── SessionContext.tsx  # Supabase session listener
│   └── ThemeContext.tsx
├── hooks/
│   ├── auth.ts             # useSignIn / useSignOut / useSession ...
│   ├── book.ts             # Booking hooks
│   ├── public.ts           # Public data hooks
│   └── useClickOutside.ts
├── lib/
│   ├── api.ts              # HTTP client w/ Bearer token
│   ├── supabase.ts         # Supabase JS client
│   ├── toast.ts            # toast.add() wrapper (sonner)
│   └── cn.ts               # clsx + tailwind-merge
├── types/                  # Re-exported domain types
└── utils/
    └── helpers.ts
```

### `packages/ui` (`@xtablo/ui`)

Radix UI + Tailwind component library. Source-only.

```
packages/ui/src/
├── components/             # button.tsx, dialog.tsx, select.tsx,
│                           # popover.tsx, tabs.tsx, dropdown-menu.tsx, ...
├── hooks/
└── styles/
```

### `packages/shared-types` (`@xtablo/shared-types`)

Zero-dependency TypeScript types — safe to import from API and frontend.

```
packages/shared-types/src/
├── index.ts
├── database.types.ts       # Auto-generated by supabase gen types
├── tablos.types.ts
├── tablo-data.types.ts
├── events.types.ts
├── kanban.types.ts
├── stripe.types.ts
├── admin.types.ts
└── utils.ts
```

### `packages/auth-ui` (`@xtablo/auth-ui`)

Auth screens shared between apps: `AuthCardShell.tsx`, `AuthEmailPasswordForm.tsx`, `AuthInfoBanner.tsx`.

### `packages/chat-ui` (`@xtablo/chat-ui`)

Chat-specific UI (`components/`, `hooks.ts`, `security.ts`, `types.ts`, `chat-ui.css`).

### `packages/tablo-views` (`@xtablo/tablo-views`)

Tablo detail sections and shell: `TabloDetailsShell.tsx`, `TabloEventsSection.tsx`, `TabloFilesSection.tsx`, `TabloTasksSection.tsx`, `TabloDiscussionSection.tsx`, `TabloHeaderActions.tsx`, `EtapesSection.tsx`, `RoadmapSection.tsx`, plus `single-tablo/`, `components/`, `hooks/`, `styles/`.

## Naming Conventions

- **Modals**: `*Modal.tsx` — e.g. `apps/main/src/components/CreateTabloModal.tsx`, `EventDetailsModal.tsx`.
- **Sections**: `*Section.tsx` — e.g. `packages/tablo-views/src/TabloFilesSection.tsx`.
- **Cards**: `*Card.tsx` — e.g. `apps/main/src/components/EventTypeCard.tsx`, `AvailabilityCard.tsx`.
- **Pages**: live in `apps/<app>/src/pages/`, lowercased file names matching the route (`planning.tsx`, `events.tsx`, `login.tsx`).
- **Tests**: co-located as `*.test.tsx` / `*.test.ts` next to the source (`AvailabilityCard.test.tsx`, `auth.signup.test.ts`).
- **Hooks**: lowercase domain file in `hooks/` (`tablos.ts`, `events.ts`, `tasks.ts`); each exports multiple named hooks.
- **UI primitives**: lowercase in `packages/ui/src/components/` (`button.tsx`, `select.tsx`).

## Key File Locations (Cheatsheet)

| Concern                       | Path                                                    |
|-------------------------------|---------------------------------------------------------|
| Main app routes (protected)   | `apps/main/src/lib/routes.tsx`                          |
| Main app public routes        | `apps/main/src/lib/publicRoutes.tsx`                    |
| External app routes           | `apps/external/src/routes.tsx`                          |
| Clients app routes            | `apps/clients/src/routes.tsx`                           |
| Admin app routes              | `apps/admin/src/routes.tsx`                             |
| Session context               | `packages/shared/src/contexts/SessionContext.tsx`       |
| User store (Zustand)          | `apps/main/src/providers/UserStoreProvider.tsx`         |
| HTTP API client               | `packages/shared/src/lib/api.ts`                        |
| Supabase client (browser)     | `packages/shared/src/lib/supabase.ts`                   |
| API entry                     | `apps/api/src/index.ts`                                 |
| API router composition        | `apps/api/src/routers/index.ts`                         |
| API middleware singleton      | `apps/api/src/middlewares/middleware.ts`                |
| API config                    | `apps/api/src/config.ts`                                |
| API secrets loader            | `apps/api/src/secrets.ts`                               |
| Auto-generated DB types       | `packages/shared-types/src/database.types.ts`           |
| Shared barrel export          | `packages/shared/src/index.ts`                          |
| UI component barrel           | `packages/ui/src/components/index.ts`                   |
| Turborepo pipeline            | `turbo.json`                                            |
| Workspace definition          | `pnpm-workspace.yaml`                                   |
| Root Claude guidance          | `CLAUDE.md`                                             |

## Adding New Features (5-Step Flow)

From `CLAUDE.md`, the canonical workflow for a new feature is:

1. **Define types** in `packages/shared-types/src/` (or run `npx supabase gen types typescript > packages/shared-types/src/database.types.ts` if you changed the DB schema).
2. **Add API endpoint** in `apps/api/src/routers/` — pick the right router (`public.ts`, `maybeAuthRouter.ts`, `authRouter.ts`, or a domain router like `tasks.ts`). Mount it in `apps/api/src/routers/index.ts` if it's new.
3. **Create a React Query hook** in either `packages/shared/src/hooks/` (cross-app) or `apps/main/src/hooks/` (app-local). Use the hierarchical query-key convention so mutations can invalidate predictably.
4. **Build the UI** using primitives from `@xtablo/ui` and patterns from `@xtablo/tablo-views` / `@xtablo/chat-ui` where applicable. Follow the `*Modal.tsx` / `*Section.tsx` / `*Card.tsx` naming convention and co-locate a `*.test.tsx`.
5. **Wire the route** in `apps/main/src/lib/routes.tsx` (or the equivalent in the relevant app) so the new page is reachable.

## Testing Locations

- **API tests**: `apps/api/src/__tests__/` (Vitest, mock Supabase).
- **Frontend tests**: co-located `*.test.tsx` next to source, run via `pnpm test` per app (`apps/main/src/setupTests.ts` configures happy-dom + Testing Library).
- **Docs on testing**: `docs/API_TESTS.md`, `docs/MIDDLEWARE_TESTS.md`.

## Documentation Index (in `docs/`)

- `DEVELOPMENT.md` — broad dev guide.
- `API_*.md` — API testing and integration.
- `STRIPE_*.md` — Stripe integration deep-dives.
- `AUTH_*.md` — Authentication patterns.
- `DOCKER_*.md` — Docker build optimization.
- `CLOUD_BUILD_*.md` — GCP Cloud Build setup.
