Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesLocationsLocations (Spaces) — Code Map

Locations (Spaces) — Code Map

API

FilePurpose
apps/api/src/locations/locations.module.tsWires controller + service; imports MembershipsModule.
apps/api/src/locations/locations.controller.tsRoutes, toLocationResponse mapper.
apps/api/src/locations/locations.service.tsCRUD + tier-limit check (maxSpaces) via @taikan/shared.checkTierLimit. List ordered by sortOrder, name.
apps/api/src/locations/dto/create-location.dto.tsname, address?, city?, country?, latitude?, longitude?, capacity?, concurrency? (exclusive|shared), sortOrder? (int ≥ 0).
apps/api/src/locations/dto/update-location.dto.tsPartialType of create.

Routes (prefix organizations/:orgId/locations)

MethodPathHandlerNotes
POST/createowner/admin + maxSpaces tier check
GET/listany member
GET/:idgetByIdany member
PATCH/:idupdateowner/admin
DELETE/:idremove (deactivate)owner/admin

Web

  • apps/web/src/components/onboarding/address-autocomplete.tsx — Google Places integration that fills address + city + country + lat/lng.
  • apps/web/src/app/[lang]/(protected)/dashboard/settings/(shell)/locations/page.tsx — settings page, titled Spaces in the UI. Renders LocationsTab.
  • apps/web/src/components/overview/schedule/locations-tab.tsx + location-dialog.tsx — list and create/edit dialog (i18n keys: schedule.locationsTab, schedule.locationSheet). Driver + spec: locations-tab.driver.tsx, locations-tab.int.spec.tsx.
  • Space pickers elsewhere: class-type-dialog.tsx (default space), create-session-sheet.tsx / session-fields.tsx / session-detail-sheet.tsx (session space; required when the spaces flag is on and the org has two or more active spaces; owner/admin override dialog on the space 409s, space-override-dialog.tsx).

DB tables

TableUsed as
locationsOwned.
organizationsDirect parent (FK on locations.organization_id).
class_sessionsReferences location_id (nullable). Index class_sessions_location_starts_at_idx on (location_id, starts_at) serves the overlap check.
class_typesdefault_location_id (nullable, ON DELETE SET NULL).

Shared schemas

  • libs/shared/src/lib/schemas/scheduling.schema.ts — locationResponseSchema / LocationResponse ({ id, name, address, city, country, latitude, longitude, capacity, concurrency, sortOrder, isActive, createdAt }); spaceConcurrencyValues / SpaceConcurrency; spaceOverrideSchema; spaceConflictErrorSchema / spaceCapacityErrorSchema (409 bodies).
  • libs/shared/src/lib/constants/scheduling-error-codes.ts — SchedulingErrorCodes.SPACE_CONFLICT (space_conflict), SPACE_CAPACITY_EXCEEDED (space_capacity_exceeded).
  • libs/shared/src/lib/constants/feature-flags.ts — FeatureFlags.SPACES (spaces).
  • checkTierLimit(platformTier, 'maxSpaces', currentCount) exported from @taikan/shared (platform-tiers.ts).

Migration

libs/db/drizzle/0167_spaces.sql — enum space_concurrency, locations.concurrency (default exclusive), locations.sort_order (default 0), class_types.default_location_id, index class_sessions_location_starts_at_idx.

Tests

No dedicated locations.service.unit.spec.ts found. Tier limit logic is covered via integration tests on the platform-tiers module. TODO: add unit tests for CRUD + tier rejection.