PhoenixKit.Migrations.Postgres.V112 (phoenix_kit v1.7.118)

Copy Markdown View Source

V112: Project lifecycle + translations + drop unique-name indexes.

Three related changes to the phoenix_kit_projects* tables:

1. archived_at on phoenix_kit_projects

Replaces the dual-purpose status field (which held both lifecycle state and a soft-hide flag) with a dedicated nullable timestamp. Mirrors the workspace convention used by phoenix_kit_publishing's posts.trashed_at and phoenix_kit_files.trashed_at — null = visible, non-null = soft-hidden, with the timestamp doubling as audit metadata.

The status column is kept (intentional — see phoenix_kit_projects/AGENTS.md) so a future workflow concept that legitimately wants a string lifecycle state (e.g. "paused", "blocked", "on_hold") can reuse the column without another migration. Application code stops reading or writing it; existing rows whose status is "archived" get backfilled into archived_at so the dashboard filters keep working transparently.

2. translations JSONB on the three project tables

Adds translations JSONB NOT NULL DEFAULT '{}' to:

  • phoenix_kit_projects (Project — translatable: name, description)
  • phoenix_kit_project_tasks (Task — translatable: title, description)
  • phoenix_kit_project_assignments (Assignment — translatable: description)

Storage shape mirrors the entities-module "settings translations" pattern from PhoenixKitWeb.Components.MultilangForm's <.translatable_field> (the variant where secondary_name and lang_data_key are passed explicitly):

%{
  "es-ES" => %{"name" => "Proyecto", "description" => "..."},
  "fr-FR" => %{"name" => "Projet"}
}

The primary-language values stay in their existing columns (name, title, description) — the JSONB only holds secondary-language overrides. Empty/missing override falls back to the primary value at render time. No primary-language marker key (_primary_language) is needed because the primary lives outside the JSONB.

3. Drop unique-name indexes on projects + tasks

V105 split phoenix_kit_projects_name_index into two partial unique indexes (one per is_template). V112 drops both of those plus the unique-title index on phoenix_kit_project_tasks because user-input display names are policy, not structure: code references resources by uuid, so duplicate names across projects/templates/tasks is fine and the unique constraint just made common workflows (clone twice, two teams' "Onboarding" templates) raise a constraint error.

Indexes removed:

  • phoenix_kit_projects_name_template_index (V105)
  • phoenix_kit_projects_name_project_index (V105)
  • phoenix_kit_project_tasks_title_index (V101)

4. Retype scheduled_start_date from date to timestamp(0)

The "scheduled start" field originally held only a date — fine for the daily-cadence projects, awkward for "this campaign starts at 09:00 sharp" or "the announcement at 14:30." V112 promotes it to timestamp(0) so the form / popup can carry hour-and-minute precision. Existing date values are preserved at midnight UTC.

The column name is kept (scheduled_start_date) — renaming to scheduled_start_at would force every call site, the changeset cast list, and any in-flight URL params to chase. Lying name + honest type beats a churn pass; future cleanup can rename when a larger refactor is on the table.

5. Add position to phoenix_kit_project_tasks and phoenix_kit_projects

Drives manual reorder of the task library, project list, and template list views. NOT NULL with a default of 0; existing rows fold into the same 0 bucket and the schema's secondary order-by-inserted_at kicks in until the user actually drags. New rows should be inserted via next_task_position/0 / next_project_position/1 so they land at the bottom of their bucket.

phoenix_kit_projects.position is interpreted per is_template scope — projects and templates share the same column but order independently (the LV sorts within is_template = false for the project list, is_template = true for the template list).

Idempotent: re-running is a no-op once the columns + indexes are in the post-V112 shape.

Summary

Functions

down(opts)

up(opts)