diff --git a/lib/mv_web/live/member_live/index.html.heex b/lib/mv_web/live/member_live/index.html.heex
index c8ba7e4..1557ed9 100644
--- a/lib/mv_web/live/member_live/index.html.heex
+++ b/lib/mv_web/live/member_live/index.html.heex
@@ -239,24 +239,6 @@
>
{member.city}
- <:col
- :let={member}
- :if={:phone_number in @member_fields_visible}
- label={
- ~H"""
- <.live_component
- module={MvWeb.Components.SortHeaderComponent}
- id={:sort_phone_number}
- field={:phone_number}
- label={gettext("Phone Number")}
- sort_field={@sort_field}
- sort_order={@sort_order}
- />
- """
- }
- >
- {member.phone_number}
-
<:col
:let={member}
:if={:join_date in @member_fields_visible}
diff --git a/lib/mv_web/live/member_live/show.ex b/lib/mv_web/live/member_live/show.ex
index c2af0a9..997cb1a 100644
--- a/lib/mv_web/live/member_live/show.ex
+++ b/lib/mv_web/live/member_live/show.ex
@@ -35,7 +35,7 @@ defmodule MvWeb.MemberLive.Show do
- {@member.first_name} {@member.last_name}
+ {MvWeb.Helpers.MemberHelpers.display_name(@member)}
<.button variant="primary" navigate={~p"/members/#{@member}/edit?return_to=show"}>
@@ -104,11 +104,6 @@ defmodule MvWeb.MemberLive.Show do
- <.data_field label={gettext("Phone")} value={@member.phone_number} />
-
<.data_field
diff --git a/lib/mv_web/live/user_live/form.ex b/lib/mv_web/live/user_live/form.ex
index 0639e75..f0cc1ce 100644
--- a/lib/mv_web/live/user_live/form.ex
+++ b/lib/mv_web/live/user_live/form.ex
@@ -131,7 +131,7 @@ defmodule MvWeb.UserLive.Form do
- {@user.member.first_name} {@user.member.last_name}
+ {MvWeb.Helpers.MemberHelpers.display_name(@user.member)}
{@user.member.email}
@@ -210,7 +210,7 @@ defmodule MvWeb.UserLive.Form do
)
]}
>
-
{member.first_name} {member.last_name}
+
{MvWeb.Helpers.MemberHelpers.display_name(member)}
{member.email}
<% end %>
@@ -438,7 +438,7 @@ defmodule MvWeb.UserLive.Form do
member_name =
if selected_member,
- do: "#{selected_member.first_name} #{selected_member.last_name}",
+ do: MvWeb.Helpers.MemberHelpers.display_name(selected_member),
else: ""
# Store the selected member ID and name in socket state and clear unlink flag
diff --git a/lib/mv_web/live/user_live/index.html.heex b/lib/mv_web/live/user_live/index.html.heex
index 9a98159..e7fd72e 100644
--- a/lib/mv_web/live/user_live/index.html.heex
+++ b/lib/mv_web/live/user_live/index.html.heex
@@ -51,7 +51,7 @@
<:col :let={user} label={gettext("Linked Member")}>
<%= if user.member do %>
- {user.member.first_name} {user.member.last_name}
+ {MvWeb.Helpers.MemberHelpers.display_name(user.member)}
<% else %>
{gettext("No member linked")}
<% end %>
diff --git a/lib/mv_web/live/user_live/show.ex b/lib/mv_web/live/user_live/show.ex
index 777def1..9eaa4fa 100644
--- a/lib/mv_web/live/user_live/show.ex
+++ b/lib/mv_web/live/user_live/show.ex
@@ -57,7 +57,7 @@ defmodule MvWeb.UserLive.Show do
class="text-blue-600 underline hover:text-blue-800"
>
<.icon name="hero-users" class="inline w-4 h-4 mr-1" />
- {@user.member.first_name} {@user.member.last_name}
+ {MvWeb.Helpers.MemberHelpers.display_name(@user.member)}
<% else %>
{gettext("No member linked")}
diff --git a/lib/mv_web/translations/member_fields.ex b/lib/mv_web/translations/member_fields.ex
index f10e0d2..2d6834a 100644
--- a/lib/mv_web/translations/member_fields.ex
+++ b/lib/mv_web/translations/member_fields.ex
@@ -20,7 +20,6 @@ defmodule MvWeb.Translations.MemberFields do
def label(:first_name), do: gettext("First Name")
def label(:last_name), do: gettext("Last Name")
def label(:email), do: gettext("Email")
- def label(:phone_number), do: gettext("Phone")
def label(:join_date), do: gettext("Join Date")
def label(:exit_date), do: gettext("Exit Date")
def label(:notes), do: gettext("Notes")
diff --git a/priv/gettext/de/LC_MESSAGES/default.po b/priv/gettext/de/LC_MESSAGES/default.po
index ef28ae8..9467ed7 100644
--- a/priv/gettext/de/LC_MESSAGES/default.po
+++ b/priv/gettext/de/LC_MESSAGES/default.po
@@ -150,11 +150,6 @@ msgstr "Notizen"
msgid "Paid"
msgstr "Bezahlt"
-#: lib/mv_web/live/member_live/index.html.heex
-#, elixir-autogen, elixir-format
-msgid "Phone Number"
-msgstr "Telefonnummer"
-
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/translations/member_fields.ex
@@ -842,13 +837,6 @@ msgstr "Zahlungen"
msgid "Personal Data"
msgstr "Persönliche Daten"
-#: lib/mv_web/live/member_live/form.ex
-#: lib/mv_web/live/member_live/show.ex
-#: lib/mv_web/translations/member_fields.ex
-#, elixir-autogen, elixir-format
-msgid "Phone"
-msgstr "Telefon"
-
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
@@ -1903,6 +1891,18 @@ msgstr "Nicht gesetzt"
#~ msgid "Pending"
#~ msgstr "Ausstehend"
+#~ #: lib/mv_web/live/member_live/form.ex
+#~ #: lib/mv_web/live/member_live/show.ex
+#~ #: lib/mv_web/translations/member_fields.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Phone"
+#~ msgstr "Telefon"
+
+#~ #: lib/mv_web/live/member_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Phone Number"
+#~ msgstr "Telefonnummer"
+
#~ #: lib/mv_web/live/contribution_settings_live.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Quarterly Interval - Joining Period Excluded"
diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot
index be36eb6..77931d4 100644
--- a/priv/gettext/default.pot
+++ b/priv/gettext/default.pot
@@ -151,11 +151,6 @@ msgstr ""
msgid "Paid"
msgstr ""
-#: lib/mv_web/live/member_live/index.html.heex
-#, elixir-autogen, elixir-format
-msgid "Phone Number"
-msgstr ""
-
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/translations/member_fields.ex
@@ -843,13 +838,6 @@ msgstr ""
msgid "Personal Data"
msgstr ""
-#: lib/mv_web/live/member_live/form.ex
-#: lib/mv_web/live/member_live/show.ex
-#: lib/mv_web/translations/member_fields.ex
-#, elixir-autogen, elixir-format
-msgid "Phone"
-msgstr ""
-
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po
index 9c2dc9a..5846f7b 100644
--- a/priv/gettext/en/LC_MESSAGES/default.po
+++ b/priv/gettext/en/LC_MESSAGES/default.po
@@ -151,11 +151,6 @@ msgstr ""
msgid "Paid"
msgstr ""
-#: lib/mv_web/live/member_live/index.html.heex
-#, elixir-autogen, elixir-format
-msgid "Phone Number"
-msgstr ""
-
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/translations/member_fields.ex
@@ -843,13 +838,6 @@ msgstr ""
msgid "Personal Data"
msgstr ""
-#: lib/mv_web/live/member_live/form.ex
-#: lib/mv_web/live/member_live/show.ex
-#: lib/mv_web/translations/member_fields.ex
-#, elixir-autogen, elixir-format, fuzzy
-msgid "Phone"
-msgstr ""
-
#: lib/mv_web/live/member_live/form.ex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format, fuzzy
@@ -1827,46 +1815,62 @@ msgstr ""
msgid "Not set"
msgstr ""
+#~ #: lib/mv_web/live/member_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Show current cycle"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/member_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Unpaid in last cycle"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/custom_field_live/index_component.ex
+#~ #, elixir-autogen, elixir-format, fuzzy
+#~ msgid "New Custom field"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/member_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Show Last/Current Cycle Payment Status"
+#~ msgstr ""
+
#~ #: lib/mv_web/live/components/payment_filter_component.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "All payment statuses"
#~ msgstr ""
-#~ #: lib/mv_web/live/custom_field_live/show.ex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Auto-generated identifier (immutable)"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/contribution_settings_live.ex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Configure global settings for membership contributions."
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/member_live/form.ex
-#~ #: lib/mv_web/live/member_live/show.ex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Contribution"
-#~ msgstr ""
-
-#~ #: lib/mv_web/components/layouts/navbar.ex
-#~ #: lib/mv_web/live/contribution_settings_live.ex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Contribution Settings"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/contribution_settings_live.ex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Contribution start"
-#~ msgstr ""
-
#~ #: lib/mv_web/live/member_live/index.html.heex
#~ #, elixir-autogen, elixir-format
#~ msgid "Copy emails"
#~ msgstr ""
+#~ #: lib/mv_web/live/member_live/form.ex
+#~ #: lib/mv_web/live/member_live/show.ex
+#~ #: lib/mv_web/translations/member_fields.ex
+#~ #, elixir-autogen, elixir-format, fuzzy
+#~ msgid "Phone"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/member_live/show.ex
+#~ #, elixir-autogen, elixir-format, fuzzy
+#~ msgid "Pending"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/member_live/show.ex
+#~ #, elixir-autogen, elixir-format, fuzzy
+#~ msgid "Payment Cycle"
+#~ msgstr ""
+
#~ #: lib/mv_web/live/contribution_settings_live.ex
#~ #, elixir-autogen, elixir-format
-#~ msgid "Default Contribution Type"
+#~ msgid "View Example Member"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/member_live/form.ex
+#~ #: lib/mv_web/live/member_live/show.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "This data is for demonstration purposes only (mockup)."
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_settings_live.ex
@@ -1875,6 +1879,11 @@ msgstr ""
#~ msgid "Edit amount"
#~ msgstr ""
+#~ #: lib/mv_web/live/contribution_settings_live.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Quarterly Interval - Joining Period Excluded"
+#~ msgstr ""
+
#~ #: lib/mv_web/live/contribution_settings_live.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Example: Member Contribution View"
@@ -1885,20 +1894,20 @@ msgstr ""
#~ msgid "Failed to delete some cycles: %{errors}"
#~ msgstr ""
+#~ #: lib/mv_web/live/member_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Switch to current cycle"
+#~ msgstr ""
+
#~ #: lib/mv_web/live/membership_fee_settings_live.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Failed to save settings. Please check the errors below."
#~ msgstr ""
-#~ #: lib/mv_web/live/user_live/index.html.heex
-#~ #: lib/mv_web/live/user_live/show.ex
+#~ #: lib/mv_web/components/layouts/navbar.ex
+#~ #: lib/mv_web/live/contribution_settings_live.ex
#~ #, elixir-autogen, elixir-format
-#~ msgid "Generated periods"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/custom_field_live/form_component.ex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Immutable"
+#~ msgid "Contribution Settings"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_settings_live.ex
@@ -1906,80 +1915,9 @@ msgstr ""
#~ msgid "Include joining period"
#~ msgstr ""
-#~ #: lib/mv_web/live/custom_field_live/index_component.ex
-#~ #, elixir-autogen, elixir-format, fuzzy
-#~ msgid "New Custom field"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/components/payment_filter_component.ex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Not paid"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/member_live/show.ex
-#~ #, elixir-autogen, elixir-format, fuzzy
-#~ msgid "Payment Cycle"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/member_live/show.ex
-#~ #, elixir-autogen, elixir-format, fuzzy
-#~ msgid "Pending"
-#~ msgstr ""
-
#~ #: lib/mv_web/live/contribution_settings_live.ex
#~ #, elixir-autogen, elixir-format
-#~ msgid "Quarterly Interval - Joining Period Excluded"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/member_live/index.html.heex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Show Last/Current Cycle Payment Status"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/member_live/index.html.heex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Show current cycle"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/member_live/index.html.heex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Show last completed cycle"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/member_live/index.html.heex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Switch to current cycle"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/member_live/index.html.heex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Switch to last completed cycle"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/member_live/form.ex
-#~ #: lib/mv_web/live/member_live/show.ex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "This data is for demonstration purposes only (mockup)."
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/member_live/index.html.heex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Unpaid in current cycle"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/member_live/index.html.heex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Unpaid in last cycle"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/contribution_settings_live.ex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "View Example Member"
-#~ msgstr ""
-
-#~ #: lib/mv_web/live/contribution_settings_live.ex
-#~ #, elixir-autogen, elixir-format
-#~ msgid "Yearly Interval - Joining Period Included"
+#~ msgid "Contribution start"
#~ msgstr ""
#~ #: lib/mv_web/live/member_live/form.ex
@@ -1988,7 +1926,69 @@ msgstr ""
#~ msgid "monthly"
#~ msgstr ""
+#~ #: lib/mv_web/live/member_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Show last completed cycle"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/components/payment_filter_component.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Not paid"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/contribution_settings_live.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Yearly Interval - Joining Period Included"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/custom_field_live/form_component.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Immutable"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/member_live/form.ex
+#~ #: lib/mv_web/live/member_live/show.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Contribution"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/user_live/index.html.heex
+#~ #: lib/mv_web/live/user_live/show.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Generated periods"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/member_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Switch to last completed cycle"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/contribution_settings_live.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Configure global settings for membership contributions."
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/custom_field_live/show.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Auto-generated identifier (immutable)"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/contribution_settings_live.ex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Default Contribution Type"
+#~ msgstr ""
+
#~ #: lib/mv_web/live/member_live/form.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "yearly"
#~ msgstr ""
+
+#~ #: lib/mv_web/live/member_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Phone Number"
+#~ msgstr ""
+
+#~ #: lib/mv_web/live/member_live/index.html.heex
+#~ #, elixir-autogen, elixir-format
+#~ msgid "Unpaid in current cycle"
+#~ msgstr ""
diff --git a/priv/repo/migrations/20260102155350_remove_phone_number_and_make_fields_optional.exs b/priv/repo/migrations/20260102155350_remove_phone_number_and_make_fields_optional.exs
new file mode 100644
index 0000000..5943b78
--- /dev/null
+++ b/priv/repo/migrations/20260102155350_remove_phone_number_and_make_fields_optional.exs
@@ -0,0 +1,404 @@
+defmodule Mv.Repo.Migrations.RemovePhoneNumberAndMakeFieldsOptional do
+ @moduledoc """
+ Removes phone_number field from members table and makes first_name/last_name optional.
+
+ This migration:
+ 1. Removes phone_number column from members table
+ 2. Makes first_name and last_name columns nullable
+ 3. Updates members_search_vector_trigger() function to remove phone_number
+ 4. Updates update_member_search_vector_from_custom_field_value() function to remove phone_number
+ 5. Updates existing search_vector values for all members
+ """
+
+ use Ecto.Migration
+
+ def up do
+ # Update the main trigger function to remove phone_number
+ execute("""
+ CREATE OR REPLACE FUNCTION members_search_vector_trigger() RETURNS trigger AS $$
+ DECLARE
+ custom_values_text text;
+ BEGIN
+ -- Aggregate all custom field values for this member
+ -- Support both formats: _union_type/_union_value (Ash format) and type/value (legacy)
+ -- ->> operator always returns TEXT directly (no need for -> + ::text fallback)
+ SELECT string_agg(
+ CASE
+ WHEN value ? '_union_value' THEN value->>'_union_value'
+ WHEN value ? 'value' THEN value->>'value'
+ ELSE ''
+ END,
+ ' '
+ )
+ INTO custom_values_text
+ FROM custom_field_values
+ WHERE member_id = NEW.id AND value IS NOT NULL;
+
+ -- Build search_vector with member fields and custom field values
+ NEW.search_vector :=
+ setweight(to_tsvector('simple', coalesce(NEW.first_name, '')), 'A') ||
+ setweight(to_tsvector('simple', coalesce(NEW.last_name, '')), 'A') ||
+ setweight(to_tsvector('simple', coalesce(NEW.email, '')), 'B') ||
+ setweight(to_tsvector('simple', coalesce(NEW.join_date::text, '')), 'D') ||
+ setweight(to_tsvector('simple', coalesce(NEW.exit_date::text, '')), 'D') ||
+ setweight(to_tsvector('simple', coalesce(NEW.notes, '')), 'B') ||
+ setweight(to_tsvector('simple', coalesce(NEW.city, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(NEW.street, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(NEW.house_number::text, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(NEW.postal_code::text, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(custom_values_text, '')), 'C');
+ RETURN NEW;
+ END
+ $$ LANGUAGE plpgsql;
+ """)
+
+ # Update trigger function to remove phone_number
+ execute("""
+ CREATE OR REPLACE FUNCTION update_member_search_vector_from_custom_field_value() RETURNS trigger AS $$
+ DECLARE
+ member_id_val uuid;
+ member_first_name text;
+ member_last_name text;
+ member_email text;
+ member_join_date date;
+ member_exit_date date;
+ member_notes text;
+ member_city text;
+ member_street text;
+ member_house_number text;
+ member_postal_code text;
+ custom_values_text text;
+ old_value_text text;
+ new_value_text text;
+ BEGIN
+ -- Get member ID from trigger context
+ member_id_val := COALESCE(NEW.member_id, OLD.member_id);
+
+ -- Optimization: For UPDATE operations, check if value actually changed
+ -- If value hasn't changed, we can skip the expensive re-aggregation
+ IF TG_OP = 'UPDATE' THEN
+ -- Extract OLD value for comparison (handle both JSONB formats)
+ -- ->> operator always returns TEXT directly
+ old_value_text := COALESCE(
+ NULLIF(OLD.value->>'_union_value', ''),
+ NULLIF(OLD.value->>'value', ''),
+ ''
+ );
+
+ -- Extract NEW value for comparison (handle both JSONB formats)
+ new_value_text := COALESCE(
+ NULLIF(NEW.value->>'_union_value', ''),
+ NULLIF(NEW.value->>'value', ''),
+ ''
+ );
+
+ -- Check if value, member_id, or custom_field_id actually changed
+ -- If nothing changed, skip expensive re-aggregation
+ IF (old_value_text IS NOT DISTINCT FROM new_value_text) AND
+ (OLD.member_id IS NOT DISTINCT FROM NEW.member_id) AND
+ (OLD.custom_field_id IS NOT DISTINCT FROM NEW.custom_field_id) THEN
+ RETURN COALESCE(NEW, OLD);
+ END IF;
+ END IF;
+
+ -- Fetch only required fields instead of full record (performance optimization)
+ SELECT
+ first_name,
+ last_name,
+ email,
+ join_date,
+ exit_date,
+ notes,
+ city,
+ street,
+ house_number,
+ postal_code
+ INTO
+ member_first_name,
+ member_last_name,
+ member_email,
+ member_join_date,
+ member_exit_date,
+ member_notes,
+ member_city,
+ member_street,
+ member_house_number,
+ member_postal_code
+ FROM members
+ WHERE id = member_id_val;
+
+ -- Aggregate all custom field values for this member
+ -- Support both formats: _union_type/_union_value (Ash format) and type/value (legacy)
+ -- ->> operator always returns TEXT directly
+ SELECT string_agg(
+ CASE
+ WHEN value ? '_union_value' THEN value->>'_union_value'
+ WHEN value ? 'value' THEN value->>'value'
+ ELSE ''
+ END,
+ ' '
+ )
+ INTO custom_values_text
+ FROM custom_field_values
+ WHERE member_id = member_id_val AND value IS NOT NULL;
+
+ -- Update the search_vector for the affected member
+ UPDATE members
+ SET search_vector =
+ setweight(to_tsvector('simple', coalesce(member_first_name, '')), 'A') ||
+ setweight(to_tsvector('simple', coalesce(member_last_name, '')), 'A') ||
+ setweight(to_tsvector('simple', coalesce(member_email, '')), 'B') ||
+ setweight(to_tsvector('simple', coalesce(member_join_date::text, '')), 'D') ||
+ setweight(to_tsvector('simple', coalesce(member_exit_date::text, '')), 'D') ||
+ setweight(to_tsvector('simple', coalesce(member_notes, '')), 'B') ||
+ setweight(to_tsvector('simple', coalesce(member_city, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(member_street, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(member_house_number::text, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(member_postal_code::text, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(custom_values_text, '')), 'C')
+ WHERE id = member_id_val;
+
+ RETURN COALESCE(NEW, OLD);
+ END
+ $$ LANGUAGE plpgsql;
+ """)
+
+ # Update existing search_vector values for all members
+ execute("""
+ UPDATE members m
+ SET search_vector =
+ setweight(to_tsvector('simple', coalesce(m.first_name, '')), 'A') ||
+ setweight(to_tsvector('simple', coalesce(m.last_name, '')), 'A') ||
+ setweight(to_tsvector('simple', coalesce(m.email, '')), 'B') ||
+ setweight(to_tsvector('simple', coalesce(m.join_date::text, '')), 'D') ||
+ setweight(to_tsvector('simple', coalesce(m.exit_date::text, '')), 'D') ||
+ setweight(to_tsvector('simple', coalesce(m.notes, '')), 'B') ||
+ setweight(to_tsvector('simple', coalesce(m.city, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(m.street, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(m.house_number::text, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(m.postal_code::text, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(
+ (SELECT string_agg(
+ CASE
+ WHEN value ? '_union_value' THEN value->>'_union_value'
+ WHEN value ? 'value' THEN value->>'value'
+ ELSE ''
+ END,
+ ' '
+ )
+ FROM custom_field_values
+ WHERE member_id = m.id AND value IS NOT NULL),
+ ''
+ )), 'C')
+ """)
+
+ # Make first_name and last_name nullable
+ execute("ALTER TABLE members ALTER COLUMN first_name DROP NOT NULL")
+ execute("ALTER TABLE members ALTER COLUMN last_name DROP NOT NULL")
+
+ # Remove phone_number column
+ alter table(:members) do
+ remove :phone_number
+ end
+ end
+
+ def down do
+ # Set default values for NULL fields before restoring NOT NULL constraint
+ # This prevents the migration from failing if NULL values exist
+ execute("UPDATE members SET first_name = '' WHERE first_name IS NULL")
+ execute("UPDATE members SET last_name = '' WHERE last_name IS NULL")
+
+ # Restore first_name and last_name as NOT NULL
+ execute("ALTER TABLE members ALTER COLUMN first_name SET NOT NULL")
+ execute("ALTER TABLE members ALTER COLUMN last_name SET NOT NULL")
+
+ # Add phone_number column back
+ alter table(:members) do
+ add :phone_number, :text
+ end
+
+ # Restore trigger functions with phone_number
+ execute("""
+ CREATE OR REPLACE FUNCTION members_search_vector_trigger() RETURNS trigger AS $$
+ DECLARE
+ custom_values_text text;
+ BEGIN
+ -- Aggregate all custom field values for this member
+ -- Support both formats: _union_type/_union_value (Ash format) and type/value (legacy)
+ -- ->> operator always returns TEXT directly (no need for -> + ::text fallback)
+ SELECT string_agg(
+ CASE
+ WHEN value ? '_union_value' THEN value->>'_union_value'
+ WHEN value ? 'value' THEN value->>'value'
+ ELSE ''
+ END,
+ ' '
+ )
+ INTO custom_values_text
+ FROM custom_field_values
+ WHERE member_id = NEW.id AND value IS NOT NULL;
+
+ -- Build search_vector with member fields and custom field values
+ NEW.search_vector :=
+ setweight(to_tsvector('simple', coalesce(NEW.first_name, '')), 'A') ||
+ setweight(to_tsvector('simple', coalesce(NEW.last_name, '')), 'A') ||
+ setweight(to_tsvector('simple', coalesce(NEW.email, '')), 'B') ||
+ setweight(to_tsvector('simple', coalesce(NEW.phone_number, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(NEW.join_date::text, '')), 'D') ||
+ setweight(to_tsvector('simple', coalesce(NEW.exit_date::text, '')), 'D') ||
+ setweight(to_tsvector('simple', coalesce(NEW.notes, '')), 'B') ||
+ setweight(to_tsvector('simple', coalesce(NEW.city, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(NEW.street, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(NEW.house_number::text, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(NEW.postal_code::text, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(custom_values_text, '')), 'C');
+ RETURN NEW;
+ END
+ $$ LANGUAGE plpgsql;
+ """)
+
+ execute("""
+ CREATE OR REPLACE FUNCTION update_member_search_vector_from_custom_field_value() RETURNS trigger AS $$
+ DECLARE
+ member_id_val uuid;
+ member_first_name text;
+ member_last_name text;
+ member_email text;
+ member_phone_number text;
+ member_join_date date;
+ member_exit_date date;
+ member_notes text;
+ member_city text;
+ member_street text;
+ member_house_number text;
+ member_postal_code text;
+ custom_values_text text;
+ old_value_text text;
+ new_value_text text;
+ BEGIN
+ -- Get member ID from trigger context
+ member_id_val := COALESCE(NEW.member_id, OLD.member_id);
+
+ -- Optimization: For UPDATE operations, check if value actually changed
+ -- If value hasn't changed, we can skip the expensive re-aggregation
+ IF TG_OP = 'UPDATE' THEN
+ -- Extract OLD value for comparison (handle both JSONB formats)
+ -- ->> operator always returns TEXT directly
+ old_value_text := COALESCE(
+ NULLIF(OLD.value->>'_union_value', ''),
+ NULLIF(OLD.value->>'value', ''),
+ ''
+ );
+
+ -- Extract NEW value for comparison (handle both JSONB formats)
+ new_value_text := COALESCE(
+ NULLIF(NEW.value->>'_union_value', ''),
+ NULLIF(NEW.value->>'value', ''),
+ ''
+ );
+
+ -- Check if value, member_id, or custom_field_id actually changed
+ -- If nothing changed, skip expensive re-aggregation
+ IF (old_value_text IS NOT DISTINCT FROM new_value_text) AND
+ (OLD.member_id IS NOT DISTINCT FROM NEW.member_id) AND
+ (OLD.custom_field_id IS NOT DISTINCT FROM NEW.custom_field_id) THEN
+ RETURN COALESCE(NEW, OLD);
+ END IF;
+ END IF;
+
+ -- Fetch only required fields instead of full record (performance optimization)
+ SELECT
+ first_name,
+ last_name,
+ email,
+ phone_number,
+ join_date,
+ exit_date,
+ notes,
+ city,
+ street,
+ house_number,
+ postal_code
+ INTO
+ member_first_name,
+ member_last_name,
+ member_email,
+ member_phone_number,
+ member_join_date,
+ member_exit_date,
+ member_notes,
+ member_city,
+ member_street,
+ member_house_number,
+ member_postal_code
+ FROM members
+ WHERE id = member_id_val;
+
+ -- Aggregate all custom field values for this member
+ -- Support both formats: _union_type/_union_value (Ash format) and type/value (legacy)
+ -- ->> operator always returns TEXT directly
+ SELECT string_agg(
+ CASE
+ WHEN value ? '_union_value' THEN value->>'_union_value'
+ WHEN value ? 'value' THEN value->>'value'
+ ELSE ''
+ END,
+ ' '
+ )
+ INTO custom_values_text
+ FROM custom_field_values
+ WHERE member_id = member_id_val AND value IS NOT NULL;
+
+ -- Update the search_vector for the affected member
+ UPDATE members
+ SET search_vector =
+ setweight(to_tsvector('simple', coalesce(member_first_name, '')), 'A') ||
+ setweight(to_tsvector('simple', coalesce(member_last_name, '')), 'A') ||
+ setweight(to_tsvector('simple', coalesce(member_email, '')), 'B') ||
+ setweight(to_tsvector('simple', coalesce(member_phone_number, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(member_join_date::text, '')), 'D') ||
+ setweight(to_tsvector('simple', coalesce(member_exit_date::text, '')), 'D') ||
+ setweight(to_tsvector('simple', coalesce(member_notes, '')), 'B') ||
+ setweight(to_tsvector('simple', coalesce(member_city, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(member_street, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(member_house_number::text, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(member_postal_code::text, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(custom_values_text, '')), 'C')
+ WHERE id = member_id_val;
+
+ RETURN COALESCE(NEW, OLD);
+ END
+ $$ LANGUAGE plpgsql;
+ """)
+
+ # Update existing search_vector values to include phone_number
+ execute("""
+ UPDATE members m
+ SET search_vector =
+ setweight(to_tsvector('simple', coalesce(m.first_name, '')), 'A') ||
+ setweight(to_tsvector('simple', coalesce(m.last_name, '')), 'A') ||
+ setweight(to_tsvector('simple', coalesce(m.email, '')), 'B') ||
+ setweight(to_tsvector('simple', coalesce(m.phone_number, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(m.join_date::text, '')), 'D') ||
+ setweight(to_tsvector('simple', coalesce(m.exit_date::text, '')), 'D') ||
+ setweight(to_tsvector('simple', coalesce(m.notes, '')), 'B') ||
+ setweight(to_tsvector('simple', coalesce(m.city, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(m.street, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(m.house_number::text, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(m.postal_code::text, '')), 'C') ||
+ setweight(to_tsvector('simple', coalesce(
+ (SELECT string_agg(
+ CASE
+ WHEN value ? '_union_value' THEN value->>'_union_value'
+ WHEN value ? 'value' THEN value->>'value'
+ ELSE ''
+ END,
+ ' '
+ )
+ FROM custom_field_values
+ WHERE member_id = m.id AND value IS NOT NULL),
+ ''
+ )), 'C')
+ """)
+ end
+end
diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs
index fb102f4..4f99e5b 100644
--- a/priv/repo/seeds.exs
+++ b/priv/repo/seeds.exs
@@ -147,7 +147,6 @@ member_attrs_list = [
last_name: "Müller",
email: "hans.mueller@example.de",
join_date: ~D[2023-01-15],
- phone_number: "+49301234567",
city: "München",
street: "Hauptstraße",
house_number: "42",
@@ -160,7 +159,6 @@ member_attrs_list = [
last_name: "Schmidt",
email: "greta.schmidt@example.de",
join_date: ~D[2023-02-01],
- phone_number: "+49309876543",
city: "Hamburg",
street: "Lindenstraße",
house_number: "17",
@@ -174,7 +172,6 @@ member_attrs_list = [
last_name: "Wagner",
email: "friedrich.wagner@example.de",
join_date: ~D[2022-11-10],
- phone_number: "+49301122334",
city: "Berlin",
street: "Kastanienallee",
house_number: "8",
@@ -186,7 +183,6 @@ member_attrs_list = [
last_name: "Wagner",
email: "marianne.wagner@example.de",
join_date: ~D[2022-11-10],
- phone_number: "+49301122334",
city: "Berlin",
street: "Kastanienallee",
house_number: "8"
@@ -299,7 +295,6 @@ linked_members = [
last_name: "Weber",
email: "maria.weber@example.de",
join_date: ~D[2023-03-15],
- phone_number: "+49301357924",
city: "Frankfurt",
street: "Goetheplatz",
house_number: "5",
@@ -313,7 +308,6 @@ linked_members = [
last_name: "Klein",
email: "thomas.klein@example.de",
join_date: ~D[2023-04-01],
- phone_number: "+49302468135",
city: "Köln",
street: "Rheinstraße",
house_number: "23",
diff --git a/test/membership/member_test.exs b/test/membership/member_test.exs
index 1c4beb1..258d8be 100644
--- a/test/membership/member_test.exs
+++ b/test/membership/member_test.exs
@@ -7,7 +7,6 @@ defmodule Mv.Membership.MemberTest do
first_name: "John",
last_name: "Doe",
email: "john@example.com",
- phone_number: "+49123456789",
join_date: ~D[2020-01-01],
exit_date: nil,
notes: "Test note",
@@ -17,16 +16,14 @@ defmodule Mv.Membership.MemberTest do
postal_code: "12345"
}
- test "First name is required and must not be empty" do
- attrs = Map.put(@valid_attrs, :first_name, "")
- assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs)
- assert error_message(errors, :first_name) =~ "must be present"
+ test "First name is optional" do
+ attrs = Map.delete(@valid_attrs, :first_name)
+ assert {:ok, _member} = Membership.create_member(attrs)
end
- test "Last name is required and must not be empty" do
- attrs = Map.put(@valid_attrs, :last_name, "")
- assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs)
- assert error_message(errors, :last_name) =~ "must be present"
+ test "Last name is optional" do
+ attrs = Map.delete(@valid_attrs, :last_name)
+ assert {:ok, _member} = Membership.create_member(attrs)
end
test "Email is required" do
@@ -41,14 +38,6 @@ defmodule Mv.Membership.MemberTest do
assert error_message(errors, :email) =~ "is not a valid email"
end
- test "Phone number is optional but must have a valid format if specified" do
- attrs = Map.put(@valid_attrs, :phone_number, "abc")
- assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs)
- assert error_message(errors, :phone_number) =~ "is not a valid phone number"
- attrs2 = Map.delete(@valid_attrs, :phone_number)
- assert {:ok, _member} = Membership.create_member(attrs2)
- end
-
test "Join date cannot be in the future" do
attrs = Map.put(@valid_attrs, :join_date, Date.utc_today() |> Date.add(1))
diff --git a/test/mv_web/components/sort_header_component_test.exs b/test/mv_web/components/sort_header_component_test.exs
index e199635..6d23ab4 100644
--- a/test/mv_web/components/sort_header_component_test.exs
+++ b/test/mv_web/components/sort_header_component_test.exs
@@ -24,7 +24,6 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
:house_number,
:postal_code,
:city,
- :phone_number,
:join_date
]
@@ -101,7 +100,6 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
assert has_element?(view, "[data-testid='street'] .opacity-40")
assert has_element?(view, "[data-testid='house_number'] .opacity-40")
assert has_element?(view, "[data-testid='postal_code'] .opacity-40")
- assert has_element?(view, "[data-testid='phone_number'] .opacity-40")
assert has_element?(view, "[data-testid='join_date'] .opacity-40")
end
diff --git a/test/mv_web/helpers/member_helpers_test.exs b/test/mv_web/helpers/member_helpers_test.exs
new file mode 100644
index 0000000..7a11235
--- /dev/null
+++ b/test/mv_web/helpers/member_helpers_test.exs
@@ -0,0 +1,141 @@
+defmodule MvWeb.Helpers.MemberHelpersTest do
+ @moduledoc """
+ Tests for the display_name/1 helper function in MemberHelpers.
+ """
+ use Mv.DataCase, async: true
+
+ alias Mv.Membership.Member
+ alias MvWeb.Helpers.MemberHelpers
+
+ describe "display_name/1" do
+ test "returns full name when both first_name and last_name are present" do
+ member = %Member{
+ first_name: "John",
+ last_name: "Doe",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "John Doe"
+ end
+
+ test "returns email when both first_name and last_name are nil" do
+ member = %Member{
+ first_name: nil,
+ last_name: nil,
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "john@example.com"
+ end
+
+ test "returns first_name only when last_name is nil" do
+ member = %Member{
+ first_name: "John",
+ last_name: nil,
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "John"
+ end
+
+ test "returns last_name only when first_name is nil" do
+ member = %Member{
+ first_name: nil,
+ last_name: "Doe",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "Doe"
+ end
+
+ test "returns email when first_name and last_name are empty strings" do
+ member = %Member{
+ first_name: "",
+ last_name: "",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "john@example.com"
+ end
+
+ test "returns email when first_name and last_name are whitespace only" do
+ member = %Member{
+ first_name: " ",
+ last_name: " \t ",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "john@example.com"
+ end
+
+ test "trims whitespace from name parts" do
+ member = %Member{
+ first_name: " John ",
+ last_name: " Doe ",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "John Doe"
+ end
+
+ test "handles one empty string and one nil" do
+ member = %Member{
+ first_name: "",
+ last_name: nil,
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "john@example.com"
+ end
+
+ test "handles one nil and one empty string" do
+ member = %Member{
+ first_name: nil,
+ last_name: "",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "john@example.com"
+ end
+
+ test "handles one whitespace and one nil" do
+ member = %Member{
+ first_name: " ",
+ last_name: nil,
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "john@example.com"
+ end
+
+ test "handles one valid name and one whitespace" do
+ member = %Member{
+ first_name: "John",
+ last_name: " ",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "John"
+ end
+
+ test "handles member with only first_name containing whitespace" do
+ member = %Member{
+ first_name: " John ",
+ last_name: nil,
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "John"
+ end
+
+ test "handles member with only last_name containing whitespace" do
+ member = %Member{
+ first_name: nil,
+ last_name: " Doe ",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "Doe"
+ end
+ end
+end
diff --git a/test/mv_web/member_live/index_display_name_test.exs b/test/mv_web/member_live/index_display_name_test.exs
new file mode 100644
index 0000000..7a11235
--- /dev/null
+++ b/test/mv_web/member_live/index_display_name_test.exs
@@ -0,0 +1,141 @@
+defmodule MvWeb.Helpers.MemberHelpersTest do
+ @moduledoc """
+ Tests for the display_name/1 helper function in MemberHelpers.
+ """
+ use Mv.DataCase, async: true
+
+ alias Mv.Membership.Member
+ alias MvWeb.Helpers.MemberHelpers
+
+ describe "display_name/1" do
+ test "returns full name when both first_name and last_name are present" do
+ member = %Member{
+ first_name: "John",
+ last_name: "Doe",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "John Doe"
+ end
+
+ test "returns email when both first_name and last_name are nil" do
+ member = %Member{
+ first_name: nil,
+ last_name: nil,
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "john@example.com"
+ end
+
+ test "returns first_name only when last_name is nil" do
+ member = %Member{
+ first_name: "John",
+ last_name: nil,
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "John"
+ end
+
+ test "returns last_name only when first_name is nil" do
+ member = %Member{
+ first_name: nil,
+ last_name: "Doe",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "Doe"
+ end
+
+ test "returns email when first_name and last_name are empty strings" do
+ member = %Member{
+ first_name: "",
+ last_name: "",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "john@example.com"
+ end
+
+ test "returns email when first_name and last_name are whitespace only" do
+ member = %Member{
+ first_name: " ",
+ last_name: " \t ",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "john@example.com"
+ end
+
+ test "trims whitespace from name parts" do
+ member = %Member{
+ first_name: " John ",
+ last_name: " Doe ",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "John Doe"
+ end
+
+ test "handles one empty string and one nil" do
+ member = %Member{
+ first_name: "",
+ last_name: nil,
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "john@example.com"
+ end
+
+ test "handles one nil and one empty string" do
+ member = %Member{
+ first_name: nil,
+ last_name: "",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "john@example.com"
+ end
+
+ test "handles one whitespace and one nil" do
+ member = %Member{
+ first_name: " ",
+ last_name: nil,
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "john@example.com"
+ end
+
+ test "handles one valid name and one whitespace" do
+ member = %Member{
+ first_name: "John",
+ last_name: " ",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "John"
+ end
+
+ test "handles member with only first_name containing whitespace" do
+ member = %Member{
+ first_name: " John ",
+ last_name: nil,
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "John"
+ end
+
+ test "handles member with only last_name containing whitespace" do
+ member = %Member{
+ first_name: nil,
+ last_name: " Doe ",
+ email: "john@example.com"
+ }
+
+ assert MemberHelpers.display_name(member) == "Doe"
+ end
+ end
+end
diff --git a/test/mv_web/member_live/index_member_fields_display_test.exs b/test/mv_web/member_live/index_member_fields_display_test.exs
index 6b4f50c..c6fd39f 100644
--- a/test/mv_web/member_live/index_member_fields_display_test.exs
+++ b/test/mv_web/member_live/index_member_fields_display_test.exs
@@ -16,7 +16,6 @@ defmodule MvWeb.MemberLive.IndexMemberFieldsDisplayTest do
house_number: "123",
postal_code: "12345",
city: "Berlin",
- phone_number: "+49123456789",
join_date: ~D[2020-01-15]
})
|> Ash.create()
diff --git a/test/mv_web/member_live/index_test.exs b/test/mv_web/member_live/index_test.exs
index d4f5644..acca9bf 100644
--- a/test/mv_web/member_live/index_test.exs
+++ b/test/mv_web/member_live/index_test.exs
@@ -121,7 +121,6 @@ defmodule MvWeb.MemberLive.IndexTest do
:house_number,
:postal_code,
:city,
- :phone_number,
:join_date
]