296 lines
12 KiB
Markdown
296 lines
12 KiB
Markdown
# 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.
|