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 +
whenfilters), 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).
- an append-only revision history (
- Spaces (
/office/spaces): list, create, edit rentable studio spaces.reservation_items.space_idlets 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, migration0002). - 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 viadeleted_at, migration0004) 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 page✅ Built./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)
Phase-1 inventory cutover✅ Done — and most of it had been for a while. Migration0005droppedinventory_canonical_sourceand 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). Seedocs/architecture/inventory-provenance.md.- Payment provider integration —
paymentsalready models Stripe/Wompi. Wire Stripe PaymentIntents (capture_method: manualfor deposits) + a/api/stripe/webhookthat writespaymentsrows and advances reservations. Wompi for COP-native later. Settings editing✅ Done./office/settingsis 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/historyshows the trail. (The old "set the Tier B/C thresholds" TODO is gone — vetting/insurance tiering was removed entirely in0005.)Transactional emails✅ Done (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_RUNoverrides both ways); a failed email never fails the transition.Cron jobs✅ Done (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), allaudit_log.source='system'and CRON_SECRET-gated (armed at launch — seedocs/business/launch-checklist.md).coi_expiry_warningsis struck: COI and insurance were removed as policy in0005, so that job will never exist.- Kits — ✅ CRUD + reservation-picker expansion done.
/office/inventory/kits(linked from the inventory header) manages kits; membership lives inkits.item_linesjsonb ([{ item_id, qty }]—bundle_itemsis 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 (expandKitinsrc/lib/office/cart.ts), each priced by the normal per-item math —reservation_items.kit_idstays unused andcreateReservationis 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. Sync alerts surfaceObsolete. Thesync_errors/sync_eventstables were dropped with the whole sheet-sync subsystem in0005; nothing reads them and there is no sync to alert on. (Inventory is DB-canonical with a CSV export as the spreadsheet mirror.)- Availability / double-booking guard — ✅ shipped as
getReservationConflicts(src/lib/office/conflicts.ts): item demand vs serviceable units across overlapping held/confirmed reservations, space takers, andasset_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 intransitionReservationbeforeconfirmed. 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
mediabucket (seedocs/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_unitsRPC (0005) claims any available rentable asset of the item withfor 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.