- {gettext("Auto-generated identifier (immutable)")} -
- - <:item title="Name">{@custom_field.name} <:item title="Description">{@custom_field.description} diff --git a/lib/mv_web/live/custom_field_value_live/form.ex b/lib/mv_web/live/custom_field_value_live/form.ex index 4a7b02d..7df4c69 100644 --- a/lib/mv_web/live/custom_field_value_live/form.ex +++ b/lib/mv_web/live/custom_field_value_live/form.ex @@ -39,7 +39,7 @@ defmodule MvWeb.CustomFieldValueLive.Form do <.header> {@page_title} <:subtitle> - {gettext("Use this form to manage Custom Field Value records in your database.")} + {gettext("Use this form to manage custom_field_value records in your database.")} diff --git a/mix.exs b/mix.exs index c6e4fb5..b215d59 100644 --- a/mix.exs +++ b/mix.exs @@ -75,8 +75,7 @@ defmodule Mv.MixProject do {:mix_audit, "~> 2.1", only: [:dev, :test], runtime: false}, {:sobelow, "~> 0.14", only: [:dev, :test], runtime: false}, {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, - {:ecto_commons, "~> 0.3"}, - {:slugify, "~> 1.3"} + {:ecto_commons, "~> 0.3"} ] end diff --git a/priv/gettext/de/LC_MESSAGES/default.po b/priv/gettext/de/LC_MESSAGES/default.po index 842ab40..f6acdca 100644 --- a/priv/gettext/de/LC_MESSAGES/default.po +++ b/priv/gettext/de/LC_MESSAGES/default.po @@ -158,7 +158,7 @@ msgstr "Postleitzahl" msgid "Save Member" msgstr "Mitglied speichern" -#: lib/mv_web/live/custom_field_live/form.ex:64 +#: lib/mv_web/live/custom_field_live/form.ex:63 #: lib/mv_web/live/custom_field_value_live/form.ex:74 #: lib/mv_web/live/member_live/form.ex:79 #: lib/mv_web/live/user_live/form.ex:124 @@ -203,14 +203,14 @@ msgstr "Dies ist ein Mitglied aus deiner Datenbank." msgid "Yes" msgstr "Ja" -#: lib/mv_web/live/custom_field_live/form.ex:108 +#: lib/mv_web/live/custom_field_live/form.ex:107 #: lib/mv_web/live/custom_field_value_live/form.ex:233 #: lib/mv_web/live/member_live/form.ex:138 #, elixir-autogen, elixir-format msgid "create" msgstr "erstellt" -#: lib/mv_web/live/custom_field_live/form.ex:109 +#: lib/mv_web/live/custom_field_live/form.ex:108 #: lib/mv_web/live/custom_field_value_live/form.ex:234 #: lib/mv_web/live/member_live/form.ex:139 #, elixir-autogen, elixir-format @@ -252,8 +252,7 @@ msgstr "Ihre E-Mail-Adresse wurde bestätigt" msgid "Your password has successfully been reset" msgstr "Ihr Passwort wurde erfolgreich zurückgesetzt" -#: lib/mv_web/live/custom_field_live/form.ex:67 -#: lib/mv_web/live/custom_field_live/index.ex:120 +#: lib/mv_web/live/custom_field_live/form.ex:66 #: lib/mv_web/live/custom_field_value_live/form.ex:77 #: lib/mv_web/live/member_live/form.ex:82 #: lib/mv_web/live/user_live/form.ex:127 @@ -266,7 +265,7 @@ msgstr "Abbrechen" msgid "Choose a member" msgstr "Mitglied auswählen" -#: lib/mv_web/live/custom_field_live/form.ex:60 +#: lib/mv_web/live/custom_field_live/form.ex:59 #, elixir-autogen, elixir-format msgid "Description" msgstr "Beschreibung" @@ -286,7 +285,7 @@ msgstr "Aktiviert" msgid "ID" msgstr "ID" -#: lib/mv_web/live/custom_field_live/form.ex:61 +#: lib/mv_web/live/custom_field_live/form.ex:60 #, elixir-autogen, elixir-format msgid "Immutable" msgstr "Unveränderlich" @@ -356,7 +355,7 @@ msgstr "Passwort-Authentifizierung" msgid "Profil" msgstr "Profil" -#: lib/mv_web/live/custom_field_live/form.ex:62 +#: lib/mv_web/live/custom_field_live/form.ex:61 #, elixir-autogen, elixir-format msgid "Required" msgstr "Erforderlich" @@ -412,7 +411,7 @@ msgstr "Benutzer*in" msgid "Value" msgstr "Wert" -#: lib/mv_web/live/custom_field_live/form.ex:55 +#: lib/mv_web/live/custom_field_live/form.ex:54 #, elixir-autogen, elixir-format msgid "Value type" msgstr "Wertetyp" @@ -617,7 +616,7 @@ msgstr "Benutzerdefinierte Feldwerte" msgid "Custom field" msgstr "Benutzerdefiniertes Feld" -#: lib/mv_web/live/custom_field_live/form.ex:115 +#: lib/mv_web/live/custom_field_live/form.ex:114 #, elixir-autogen, elixir-format msgid "Custom field %{action} successfully" msgstr "Benutzerdefiniertes Feld erfolgreich %{action}" @@ -632,7 +631,7 @@ msgstr "Benutzerdefinierter Feldwert erfolgreich %{action}" msgid "Please select a custom field first" msgstr "Bitte wähle zuerst ein Benutzerdefiniertes Feld" -#: lib/mv_web/live/custom_field_live/form.ex:65 +#: lib/mv_web/live/custom_field_live/form.ex:64 #, elixir-autogen, elixir-format msgid "Save Custom field" msgstr "Benutzerdefiniertes Feld speichern" @@ -647,54 +646,12 @@ msgstr "Benutzerdefinierten Feldwert speichern" msgid "Use this form to manage custom_field records in your database." msgstr "Verwende dieses Formular, um Benutzerdefinierte Felder in deiner Datenbank zu verwalten." +#: lib/mv_web/live/custom_field_value_live/form.ex:42 +#, elixir-autogen, elixir-format +msgid "Use this form to manage custom_field_value records in your database." +msgstr "Verwende dieses Formular, um Benutzerdefinierte Feldwerte in deiner Datenbank zu verwalten." + #: lib/mv_web/components/layouts/navbar.ex:20 #, elixir-autogen, elixir-format msgid "Custom Fields" msgstr "Benutzerdefinierte Felder" - -#: lib/mv_web/live/custom_field_value_live/form.ex:42 -#, elixir-autogen, elixir-format, fuzzy -msgid "Use this form to manage Custom Field Value records in your database." -msgstr "Verwende dieses Formular, um Benutzerdefinierte Feldwerte in deiner Datenbank zu verwalten." - -#: lib/mv_web/live/custom_field_live/show.ex:56 -#, elixir-autogen, elixir-format -msgid "Auto-generated identifier (immutable)" -msgstr "Automatisch generierter Bezeichner (unveränderlich)" - -#: lib/mv_web/live/custom_field_live/index.ex:79 -#, elixir-autogen, elixir-format -msgid "%{count} member has a value assigned for this custom field." -msgid_plural "%{count} members have values assigned for this custom field." -msgstr[0] "%{count} Mitglied hat einen Wert für dieses benutzerdefinierte Feld zugewiesen." -msgstr[1] "%{count} Mitglieder haben Werte für dieses benutzerdefinierte Feld zugewiesen." - -#: lib/mv_web/live/custom_field_live/index.ex:87 -#, elixir-autogen, elixir-format -msgid "All custom field values will be permanently deleted when you delete this custom field." -msgstr "Alle benutzerdefinierten Feldwerte werden beim Löschen dieses benutzerdefinierten Feldes dauerhaft gelöscht." - -#: lib/mv_web/live/custom_field_live/index.ex:72 -#, elixir-autogen, elixir-format -msgid "Delete Custom Field" -msgstr "Benutzerdefiniertes Feld löschen" - -#: lib/mv_web/live/custom_field_live/index.ex:127 -#, elixir-autogen, elixir-format -msgid "Delete Custom Field and All Values" -msgstr "Benutzerdefiniertes Feld und alle Werte löschen" - -#: lib/mv_web/live/custom_field_live/index.ex:109 -#, elixir-autogen, elixir-format -msgid "Enter the text above to confirm" -msgstr "Obigen Text zur Bestätigung eingeben" - -#: lib/mv_web/live/custom_field_live/index.ex:97 -#, elixir-autogen, elixir-format, fuzzy -msgid "To confirm deletion, please enter this text:" -msgstr "Um die Löschung zu bestätigen, gib bitte folgenden Text ein:" - -#~ #: lib/mv_web/live/custom_field_live/index.ex:97 -#~ #, elixir-autogen, elixir-format -#~ msgid "To confirm deletion, please enter the custom field slug:" -#~ msgstr "Um die Löschung zu bestätigen, gib bitte den Slug des benutzerdefinierten Feldes ein:" diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index 5942951..d150a60 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -159,7 +159,7 @@ msgstr "" msgid "Save Member" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:64 +#: lib/mv_web/live/custom_field_live/form.ex:63 #: lib/mv_web/live/custom_field_value_live/form.ex:74 #: lib/mv_web/live/member_live/form.ex:79 #: lib/mv_web/live/user_live/form.ex:124 @@ -204,14 +204,14 @@ msgstr "" msgid "Yes" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:108 +#: lib/mv_web/live/custom_field_live/form.ex:107 #: lib/mv_web/live/custom_field_value_live/form.ex:233 #: lib/mv_web/live/member_live/form.ex:138 #, elixir-autogen, elixir-format msgid "create" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:109 +#: lib/mv_web/live/custom_field_live/form.ex:108 #: lib/mv_web/live/custom_field_value_live/form.ex:234 #: lib/mv_web/live/member_live/form.ex:139 #, elixir-autogen, elixir-format @@ -253,8 +253,7 @@ msgstr "" msgid "Your password has successfully been reset" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:67 -#: lib/mv_web/live/custom_field_live/index.ex:120 +#: lib/mv_web/live/custom_field_live/form.ex:66 #: lib/mv_web/live/custom_field_value_live/form.ex:77 #: lib/mv_web/live/member_live/form.ex:82 #: lib/mv_web/live/user_live/form.ex:127 @@ -267,7 +266,7 @@ msgstr "" msgid "Choose a member" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:60 +#: lib/mv_web/live/custom_field_live/form.ex:59 #, elixir-autogen, elixir-format msgid "Description" msgstr "" @@ -287,7 +286,7 @@ msgstr "" msgid "ID" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:61 +#: lib/mv_web/live/custom_field_live/form.ex:60 #, elixir-autogen, elixir-format msgid "Immutable" msgstr "" @@ -357,7 +356,7 @@ msgstr "" msgid "Profil" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:62 +#: lib/mv_web/live/custom_field_live/form.ex:61 #, elixir-autogen, elixir-format msgid "Required" msgstr "" @@ -413,7 +412,7 @@ msgstr "" msgid "Value" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:55 +#: lib/mv_web/live/custom_field_live/form.ex:54 #, elixir-autogen, elixir-format msgid "Value type" msgstr "" @@ -618,7 +617,7 @@ msgstr "" msgid "Custom field" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:115 +#: lib/mv_web/live/custom_field_live/form.ex:114 #, elixir-autogen, elixir-format msgid "Custom field %{action} successfully" msgstr "" @@ -633,7 +632,7 @@ msgstr "" msgid "Please select a custom field first" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:65 +#: lib/mv_web/live/custom_field_live/form.ex:64 #, elixir-autogen, elixir-format msgid "Save Custom field" msgstr "" @@ -648,49 +647,12 @@ msgstr "" msgid "Use this form to manage custom_field records in your database." msgstr "" +#: lib/mv_web/live/custom_field_value_live/form.ex:42 +#, elixir-autogen, elixir-format +msgid "Use this form to manage custom_field_value records in your database." +msgstr "" + #: lib/mv_web/components/layouts/navbar.ex:20 #, elixir-autogen, elixir-format msgid "Custom Fields" msgstr "" - -#: lib/mv_web/live/custom_field_value_live/form.ex:42 -#, elixir-autogen, elixir-format -msgid "Use this form to manage Custom Field Value records in your database." -msgstr "" - -#: lib/mv_web/live/custom_field_live/show.ex:56 -#, elixir-autogen, elixir-format -msgid "Auto-generated identifier (immutable)" -msgstr "" - -#: lib/mv_web/live/custom_field_live/index.ex:79 -#, elixir-autogen, elixir-format -msgid "%{count} member has a value assigned for this custom field." -msgid_plural "%{count} members have values assigned for this custom field." -msgstr[0] "" -msgstr[1] "" - -#: lib/mv_web/live/custom_field_live/index.ex:87 -#, elixir-autogen, elixir-format -msgid "All custom field values will be permanently deleted when you delete this custom field." -msgstr "" - -#: lib/mv_web/live/custom_field_live/index.ex:72 -#, elixir-autogen, elixir-format -msgid "Delete Custom Field" -msgstr "" - -#: lib/mv_web/live/custom_field_live/index.ex:127 -#, elixir-autogen, elixir-format -msgid "Delete Custom Field and All Values" -msgstr "" - -#: lib/mv_web/live/custom_field_live/index.ex:109 -#, elixir-autogen, elixir-format -msgid "Enter the text above to confirm" -msgstr "" - -#: lib/mv_web/live/custom_field_live/index.ex:97 -#, elixir-autogen, elixir-format -msgid "To confirm deletion, please enter this text:" -msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po index 32a2d76..df56e75 100644 --- a/priv/gettext/en/LC_MESSAGES/default.po +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -159,7 +159,7 @@ msgstr "" msgid "Save Member" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:64 +#: lib/mv_web/live/custom_field_live/form.ex:63 #: lib/mv_web/live/custom_field_value_live/form.ex:74 #: lib/mv_web/live/member_live/form.ex:79 #: lib/mv_web/live/user_live/form.ex:124 @@ -204,14 +204,14 @@ msgstr "" msgid "Yes" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:108 +#: lib/mv_web/live/custom_field_live/form.ex:107 #: lib/mv_web/live/custom_field_value_live/form.ex:233 #: lib/mv_web/live/member_live/form.ex:138 #, elixir-autogen, elixir-format msgid "create" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:109 +#: lib/mv_web/live/custom_field_live/form.ex:108 #: lib/mv_web/live/custom_field_value_live/form.ex:234 #: lib/mv_web/live/member_live/form.ex:139 #, elixir-autogen, elixir-format @@ -253,8 +253,7 @@ msgstr "" msgid "Your password has successfully been reset" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:67 -#: lib/mv_web/live/custom_field_live/index.ex:120 +#: lib/mv_web/live/custom_field_live/form.ex:66 #: lib/mv_web/live/custom_field_value_live/form.ex:77 #: lib/mv_web/live/member_live/form.ex:82 #: lib/mv_web/live/user_live/form.ex:127 @@ -267,7 +266,7 @@ msgstr "" msgid "Choose a member" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:60 +#: lib/mv_web/live/custom_field_live/form.ex:59 #, elixir-autogen, elixir-format msgid "Description" msgstr "" @@ -287,7 +286,7 @@ msgstr "" msgid "ID" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:61 +#: lib/mv_web/live/custom_field_live/form.ex:60 #, elixir-autogen, elixir-format msgid "Immutable" msgstr "" @@ -357,7 +356,7 @@ msgstr "" msgid "Profil" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:62 +#: lib/mv_web/live/custom_field_live/form.ex:61 #, elixir-autogen, elixir-format msgid "Required" msgstr "" @@ -413,7 +412,7 @@ msgstr "" msgid "Value" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:55 +#: lib/mv_web/live/custom_field_live/form.ex:54 #, elixir-autogen, elixir-format msgid "Value type" msgstr "" @@ -618,7 +617,7 @@ msgstr "" msgid "Custom field" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:115 +#: lib/mv_web/live/custom_field_live/form.ex:114 #, elixir-autogen, elixir-format msgid "Custom field %{action} successfully" msgstr "" @@ -633,7 +632,7 @@ msgstr "" msgid "Please select a custom field first" msgstr "" -#: lib/mv_web/live/custom_field_live/form.ex:65 +#: lib/mv_web/live/custom_field_live/form.ex:64 #, elixir-autogen, elixir-format msgid "Save Custom field" msgstr "" @@ -648,54 +647,12 @@ msgstr "" msgid "Use this form to manage custom_field records in your database." msgstr "" +#: lib/mv_web/live/custom_field_value_live/form.ex:42 +#, elixir-autogen, elixir-format, fuzzy +msgid "Use this form to manage custom_field_value records in your database." +msgstr "" + #: lib/mv_web/components/layouts/navbar.ex:20 #, elixir-autogen, elixir-format, fuzzy msgid "Custom Fields" msgstr "" - -#: lib/mv_web/live/custom_field_value_live/form.ex:42 -#, elixir-autogen, elixir-format, fuzzy -msgid "Use this form to manage Custom Field Value records in your database." -msgstr "" - -#: lib/mv_web/live/custom_field_live/show.ex:56 -#, elixir-autogen, elixir-format -msgid "Auto-generated identifier (immutable)" -msgstr "" - -#: lib/mv_web/live/custom_field_live/index.ex:79 -#, elixir-autogen, elixir-format -msgid "%{count} member has a value assigned for this custom field." -msgid_plural "%{count} members have values assigned for this custom field." -msgstr[0] "" -msgstr[1] "" - -#: lib/mv_web/live/custom_field_live/index.ex:87 -#, elixir-autogen, elixir-format -msgid "All custom field values will be permanently deleted when you delete this custom field." -msgstr "" - -#: lib/mv_web/live/custom_field_live/index.ex:72 -#, elixir-autogen, elixir-format -msgid "Delete Custom Field" -msgstr "" - -#: lib/mv_web/live/custom_field_live/index.ex:127 -#, elixir-autogen, elixir-format -msgid "Delete Custom Field and All Values" -msgstr "" - -#: lib/mv_web/live/custom_field_live/index.ex:109 -#, elixir-autogen, elixir-format -msgid "Enter the text above to confirm" -msgstr "" - -#: lib/mv_web/live/custom_field_live/index.ex:97 -#, elixir-autogen, elixir-format, fuzzy -msgid "To confirm deletion, please enter this text:" -msgstr "" - -#~ #: lib/mv_web/live/custom_field_live/index.ex:97 -#~ #, elixir-autogen, elixir-format -#~ msgid "To confirm deletion, please enter the custom field slug:" -#~ msgstr "" diff --git a/priv/repo/migrations/20251113180429_add_slug_to_custom_fields.exs b/priv/repo/migrations/20251113180429_add_slug_to_custom_fields.exs deleted file mode 100644 index bebf799..0000000 --- a/priv/repo/migrations/20251113180429_add_slug_to_custom_fields.exs +++ /dev/null @@ -1,47 +0,0 @@ -defmodule Mv.Repo.Migrations.AddSlugToCustomFields do - @moduledoc """ - Updates resources based on their most recent snapshots. - - This file was autogenerated with `mix ash_postgres.generate_migrations` - """ - - use Ecto.Migration - - def up do - # Step 1: Add slug column as nullable first - alter table(:custom_fields) do - add :slug, :text, null: true - end - - # Step 2: Generate slugs for existing custom fields - execute(""" - UPDATE custom_fields - SET slug = lower( - regexp_replace( - regexp_replace( - regexp_replace(name, '[^a-zA-Z0-9\\s-]', '', 'g'), - '\\s+', '-', 'g' - ), - '-+', '-', 'g' - ) - ) - WHERE slug IS NULL - """) - - # Step 3: Make slug NOT NULL - alter table(:custom_fields) do - modify :slug, :text, null: false - end - - # Step 4: Create unique index - create unique_index(:custom_fields, [:slug], name: "custom_fields_unique_slug_index") - end - - def down do - drop_if_exists unique_index(:custom_fields, [:slug], name: "custom_fields_unique_slug_index") - - alter table(:custom_fields) do - remove :slug - end - end -end diff --git a/priv/repo/migrations/20251113183538_change_custom_field_delete_cascade.exs b/priv/repo/migrations/20251113183538_change_custom_field_delete_cascade.exs deleted file mode 100644 index 32b8037..0000000 --- a/priv/repo/migrations/20251113183538_change_custom_field_delete_cascade.exs +++ /dev/null @@ -1,38 +0,0 @@ -defmodule Mv.Repo.Migrations.ChangeCustomFieldDeleteCascade do - @moduledoc """ - Updates resources based on their most recent snapshots. - - This file was autogenerated with `mix ash_postgres.generate_migrations` - """ - - use Ecto.Migration - - def up do - drop constraint(:custom_field_values, "custom_field_values_custom_field_id_fkey") - - alter table(:custom_field_values) do - modify :custom_field_id, - references(:custom_fields, - column: :id, - name: "custom_field_values_custom_field_id_fkey", - type: :uuid, - prefix: "public", - on_delete: :delete_all - ) - end - end - - def down do - drop constraint(:custom_field_values, "custom_field_values_custom_field_id_fkey") - - alter table(:custom_field_values) do - modify :custom_field_id, - references(:custom_fields, - column: :id, - name: "custom_field_values_custom_field_id_fkey", - type: :uuid, - prefix: "public" - ) - end - end -end diff --git a/priv/resource_snapshots/repo/custom_field_values/20251113183538.json b/priv/resource_snapshots/repo/custom_field_values/20251113183538.json deleted file mode 100644 index fc27f19..0000000 --- a/priv/resource_snapshots/repo/custom_field_values/20251113183538.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "attributes": [ - { - "allow_nil?": false, - "default": "fragment(\"gen_random_uuid()\")", - "generated?": false, - "precision": null, - "primary_key?": true, - "references": null, - "scale": null, - "size": null, - "source": "id", - "type": "uuid" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "value", - "type": "map" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": { - "deferrable": false, - "destination_attribute": "id", - "destination_attribute_default": null, - "destination_attribute_generated": null, - "index?": false, - "match_type": null, - "match_with": null, - "multitenancy": { - "attribute": null, - "global": null, - "strategy": null - }, - "name": "custom_field_values_member_id_fkey", - "on_delete": "delete", - "on_update": null, - "primary_key?": true, - "schema": "public", - "table": "members" - }, - "scale": null, - "size": null, - "source": "member_id", - "type": "uuid" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": { - "deferrable": false, - "destination_attribute": "id", - "destination_attribute_default": null, - "destination_attribute_generated": null, - "index?": false, - "match_type": null, - "match_with": null, - "multitenancy": { - "attribute": null, - "global": null, - "strategy": null - }, - "name": "custom_field_values_custom_field_id_fkey", - "on_delete": "delete", - "on_update": null, - "primary_key?": true, - "schema": "public", - "table": "custom_fields" - }, - "scale": null, - "size": null, - "source": "custom_field_id", - "type": "uuid" - } - ], - "base_filter": null, - "check_constraints": [], - "custom_indexes": [], - "custom_statements": [], - "has_create_action": true, - "hash": "BDEC02A7F12B14AB65FBA1A4BD834D291E3BEC61D065473D51BBE453486512ED", - "identities": [ - { - "all_tenants?": false, - "base_filter": null, - "index_name": "custom_field_values_unique_custom_field_per_member_index", - "keys": [ - { - "type": "atom", - "value": "member_id" - }, - { - "type": "atom", - "value": "custom_field_id" - } - ], - "name": "unique_custom_field_per_member", - "nils_distinct?": true, - "where": null - } - ], - "multitenancy": { - "attribute": null, - "global": null, - "strategy": null - }, - "repo": "Elixir.Mv.Repo", - "schema": null, - "table": "custom_field_values" -} \ No newline at end of file diff --git a/priv/resource_snapshots/repo/custom_fields/20251113180429.json b/priv/resource_snapshots/repo/custom_fields/20251113180429.json deleted file mode 100644 index 5a89de9..0000000 --- a/priv/resource_snapshots/repo/custom_fields/20251113180429.json +++ /dev/null @@ -1,132 +0,0 @@ -{ - "attributes": [ - { - "allow_nil?": false, - "default": "fragment(\"gen_random_uuid()\")", - "generated?": false, - "precision": null, - "primary_key?": true, - "references": null, - "scale": null, - "size": null, - "source": "id", - "type": "uuid" - }, - { - "allow_nil?": false, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "name", - "type": "text" - }, - { - "allow_nil?": false, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "slug", - "type": "text" - }, - { - "allow_nil?": false, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "value_type", - "type": "text" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "description", - "type": "text" - }, - { - "allow_nil?": false, - "default": "false", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "immutable", - "type": "boolean" - }, - { - "allow_nil?": false, - "default": "false", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "required", - "type": "boolean" - } - ], - "base_filter": null, - "check_constraints": [], - "custom_indexes": [], - "custom_statements": [], - "has_create_action": true, - "hash": "DB1D3D9F2F76F518CAEEA2CC855996CCD87FC4C8FDD3A37345CEF2980674D8F3", - "identities": [ - { - "all_tenants?": false, - "base_filter": null, - "index_name": "custom_fields_unique_name_index", - "keys": [ - { - "type": "atom", - "value": "name" - } - ], - "name": "unique_name", - "nils_distinct?": true, - "where": null - }, - { - "all_tenants?": false, - "base_filter": null, - "index_name": "custom_fields_unique_slug_index", - "keys": [ - { - "type": "atom", - "value": "slug" - } - ], - "name": "unique_slug", - "nils_distinct?": true, - "where": null - } - ], - "multitenancy": { - "attribute": null, - "global": null, - "strategy": null - }, - "repo": "Elixir.Mv.Repo", - "schema": null, - "table": "custom_fields" -} \ No newline at end of file diff --git a/test/membership/custom_field_deletion_test.exs b/test/membership/custom_field_deletion_test.exs deleted file mode 100644 index 50623b6..0000000 --- a/test/membership/custom_field_deletion_test.exs +++ /dev/null @@ -1,254 +0,0 @@ -defmodule Mv.Membership.CustomFieldDeletionTest do - @moduledoc """ - Tests for CustomField deletion with CASCADE behavior. - - Tests cover: - - Deletion of custom fields without assigned values - - Deletion of custom fields with assigned values (CASCADE) - - assigned_members_count calculation - - prepare_deletion action with count loading - - CASCADE deletion only affects specific custom field values - """ - use Mv.DataCase, async: true - - alias Mv.Membership.{CustomField, CustomFieldValue, Member} - - describe "assigned_members_count calculation" do - test "returns 0 for custom field without any values" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "test_field", - value_type: :string - }) - |> Ash.create() - - custom_field_with_count = Ash.load!(custom_field, :assigned_members_count) - assert custom_field_with_count.assigned_members_count == 0 - end - - test "returns correct count for custom field with one member" do - {:ok, member} = create_member() - {:ok, custom_field} = create_custom_field("test_field", :string) - - {:ok, _custom_field_value} = - CustomFieldValue - |> Ash.Changeset.for_create(:create, %{ - member_id: member.id, - custom_field_id: custom_field.id, - value: %{"_union_type" => "string", "_union_value" => "test"} - }) - |> Ash.create() - - custom_field_with_count = Ash.load!(custom_field, :assigned_members_count) - assert custom_field_with_count.assigned_members_count == 1 - end - - test "returns correct count for custom field with multiple members" do - {:ok, member1} = create_member() - {:ok, member2} = create_member() - {:ok, member3} = create_member() - {:ok, custom_field} = create_custom_field("test_field", :string) - - # Create custom field value for each member - for member <- [member1, member2, member3] do - {:ok, _} = - CustomFieldValue - |> Ash.Changeset.for_create(:create, %{ - member_id: member.id, - custom_field_id: custom_field.id, - value: %{"_union_type" => "string", "_union_value" => "test"} - }) - |> Ash.create() - end - - custom_field_with_count = Ash.load!(custom_field, :assigned_members_count) - assert custom_field_with_count.assigned_members_count == 3 - end - - test "counts distinct members (not multiple values per member)" do - {:ok, member} = create_member() - {:ok, custom_field} = create_custom_field("test_field", :string) - - # Create custom field value for member - {:ok, _} = - CustomFieldValue - |> Ash.Changeset.for_create(:create, %{ - member_id: member.id, - custom_field_id: custom_field.id, - value: %{"_union_type" => "string", "_union_value" => "test"} - }) - |> Ash.create() - - custom_field_with_count = Ash.load!(custom_field, :assigned_members_count) - - # Should still be 1, not 2, even if we tried to create multiple (which would fail due to uniqueness) - assert custom_field_with_count.assigned_members_count == 1 - end - end - - describe "prepare_deletion action" do - test "loads assigned_members_count for deletion preparation" do - {:ok, member} = create_member() - {:ok, custom_field} = create_custom_field("test_field", :string) - - {:ok, _} = - CustomFieldValue - |> Ash.Changeset.for_create(:create, %{ - member_id: member.id, - custom_field_id: custom_field.id, - value: %{"_union_type" => "string", "_union_value" => "test"} - }) - |> Ash.create() - - # Use prepare_deletion action - [prepared_custom_field] = - CustomField - |> Ash.Query.for_read(:prepare_deletion, %{id: custom_field.id}) - |> Ash.read!() - - assert prepared_custom_field.assigned_members_count == 1 - assert prepared_custom_field.id == custom_field.id - end - - test "returns empty list for non-existent custom field" do - non_existent_id = Ash.UUID.generate() - - result = - CustomField - |> Ash.Query.for_read(:prepare_deletion, %{id: non_existent_id}) - |> Ash.read!() - - assert result == [] - end - end - - describe "destroy_with_values action" do - test "deletes custom field without any values" do - {:ok, custom_field} = create_custom_field("test_field", :string) - - assert :ok = Ash.destroy(custom_field) - - # Verify custom field is deleted - assert {:error, _} = Ash.get(CustomField, custom_field.id) - end - - test "deletes custom field and cascades to all its values" do - {:ok, member} = create_member() - {:ok, custom_field} = create_custom_field("test_field", :string) - - {:ok, custom_field_value} = - CustomFieldValue - |> Ash.Changeset.for_create(:create, %{ - member_id: member.id, - custom_field_id: custom_field.id, - value: %{"_union_type" => "string", "_union_value" => "test"} - }) - |> Ash.create() - - # Delete custom field - assert :ok = Ash.destroy(custom_field) - - # Verify custom field is deleted - assert {:error, _} = Ash.get(CustomField, custom_field.id) - - # Verify custom field value is also deleted (CASCADE) - assert {:error, _} = Ash.get(CustomFieldValue, custom_field_value.id) - - # Verify member still exists - assert {:ok, _} = Ash.get(Member, member.id) - end - - test "deletes only values of the specific custom field" do - {:ok, member} = create_member() - {:ok, custom_field1} = create_custom_field("field1", :string) - {:ok, custom_field2} = create_custom_field("field2", :string) - - # Create value for custom_field1 - {:ok, value1} = - CustomFieldValue - |> Ash.Changeset.for_create(:create, %{ - member_id: member.id, - custom_field_id: custom_field1.id, - value: %{"_union_type" => "string", "_union_value" => "value1"} - }) - |> Ash.create() - - # Create value for custom_field2 - {:ok, value2} = - CustomFieldValue - |> Ash.Changeset.for_create(:create, %{ - member_id: member.id, - custom_field_id: custom_field2.id, - value: %{"_union_type" => "string", "_union_value" => "value2"} - }) - |> Ash.create() - - # Delete custom_field1 - assert :ok = Ash.destroy(custom_field1) - - # Verify custom_field1 and value1 are deleted - assert {:error, _} = Ash.get(CustomField, custom_field1.id) - assert {:error, _} = Ash.get(CustomFieldValue, value1.id) - - # Verify custom_field2 and value2 still exist - assert {:ok, _} = Ash.get(CustomField, custom_field2.id) - assert {:ok, _} = Ash.get(CustomFieldValue, value2.id) - end - - test "deletes custom field with values from multiple members" do - {:ok, member1} = create_member() - {:ok, member2} = create_member() - {:ok, member3} = create_member() - {:ok, custom_field} = create_custom_field("test_field", :string) - - # Create value for each member - values = - for member <- [member1, member2, member3] do - {:ok, value} = - CustomFieldValue - |> Ash.Changeset.for_create(:create, %{ - member_id: member.id, - custom_field_id: custom_field.id, - value: %{"_union_type" => "string", "_union_value" => "test"} - }) - |> Ash.create() - - value - end - - # Delete custom field - assert :ok = Ash.destroy(custom_field) - - # Verify all values are deleted - for value <- values do - assert {:error, _} = Ash.get(CustomFieldValue, value.id) - end - - # Verify all members still exist - for member <- [member1, member2, member3] do - assert {:ok, _} = Ash.get(Member, member.id) - end - end - end - - # Helper functions - defp create_member do - Member - |> Ash.Changeset.for_create(:create_member, %{ - first_name: "Test", - last_name: "User#{System.unique_integer([:positive])}", - email: "test#{System.unique_integer([:positive])}@example.com" - }) - |> Ash.create() - end - - defp create_custom_field(name, value_type) do - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "#{name}_#{System.unique_integer([:positive])}", - value_type: value_type - }) - |> Ash.create() - end -end diff --git a/test/membership/custom_field_slug_test.exs b/test/membership/custom_field_slug_test.exs deleted file mode 100644 index ae6c42e..0000000 --- a/test/membership/custom_field_slug_test.exs +++ /dev/null @@ -1,397 +0,0 @@ -defmodule Mv.Membership.CustomFieldSlugTest do - @moduledoc """ - Tests for automatic slug generation on CustomField resource. - - This test suite verifies: - 1. Slugs are automatically generated from the name attribute - 2. Slugs are unique (cannot have duplicates) - 3. Slugs are immutable (don't change when name changes) - 4. Slugs handle various edge cases (unicode, special chars, etc.) - 5. Slugs can be used for lookups - """ - use Mv.DataCase, async: true - - alias Mv.Membership.CustomField - - describe "automatic slug generation on create" do - test "generates slug from name with simple ASCII text" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Mobile Phone", - value_type: :string - }) - |> Ash.create() - - assert custom_field.slug == "mobile-phone" - end - - test "generates slug from name with German umlauts" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Café Müller", - value_type: :string - }) - |> Ash.create() - - assert custom_field.slug == "cafe-muller" - end - - test "generates slug with lowercase conversion" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "TEST NAME", - value_type: :string - }) - |> Ash.create() - - assert custom_field.slug == "test-name" - end - - test "generates slug by removing special characters" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "E-Mail & Address!", - value_type: :string - }) - |> Ash.create() - - assert custom_field.slug == "e-mail-address" - end - - test "generates slug by replacing multiple spaces with single hyphen" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Multiple Spaces", - value_type: :string - }) - |> Ash.create() - - assert custom_field.slug == "multiple-spaces" - end - - test "trims leading and trailing hyphens" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "-Test-", - value_type: :string - }) - |> Ash.create() - - assert custom_field.slug == "test" - end - - test "handles unicode characters properly (ß becomes ss)" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Straße", - value_type: :string - }) - |> Ash.create() - - assert custom_field.slug == "strasse" - end - end - - describe "slug uniqueness" do - test "prevents creating custom field with duplicate slug" do - # Create first custom field - {:ok, _custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Test", - value_type: :string - }) - |> Ash.create() - - # Attempt to create second custom field with same slug (different case in name) - assert {:error, %Ash.Error.Invalid{} = error} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "test", - value_type: :integer - }) - |> Ash.create() - - assert Exception.message(error) =~ "has already been taken" - end - - test "allows custom fields with different slugs" do - {:ok, custom_field1} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Test One", - value_type: :string - }) - |> Ash.create() - - {:ok, custom_field2} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Test Two", - value_type: :string - }) - |> Ash.create() - - assert custom_field1.slug == "test-one" - assert custom_field2.slug == "test-two" - assert custom_field1.slug != custom_field2.slug - end - - test "prevents duplicate slugs when names differ only in special characters" do - {:ok, custom_field1} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Test!!!", - value_type: :string - }) - |> Ash.create() - - assert custom_field1.slug == "test" - - # Second custom field with name that generates the same slug should fail - assert {:error, %Ash.Error.Invalid{} = error} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Test???", - value_type: :string - }) - |> Ash.create() - - # Should fail with uniqueness constraint error - assert Exception.message(error) =~ "has already been taken" - end - end - - describe "slug immutability" do - test "slug cannot be manually set on create" do - # Attempting to set slug manually should fail because slug is not writable - result = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Test", - value_type: :string, - slug: "custom-slug" - }) - |> Ash.create() - - # Should fail because slug is not an accepted input - assert {:error, %Ash.Error.Invalid{}} = result - assert Exception.message(elem(result, 1)) =~ "No such input" - end - - test "slug does not change when name is updated" do - # Create custom field - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Original Name", - value_type: :string - }) - |> Ash.create() - - original_slug = custom_field.slug - assert original_slug == "original-name" - - # Update the name - {:ok, updated_custom_field} = - custom_field - |> Ash.Changeset.for_update(:update, %{ - name: "New Different Name" - }) - |> Ash.update() - - # Slug should remain unchanged - assert updated_custom_field.slug == original_slug - assert updated_custom_field.slug == "original-name" - assert updated_custom_field.name == "New Different Name" - end - - test "slug cannot be manually updated" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Test", - value_type: :string - }) - |> Ash.create() - - original_slug = custom_field.slug - assert original_slug == "test" - - # Attempt to manually update slug should fail because slug is not writable - result = - custom_field - |> Ash.Changeset.for_update(:update, %{ - slug: "new-slug" - }) - |> Ash.update() - - # Should fail because slug is not an accepted input - assert {:error, %Ash.Error.Invalid{}} = result - assert Exception.message(elem(result, 1)) =~ "No such input" - - # Reload to verify slug hasn't changed - reloaded = Ash.get!(CustomField, custom_field.id) - assert reloaded.slug == "test" - end - end - - describe "slug edge cases" do - test "handles very long names by truncating slug" do - # Create a name at the maximum length (100 chars) - long_name = String.duplicate("abcdefghij", 10) - # 100 characters exactly - - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: long_name, - value_type: :string - }) - |> Ash.create() - - # Slug should be truncated to maximum 100 characters - assert String.length(custom_field.slug) <= 100 - # Should be the full slugified version since name is exactly 100 chars - assert custom_field.slug == long_name - end - - test "rejects name with only special characters" do - # When name contains only special characters, slug would be empty - # This should fail validation - assert {:error, %Ash.Error.Invalid{} = error} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "!!!", - value_type: :string - }) - |> Ash.create() - - # Should fail because slug would be empty - error_message = Exception.message(error) - assert error_message =~ "Slug cannot be empty" or error_message =~ "is required" - end - - test "handles mixed special characters and text" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Test@#$%Name", - value_type: :string - }) - |> Ash.create() - - # slugify keeps the hyphen between words - assert custom_field.slug == "test-name" - end - - test "handles numbers in name" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Field 123 Test", - value_type: :string - }) - |> Ash.create() - - assert custom_field.slug == "field-123-test" - end - - test "handles consecutive hyphens in name" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Test---Name", - value_type: :string - }) - |> Ash.create() - - # Should reduce multiple hyphens to single hyphen - assert custom_field.slug == "test-name" - end - - test "handles name with dots and underscores" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "test.field_name", - value_type: :string - }) - |> Ash.create() - - # Dots and underscores should be handled (either kept or converted) - assert custom_field.slug =~ ~r/^[a-z0-9-]+$/ - end - end - - describe "slug in queries and responses" do - test "slug is included in struct after create" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Test", - value_type: :string - }) - |> Ash.create() - - # Slug should be present in the struct - assert Map.has_key?(custom_field, :slug) - assert custom_field.slug != nil - end - - test "can load custom field and slug is present" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Test", - value_type: :string - }) - |> Ash.create() - - # Load it back - loaded_custom_field = Ash.get!(CustomField, custom_field.id) - - assert loaded_custom_field.slug == "test" - end - - test "slug is returned in list queries" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Test", - value_type: :string - }) - |> Ash.create() - - custom_fields = Ash.read!(CustomField) - - found = Enum.find(custom_fields, &(&1.id == custom_field.id)) - assert found.slug == "test" - end - end - - describe "slug-based lookup (future feature)" do - @tag :skip - test "can find custom field by slug" do - {:ok, custom_field} = - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "Test Field", - value_type: :string - }) - |> Ash.create() - - # This test is for future implementation - # We might add a custom action like :by_slug - found = Ash.get!(CustomField, custom_field.slug, load: [:slug]) - assert found.id == custom_field.id - end - end -end diff --git a/test/mv_web/live/custom_field_live/deletion_test.exs b/test/mv_web/live/custom_field_live/deletion_test.exs deleted file mode 100644 index f0317e0..0000000 --- a/test/mv_web/live/custom_field_live/deletion_test.exs +++ /dev/null @@ -1,251 +0,0 @@ -defmodule MvWeb.CustomFieldLive.DeletionTest do - @moduledoc """ - Tests for CustomFieldLive.Index deletion modal and slug confirmation. - - Tests cover: - - Opening deletion confirmation modal - - Displaying correct member count - - Slug confirmation input - - Successful deletion with correct slug - - Failed deletion with incorrect slug - - Canceling deletion - - Button states (enabled/disabled based on slug match) - """ - use MvWeb.ConnCase, async: true - - import Phoenix.LiveViewTest - - alias Mv.Membership.{CustomField, CustomFieldValue, Member} - - setup do - # Create admin user for testing - {:ok, user} = - Mv.Accounts.User - |> Ash.Changeset.for_create(:register_with_password, %{ - email: "admin#{System.unique_integer([:positive])}@mv.local", - password: "testpassword123" - }) - |> Ash.create() - - conn = log_in_user(build_conn(), user) - %{conn: conn, user: user} - end - - describe "delete button and modal" do - test "opens modal with correct member count when delete is clicked", %{conn: conn} do - {:ok, member} = create_member() - {:ok, custom_field} = create_custom_field("test_field", :string) - - # Create custom field value - create_custom_field_value(member, custom_field, "test") - - {:ok, view, _html} = live(conn, ~p"/custom_fields") - - # Click delete button - view - |> element("a", "Delete") - |> render_click() - - # Modal should be visible - assert has_element?(view, "#delete-custom-field-modal") - - # Should show correct member count (1 member) - assert render(view) =~ "1 member has a value assigned for this custom field" - - # Should show the slug - assert render(view) =~ custom_field.slug - end - - test "shows correct plural form for multiple members", %{conn: conn} do - {:ok, member1} = create_member() - {:ok, member2} = create_member() - {:ok, custom_field} = create_custom_field("test_field", :string) - - # Create values for both members - create_custom_field_value(member1, custom_field, "test1") - create_custom_field_value(member2, custom_field, "test2") - - {:ok, view, _html} = live(conn, ~p"/custom_fields") - - view - |> element("a", "Delete") - |> render_click() - - # Should show plural form - assert render(view) =~ "2 members have values assigned for this custom field" - end - - test "shows 0 members for custom field without values", %{conn: conn} do - {:ok, _custom_field} = create_custom_field("test_field", :string) - - {:ok, view, _html} = live(conn, ~p"/custom_fields") - - view - |> element("a", "Delete") - |> render_click() - - # Should show 0 members - assert render(view) =~ "0 members have values assigned for this custom field" - end - end - - describe "slug confirmation input" do - test "updates confirmation state when typing", %{conn: conn} do - {:ok, custom_field} = create_custom_field("test_field", :string) - - {:ok, view, _html} = live(conn, ~p"/custom_fields") - - view - |> element("a", "Delete") - |> render_click() - - # Type in slug input - view - |> render_change("update_slug_confirmation", %{"slug" => custom_field.slug}) - - # Confirm button should be enabled now (no disabled attribute) - html = render(view) - refute html =~ ~r/disabled(?:=""|(?!\w))/ - end - - test "delete button is disabled when slug doesn't match", %{conn: conn} do - {:ok, _custom_field} = create_custom_field("test_field", :string) - - {:ok, view, _html} = live(conn, ~p"/custom_fields") - - view - |> element("a", "Delete") - |> render_click() - - # Type wrong slug - view - |> render_change("update_slug_confirmation", %{"slug" => "wrong-slug"}) - - # Button should be disabled - html = render(view) - assert html =~ ~r/disabled(?:=""|(?!\w))/ - end - end - - describe "confirm deletion" do - test "successfully deletes custom field with correct slug", %{conn: conn} do - {:ok, member} = create_member() - {:ok, custom_field} = create_custom_field("test_field", :string) - {:ok, custom_field_value} = create_custom_field_value(member, custom_field, "test") - - {:ok, view, _html} = live(conn, ~p"/custom_fields") - - # Open modal - view - |> element("a", "Delete") - |> render_click() - - # Enter correct slug - view - |> render_change("update_slug_confirmation", %{"slug" => custom_field.slug}) - - # Click confirm - view - |> element("button", "Delete Custom Field and All Values") - |> render_click() - - # Should show success message - assert render(view) =~ "Custom field deleted successfully" - - # Custom field should be gone from database - assert {:error, _} = Ash.get(CustomField, custom_field.id) - - # Custom field value should also be gone (CASCADE) - assert {:error, _} = Ash.get(CustomFieldValue, custom_field_value.id) - - # Member should still exist - assert {:ok, _} = Ash.get(Member, member.id) - end - - test "shows error when slug doesn't match", %{conn: conn} do - {:ok, custom_field} = create_custom_field("test_field", :string) - - {:ok, view, _html} = live(conn, ~p"/custom_fields") - - view - |> element("a", "Delete") - |> render_click() - - # Enter wrong slug - view - |> render_change("update_slug_confirmation", %{"slug" => "wrong-slug"}) - - # Try to confirm (button should be disabled, but test the handler anyway) - view - |> render_click("confirm_delete", %{}) - - # Should show error message - assert render(view) =~ "Slug does not match" - - # Custom field should still exist - assert {:ok, _} = Ash.get(CustomField, custom_field.id) - end - end - - describe "cancel deletion" do - test "closes modal without deleting", %{conn: conn} do - {:ok, custom_field} = create_custom_field("test_field", :string) - - {:ok, view, _html} = live(conn, ~p"/custom_fields") - - view - |> element("a", "Delete") - |> render_click() - - # Modal should be visible - assert has_element?(view, "#delete-custom-field-modal") - - # Click cancel - view - |> element("button", "Cancel") - |> render_click() - - # Modal should be gone - refute has_element?(view, "#delete-custom-field-modal") - - # Custom field should still exist - assert {:ok, _} = Ash.get(CustomField, custom_field.id) - end - end - - # Helper functions - defp create_member do - Member - |> Ash.Changeset.for_create(:create_member, %{ - first_name: "Test", - last_name: "User#{System.unique_integer([:positive])}", - email: "test#{System.unique_integer([:positive])}@example.com" - }) - |> Ash.create() - end - - defp create_custom_field(name, value_type) do - CustomField - |> Ash.Changeset.for_create(:create, %{ - name: "#{name}_#{System.unique_integer([:positive])}", - value_type: value_type - }) - |> Ash.create() - end - - defp create_custom_field_value(member, custom_field, value) do - CustomFieldValue - |> Ash.Changeset.for_create(:create, %{ - member_id: member.id, - custom_field_id: custom_field.id, - value: %{"_union_type" => "string", "_union_value" => value} - }) - |> Ash.create() - end - - defp log_in_user(conn, user) do - conn - |> Phoenix.ConnTest.init_test_session(%{}) - |> AshAuthentication.Plug.Helpers.store_in_session(user) - end -end