# 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//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.