# `PhoenixKitStaff.Schemas.Person`
[🔗](https://github.com/BeamLabEU/phoenix_kit_staff/blob/v0.3.0/lib/phoenix_kit_staff/schemas/person.ex#L1)

A person on staff. Always linked to a PhoenixKit user (decision A for MVP);
the `user_uuid` FK is required.

All profile fields beyond the required `user_uuid` and `status` are
optional — fill in whatever's relevant.

# `t`

```elixir
@type t() :: %PhoenixKitStaff.Schemas.Person{
  __meta__: term(),
  bio: String.t() | nil,
  date_of_birth: Date.t() | nil,
  emergency_contact_name: String.t() | nil,
  emergency_contact_phone: String.t() | nil,
  emergency_contact_relationship: String.t() | nil,
  employment_end_date: Date.t() | nil,
  employment_start_date: Date.t() | nil,
  employment_type: String.t() | nil,
  inserted_at: DateTime.t() | nil,
  job_title: String.t() | nil,
  name: String.t() | nil,
  notes: String.t() | nil,
  personal_email: String.t() | nil,
  personal_phone: String.t() | nil,
  primary_department:
    PhoenixKitStaff.Schemas.Department.t()
    | Ecto.Association.NotLoaded.t()
    | nil,
  primary_department_uuid: UUIDv7.t() | nil,
  skills: String.t() | nil,
  status: String.t() | nil,
  team_memberships:
    [PhoenixKitStaff.Schemas.TeamMembership.t()]
    | Ecto.Association.NotLoaded.t(),
  translations: translations_map(),
  updated_at: DateTime.t() | nil,
  user: PhoenixKit.Users.Auth.User.t() | Ecto.Association.NotLoaded.t() | nil,
  user_uuid: UUIDv7.t() | nil,
  uuid: UUIDv7.t() | nil,
  work_location: String.t() | nil,
  work_phone: String.t() | nil
}
```

# `translations_map`

```elixir
@type translations_map() :: %{
  optional(String.t()) =&gt; %{optional(String.t()) =&gt; String.t()}
}
```

# `changeset`

```elixir
@spec changeset(t() | Ecto.Changeset.t(t()), map()) :: Ecto.Changeset.t(t())
```

# `display_name`

```elixir
@spec display_name(t()) :: String.t()
```

Best human label for a staff person, in priority order:

1. the explicit `name` column (the staff profile owns identity),
2. the linked user's `first_name`/`last_name`,
3. the linked user's email,
4. a generic `"Unnamed"` fallback.

Used by every people-listing surface (people list, org overview,
birthdays, show page) so the display is consistent. Expects `:user`
to be preloaded for the email/user-name fallbacks; an unloaded
association just skips to the generic fallback.

# `employment_type_label`

# `employment_types`

# `localized_bio`

```elixir
@spec localized_bio(t(), String.t() | nil) :: String.t() | nil
```

# `localized_job_title`

```elixir
@spec localized_job_title(t(), String.t() | nil) :: String.t() | nil
```

# `localized_notes`

```elixir
@spec localized_notes(t(), String.t() | nil) :: String.t() | nil
```

# `localized_skills`

```elixir
@spec localized_skills(t(), String.t() | nil) :: String.t() | nil
```

# `skill_list`

Splits a comma-separated skills string into a list, trimming blanks.

# `status_label`

Translated label for a status value (for UI display).

# `statuses`

# `translatable_fields`

```elixir
@spec translatable_fields() :: [String.t()]
```

DB-column field names that participate in the `translations` JSONB.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
