spazio-solazzo/lib/spazio_solazzo/booking_system/booking.ex
Víctor Martínez 2cbce8ec39
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.
2026-01-15 15:57:54 +01:00

192 lines
5.6 KiB
Elixir

defmodule SpazioSolazzo.BookingSystem.Booking do
@moduledoc """
Represents a customer booking with state management for reservation lifecycle.
"""
use Ash.Resource,
otp_app: :spazio_solazzo,
domain: SpazioSolazzo.BookingSystem,
data_layer: AshPostgres.DataLayer,
notifiers: [Ash.Notifier.PubSub],
authorizers: [Ash.Policy.Authorizer],
extensions: [AshStateMachine]
alias SpazioSolazzo.BookingSystem.Booking.EmailWorker
postgres do
table "bookings"
repo SpazioSolazzo.Repo
references do
reference :user, on_delete: :nilify, index?: true
end
end
state_machine do
initial_states([:reserved])
default_initial_state(:reserved)
transitions do
transition(:confirm_booking, from: :reserved, to: :completed)
transition(:cancel, from: :reserved, to: :cancelled)
end
end
actions do
defaults [:read]
read :list_active_asset_bookings_by_date do
argument :asset_id, :uuid, allow_nil?: false
argument :date, :date, allow_nil?: false
filter expr(
asset_id == ^arg(:asset_id) and date == ^arg(:date) and
state in [:reserved, :completed]
)
end
create :create do
argument :time_slot_template_id, :uuid, allow_nil?: false
argument :asset_id, :uuid, allow_nil?: false
argument :user_id, :uuid, allow_nil?: false
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?: true
argument :customer_comment, :string, allow_nil?: true
change manage_relationship(:time_slot_template_id, :time_slot_template,
type: :append_and_remove
)
change manage_relationship(:asset_id, :asset, type: :append_and_remove)
change manage_relationship(:user_id, :user, type: :append_and_remove, authorize?: false)
change fn changeset, _ctx ->
template_id = Ash.Changeset.get_argument(changeset, :time_slot_template_id)
case Ash.get(SpazioSolazzo.BookingSystem.TimeSlotTemplate, template_id) do
{:ok, template} ->
changeset
|> Ash.Changeset.force_change_attribute(:start_time, template.start_time)
|> Ash.Changeset.force_change_attribute(:end_time, template.end_time)
|> Ash.Changeset.force_change_attribute(
:date,
Ash.Changeset.get_argument(changeset, :date)
)
|> Ash.Changeset.force_change_attribute(
:customer_name,
Ash.Changeset.get_argument(changeset, :customer_name)
)
|> Ash.Changeset.force_change_attribute(
:customer_email,
Ash.Changeset.get_argument(changeset, :customer_email)
)
|> Ash.Changeset.force_change_attribute(
:customer_phone,
Ash.Changeset.get_argument(changeset, :customer_phone)
)
|> Ash.Changeset.force_change_attribute(
:customer_comment,
Ash.Changeset.get_argument(changeset, :customer_comment)
)
{:error, _} ->
Ash.Changeset.add_error(changeset,
field: :time_slot_template_id,
message: "Template not found"
)
end
end
change after_action(fn _changeset, booking, _ctx ->
%{
booking_id: booking.id,
customer_name: booking.customer_name,
customer_email: booking.customer_email,
customer_phone: booking.customer_phone,
customer_comment: booking.customer_comment,
date: Calendar.strftime(booking.date, "%A, %B %d"),
start_time: booking.start_time,
end_time: booking.end_time
}
|> EmailWorker.new()
|> Oban.insert!()
{:ok, booking}
end)
end
update :confirm_booking do
accept []
change transition_state(:completed)
end
update :cancel do
accept []
change transition_state(:cancelled)
end
destroy :destroy do
description "Delete a booking record"
primary? true
end
end
policies do
policy action([:cancel, :confirm_booking]) do
authorize_if always()
end
policy action_type(:destroy) do
authorize_if expr(:user_id == ^actor(:id))
end
policy action_type(:read) do
authorize_if always()
end
policy action_type(:create) do
authorize_if always()
end
end
pub_sub do
module SpazioSolazzoWeb.Endpoint
prefix "booking"
publish :create, ["created"]
publish :cancel, ["cancelled"]
end
attributes do
uuid_primary_key :id
attribute :date, :date, allow_nil?: false
attribute :customer_name, :string, allow_nil?: false
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?: true
attribute :customer_comment, :string, allow_nil?: true
attribute :state, :atom do
allow_nil? false
default :reserved
public? true
constraints one_of: [:reserved, :completed, :cancelled]
end
create_timestamp :inserted_at
update_timestamp :updated_at
end
relationships do
belongs_to :asset, SpazioSolazzo.BookingSystem.Asset
belongs_to :time_slot_template, SpazioSolazzo.BookingSystem.TimeSlotTemplate
belongs_to :user, SpazioSolazzo.Accounts.User do
allow_nil? true
end
end
end