From 2cbce8ec39dfdce63f445205fac1db91859db085 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?=
<49537445+JasterV@users.noreply.github.com>
Date: Thu, 15 Jan 2026 15:57:54 +0100
Subject: [PATCH] refactor: make phone number optional (#9)
This pull request makes phone numbers optional for both user registrations and bookings, and updates validation, database schema, and UI to reflect this change. The main focus is to ensure that users are no longer required to provide a phone number, and that the application gracefully handles cases where a phone number is absent.
**Database & Resource Model Updates**
* Made the `phone_number` attribute in the `users` table and the `customer_phone` attribute in the `bookings` table nullable, including migration and resource snapshot updates. [[1]](diffhunk://#diff-baa6aed3674c4d6cbbebeafb076662df02dc4c25231dbd9dc9c8f0534ed1a1bfR1-R29) [[2]](diffhunk://#diff-a401f66b2ae5bfb798eb1bc2221bfeeac943e258950c90d59570b0bae05d3664R1-R244) [[3]](diffhunk://#diff-0c1180d6f6abc19b5987c8703bdee9ef67905535202f950e8327c32bd5b89d8aR1-R82)
* Updated Ash resource definitions in `user.ex` and `booking.ex` to allow `phone_number` and `customer_phone` to be `nil`. [[1]](diffhunk://#diff-9194b9d80dce091f6dcb56f784217272ae160e35454c4b4ccc8850ad5ee06e38L152-R152) [[2]](diffhunk://#diff-4b1ddd6d86899f2144c69d142883b8719c755e32c03dbda5da2188208a5ad503L55-R55) [[3]](diffhunk://#diff-4b1ddd6d86899f2144c69d142883b8719c755e32c03dbda5da2188208a5ad503L170-R170)
**Validation & Parsing Logic**
* Renamed and refactored user registration field validation to `ParseRegistrationFields`, allowing phone numbers to be omitted and trimming input values. Empty phone numbers are now treated as absent rather than as errors. [[1]](diffhunk://#diff-8ffdd76e260e3cda6f0816c8e585ae76b993a90d2519c38185a5fe22b4b49e47L1-R1) [[2]](diffhunk://#diff-8ffdd76e260e3cda6f0816c8e585ae76b993a90d2519c38185a5fe22b4b49e47R14-R60)
* Updated the authentication callback logic to trim input values and omit the phone number parameter if it is blank.
**User Interface Improvements**
* Updated registration and booking forms to indicate that phone numbers are optional, removed the required attribute, and improved placeholder text. [[1]](diffhunk://#diff-f356eb84970d8c9ee6ff1992c297b0cae07bade37ff967c1e6e0de6f8b67081cL101-R115) [[2]](diffhunk://#diff-43c0e1f7a869ee5c43a911bc10dc80cbb265a8672340ef0fa7c1d3009c047f02L92-R92)
* Updated email templates and confirmation screens to display "N/A" or "-" when phone numbers are missing. [[1]](diffhunk://#diff-48468ef2d1bb2c33b5ffb40457b77532815c7faf1830932661f665bff58b2177R6-R11) [[2]](diffhunk://#diff-3f33187b4021450b481ce53fe13166addea582c627f2cfbc99c75c7ce5c34857L10-R10) [[3]](diffhunk://#diff-43c0e1f7a869ee5c43a911bc10dc80cbb265a8672340ef0fa7c1d3009c047f02L92-R92)
**Profile Management**
* Improved profile update flow to ensure the form reflects the latest user data after saving changes.
* Made the "Full Name" field explicitly required in the profile form UI.
---
.credo.exs | 2 +-
lib/spazio_solazzo/accounts/user.ex | 4 +-
.../changes/validate_registration_fields.ex | 45 +++-
lib/spazio_solazzo/booking_system/booking.ex | 4 +-
.../admin_notification.html.heex | 7 +-
.../customer_confirmation.html.heex | 2 +-
.../live/auth/auth_callback_live.ex | 15 +-
.../live/auth/auth_callback_live.html.heex | 8 +-
.../live/booking/asset_booking_live.ex | 8 +-
.../booking/booking_form_live_component.ex | 96 +++++--
.../live/user/profile_live.ex | 6 +-
.../live/user/profile_live.html.heex | 5 +-
...115004442_set_phone_number_as_nullable.exs | 29 +++
.../repo/bookings/20260115004442.json | 244 ++++++++++++++++++
.../repo/users/20260115004442.json | 82 ++++++
test/spazio_solazzo/accounts/user_test.exs | 30 +--
.../booking_system/booking_test.exs | 12 +-
.../controllers/booking_controller_test.exs | 9 +-
.../live/booking_live/asset_booking_test.exs | 136 ++++++++--
.../live/user/profile_live_test.exs | 24 +-
test/support/auth_helpers.ex | 64 +++++
test/support/conn_case.ex | 3 +
test/support/data_case.ex | 1 +
23 files changed, 699 insertions(+), 137 deletions(-)
create mode 100644 priv/repo/migrations/20260115004442_set_phone_number_as_nullable.exs
create mode 100644 priv/resource_snapshots/repo/bookings/20260115004442.json
create mode 100644 priv/resource_snapshots/repo/users/20260115004442.json
create mode 100644 test/support/auth_helpers.ex
diff --git a/.credo.exs b/.credo.exs
index f806174..5526a45 100644
--- a/.credo.exs
+++ b/.credo.exs
@@ -89,7 +89,7 @@
# If you don't want TODO comments to cause `mix credo` to fail, just
# set this value to 0 (zero).
#
- {Credo.Check.Design.TagTODO, [exit_status: 2]},
+ {Credo.Check.Design.TagTODO, [exit_status: 0]},
#
## Readability Checks
diff --git a/lib/spazio_solazzo/accounts/user.ex b/lib/spazio_solazzo/accounts/user.ex
index 23d7d3b..27af03c 100644
--- a/lib/spazio_solazzo/accounts/user.ex
+++ b/lib/spazio_solazzo/accounts/user.ex
@@ -83,7 +83,7 @@ defmodule SpazioSolazzo.Accounts.User do
change AshAuthentication.Strategy.MagicLink.SignInChange
# Conditionally validate name and phone_number for new users
- change SpazioSolazzo.Accounts.User.Changes.ValidateRegistrationFields
+ change SpazioSolazzo.Accounts.User.Changes.ParseRegistrationFields
change {AshAuthentication.Strategy.RememberMe.MaybeGenerateTokenChange,
strategy_name: :remember_me}
@@ -149,7 +149,7 @@ defmodule SpazioSolazzo.Accounts.User do
end
attribute :phone_number, :string do
- allow_nil? false
+ allow_nil? true
public? true
end
end
diff --git a/lib/spazio_solazzo/accounts/user/changes/validate_registration_fields.ex b/lib/spazio_solazzo/accounts/user/changes/validate_registration_fields.ex
index 80466bc..88501ea 100644
--- a/lib/spazio_solazzo/accounts/user/changes/validate_registration_fields.ex
+++ b/lib/spazio_solazzo/accounts/user/changes/validate_registration_fields.ex
@@ -1,4 +1,4 @@
-defmodule SpazioSolazzo.Accounts.User.Changes.ValidateRegistrationFields do
+defmodule SpazioSolazzo.Accounts.User.Changes.ParseRegistrationFields do
@moduledoc """
Conditionally validates that name and phone_number are present for new user registrations.
For existing users (upserts), these fields are not required.
@@ -11,28 +11,53 @@ defmodule SpazioSolazzo.Accounts.User.Changes.ValidateRegistrationFields do
case SpazioSolazzo.Accounts.get_user_by_email(email, authorize?: false) do
{:ok, %{phone_number: phone, name: name}} ->
+ # User is already registered, we'll just set the same values it had
changeset
|> Ash.Changeset.force_change_attribute(:name, name)
|> Ash.Changeset.force_change_attribute(:phone_number, phone)
_ ->
+ # User is not yet registered, we'll parse & validate the new values
name = Ash.Changeset.get_argument(changeset, :name)
phone = Ash.Changeset.get_argument(changeset, :phone_number)
changeset
- |> validate_required_for_registration(:name, name)
- |> validate_required_for_registration(:phone_number, phone)
+ |> parse_name(name)
+ |> parse_phone_number(phone)
end
end
- defp validate_required_for_registration(changeset, field, value) do
- if is_nil(value) || value == "" do
- Ash.Changeset.add_error(
- changeset,
- Ash.Error.Changes.Required.exception(field: field, type: :argument)
- )
+ defp parse_name(changeset, nil) do
+ Ash.Changeset.add_error(
+ changeset,
+ Ash.Error.Changes.Required.exception(field: :name, type: :argument)
+ )
+ end
+
+ defp parse_name(changeset, value) do
+ value = String.trim(value)
+
+ if value == "" do
+ parse_name(changeset, nil)
else
- Ash.Changeset.change_attribute(changeset, field, value)
+ Ash.Changeset.change_attribute(changeset, :name, value)
+ end
+ end
+
+ defp parse_phone_number(changeset, nil) do
+ # The phone number is nullable, this is fine
+ Ash.Changeset.change_attribute(changeset, :phone_number, nil)
+ end
+
+ defp parse_phone_number(changeset, value) do
+ value = String.trim(value)
+
+ if value == "" do
+ # Instead of returning an error, we'll consider an empty phone number
+ # as if the user didn't want to set one, which is valid.
+ parse_name(changeset, nil)
+ else
+ Ash.Changeset.change_attribute(changeset, :phone_number, value)
end
end
end
diff --git a/lib/spazio_solazzo/booking_system/booking.ex b/lib/spazio_solazzo/booking_system/booking.ex
index 8e99b58..8545d94 100644
--- a/lib/spazio_solazzo/booking_system/booking.ex
+++ b/lib/spazio_solazzo/booking_system/booking.ex
@@ -52,7 +52,7 @@ defmodule SpazioSolazzo.BookingSystem.Booking do
argument :date, :date, allow_nil?: false
argument :customer_name, :string, allow_nil?: false
argument :customer_email, :string, allow_nil?: false
- argument :customer_phone, :string, allow_nil?: false
+ argument :customer_phone, :string, allow_nil?: true
argument :customer_comment, :string, allow_nil?: true
change manage_relationship(:time_slot_template_id, :time_slot_template,
@@ -167,7 +167,7 @@ defmodule SpazioSolazzo.BookingSystem.Booking do
attribute :customer_email, :string, allow_nil?: false
attribute :start_time, :time, allow_nil?: false
attribute :end_time, :time, allow_nil?: false
- attribute :customer_phone, :string, allow_nil?: false
+ attribute :customer_phone, :string, allow_nil?: true
attribute :customer_comment, :string, allow_nil?: true
attribute :state, :atom do
diff --git a/lib/spazio_solazzo_web/emails/email_templates/admin_notification.html.heex b/lib/spazio_solazzo_web/emails/email_templates/admin_notification.html.heex
index 0747713..5a52af4 100644
--- a/lib/spazio_solazzo_web/emails/email_templates/admin_notification.html.heex
+++ b/lib/spazio_solazzo_web/emails/email_templates/admin_notification.html.heex
@@ -3,7 +3,12 @@