V122: Three unrelated additions bundled together because they shipped in the same release cycle.
1. phoenix_kit_location_spaces — nested floors / rooms / zones
under a phoenix_kit_locations row
Each row belongs to exactly one Location (required FK, cascade) and may optionally belong to a parent Space within the same Location (self-ref FK, cascade) forming a filesystem-like tree of arbitrary depth.
The cross-row "child belongs to same location as parent" guarantee
is enforced at application layer in PhoenixKitLocations.Spaces —
enforcing it at the DB requires a composite FK + redundant column
pair which is heavier than the consumer surface justifies.
data JSONB mirrors the parent Location's column: top-level keys
carry attachment pointers (files_folder_uuid, featured_image_uuid)
and the multilang translation tree (%{ "es-ES" => %{ "name" => "..."} }). Primary-language values stay denormalized in the
dedicated name / description columns for cheap querying.
2. translations JSONB on the three staff tables
Adds translations JSONB NOT NULL DEFAULT '{}' to
phoenix_kit_staff_departments, phoenix_kit_staff_teams, and
phoenix_kit_staff_people. Mirrors the settings-translations shape
already used by phoenix_kit_projects V112 (project / project_task /
project_assignment): primary stays in dedicated columns, the JSONB
holds non-primary overrides only.
%{"es-ES" => %{"name" => "...", "description" => "..."}}Translatable fields by schema:
- Department:
name,description - Team:
name,description - Person:
job_title,bio,skills,notes
Read paths use <Schema>.localized_<field>/2 helpers with primary-
fallback semantics.
3. name column on phoenix_kit_staff_people
Adds a single nullable name VARCHAR for the staff person's full
display name — consistent with Department, Team, Space, and Location
which all use a single name field. Owned by the staff profile
rather than phoenix_kit_users because placeholder users created via
Staff.find_or_create_user_by_email/1 are anonymous until claimed.