Locations
What is this
A location is a studio branch or physical space within an organization. It carries an address, city, country, latitude/longitude (used for GPS check-in), capacity, and an is_active flag. Class sessions optionally reference a location (class_sessions.location_id). Multi-branch studios use multiple location rows; single-studio orgs typically have one.
Who uses it
| Persona | Why |
|---|---|
| Owner | Adds/edits branches; sets coordinates to enable GPS check-in. |
| Admin | Same as owner. |
| Coach | Reads; selects a location at session-create. |
| Member | Reads location name + map (minisite has an embed); GPS check-in uses lat/lng but the member doesn’t see the coordinates. |
Persona impact
- Tier-gated:
LocationsService.createcheckscheckTierLimit(platformTier, 'maxLocations', currentCount)—liteorgs can’t add unlimited branches. - GPS check-in (
POST /sessions/:id/self-checkinwithmethod=gps) readslocation.latitudeandlocation.longitude. If either is null when a member tries GPS check-in, the booking flow throwsBadRequestException('Session location coordinates are not configured').
High-level capabilities
- CRUD — create, list, get, update, deactivate (soft via
is_active=false). - Tier enforcement —
maxLocationschecked at create time; exceeds raises 403 with upgrade copy. - Geo metadata —
latitude/longitude(doublePrecision) used by self-checkin Haversine distance. - Capacity hint —
location.capacityis informational only (not enforced against class session capacity). - Minisite integration — public minisites can render a map embed for the location (see i18n
minisite.locations*).
Relationship to other features
- scheduling-bookings —
class_sessions.location_idFKs here. GPS check-in resolvessession.location.latitude/longitude. - organizations —
locations.organization_idis the direct org boundary (unlike class_types, no parent indirection). - Platform tiers / billing — see
apps/api/src/platform-tiers/+@fitkit/shared.checkTierLimit.maxLocationslimit varies per tier.
Current status
Shipped. The schema column address is a free-text text field (not normalized). Coordinates are populated either manually by owners or via the AddressAutocomplete component during onboarding (Google Places, see apps/web/src/components/onboarding/address-autocomplete.tsx).
Known gaps
- No bulk-import for multi-branch studios; each location added one-at-a-time.
- No “default location” flag on the org — when a session is created without
location_id, none is chosen automatically. location.capacityis informational; not enforced anywhere in booking logic.