From 0f5d3d7fdd00f6198269ad93e666844f65c76fb8 Mon Sep 17 00:00:00 2001 From: Moritz Date: Mon, 2 Jun 2025 22:38:16 +0200 Subject: [PATCH 1/3] feat: add phone validation --- lib/membership/phone_number.ex | 28 ++++++++++++++++++++++++++++ lib/membership/property.ex | 3 ++- lib/membership/property_type.ex | 2 +- 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 lib/membership/phone_number.ex diff --git a/lib/membership/phone_number.ex b/lib/membership/phone_number.ex new file mode 100644 index 0000000..4613c43 --- /dev/null +++ b/lib/membership/phone_number.ex @@ -0,0 +1,28 @@ +defmodule Mv.Membership.PhoneNumber do + @match_pattern ~S/^\+?\d{5,16}$/ + @match_regex Regex.compile!(@match_pattern) + + use Ash.Type.NewType, + subtype_of: :string, + constraints: [ + match: @match_pattern, + trim?: true + ] + + @impl true + def cast_input("", _), do: {:ok, nil} + + @impl true + def cast_input(value, _) when is_binary(value) do + value = String.trim(value) + + if Regex.match?(@match_regex, value) do + {:ok, value} + else + :error + end + end + + @impl true + def cast_input(_, _), do: :error +end diff --git a/lib/membership/property.ex b/lib/membership/property.ex index 0bd5eab..6728da2 100644 --- a/lib/membership/property.ex +++ b/lib/membership/property.ex @@ -24,7 +24,8 @@ defmodule Mv.Membership.Property do date: [type: :date], integer: [type: :integer], string: [type: :string], - email: [type: Mv.Membership.Email] + email: [type: Mv.Membership.Email], + phone: [type: Mv.Membership.PhoneNumber] ] ] end diff --git a/lib/membership/property_type.ex b/lib/membership/property_type.ex index 8e42fa6..18215b4 100644 --- a/lib/membership/property_type.ex +++ b/lib/membership/property_type.ex @@ -19,7 +19,7 @@ defmodule Mv.Membership.PropertyType do attribute :name, :string, allow_nil?: false, public?: true attribute :value_type, :atom, - constraints: [one_of: [:string, :integer, :boolean, :date, :email]], + constraints: [one_of: [:string, :integer, :boolean, :date, :email, :phone]], allow_nil?: false, description: "Definies the datatype `Property.value` is interpreted as" -- 2.47.2 From 967a89b18de6f4c0aefed22863c37daa82087765 Mon Sep 17 00:00:00 2001 From: Moritz Date: Mon, 2 Jun 2025 22:38:28 +0200 Subject: [PATCH 2/3] feat: add default property_types --- priv/repo/seeds.exs | 58 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 39327d0..d6b984d 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -32,7 +32,7 @@ for attrs <- [ value_type: :boolean, description: "Status des Mitgliedsbeitrages des Mitglieds", immutable: true, - required: true + required: false }, %{ name: "Email", @@ -40,6 +40,62 @@ for attrs <- [ description: "Email-Adresse des Mitglieds", immutable: true, required: true + }, + %{ + name: "Telefonnummer", + value_type: :phone, + description: "Telefonnummer des Mitglieds", + immutable: true, + required: false + }, + %{ + name: "Eintrittsdatum", + value_type: :date, + description: "Eintrittsdatum des Mitglieds", + immutable: true, + required: false + }, + %{ + name: "Austrittsdatum", + value_type: :date, + description: "Austrittsdatum des Mitglieds", + immutable: true, + required: false + }, + %{ + name: "Notiz", + value_type: :string, + description: "Notiz", + immutable: true, + required: false + }, + %{ + name: "Stadt", + value_type: :string, + description: "Stadt", + immutable: true, + required: false + }, + %{ + name: "Straße", + value_type: :string, + description: "Straße", + immutable: true, + required: false + }, + %{ + name: "Hausnummer", + value_type: :integer, + description: "Hausnummer", + immutable: true, + required: false + }, + %{ + name: "PLZ", + value_type: :string, + description: "PLZ", + immutable: true, + required: false } ] do Membership.create_property_type!( -- 2.47.2 From 156cdb24d05035e293de2a074aec3123341b59f3 Mon Sep 17 00:00:00 2001 From: Moritz Date: Mon, 2 Jun 2025 22:41:04 +0200 Subject: [PATCH 3/3] WIP: validate required fields --- lib/membership/email.ex | 3 +++ lib/membership/membership.ex | 4 ++-- lib/membership/property.ex | 27 +++++++++++++++++++++++- lib/membership/validate_property.ex | 27 ++++++++++++++++++++++++ lib/mv_web/member_live/form_component.ex | 5 ++++- 5 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 lib/membership/validate_property.ex diff --git a/lib/membership/email.ex b/lib/membership/email.ex index c611742..c88d6ea 100644 --- a/lib/membership/email.ex +++ b/lib/membership/email.ex @@ -13,6 +13,9 @@ defmodule Mv.Membership.Email do max_length: @max_length ] + @impl true + def cast_input("", _), do: {:ok, nil} + @impl true def cast_input(value, _) when is_binary(value) do value = String.trim(value) diff --git a/lib/membership/membership.ex b/lib/membership/membership.ex index b766a13..72c463c 100644 --- a/lib/membership/membership.ex +++ b/lib/membership/membership.ex @@ -11,9 +11,9 @@ defmodule Mv.Membership do end resource Mv.Membership.Property do - define :create_property, action: :create + define :create_property, action: :create_property define :list_property, action: :read - define :update_property, action: :update + define :update_property, action: :update_property define :destroy_property, action: :destroy end diff --git a/lib/membership/property.ex b/lib/membership/property.ex index 6728da2..9e437d1 100644 --- a/lib/membership/property.ex +++ b/lib/membership/property.ex @@ -9,14 +9,28 @@ defmodule Mv.Membership.Property do end actions do - defaults [:create, :read, :update, :destroy] + defaults [:read, :destroy] default_accept [:value, :member_id, :property_type_id] + + create :create_property do + primary? true + load [:property_type] + end + + update :update_property do + primary? true + require_atomic? false + load [:property_type] + end + end + attributes do uuid_primary_key :id attribute :value, :union, + allow_nil?: true, constraints: [ storage: :type_and_value, types: [ @@ -39,4 +53,15 @@ defmodule Mv.Membership.Property do calculations do calculate :value_to_string, :string, expr(value[:value] <> "") end + + aggregates do + first :property_type_required, + :property_type, + :required + end + + validations do + validate {Mv.Membership.Validations.ValidateProperty, attribute: :value} + end + end diff --git a/lib/membership/validate_property.ex b/lib/membership/validate_property.ex new file mode 100644 index 0000000..21a1f7c --- /dev/null +++ b/lib/membership/validate_property.ex @@ -0,0 +1,27 @@ +defmodule Mv.Membership.Validations.ValidateProperty do + use Ash.Resource.Validation + + @impl true + def init(opts) do + if is_atom(opts[:value]) do + {:ok, opts} + else + {:error, "attribute must be an atom!"} + end + end + + @impl true + def validate(changeset, _opts, _context) do + changeset = Ash.Changeset.load(changeset, [:property_type]) + property_type = changeset.data.property_type + IO.inspect(property_type) + required? = property_type.required + union_value = Ash.Changeset.get_attribute(changeset, :value) + + if required? and union_value in [nil, ""] do + {:error, field: :value, message: "is required"} + else + :ok + end + end +end diff --git a/lib/mv_web/member_live/form_component.ex b/lib/mv_web/member_live/form_component.ex index 101cf6c..684ac7d 100644 --- a/lib/mv_web/member_live/form_component.ex +++ b/lib/mv_web/member_live/form_component.ex @@ -72,7 +72,10 @@ defmodule MvWeb.MemberLive.FormComponent do @impl true def handle_event("validate", %{"member" => member_params}, socket) do - {:noreply, assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, member_params))} + #IO.inspect(socket.assigns.form, label: "BEFORE_VALIDATION!!!") + form = AshPhoenix.Form.validate(socket.assigns.form, member_params) + #IO.inspect(form, label: "AFTER_VALIDATION!!!") + {:noreply, assign(socket, form: form)} end def handle_event("save", %{"member" => member_params}, socket) do -- 2.47.2