mirror of
https://codeberg.org/JasterV/spazio-solazzo.git
synced 2026-04-26 18:20:03 +00:00
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.
160 lines
3.8 KiB
Elixir
160 lines
3.8 KiB
Elixir
defmodule SpazioSolazzo.Accounts.User do
|
|
@moduledoc """
|
|
Represents a user in the system with magic link authentication.
|
|
"""
|
|
|
|
use Ash.Resource,
|
|
otp_app: :spazio_solazzo,
|
|
domain: SpazioSolazzo.Accounts,
|
|
data_layer: AshPostgres.DataLayer,
|
|
authorizers: [Ash.Policy.Authorizer],
|
|
extensions: [AshAuthentication]
|
|
|
|
authentication do
|
|
add_ons do
|
|
log_out_everywhere do
|
|
apply_on_password_change? true
|
|
end
|
|
end
|
|
|
|
tokens do
|
|
enabled? true
|
|
token_resource SpazioSolazzo.Accounts.Token
|
|
signing_secret SpazioSolazzo.Secrets
|
|
store_all_tokens? true
|
|
require_token_presence_for_authentication? true
|
|
end
|
|
|
|
strategies do
|
|
magic_link do
|
|
identity_field :email
|
|
registration_enabled? true
|
|
require_interaction? true
|
|
sender SpazioSolazzo.Accounts.User.Senders.SendMagicLinkEmail
|
|
end
|
|
|
|
remember_me :remember_me
|
|
end
|
|
end
|
|
|
|
postgres do
|
|
table "users"
|
|
repo SpazioSolazzo.Repo
|
|
end
|
|
|
|
actions do
|
|
defaults [:read]
|
|
|
|
read :get_by_email do
|
|
description "Looks up a user by their email"
|
|
argument :email, :ci_string, allow_nil?: false
|
|
get? true
|
|
filter expr(email == ^arg(:email))
|
|
end
|
|
|
|
create :sign_in_with_magic_link do
|
|
description "Sign in or register a user with magic link."
|
|
|
|
argument :token, :string do
|
|
description "The token from the magic link that was sent to the user"
|
|
allow_nil? false
|
|
end
|
|
|
|
argument :remember_me, :boolean do
|
|
description "Whether to generate a remember me token"
|
|
allow_nil? true
|
|
end
|
|
|
|
argument :name, :string do
|
|
description "User's full name (required for new users)"
|
|
allow_nil? true
|
|
end
|
|
|
|
argument :phone_number, :string do
|
|
description "User's phone number (required for new users)"
|
|
allow_nil? true
|
|
end
|
|
|
|
upsert? true
|
|
upsert_identity :unique_email
|
|
upsert_fields [:email, :name, :phone_number]
|
|
|
|
# Uses the information from the token to create or sign in the user
|
|
change AshAuthentication.Strategy.MagicLink.SignInChange
|
|
|
|
# Conditionally validate name and phone_number for new users
|
|
change SpazioSolazzo.Accounts.User.Changes.ParseRegistrationFields
|
|
|
|
change {AshAuthentication.Strategy.RememberMe.MaybeGenerateTokenChange,
|
|
strategy_name: :remember_me}
|
|
|
|
metadata :token, :string do
|
|
allow_nil? false
|
|
end
|
|
end
|
|
|
|
action :request_magic_link do
|
|
argument :email, :ci_string, allow_nil?: false
|
|
run AshAuthentication.Strategy.MagicLink.Request
|
|
end
|
|
|
|
update :update_profile do
|
|
description "Update user profile (name and phone number)"
|
|
accept [:name, :phone_number]
|
|
require_atomic? false
|
|
end
|
|
|
|
destroy :terminate_account do
|
|
description "Delete user account with optional booking data removal"
|
|
require_atomic? false
|
|
|
|
argument :delete_history, :boolean do
|
|
description "Whether to permanently delete all booking history"
|
|
default false
|
|
end
|
|
|
|
change SpazioSolazzo.Accounts.User.Changes.HandleBookingsOnAccountDeletion
|
|
end
|
|
end
|
|
|
|
policies do
|
|
bypass AshAuthentication.Checks.AshAuthenticationInteraction do
|
|
authorize_if always()
|
|
end
|
|
|
|
policy action_type(:read) do
|
|
authorize_if always()
|
|
end
|
|
|
|
policy action_type(:update) do
|
|
authorize_if expr(id == ^actor(:id))
|
|
end
|
|
|
|
policy action_type(:destroy) do
|
|
authorize_if expr(id == ^actor(:id))
|
|
end
|
|
end
|
|
|
|
attributes do
|
|
uuid_primary_key :id
|
|
|
|
attribute :email, :ci_string do
|
|
allow_nil? false
|
|
public? true
|
|
end
|
|
|
|
attribute :name, :string do
|
|
allow_nil? false
|
|
public? true
|
|
end
|
|
|
|
attribute :phone_number, :string do
|
|
allow_nil? true
|
|
public? true
|
|
end
|
|
end
|
|
|
|
identities do
|
|
identity :unique_email, [:email]
|
|
end
|
|
end
|