From a6f6d501673ca5fbede67e164cac19b646d067fc Mon Sep 17 00:00:00 2001
From: Nils Rauch <rauch@puzzle.ch>
Date: Thu, 23 Jan 2025 14:26:35 +0100
Subject: [PATCH] Adjust self registration form Refs:
 hitobito/hitobito_jubla#79

---
 .../jubla/wizards/register_new_user_wizard.rb | 22 +++++
 .../jubla/wizards/steps/new_user_form.rb      | 61 +++++++++++++
 .../wizards/steps/_new_user_form.html.haml    | 19 ++++
 config/locales/views.de.yml                   |  6 ++
 lib/hitobito_jubla/wagon.rb                   |  5 ++
 .../wizards/register_new_user_wizard_spec.rb  | 90 +++++++++++++++++++
 6 files changed, 203 insertions(+)
 create mode 100644 app/models/jubla/wizards/register_new_user_wizard.rb
 create mode 100644 app/models/jubla/wizards/steps/new_user_form.rb
 create mode 100644 app/views/wizards/steps/_new_user_form.html.haml
 create mode 100644 spec/models/wizards/register_new_user_wizard_spec.rb

diff --git a/app/models/jubla/wizards/register_new_user_wizard.rb b/app/models/jubla/wizards/register_new_user_wizard.rb
new file mode 100644
index 00000000..c8c08a42
--- /dev/null
+++ b/app/models/jubla/wizards/register_new_user_wizard.rb
@@ -0,0 +1,22 @@
+module Jubla::Wizards::RegisterNewUserWizard
+  extend ActiveSupport::Concern
+  PHONE_NUMBER_LABEL = "Privat"
+
+  prepended do
+    # attribute :address_care_of, :string
+    # attribute :street, :string
+    # attribute :housenumber, :string
+
+    # attribute :postbox, :string
+    # attribute :zip_code, :string
+    # attribute :town, :string
+    # attribute :country, :string
+
+    # attribute :phone_number, :string
+    # attribute :email, :string
+
+    # validates :email, presence: true
+  end
+
+  delegate :person_attributes, to: :new_user_form
+end
diff --git a/app/models/jubla/wizards/steps/new_user_form.rb b/app/models/jubla/wizards/steps/new_user_form.rb
new file mode 100644
index 00000000..f6aa4da0
--- /dev/null
+++ b/app/models/jubla/wizards/steps/new_user_form.rb
@@ -0,0 +1,61 @@
+module Jubla::Wizards::Steps::NewUserForm
+  extend ActiveSupport::Concern
+  PHONE_NUMBER_LABEL = "Privat"
+
+  prepended do
+    attribute :address_care_of, :string
+    attribute :street, :string
+    attribute :housenumber, :string
+
+    attribute :postbox, :string
+    attribute :zip_code, :string
+    attribute :town, :string
+    attribute :country, :string
+
+    attribute :phone_number, :string
+    attribute :email, :string
+
+    validates :email, presence: true
+
+    validate :assert_valid_phone_number
+  end
+
+  def initialize(...)
+    super
+
+    if current_user
+      self.id = current_user.id
+      self.gender ||= current_user.gender
+      self.first_name ||= current_user.first_name
+      self.last_name ||= current_user.last_name
+      self.birthday ||= current_user.birthday
+      self.email ||= current_user.email
+      self.address_care_of ||= current_user.address_care_of
+      self.street ||= current_user.street
+      self.housenumber ||= current_user.housenumber
+      self.postbox ||= current_user.postbox
+      self.zip_code ||= current_user.zip_code
+      self.town ||= current_user.town
+      self.country ||= current_user.country
+      self.phone_number ||= current_user.phone_numbers.find_by(label: PHONE_NUMBER_LABEL)&.number
+    else
+      self.country ||= Settings.addresses.imported_countries.to_a.first
+    end
+  end
+
+  def person_attributes
+    attributes.compact.symbolize_keys.except(:phone_number).then do |attrs|
+      next attrs if phone_number.blank?
+
+      attrs.merge(phone_numbers_attributes: [{label: PHONE_NUMBER_LABEL, number: phone_number, public: false}.compact])
+    end
+  end
+
+  private
+
+  def assert_valid_phone_number
+    if phone_number.present? && PhoneNumber.new(number: phone_number).tap(&:valid?).errors.key?(:number)
+      errors.add(:phone_number, :invalid)
+    end
+  end
+end
diff --git a/app/views/wizards/steps/_new_user_form.html.haml b/app/views/wizards/steps/_new_user_form.html.haml
new file mode 100644
index 00000000..acceb198
--- /dev/null
+++ b/app/views/wizards/steps/_new_user_form.html.haml
@@ -0,0 +1,19 @@
+-#  Copyright (c) 2025, Jungwacht Blauring Schweiz. This file is part of
+-#  hitobito_sac_cas and licensed under the Aformero General Public License version 3
+-#  or later. See the COPYING file at the top-level directory or at
+-#  https://github.com/hitobito/hitobito_sac_cas.
+
+= c.fields_for do |ff|
+  = ff.labeled_input_fields :first_name, :last_name
+  - if f.object.new_user_form.support_company?
+    = ff.labeled_input_fields :company_name
+    = ff.labeled_boolean_field :company
+  = render 'contactable/address_fields', f: ff
+  = ff.labeled_input_field :phone_number, placeholder: t('global.phone_placeholder')
+
+  = ff.labeled_input_field :email, help_inline: t('people.email_field.used_as_login'), class: 'd-inline'
+
+  - if f.object.steps.one?
+    = render 'groups/self_registration/adult_consent_field', f: ff
+    = field_set_tag(nil, class: 'privacy-policy-fields') do
+      = render('people/privacy_policy_acceptance_field', f: ff, policy_finder: f.object.policy_finder)
diff --git a/config/locales/views.de.yml b/config/locales/views.de.yml
index b0be848d..7593cb19 100644
--- a/config/locales/views.de.yml
+++ b/config/locales/views.de.yml
@@ -4,6 +4,12 @@
 #  https://github.com/hitobito/hitobito_jubla.
 
 de:
