diff --git a/Gemfile b/Gemfile index 51fbd5e9..ba270748 100644 --- a/Gemfile +++ b/Gemfile @@ -21,6 +21,7 @@ gem 'pg' gem 'premailer-rails' gem 'puma' gem 'rails' +gem 'rails-i18n' gem 'ruby-vips' gem 'shakapacker' gem 'sqlite3' diff --git a/Gemfile.lock b/Gemfile.lock index 0bd3e1cd..9c95070f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -352,6 +352,9 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) + rails-i18n (7.0.7) + i18n (>= 0.7, < 2) + railties (>= 6.0.0, < 8) railties (7.0.5) actionpack (= 7.0.5) activesupport (= 7.0.5) @@ -511,6 +514,7 @@ DEPENDENCIES pry-rails puma rails + rails-i18n rspec-rails rubocop rubocop-performance diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb new file mode 100644 index 00000000..523dcf71 --- /dev/null +++ b/app/controllers/accounts_controller.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +class AccountsController < ApplicationController + LOCALE_OPTIONS = { + 'en-US' => 'English (United States)', + 'en-GB' => 'English (United Kingdom)', + 'es-ES' => 'Spanish (Spain)', + 'pt-PT' => 'Portuguese (Portugal)', + 'de-DE' => 'German (Germany)' + }.freeze + + def show; end + + def update + current_account.update!(account_params) + + @encrypted_config = EncryptedConfig.find_or_initialize_by(account: current_account, + key: EncryptedConfig::APP_URL_KEY) + @encrypted_config.update!(app_url_params) + + Docuseal.refresh_default_url_options! + + redirect_to settings_account_path, notice: 'Account information has been updated' + rescue ActiveRecord::RecordInvalid + render :show, status: :unprocessable_entity + end + + private + + def account_params + params.require(:account).permit(:name, :timezone, :locale) + end + + def app_url_params + params.require(:encrypted_config).permit(:value) + end +end diff --git a/app/controllers/profile_controller.rb b/app/controllers/profile_controller.rb index 033f12a8..62e047a1 100644 --- a/app/controllers/profile_controller.rb +++ b/app/controllers/profile_controller.rb @@ -1,13 +1,11 @@ # frozen_string_literal: true class ProfileController < ApplicationController - before_action :load_encrypted_config, only: %i[index update_app_url] - def index; end def update_contact if current_user.update(contact_params) - redirect_to settings_profile_index_path, notice: 'Contact information successfully updated' + redirect_to settings_profile_index_path, notice: 'Contact information has been updated' else render :index, status: :unprocessable_entity end @@ -16,17 +14,7 @@ class ProfileController < ApplicationController def update_password if current_user.update(password_params) bypass_sign_in(current_user) - redirect_to settings_profile_index_path, notice: 'Password successfully changed' - else - render :index, status: :unprocessable_entity - end - end - - def update_app_url - if @encrypted_config.update(app_url_params) - Docuseal.refresh_default_url_options! - - redirect_to settings_profile_index_path, notice: 'App URL successfully changed' + redirect_to settings_profile_index_path, notice: 'Password has been changed' else render :index, status: :unprocessable_entity end @@ -34,20 +22,11 @@ class ProfileController < ApplicationController private - def load_encrypted_config - @encrypted_config = - EncryptedConfig.find_or_initialize_by(account: current_account, key: EncryptedConfig::APP_URL_KEY) - end - def contact_params - params.require(:user).permit(:first_name, :last_name, :email, account_attributes: %i[name]) + params.require(:user).permit(:first_name, :last_name, :email) end def password_params params.require(:user).permit(:password, :password_confirmation) end - - def app_url_params - params.require(:encrypted_config).permit(:value) - end end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 63f5523c..f0f0028c 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -5,6 +5,7 @@ class RegistrationsController < Devise::RegistrationsController def build_resource(_hash = {}) account = Account.new(account_params) + account.timezone = Accounts.normalize_timezone(account.timezone) self.resource = account.users.new(user_params) end @@ -18,6 +19,6 @@ class RegistrationsController < Devise::RegistrationsController def account_params return {} if params[:account].blank? - params.require(:account).permit(:name) + params.require(:account).permit(:name, :timezone) end end diff --git a/app/controllers/setup_controller.rb b/app/controllers/setup_controller.rb index 8fe0bbc2..e9a8a806 100644 --- a/app/controllers/setup_controller.rb +++ b/app/controllers/setup_controller.rb @@ -15,6 +15,8 @@ class SetupController < ApplicationController def create @account = Account.new(account_params) + @account.timezone = Accounts.normalize_timezone(@account.timezone) + @user = @account.users.new(user_params) if @user.save @@ -24,6 +26,8 @@ class SetupController < ApplicationController ] @account.encrypted_configs.create!(encrypted_configs) + Docuseal.refresh_default_url_options! + sign_in(@user) redirect_to root_path @@ -43,7 +47,7 @@ class SetupController < ApplicationController def account_params return {} unless params[:account] - params.require(:account).permit(:name) + params.require(:account).permit(:name, :timezone) end def encrypted_config_params diff --git a/app/controllers/start_form_controller.rb b/app/controllers/start_form_controller.rb index bdde91a0..93ca4fbf 100644 --- a/app/controllers/start_form_controller.rb +++ b/app/controllers/start_form_controller.rb @@ -16,7 +16,7 @@ class StartFormController < ApplicationController .find_or_initialize_by(**submitter_params) if @submitter.completed_at? - redirect_to start_form_completed_path(@template.slug, email: submission_params[:email]) + redirect_to start_form_completed_path(@template.slug, email: submitter_params[:email]) else @submitter.assign_attributes( uuid: @template.submitters.first['uuid'], @@ -36,7 +36,7 @@ class StartFormController < ApplicationController end def completed - @submitter = Submitter.where(submission: @template.submitters).find_by(email: params[:email]) + @submitter = Submitter.where(submission: @template.submissions).find_by!(email: params[:email]) end private @@ -46,7 +46,7 @@ class StartFormController < ApplicationController end def load_template - slug = params[:slug] || params[:start_template_slug] + slug = params[:slug] || params[:start_form_slug] @template = Template.find_by!(slug:) end diff --git a/app/controllers/submit_form_controller.rb b/app/controllers/submit_form_controller.rb index 00f3e19a..c014b6d7 100644 --- a/app/controllers/submit_form_controller.rb +++ b/app/controllers/submit_form_controller.rb @@ -19,6 +19,7 @@ class SubmitFormController < ApplicationController submitter = Submitter.find_by!(slug: params[:slug]) submitter.values.merge!(normalized_values) submitter.completed_at = Time.current if params[:completed] == 'true' + submitter.opened_at ||= Time.current submitter.save! diff --git a/app/javascript/application.js b/app/javascript/application.js index a12535d2..ac414dcb 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -12,6 +12,7 @@ import ClipboardCopy from './elements/clipboard_copy' import DynamicList from './elements/dynamic_list' import DownloadButton from './elements/download_button' import SetOriginUrl from './elements/set_origin_url' +import SetTimezone from './elements/set_timezone' document.addEventListener('turbo:before-cache', () => { window.flash?.remove() @@ -32,6 +33,7 @@ window.customElements.define('clipboard-copy', ClipboardCopy) window.customElements.define('dynamic-list', DynamicList) window.customElements.define('download-button', DownloadButton) window.customElements.define('set-origin-url', SetOriginUrl) +window.customElements.define('set-timezone', SetTimezone) window.customElements.define('template-builder', class extends HTMLElement { connectedCallback () { diff --git a/app/javascript/elements/set_timezone.js b/app/javascript/elements/set_timezone.js new file mode 100644 index 00000000..be4c05e1 --- /dev/null +++ b/app/javascript/elements/set_timezone.js @@ -0,0 +1,7 @@ +export default class extends HTMLElement { + connectedCallback () { + if (this.dataset.inputId) { + document.getElementById(this.dataset.inputId).value = Intl.DateTimeFormat().resolvedOptions().timeZone + } + } +} diff --git a/app/javascript/submission_form/area.vue b/app/javascript/submission_form/area.vue index 7aa500ff..76d33091 100644 --- a/app/javascript/submission_form/area.vue +++ b/app/javascript/submission_form/area.vue @@ -207,7 +207,7 @@ export default { }, formattedDate () { if (this.field.type === 'date' && this.modelValue) { - return new Intl.DateTimeFormat({ year: 'numeric', month: 'numeric', day: 'numeric' }).format(new Date(this.modelValue)) + return new Intl.DateTimeFormat([], { year: 'numeric', month: 'long', day: 'numeric' }).format(new Date(this.modelValue)) } else { return '' } diff --git a/app/javascript/submission_form/form.vue b/app/javascript/submission_form/form.vue index 8c424b99..76b5988b 100644 --- a/app/javascript/submission_form/form.vue +++ b/app/javascript/submission_form/form.vue @@ -41,6 +41,10 @@ >{{ currentField.name }} +
{{ currentField.name }} +
{{ currentField.name }} +