Docs.

Admin portal — roadmap (built / scaffolded / deferred)

business/roadmap.md

Built & working

  • Auth (Google OAuth for staff; magic-link/passkey for clients + DB office roles + dev bypass), office shell, dashboard.
  • Inventory (list + filters + detail + audit trail), fully editable — item create/edit, in-place asset editing, CSV export, kits.
  • Reservations: list (status + when filters), month calendar, create (client auto-link, pricing, deposit), reservation detail with the full lifecycle state machine, manual payments, claims.
  • Clients list. Finances overview. Subscribers + CSV.
  • Settings: editable (pricing classes, deposit, FX, IVA, floor/round)
    • an append-only revision history (/office/settings/history).
  • Spaces (/office/spaces): list, create, edit rentable studio spaces. reservation_items.space_id lets a line target a space instead of gear.
  • Blackouts (/office/reservations/blackouts): create/remove per-unit gear + space blackout windows.
  • Short links (/office/links): qr.studio.chat code list, create, per-code + scan stats (short_links + short_link_scans, migration 0002).
  • Contracts (/office/contracts): markdown templates with merge fields, live preview, per-reservation print/PDF, searchable executed archive.
  • Library (/office/library): the whole docs tree readable in-office.
  • Data console (/office/data): archive / restore / purge (soft-delete via deleted_at, migration 0004) for reservations, clients, and short links.

Scaffolded (schema exists, UI deferred)

  • condition_reports — table done; no pickup/return checklist UI or e-signature. The lifecycle records timestamps but not signed inspections yet.
  • Client detail pageBuilt. /office/accounts/[id] has the profile (editable), verifications (create/approve/reject/archive), reservation history, identity, and an activity trail.

Deferred (clear next milestones, in rough priority)

  1. Phase-1 inventory cutoverDone — and most of it had been for a while. Migration 0005 dropped inventory_canonical_source and the sheet-sync subsystem (the DB has been canonical since); item create/edit forms were already live. The missing pieces shipped 2026-06-10: in-place asset editing on the gear detail page, and an export csv mirror on the inventory list (one row per asset; replaces the DB→sheet job — paste into the Sheet if it's ever wanted again). See docs/architecture/inventory-provenance.md.
  2. Payment provider integrationpayments already models Stripe/Wompi. Wire Stripe PaymentIntents (capture_method: manual for deposits) + a /api/stripe/webhook that writes payments rows and advances reservations. Wompi for COP-native later.
  3. Settings editingDone. /office/settings is a live editor — FX rate, multipliers, deposit, late fees, IVA, floor/rounding, and the full per-class cost-recovery grid. Saves are an append-only revision (0002); /office/settings/history shows the trail. (The old "set the Tier B/C thresholds" TODO is gone — vetting/insurance tiering was removed entirely in 0005.)
  4. Transactional emailsDone (2026-06-10/11). Quote / confirmation / receipt send on entering (or being created in) the status, bilingual, via Resend (src/lib/email/); reminders ride the cron below. Every attempt is audited; off-production takes a logged skip path (EMAIL_DRY_RUN overrides both ways); a failed email never fails the transition.
  5. Cron jobsDone (2026-06-10). /api/cron/expire-holds (hourly), /reminders (08:00 Bogotá, T-1 pickup/return), /overdue (13:00 Bogotá, advisory late-fee flag — never charges), all audit_log.source='system' and CRON_SECRET-gated (armed at launch — see docs/business/launch-checklist.md). coi_expiry_warnings is struck: COI and insurance were removed as policy in 0005, so that job will never exist.
  6. Kits — ✅ CRUD + reservation-picker expansion done. /office/inventory/kits (linked from the inventory header) manages kits; membership lives in kits.item_lines jsonb ([{ item_id, qty }]bundle_items is the separate items-as-bundle availability model, not kit membership). A kit is a shortcut, not a priced product: picking one in the reservation picker expands into its member item lines (expandKit in src/lib/office/cart.ts), each priced by the normal per-item math — reservation_items.kit_id stays unused and createReservation is untouched, so quote math is unchanged and the choice is reversible. Whether a kit becomes a bundled-rate product (its own day/week rate on the quote, via the kit_id line path) is a future product decision.
  7. Sync alerts surface Obsolete. The sync_errors/sync_events tables were dropped with the whole sheet-sync subsystem in 0005; nothing reads them and there is no sync to alert on. (Inventory is DB-canonical with a CSV export as the spreadsheet mirror.)
  8. Availability / double-booking guard — ✅ shipped as getReservationConflicts (src/lib/office/conflicts.ts): item demand vs serviceable units across overlapping held/confirmed reservations, space takers, and asset_blackouts/space_blackouts (blacked-out units reduce an item's available supply). Surfaced as a warning panel on the reservation page and enforced as a hard block in transitionReservation before confirmed. Deliberately not surfaced on the create form (warning-on-the-page + confirm block is the chosen scope); a DB-level exclusion constraint remains an option if races ever matter.

Website / content management (the "website" ask)

The marketing site's editorial content is file-based MDX + JSON, not DB rows, so it's managed in the repo rather than the admin:

  • Projects / team / stills — MDX with front-matter in content/projects/* etc. (gray-matter). Edited in-repo; deploy publishes.
  • Section copy (rentals, services, podcasts, …)locales/en.json / locales/es.json.
  • Gear images — Supabase media bucket (see docs/operations/media-uploads.md).
  • Launch gate — env-driven (LAUNCH_*); the admin shows the subscriber list.

A future /office/website could front the launch-gate toggle + an MDX editor, but that's a larger build; for now "website management" = subscribers + the documented file/locale workflow. Flagged here so it's a conscious choice, not a gap.

Known limitations / things to check

  • Asset auto-assignment at pickup is atomic but automatic: the assign_units RPC (0005) claims any available rentable asset of the item with for update skip locked (no two reservations grab the same asset), but there's no manual asset picker yet and no hard double-booking constraint at the DB level.
  • No availability check on create — the create form doesn't verify assets are free for the requested window or consult blackouts (matches the quote-first, desk-checks model — a deliberate scope choice). The overlap guard catches it downstream: the reservation page warns and the → confirmed transition is blocked while a conflict stands (see Deferred #8).
  • Inventory and settings writes are both live — item create/edit, in-place asset editing, and the CSV export shipped 2026-06-10 (see Deferred #1); the old "Phase 0 read-only" posture is over.
  • Admin sidebar is desktop-first; the mobile layout is a minimal top bar.