+  global:
+    phone_placeholder: +41 77 123 45 67
+  activemodel:
+    attributes:
+      wizards/steps/new_user_form:
+        phone_number: Telefon (Privat)
   census_evaluation/federation:
     export_enqueued: "Export wird im Hintergrund gestartet und nach Fertigstellung heruntergeladen."
   census_evaluation/state:
diff --git a/lib/hitobito_jubla/wagon.rb b/lib/hitobito_jubla/wagon.rb
index 346ec525..3ca3c209 100644
--- a/lib/hitobito_jubla/wagon.rb
+++ b/lib/hitobito_jubla/wagon.rb
@@ -114,6 +114,11 @@ class Wagon < Rails::Engine
       ### helpers
       EventParticipationsHelper.prepend Jubla::EventParticipationsHelper
 
+      ### wizards
+      Wizards::RegisterNewUserWizard.prepend Jubla::Wizards::RegisterNewUserWizard
+      Wizards::Steps::NewUserForm.prepend Jubla::Wizards::Steps::NewUserForm
+      Wizards::Steps::NewUserForm.support_company = false
+
       # add more active_for urls to main navigation
       admin = NavigationHelper::MAIN.find { |opts| opts[:label] == :admin }
       admin[:active_for] << "event_camp_kinds"
diff --git a/spec/models/wizards/register_new_user_wizard_spec.rb b/spec/models/wizards/register_new_user_wizard_spec.rb
new file mode 100644
index 00000000..cd5c0bdf
--- /dev/null
+++ b/spec/models/wizards/register_new_user_wizard_spec.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+#  Copyright (c) 2024, Schweizer Alpen-Club. This file is part of hitobito and licensed under the
+#  Affero General Public License version 3 or later. See the COPYING file at the top-level directory
+#  or at https://github.com/hitobito/hitobito.
+
+require "spec_helper"
+
+describe Wizards::RegisterNewUserWizard do
+  let(:params) { {} }
+  let(:role_type) { Group::Flock::External }
+  let(:group) { groups(:thun) }
+
+  subject(:wizard) do
+    described_class.new(group: group, **params).tap { |w| w.step_at(0) }
+  end
+
+  subject(:new_user_form) { wizard.new_user_form }
+
+  context "with self_wizard_role_type on group" do
+    before { group.update!(self_registration_role_type: role_type) }
+
+    describe "validations" do
+      it "is invalid if attributes are not present" do
+        expect(wizard).not_to be_valid
+        expect(new_user_form.errors).to have(3).item
+        expect(new_user_form.errors[:first_name][0]).to eq "muss ausgefüllt werden"
+        expect(new_user_form.errors[:last_name][0]).to eq "muss ausgefüllt werden"
+        expect(new_user_form.errors[:email][0]).to eq "muss ausgefüllt werden"
+      end
+
+      it "is valid if required attributes are present" do
+        params[:new_user_form] = {first_name: "test", last_name: "test", email: "tester@example.com"}
+        expect(wizard).to be_valid
+      end
+
+      it "is invalid if phone number is invalid" do
+        params[:new_user_form] = {first_name: "test", last_name: "test", email: "tester@example.com", phone_number: "1234"}
+        expect(wizard).not_to be_valid
+        expect(new_user_form.errors).to have(1).item
+        expect(new_user_form.errors[:phone_number][0]).to eq "ist nicht gültig"
+      end
+    end
+
+    describe "#save!" do
+      let(:person) { Person.find_by(first_name: "test") }
+
+      it "updates all person fields" do
+        params[:new_user_form] = {
+          first_name: "test",
+          last_name: "dummy",
+          address_care_of: "",
+          street: "Bergstrasse",
+          housenumber: "41",
+          postbox: "3",
+          zip_code: "3000",
+          town: "Bern",
+          email: "test@example.com",
+          privacy_policy_accepted: true
+        }
+        freeze_time
+        wizard.save!
+        expect(person.privacy_policy_accepted_at).to eq Time.zone.now
+        expect(person.last_name).to eq "dummy"
+        expect(person.street).to eq "Bergstrasse"
+        expect(person.housenumber).to eq "41"
+        expect(person.postbox).to eq "3"
+        expect(person.zip_code).to eq "3000"
+        expect(person.town).to eq "Bern"
+        expect(person.email).to eq "test@example.com"
+      end
+
+      it "sets non-public phone number with correct label" do
+        params[:new_user_form] = {
+          first_name: "test",
+          last_name: "dummy",
+          phone_number: "0781234567",
+          email: "test@example.com",
+          privacy_policy_accepted: true
+        }
+        wizard.save!
+        expect(person.phone_numbers).to have(1).item
+        number = person.phone_numbers.first
+        expect(number.label).to eq "Privat"
+        expect(number.number).to eq "+41 78 123 45 67"
+        expect(number.public).to eq false
+      end
+    end
+  end
+end