# `PhoenixKitProjects.Schemas.ProjectStatus`
[🔗](https://github.com/BeamLabEU/phoenix_kit_projects/blob/v0.14.0/lib/phoenix_kit_projects/schemas/project_status.ex#L1)

A project's **cemented** workflow status — a local snapshot of a catalog
status row, copied from the `phoenix_kit_entities` vocabulary when the
project starts.

Once cemented, a project's statuses live here and are edited
independently of the catalog entity (mirrors the way an `Assignment`
copies its `Task` template's fields at creation, then diverges). The
`current_status_slug` column on `phoenix_kit_projects` selects one of
these rows by `slug`.

`source_entity_data_uuid` is provenance only (which catalog row this was
copied from) — intentionally not a foreign key, since the catalog lives
in the optional `phoenix_kit_entities` package and the snapshot must
outlive its source.

Table created by core migration V125.

# `t`

```elixir
@type t() :: %PhoenixKitProjects.Schemas.ProjectStatus{
  __meta__: term(),
  data: map(),
  inserted_at: DateTime.t() | nil,
  label: String.t() | nil,
  position: integer() | nil,
  project:
    PhoenixKitProjects.Schemas.Project.t()
    | Ecto.Association.NotLoaded.t()
    | nil,
  project_uuid: UUIDv7.t() | nil,
  slug: String.t() | nil,
  source_entity_data_uuid: UUIDv7.t() | nil,
  translations: map(),
  updated_at: DateTime.t() | nil,
  uuid: UUIDv7.t() | nil
}
```

# `changeset`

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

Changeset for a cemented status row. Requires `project_uuid` + `label`;
derives a URL-safe `slug` from the label when one isn't supplied; and
enforces slug uniqueness within the project (the
`(project_uuid, slug)` unique index from V125).

# `color`

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

Colour for this status, read from the `data` JSONB (`nil` if unset).

# `localized_label`

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

Label in the requested language, falling back to the primary `label`
column when the language has no override (or the override is empty).
`lang` may be `nil` (multilang disabled) → the primary label.
Future-proofs status-label i18n; `translations` is empty until wired.

# `slugify`

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

Lower-cases and hyphenates an arbitrary string into a stable slug
(`[a-z0-9]` runs joined by single hyphens, no leading/trailing hyphen).

---

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