pull/381/head
Oleksandr Turchyn 1 year ago committed by Oleksandr Turchyn
parent 3c60fb4ee6
commit e9d728fa4a

@ -24,7 +24,7 @@ class AccountsController < ApplicationController
@encrypted_config.assign_attributes(app_url_params) @encrypted_config.assign_attributes(app_url_params)
unless URI.parse(@encrypted_config.value.to_s).class.in?([URI::HTTP, URI::HTTPS]) unless URI.parse(@encrypted_config.value.to_s).class.in?([URI::HTTP, URI::HTTPS])
@encrypted_config.errors.add(:value, 'should be a valid URL') @encrypted_config.errors.add(:value, I18n.t('should_be_a_valid_url'))
return render :show, status: :unprocessable_entity return render :show, status: :unprocessable_entity
end end
@ -33,7 +33,7 @@ class AccountsController < ApplicationController
Docuseal.refresh_default_url_options! Docuseal.refresh_default_url_options!
redirect_to settings_account_path, notice: 'Account information has been updated' redirect_to settings_account_path, notice: I18n.t('account_information_has_been_updated')
rescue ActiveRecord::RecordInvalid rescue ActiveRecord::RecordInvalid
render :show, status: :unprocessable_entity render :show, status: :unprocessable_entity
end end
@ -43,11 +43,12 @@ class AccountsController < ApplicationController
true_user.update!(locked_at: Time.current) true_user.update!(locked_at: Time.current)
# rubocop:disable Layout/LineLength
render turbo_stream: turbo_stream.replace( render turbo_stream: turbo_stream.replace(
:account_delete_button, :account_delete_button,
html: helpers.tag.p('Your account removal request will be processed within 2 weeks. ' \ html: helpers.tag.p(I18n.t('your_account_removal_request_will_be_processed_within_2_weeks_please_contact_us_if_you_want_to_keep_your_account'))
'Please contact us if you want to keep your account.')
) )
# rubocop:enable Layout/LineLength
end end
private private

@ -12,6 +12,6 @@ class ApiSettingsController < ApplicationController
current_user.access_token.save! current_user.access_token.save!
redirect_back(fallback_location: settings_api_index_path, notice: 'API token as been updated.') redirect_back(fallback_location: settings_api_index_path, notice: I18n.t('api_token_has_been_updated'))
end end
end end

@ -79,7 +79,8 @@ class ApplicationController < ActionController::Base
redirect_to setup_index_path unless User.exists? redirect_to setup_index_path unless User.exists?
end end
def button_title(title: 'Submit', disabled_with: 'Submitting', title_class: '', icon: nil, icon_disabled: nil) def button_title(title: I18n.t('submit'), disabled_with: I18n.t('submitting'), title_class: '', icon: nil,
icon_disabled: nil)
render_to_string(partial: 'shared/button_title', render_to_string(partial: 'shared/button_title',
locals: { title:, disabled_with:, title_class:, icon:, icon_disabled: }) locals: { title:, disabled_with:, title_class:, icon:, icon_disabled: })
end end

@ -33,7 +33,7 @@ class DashboardController < ApplicationController
account_id: current_user.account_id, account_id: current_user.account_id,
key: AccountConfig::FORCE_MFA) key: AccountConfig::FORCE_MFA)
redirect_to mfa_setup_path, notice: 'Setup 2FA to continue' redirect_to mfa_setup_path, notice: I18n.t('setup_2fa_to_continue')
end end
def maybe_render_landing def maybe_render_landing

@ -11,7 +11,7 @@ class EmailSmtpSettingsController < ApplicationController
if @encrypted_config.update(email_configs) if @encrypted_config.update(email_configs)
SettingsMailer.smtp_successful_setup(@encrypted_config.value['from_email']).deliver_now! SettingsMailer.smtp_successful_setup(@encrypted_config.value['from_email']).deliver_now!
redirect_to settings_email_index_path, notice: 'Changes have been saved' redirect_to settings_email_index_path, notice: I18n.t('changes_have_been_saved')
else else
render :index, status: :unprocessable_entity render :index, status: :unprocessable_entity
end end

@ -48,7 +48,7 @@ class EsignSettingsController < ApplicationController
if (@encrypted_config.value && @encrypted_config.value['custom']&.any? { |e| e['name'] == @cert_record.name }) || if (@encrypted_config.value && @encrypted_config.value['custom']&.any? { |e| e['name'] == @cert_record.name }) ||
@cert_record.name == DEFAULT_CERT_NAME @cert_record.name == DEFAULT_CERT_NAME
@cert_record.errors.add(:name, 'already exists') @cert_record.errors.add(:name, I18n.t('already_exists'))
return render turbo_stream: turbo_stream.replace(:modal, template: 'esign_settings/new'), return render turbo_stream: turbo_stream.replace(:modal, template: 'esign_settings/new'),
status: :unprocessable_entity status: :unprocessable_entity
@ -56,7 +56,7 @@ class EsignSettingsController < ApplicationController
save_new_cert!(@encrypted_config, @cert_record) save_new_cert!(@encrypted_config, @cert_record)
redirect_to settings_esign_path, notice: 'Certificate has been successfully added!' redirect_to settings_esign_path, notice: I18n.t('certificate_has_been_successfully_added_')
rescue OpenSSL::PKCS12::PKCS12Error => e rescue OpenSSL::PKCS12::PKCS12Error => e
Rollbar.error(e) if defined?(Rollbar) Rollbar.error(e) if defined?(Rollbar)
@ -73,7 +73,7 @@ class EsignSettingsController < ApplicationController
@encrypted_config.save! @encrypted_config.save!
redirect_to settings_esign_path, notice: 'Default certificate has been selected' redirect_to settings_esign_path, notice: I18n.t('default_certificate_has_been_selected')
end end
def destroy def destroy
@ -81,7 +81,7 @@ class EsignSettingsController < ApplicationController
@encrypted_config.save! @encrypted_config.save!
redirect_to settings_esign_path, notice: 'Certificate has been removed' redirect_to settings_esign_path, notice: I18m.t('certificate_has_been_removed')
end end
private private

@ -18,11 +18,11 @@ class MfaSetupController < ApplicationController
current_user.otp_required_for_login = true current_user.otp_required_for_login = true
current_user.save! current_user.save!
redirect_to settings_profile_index_path, notice: '2FA has been configured' redirect_to settings_profile_index_path, notice: I18n.t('2fa_has_been_configured')
else else
@provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Docuseal.product_name) @provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Docuseal.product_name)
@error_message = 'Code is invalid' @error_message = I18n.t('code_is_invalid')
render turbo_stream: turbo_stream.replace(:mfa_form, partial: 'mfa_setup/form'), status: :unprocessable_entity render turbo_stream: turbo_stream.replace(:mfa_form, partial: 'mfa_setup/form'), status: :unprocessable_entity
end end
@ -32,9 +32,9 @@ class MfaSetupController < ApplicationController
if current_user.validate_and_consume_otp!(params[:otp_attempt]) if current_user.validate_and_consume_otp!(params[:otp_attempt])
current_user.update!(otp_required_for_login: false, otp_secret: nil) current_user.update!(otp_required_for_login: false, otp_secret: nil)
redirect_to settings_profile_index_path, notice: '2FA has been removed' redirect_to settings_profile_index_path, notice: I18n.t('2fa_has_been_removed')
else else
@error_message = 'Code is invalid' @error_message = I18n.t('code_is_invalid')
render turbo_stream: turbo_stream.replace(:modal, template: 'mfa_setup/edit'), status: :unprocessable_entity render turbo_stream: turbo_stream.replace(:modal, template: 'mfa_setup/edit'), status: :unprocessable_entity
end end
@ -43,7 +43,7 @@ class MfaSetupController < ApplicationController
private private
def set_provision_url def set_provision_url
return redirect_to root_path, alert: '2FA has been set up already' if current_user.otp_required_for_login return redirect_to root_path, alert: I18n.t('2fa_has_been_set_up_already') if current_user.otp_required_for_login
current_user.otp_secret ||= User.generate_otp_secret current_user.otp_secret ||= User.generate_otp_secret

@ -13,9 +13,9 @@ class NotificationsSettingsController < ApplicationController
def create def create
if @account_config.value.present? ? @account_config.save : @account_config.delete if @account_config.value.present? ? @account_config.save : @account_config.delete
redirect_back fallback_location: settings_notifications_path, notice: 'Changes have been saved' redirect_back fallback_location: settings_notifications_path, notice: I18n.t('changes_have_been_saved')
else else
redirect_back fallback_location: settings_notifications_path, alert: 'Unable to save' redirect_back fallback_location: settings_notifications_path, alert: I18n.t('unable_to_save')
end end
end end

@ -30,7 +30,7 @@ class PersonalizationSettingsController < ApplicationController
@account_config.save! @account_config.save!
end end
redirect_back(fallback_location: settings_personalization_path, notice: 'Settings have been saved.') redirect_back(fallback_location: settings_personalization_path, notice: I18n.t('settings_have_been_saved'))
end end
private private

@ -9,7 +9,7 @@ class ProfileController < ApplicationController
def update_contact def update_contact
if current_user.update(contact_params) if current_user.update(contact_params)
redirect_to settings_profile_index_path, notice: 'Contact information has been updated' redirect_to settings_profile_index_path, notice: I18n.t('contact_information_has_been_update')
else else
render :index, status: :unprocessable_entity render :index, status: :unprocessable_entity
end end
@ -18,7 +18,7 @@ class ProfileController < ApplicationController
def update_password def update_password
if current_user.update(password_params) if current_user.update(password_params)
bypass_sign_in(current_user) bypass_sign_in(current_user)
redirect_to settings_profile_index_path, notice: 'Password has been changed' redirect_to settings_profile_index_path, notice: I18n.t('password_has_been_changed')
else else
render :index, status: :unprocessable_entity render :index, status: :unprocessable_entity
end end

@ -12,7 +12,7 @@ class SessionsController < Devise::SessionsController
Rollbar.warning('Sign in new user') if defined?(Rollbar) Rollbar.warning('Sign in new user') if defined?(Rollbar)
return redirect_to new_registration_path(sign_up: true, user: sign_in_params.slice(:email)), return redirect_to new_registration_path(sign_up: true, user: sign_in_params.slice(:email)),
notice: 'Create a new account' notice: I18n.t('create_a_new_account')
end end
if User.exists?(email:, otp_required_for_login: true) && sign_in_params[:otp_attempt].blank? if User.exists?(email:, otp_required_for_login: true) && sign_in_params[:otp_attempt].blank?

@ -21,7 +21,7 @@ class SetupController < ApplicationController
@encrypted_config = EncryptedConfig.new(encrypted_config_params) @encrypted_config = EncryptedConfig.new(encrypted_config_params)
unless URI.parse(encrypted_config_params[:value].to_s).class.in?([URI::HTTP, URI::HTTPS]) unless URI.parse(encrypted_config_params[:value].to_s).class.in?([URI::HTTP, URI::HTTPS])
@encrypted_config.errors.add(:value, 'should be a valid URL') @encrypted_config.errors.add(:value, I18n.t('should_be_a_valid_url'))
return render :index, status: :unprocessable_entity return render :index, status: :unprocessable_entity
end end
@ -66,10 +66,10 @@ class SetupController < ApplicationController
end end
def redirect_to_root_if_signed def redirect_to_root_if_signed
redirect_to root_path, notice: 'You are already signed in' redirect_to root_path, notice: I18n.t('you_are_already_signed_in')
end end
def ensure_first_user_not_created! def ensure_first_user_not_created!
redirect_to new_user_session_path, notice: 'Please sign in.' if User.exists? redirect_to new_user_session_path, notice: I18n.t('please_sign_in') if User.exists?
end end
end end

@ -24,7 +24,7 @@ class StartFormController < ApplicationController
redirect_to start_form_completed_path(@template.slug, email: submitter_params[:email]) redirect_to start_form_completed_path(@template.slug, email: submitter_params[:email])
else else
if filter_undefined_submitters(@template).size > 1 && @submitter.new_record? if filter_undefined_submitters(@template).size > 1 && @submitter.new_record?
@error_message = 'Not found' @error_message = I18n.t('not_found')
return render :show return render :show
end end

@ -11,7 +11,7 @@ class StorageSettingsController < ApplicationController
if @encrypted_config.update(storage_configs) if @encrypted_config.update(storage_configs)
LoadActiveStorageConfigs.reload LoadActiveStorageConfigs.reload
redirect_to settings_storage_index_path, notice: 'Changes have been saved' redirect_to settings_storage_index_path, notice: I18n.t('changes_have_been_saved')
else else
render :index, status: :unprocessable_entity render :index, status: :unprocessable_entity
end end

@ -49,7 +49,7 @@ class SubmissionsController < ApplicationController
Submissions.send_signature_requests(submissions) Submissions.send_signature_requests(submissions)
redirect_to template_path(@template), notice: 'New recipients have been added' redirect_to template_path(@template), notice: I18n.t('new_recipients_have_been_added')
end end
def destroy def destroy
@ -57,13 +57,13 @@ class SubmissionsController < ApplicationController
if params[:permanently].present? if params[:permanently].present?
@submission.destroy! @submission.destroy!
'Submission has been removed' I18n.t('submission_has_been_deleted')
else else
@submission.update!(archived_at: Time.current) @submission.update!(archived_at: Time.current)
SendSubmissionArchivedWebhookRequestJob.perform_async('submission_id' => @submission.id) SendSubmissionArchivedWebhookRequestJob.perform_async('submission_id' => @submission.id)
'Submission has been archived' I18n.t('submission_has_been_archived')
end end
redirect_back(fallback_location: template_path(@submission.template), notice:) redirect_back(fallback_location: template_path(@submission.template), notice:)

@ -19,7 +19,7 @@ class SubmissionsPreviewController < ApplicationController
@submission ||= Submission.find_by!(slug: params[:slug]) @submission ||= Submission.find_by!(slug: params[:slug])
if !@submission.submitters.all?(&:completed_at?) && current_user.blank? if !@submission.submitters.all?(&:completed_at?) && current_user.blank?
raise ActionController::RoutingError, 'Not Found' raise ActionController::RoutingError, I18n.t('not_found')
end end
if !submission_valid_ttl?(@submission) && !signature_valid if !submission_valid_ttl?(@submission) && !signature_valid

@ -46,18 +46,21 @@ class SubmitFormController < ApplicationController
submitter = Submitter.find_by!(slug: params[:slug]) submitter = Submitter.find_by!(slug: params[:slug])
if submitter.completed_at? if submitter.completed_at?
return render json: { error: 'Form has been completed already.' }, status: :unprocessable_entity return render json: { error: I18n.t('form_has_been_completed_already') }, status: :unprocessable_entity
end end
if submitter.template.archived_at? || submitter.submission.archived_at? if submitter.template.archived_at? || submitter.submission.archived_at?
return render json: { error: 'Form has been archived.' }, status: :unprocessable_entity return render json: { error: I18n.t('form_has_been_archived') }, status: :unprocessable_entity
end end
if submitter.submission.expired? if submitter.submission.expired?
return render json: { error: 'Form has been expired.' }, status: :unprocessable_entity return render json: { error: I18n.t('form_has_been_expired') }, status: :unprocessable_entity
end end
return render json: { error: 'Form has been declined.' }, status: :unprocessable_entity if submitter.declined_at? if submitter.declined_at?
return render json: { error: I18n.t('form_has_been_declined') },
status: :unprocessable_entity
end
Submitters::SubmitValues.call(submitter, params, request) Submitters::SubmitValues.call(submitter, params, request)

@ -10,7 +10,7 @@ class SubmittersSendEmailController < ApplicationController
Rollbar.warning("Already sent: #{@submitter.id}") if defined?(Rollbar) Rollbar.warning("Already sent: #{@submitter.id}") if defined?(Rollbar)
return redirect_back(fallback_location: submission_path(@submitter.submission), return redirect_back(fallback_location: submission_path(@submitter.submission),
alert: 'Email has been sent already.') alert: I18n.t('email_has_been_sent_already'))
end end
SendSubmitterInvitationEmailJob.perform_async('submitter_id' => @submitter.id) SendSubmitterInvitationEmailJob.perform_async('submitter_id' => @submitter.id)
@ -18,6 +18,6 @@ class SubmittersSendEmailController < ApplicationController
@submitter.sent_at ||= Time.current @submitter.sent_at ||= Time.current
@submitter.save! @submitter.save!
redirect_back(fallback_location: submission_path(@submitter.submission), notice: 'Email has been sent') redirect_back(fallback_location: submission_path(@submitter.submission), notice: I18n.t('email_has_been_sent'))
end end
end end

@ -5,7 +5,7 @@ class TemplateDocumentsController < ApplicationController
def create def create
if params[:blobs].blank? && params[:files].blank? if params[:blobs].blank? && params[:files].blank?
return render json: { error: 'File is missing' }, status: :unprocessable_entity return render json: { error: I18n.t('file_is_missing') }, status: :unprocessable_entity
end end
old_fields_hash = @template.fields.hash old_fields_hash = @template.fields.hash
@ -28,6 +28,6 @@ class TemplateDocumentsController < ApplicationController
) )
} }
rescue Templates::CreateAttachments::PdfEncrypted rescue Templates::CreateAttachments::PdfEncrypted
render json: { error: 'PDF encrypted' }, status: :unprocessable_entity render json: { error: 'PDF encrypted', status: 'pdf_encrypted' }, status: :unprocessable_entity
end end
end end

@ -15,9 +15,9 @@ class TemplateFoldersController < ApplicationController
def update def update
if @template_folder != current_account.default_template_folder && if @template_folder != current_account.default_template_folder &&
@template_folder.update(template_folder_params) @template_folder.update(template_folder_params)
redirect_to folder_path(@template_folder), notice: 'Folder name has been updated' redirect_to folder_path(@template_folder), notice: I18n.t('folder_name_has_been_updated')
else else
redirect_to folder_path(@template_folder), alert: 'Unable to rename folder' redirect_to folder_path(@template_folder), alert: I18n.t('unable_to_rename_folder')
end end
end end

@ -21,7 +21,7 @@ class TemplatesController < ApplicationController
end end
def new def new
@template.name = "#{@base_template.name} (Clone)" if @base_template @template.name = "#{@base_template.name} (#{I18n.t('clone')})" if @base_template
end end
def edit def edit
@ -87,11 +87,11 @@ class TemplatesController < ApplicationController
if params[:permanently].present? if params[:permanently].present?
@template.destroy! @template.destroy!
'Template has been removed.' I18n.t('template_has_been_removed')
else else
@template.update!(archived_at: Time.current) @template.update!(archived_at: Time.current)
'Template has been archived.' I18n.t('template_has_been_archived')
end end
redirect_back(fallback_location: root_path, notice:) redirect_back(fallback_location: root_path, notice:)
@ -123,7 +123,7 @@ class TemplatesController < ApplicationController
if template.account == current_account if template.account == current_account
redirect_to(edit_template_path(@template)) redirect_to(edit_template_path(@template))
else else
redirect_back(fallback_location: root_path, notice: 'Template has been cloned') redirect_back(fallback_location: root_path, notice: I18n.t('template_has_been_cloned'))
end end
end end

@ -9,9 +9,9 @@ class TemplatesFoldersController < ApplicationController
@template.folder = TemplateFolders.find_or_create_by_name(current_user, params[:name]) @template.folder = TemplateFolders.find_or_create_by_name(current_user, params[:name])
if @template.save if @template.save
redirect_back(fallback_location: template_path(@template), notice: 'Document template has been moved') redirect_back(fallback_location: template_path(@template), notice: I18n.t('document_template_has_been_moved'))
else else
redirect_back(fallback_location: template_path(@template), notice: 'Unable to move template into folder') redirect_back(fallback_location: template_path(@template), notice: I18n.t('unable_to_move_template_into_folder'))
end end
end end

@ -6,6 +6,6 @@ class TemplatesRestoreController < ApplicationController
def create def create
@template.update!(archived_at: nil) @template.update!(archived_at: nil)
redirect_to template_path(@template), notice: 'Template has been unarchived' redirect_to template_path(@template), notice: I18n.t('template_has_been_unarchived')
end end
end end

@ -33,7 +33,7 @@ class TemplatesUploadsController < ApplicationController
raise if Rails.env.local? raise if Rails.env.local?
redirect_to root_path, alert: 'Unable to upload file' redirect_to root_path, alert: I18n.t('unable_to_update_file')
end end
private private

@ -14,12 +14,12 @@ class TimestampServerController < ApplicationController
test_timeserver_url(@encrypted_config.value) if @encrypted_config.value.present? test_timeserver_url(@encrypted_config.value) if @encrypted_config.value.present?
if @encrypted_config.value.present? ? @encrypted_config.save : @encrypted_config.delete if @encrypted_config.value.present? ? @encrypted_config.save : @encrypted_config.delete
redirect_back fallback_location: settings_notifications_path, notice: 'Changes have been saved' redirect_back fallback_location: settings_notifications_path, notice: I18n.t('changes_have_been_saved')
else else
redirect_back fallback_location: settings_notifications_path, alert: 'Unable to save' redirect_back fallback_location: settings_notifications_path, alert: I18n.t('unable_to_save')
end end
rescue SocketError, TimestampError, OpenSSL::Timestamp::TimestampError rescue SocketError, TimestampError, OpenSSL::Timestamp::TimestampError
redirect_back fallback_location: settings_notifications_path, alert: 'Invalid Timeserver' redirect_back fallback_location: settings_notifications_path, alert: t('invalid_timeserver')
end end
private private

@ -9,7 +9,7 @@ class UserInitialsController < ApplicationController
def update def update
file = params[:file] file = params[:file]
return redirect_to settings_profile_index_path, notice: 'Unable to save initials' if file.blank? return redirect_to settings_profile_index_path, notice: I18n.t('unable_to_save_initials') if file.blank?
blob = ActiveStorage::Blob.create_and_upload!(io: file.open, blob = ActiveStorage::Blob.create_and_upload!(io: file.open,
filename: file.original_filename, filename: file.original_filename,
@ -22,16 +22,16 @@ class UserInitialsController < ApplicationController
) )
if @user_config.update(value: attachment.uuid) if @user_config.update(value: attachment.uuid)
redirect_to settings_profile_index_path, notice: 'Initials has been saved' redirect_to settings_profile_index_path, notice: I18n.t('initials_has_been_saved')
else else
redirect_to settings_profile_index_path, notice: 'Unable to save initials' redirect_to settings_profile_index_path, notice: I18n.t('unable_to_save_initials')
end end
end end
def destroy def destroy
@user_config.destroy @user_config.destroy
redirect_to settings_profile_index_path, notice: 'Initials has been removed' redirect_to settings_profile_index_path, notice: I18n.t('initials_has_been_removed')
end end
private private

@ -9,7 +9,7 @@ class UserSignaturesController < ApplicationController
def update def update
file = params[:file] file = params[:file]
return redirect_to settings_profile_index_path, notice: 'Unable to save signature' if file.blank? return redirect_to settings_profile_index_path, notice: I18n.t('Unable to save signature') if file.blank?
blob = ActiveStorage::Blob.create_and_upload!(io: file.open, blob = ActiveStorage::Blob.create_and_upload!(io: file.open,
filename: file.original_filename, filename: file.original_filename,
@ -22,16 +22,16 @@ class UserSignaturesController < ApplicationController
) )
if @user_config.update(value: attachment.uuid) if @user_config.update(value: attachment.uuid)
redirect_to settings_profile_index_path, notice: 'Signature has been saved' redirect_to settings_profile_index_path, notice: I18n.t('signature_has_been_saved')
else else
redirect_to settings_profile_index_path, notice: 'Unable to save signature' redirect_to settings_profile_index_path, notice: I18n.t('Unable to save signature')
end end
end end
def destroy def destroy
@user_config.destroy @user_config.destroy
redirect_to settings_profile_index_path, notice: 'Signature has been removed' redirect_to settings_profile_index_path, notice: I18n.t('signature_has_been_removed')
end end
private private

@ -35,14 +35,14 @@ class UsersController < ApplicationController
if @user.save if @user.save
UserMailer.invitation_email(@user).deliver_later! UserMailer.invitation_email(@user).deliver_later!
redirect_back fallback_location: settings_users_path, notice: 'User has been invited' redirect_back fallback_location: settings_users_path, notice: I18n.t('user_has_been_invited')
else else
render turbo_stream: turbo_stream.replace(:modal, template: 'users/new'), status: :unprocessable_entity render turbo_stream: turbo_stream.replace(:modal, template: 'users/new'), status: :unprocessable_entity
end end
end end
def update def update
return redirect_to settings_users_path, notice: 'Unable to update user.' if Docuseal.demo? return redirect_to settings_users_path, notice: I18n.t('unable_to_update_user') if Docuseal.demo?
attrs = user_params.compact_blank.merge(user_params.slice(:archived_at)) attrs = user_params.compact_blank.merge(user_params.slice(:archived_at))
attrs.delete(:role) if !role_valid?(attrs[:role]) || current_user == @user attrs.delete(:role) if !role_valid?(attrs[:role]) || current_user == @user
@ -56,7 +56,7 @@ class UsersController < ApplicationController
end end
if @user.update(attrs) if @user.update(attrs)
redirect_back fallback_location: settings_users_path, notice: 'User has been updated' redirect_back fallback_location: settings_users_path, notice: I18n.t('user_has_been_updated')
else else
render turbo_stream: turbo_stream.replace(:modal, template: 'users/edit'), status: :unprocessable_entity render turbo_stream: turbo_stream.replace(:modal, template: 'users/edit'), status: :unprocessable_entity
end end
@ -64,12 +64,12 @@ class UsersController < ApplicationController
def destroy def destroy
if Docuseal.demo? || @user.id == current_user.id if Docuseal.demo? || @user.id == current_user.id
return redirect_to settings_users_path, notice: 'Unable to remove user' return redirect_to settings_users_path, notice: I18n.t('unable_to_remove_user')
end end
@user.update!(archived_at: Time.current) @user.update!(archived_at: Time.current)
redirect_back fallback_location: settings_users_path, notice: 'User has been removed' redirect_back fallback_location: settings_users_path, notice: I18n.t('user_has_been_removed')
end end
private private

@ -14,6 +14,6 @@ class VerifyPdfSignatureController < ApplicationController
render turbo_stream: turbo_stream.replace('result', partial: 'result', render turbo_stream: turbo_stream.replace('result', partial: 'result',
locals: { pdfs:, files: params[:files], trusted_certs: }) locals: { pdfs:, files: params[:files], trusted_certs: })
rescue HexaPDF::MalformedPDFError rescue HexaPDF::MalformedPDFError
render turbo_stream: turbo_stream.replace('result', html: helpers.tag.div('Invalid PDF', id: 'result')) render turbo_stream: turbo_stream.replace('result', html: helpers.tag.div(I18n.t('invalid_pdf'), id: 'result'))
end end
end end

@ -13,7 +13,7 @@ class WebhookSecretController < ApplicationController
@encrypted_config.value.present? ? @encrypted_config.save! : @encrypted_config.delete @encrypted_config.value.present? ? @encrypted_config.save! : @encrypted_config.delete
redirect_back(fallback_location: settings_webhooks_path, notice: 'Webhook Secret has been saved.') redirect_back(fallback_location: settings_webhooks_path, notice: I18n.t('webhook_secret_has_been_saved'))
end end
private private

@ -11,7 +11,7 @@ class WebhookSettingsController < ApplicationController
@encrypted_config.value.present? ? @encrypted_config.save! : @encrypted_config.delete @encrypted_config.value.present? ? @encrypted_config.save! : @encrypted_config.delete
redirect_back(fallback_location: settings_webhooks_path, notice: 'Webhook URL has been saved.') redirect_back(fallback_location: settings_webhooks_path, notice: I18n.t('webhook_url_has_been_saved'))
end end
def update def update
@ -20,7 +20,7 @@ class WebhookSettingsController < ApplicationController
SendFormCompletedWebhookRequestJob.perform_async({ 'submitter_id' => submitter.id, SendFormCompletedWebhookRequestJob.perform_async({ 'submitter_id' => submitter.id,
'encrypted_config_id' => @encrypted_config.id }) 'encrypted_config_id' => @encrypted_config.id })
redirect_back(fallback_location: settings_webhooks_path, notice: 'Webhook request has been sent.') redirect_back(fallback_location: settings_webhooks_path, notice: I18n.t('webhook_request_has_been_sent'))
end end
private private

@ -131,7 +131,7 @@
class="flex items-center justify-center space-x-2" class="flex items-center justify-center space-x-2"
> >
<IconEye class="w-6 h-6 flex-shrink-0" /> <IconEye class="w-6 h-6 flex-shrink-0" />
<span class="whitespace-nowrap">Save and Preview</span> <span class="whitespace-nowrap">{{ t('save_and_preview') }}</span>
</a> </a>
</li> </li>
<li> <li>
@ -142,7 +142,7 @@
@click="closeDropdown" @click="closeDropdown"
> >
<IconAdjustments class="w-6 h-6 flex-shrink-0" /> <IconAdjustments class="w-6 h-6 flex-shrink-0" />
<span class="whitespace-nowrap">Preferences</span> <span class="whitespace-nowrap">{{ t('preferences') }}</span>
</a> </a>
</li> </li>
</ul> </ul>

@ -26,7 +26,7 @@
href="https://www.docuseal.co/pricing" href="https://www.docuseal.co/pricing"
target="_blank" target="_blank"
class="link" class="link"
>Available in Pro</a> >{{ t('available_in_pro') }}</a>
</div> </div>
<form @submit.prevent="validateSaveAndClose"> <form @submit.prevent="validateSaveAndClose">
<div class="my-4 space-y-5"> <div class="my-4 space-y-5">

@ -26,7 +26,7 @@
href="https://www.docuseal.co/pricing" href="https://www.docuseal.co/pricing"
target="_blank" target="_blank"
class="link" class="link"
>Available in Pro</a> >{{ t('available_in_pro') }}</a>
</div> </div>
<div class="flex-inline mb-2 gap-2 space-y-1"> <div class="flex-inline mb-2 gap-2 space-y-1">
<button <button
@ -201,7 +201,7 @@ export default {
const normalizedFormula = this.normalizeFormula(this.formula) const normalizedFormula = this.normalizeFormula(this.formula)
if (normalizedFormula.includes('FIELD NOT FOUND')) { if (normalizedFormula.includes('FIELD NOT FOUND')) {
alert('Some fields are missing in the formula.') alert(this.t('some_fields_are_missing_in_the_formula'))
} else { } else {
this.field.preferences.formula = normalizedFormula this.field.preferences.formula = normalizedFormula

@ -117,7 +117,15 @@ const en = {
custom: 'Custom', custom: 'Custom',
numbers_only: 'Numbers only', numbers_only: 'Numbers only',
letters_only: 'Letters only', letters_only: 'Letters only',
regexp_validation: 'Regexp validation' regexp_validation: 'Regexp validation',
enter_pdf_password: 'Enter PDF password',
wrong_password: 'Wrong password',
currency: 'Currency',
save_and_preview: 'Save and Preview',
preferences: 'Preferences',
available_in_pro: 'Available in Pro',
some_fields_are_missing_in_the_formula: 'Some fields are missing in the formula.',
learn_more: 'Learn more'
} }
export { en } export { en }

@ -119,7 +119,7 @@
<div class="flex items-center pl-1"> <div class="flex items-center pl-1">
<span <span
class="tooltip tooltip-top" class="tooltip tooltip-top"
data-tip="Remove" data-tip="<%= t('remove') %>"
> >
<button <button
:disabled="mappings.filter((m) => m.submitter_uuid === submitter.uuid).length < 2" :disabled="mappings.filter((m) => m.submitter_uuid === submitter.uuid).length < 2"

@ -43,7 +43,7 @@
class="absolute -top-1 left-2.5 px-1 h-4" class="absolute -top-1 left-2.5 px-1 h-4"
style="font-size: 8px" style="font-size: 8px"
> >
Currency {{ t('currency') }}
</label> </label>
</div> </div>
<div <div
@ -156,7 +156,7 @@
href="https://www.docuseal.co/blog/accept-payments-and-request-signatures-with-ease" href="https://www.docuseal.co/blog/accept-payments-and-request-signatures-with-ease"
target="_blank" target="_blank"
data-turbo="false" data-turbo="false"
>Learn more</a> >{{ t('learn_more') }}</a>
</div> </div>
<li class="mb-1"> <li class="mb-1">
<label <label

@ -90,10 +90,10 @@ export default {
}) })
} else if (resp.status === 422) { } else if (resp.status === 422) {
resp.json().then((data) => { resp.json().then((data) => {
if (data.error === 'PDF encrypted') { if (data.status === 'pdf_encrypted') {
const formData = new FormData(this.$refs.form) const formData = new FormData(this.$refs.form)
formData.append('password', prompt('Enter PDF password')) formData.append('password', prompt(this.t('enter_pdf_password')))
this.baseFetch(`/templates/${this.templateId}/documents`, { this.baseFetch(`/templates/${this.templateId}/documents`, {
method: 'POST', method: 'POST',
@ -103,7 +103,7 @@ export default {
this.$emit('success', await resp.json()) this.$emit('success', await resp.json())
this.$refs.input.value = '' this.$refs.input.value = ''
} else { } else {
alert('Wrong password') alert(this.t('wrong_password'))
} }
}) })
} }

@ -1,22 +1,24 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0"> <div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0">
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto"> <div class="flex-grow max-w-xl mx-auto">
<h1 class="text-4xl font-bold mb-4">Account</h1> <h1 class="text-4xl font-bold mb-4">
<%= t('account') %>
</h1>
<%= form_for '', url: settings_account_path, method: :patch, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %> <%= form_for '', url: settings_account_path, method: :patch, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<%= fields_for current_account do |ff| %> <%= fields_for current_account do |ff| %>
<div class="form-control"> <div class="form-control">
<%= ff.label :name, 'Company Name', class: 'label' %> <%= ff.label :name, t('company_name'), class: 'label' %>
<%= ff.text_field :name, required: true, class: 'base-input', dir: 'auto' %> <%= ff.text_field :name, required: true, class: 'base-input', dir: 'auto' %>
</div> </div>
<div class="grid md:grid-cols-2 gap-4"> <div class="grid md:grid-cols-2 gap-4">
<div class="form-control"> <div class="form-control">
<%= ff.label :timezone, class: 'label' %> <%= ff.label :timezone, t('time_zone'), class: 'label' %>
<%= ff.select :timezone, nil, {}, class: 'base-select' do %> <%= ff.select :timezone, nil, {}, class: 'base-select' do %>
<%= time_zone_options_for_select(current_account.timezone) %> <%= time_zone_options_for_select(current_account.timezone) %>
<% end %> <% end %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= ff.label :locale, 'Time format', class: 'label' %> <%= ff.label :locale, t('time_format'), class: 'label' %>
<%= ff.select :locale, options_for_select(controller.class::LOCALE_OPTIONS.invert, current_account.locale), {}, class: 'base-select' %> <%= ff.select :locale, options_for_select(controller.class::LOCALE_OPTIONS.invert, current_account.locale), {}, class: 'base-select' %>
</div> </div>
</div> </div>
@ -25,27 +27,29 @@
<% if !Docuseal.multitenant? && can?(:manage, encrypted_config) && !current_account.testing? && ENV['APP_URL'].blank? %> <% if !Docuseal.multitenant? && can?(:manage, encrypted_config) && !current_account.testing? && ENV['APP_URL'].blank? %>
<%= fields_for encrypted_config do |ff| %> <%= fields_for encrypted_config do |ff| %>
<div class="form-control"> <div class="form-control">
<%= ff.label :value, 'App URL', class: 'label' %> <%= ff.label :value, t('app_url'), class: 'label' %>
<%= ff.text_field :value, autocomplete: 'off', class: 'base-input' %> <%= ff.text_field :value, autocomplete: 'off', class: 'base-input' %>
</div> </div>
<% end %> <% end %>
<% end %> <% end %>
<% if can?(:update, current_account) %> <% if can?(:update, current_account) %>
<div class="form-control pt-2"> <div class="form-control pt-2">
<%= f.button button_title(title: 'Update', disabled_with: 'Updating'), class: 'base-button' %> <%= f.button button_title(title: t('update'), disabled_with: t('updating')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
<% end %> <% end %>
<% if can?(:manage, AccountConfig) %> <% if can?(:manage, AccountConfig) %>
<div class="px-1 mt-8"> <div class="px-1 mt-8">
<h2 class="text-2xl font-bold mb-2">Preferences</h2> <h2 class="text-2xl font-bold mb-2">
<%= t('preferences') %>
</h2>
<% account_config = AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::FORCE_MFA) %> <% account_config = AccountConfig.find_or_initialize_by(account: current_account, key: AccountConfig::FORCE_MFA) %>
<% if can?(:manage, account_config) %> <% if can?(:manage, account_config) %>
<%= form_for account_config, url: account_configs_path, method: :post do |f| %> <%= form_for account_config, url: account_configs_path, method: :post do |f| %>
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<div class="flex items-center justify-between py-2.5"> <div class="flex items-center justify-between py-2.5">
<span> <span>
Force 2FA with Authenticator App <%= t('force_2fa_with_authenticator_app') %>
</span> </span>
<%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()' %> <%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()' %>
</div> </div>
@ -57,7 +61,7 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<div class="flex items-center justify-between py-2.5"> <div class="flex items-center justify-between py-2.5">
<span> <span>
Add signature ID to the documents <%= t('add_signature_id_to_the_documents') %>
</span> </span>
<%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()' %> <%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()' %>
</div> </div>
@ -69,7 +73,7 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<div class="flex items-center justify-between py-2.5"> <div class="flex items-center justify-between py-2.5">
<span> <span>
Require signing reason <%= t('require_signing_reason') %>
</span> </span>
<%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()' %> <%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()' %>
</div> </div>
@ -81,7 +85,7 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<div class="flex items-center justify-between py-2.5"> <div class="flex items-center justify-between py-2.5">
<span> <span>
Allow typed text signatures <%= t('allow_typed_text_signatures') %>
</span> </span>
<%= f.check_box :value, class: 'toggle', checked: account_config.value != false, onchange: 'this.form.requestSubmit()' %> <%= f.check_box :value, class: 'toggle', checked: account_config.value != false, onchange: 'this.form.requestSubmit()' %>
</div> </div>
@ -93,7 +97,7 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<div class="flex items-center justify-between py-2.5"> <div class="flex items-center justify-between py-2.5">
<span> <span>
Allow to resubmit completed forms <%= t('allow_to_resubmit_completed_forms') %>
</span> </span>
<%= f.check_box :value, class: 'toggle', checked: account_config.value != false, onchange: 'this.form.requestSubmit()' %> <%= f.check_box :value, class: 'toggle', checked: account_config.value != false, onchange: 'this.form.requestSubmit()' %>
</div> </div>
@ -105,7 +109,7 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<div class="flex items-center justify-between py-2.5"> <div class="flex items-center justify-between py-2.5">
<span> <span>
Remember and pre-fill signatures <%= t('remember_and_pre_fill_signatures') %>
</span> </span>
<%= f.check_box :value, class: 'toggle', checked: account_config.value != false, onchange: 'this.form.requestSubmit()' %> <%= f.check_box :value, class: 'toggle', checked: account_config.value != false, onchange: 'this.form.requestSubmit()' %>
</div> </div>
@ -117,7 +121,7 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<div class="flex items-center justify-between py-2.5"> <div class="flex items-center justify-between py-2.5">
<span> <span>
Require authentication for file download links <%= t('require_authentication_for_file_download_links') %>
</span> </span>
<%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()' %> <%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()' %>
</div> </div>
@ -129,7 +133,7 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<div class="flex items-center justify-between py-2.5"> <div class="flex items-center justify-between py-2.5">
<span> <span>
Combine completed documents and Audit Log <%= t('combine_completed_documents_and_audit_log') %>
</span> </span>
<%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()' %> <%= f.check_box :value, class: 'toggle', checked: account_config.value, onchange: 'this.form.requestSubmit()' %>
</div> </div>
@ -140,8 +144,10 @@
<%= render 'integrations' %> <%= render 'integrations' %>
<% if can?(:manage, current_account) && Docuseal.multitenant? && true_user == current_user %> <% if can?(:manage, current_account) && Docuseal.multitenant? && true_user == current_user %>
<div class="px-1 mt-8"> <div class="px-1 mt-8">
<h2 class="text-2xl font-bold mb-2">Danger Zone</h2> <h2 class="text-2xl font-bold mb-2">
<%= button_to button_title(title: 'Delete my account'), settings_account_path, class: 'btn btn-outline btn-error block', data: { turbo_confirm: 'Schedule account for deletion?' }, method: :delete, id: :account_delete_button %> <%= t('danger_zone') %>
</h2>
<%= button_to button_title(title: t('delete_my_account')), settings_account_path, class: 'btn btn-outline btn-error block', data: { turbo_confirm: t('schedule_account_for_deletion_') }, method: :delete, id: :account_delete_button %>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -11,9 +11,9 @@
<div class="flex flex-col md:flex-row gap-4"> <div class="flex flex-col md:flex-row gap-4">
<div class="flex w-full space-x-4"> <div class="flex w-full space-x-4">
<input id="api_key" type="text" value="<%= current_user.access_token.token %>" class="input font-mono input-bordered w-full" autocomplete="off" readonly> <input id="api_key" type="text" value="<%= current_user.access_token.token %>" class="input font-mono input-bordered w-full" autocomplete="off" readonly>
<%= render 'shared/clipboard_copy', icon: 'copy', text: current_user.access_token.token, class: 'base-button', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy', copied_title: 'Copied' %> <%= render 'shared/clipboard_copy', icon: 'copy', text: current_user.access_token.token, class: 'base-button', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
</div> </div>
<%= button_to button_title(title: 'Rotate', disabled_with: 'Rotate', icon: svg_icon('reload', class: 'w-6 h-6')), settings_api_index_path, class: 'white-button w-full', data: { turbo_confirm: 'Remove existing API token and generated a new one. Are you sure?' } %> <%= button_to button_title(title: t('rotate'), disabled_with: t('rotate'), icon: svg_icon('reload', class: 'w-6 h-6')), settings_api_index_path, class: 'white-button w-full', data: { turbo_confirm: t('remove_existing_api_token_and_generated_a_new_one_are_you_sure_') } %>
</div> </div>
</div> </div>
</div> </div>
@ -22,7 +22,7 @@
<input type="checkbox"> <input type="checkbox">
<div class="collapse-title text-xl font-medium"> <div class="collapse-title text-xl font-medium">
<div> <div>
Request signature, multiple submitters with default values <%= t('request_signature_multiple_submitters_with_default_values') %>
</div> </div>
<div class="mt-1"> <div class="mt-1">
<div class="badge badge-warning badge-lg">POST</div> <div class="badge badge-warning badge-lg">POST</div>
@ -48,7 +48,7 @@
] ]
}'<% end.to_str %> }'<% end.to_str %>
<span class="top-0 right-0 absolute"> <span class="top-0 right-0 absolute">
<%= render 'shared/clipboard_copy', icon: 'copy', text:, class: 'btn btn-ghost text-white', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy', copied_title: 'Copied' %> <%= render 'shared/clipboard_copy', icon: 'copy', text:, class: 'btn btn-ghost text-white', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
</span> </span>
<pre data-prefix="$"><code class="overflow-hidden w-full"><%= text %></code></pre> <pre data-prefix="$"><code class="overflow-hidden w-full"><%= text %></code></pre>
</div> </div>
@ -58,7 +58,7 @@
<input type="checkbox"> <input type="checkbox">
<div class="collapse-title text-xl font-medium"> <div class="collapse-title text-xl font-medium">
<div> <div>
Request signature, single submitter <%= t('request_signature_single_submitter') %>
</div> </div>
<div class="mt-1"> <div class="mt-1">
<div class="badge badge-warning badge-lg">POST</div> <div class="badge badge-warning badge-lg">POST</div>
@ -74,7 +74,7 @@
"emails": "<%= current_user.email.sub('@', '+test@') %>, <%= current_user.email.sub('@', '+test2@') %>" "emails": "<%= current_user.email.sub('@', '+test@') %>, <%= current_user.email.sub('@', '+test2@') %>"
}'<% end.to_str %> }'<% end.to_str %>
<span class="top-0 right-0 absolute"> <span class="top-0 right-0 absolute">
<%= render 'shared/clipboard_copy', icon: 'copy', text:, class: 'btn btn-ghost text-white', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy', copied_title: 'Copied' %> <%= render 'shared/clipboard_copy', icon: 'copy', text:, class: 'btn btn-ghost text-white', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
</span> </span>
<pre data-prefix="$"><code class="overflow-hidden w-full"><%= text %></code></pre> <pre data-prefix="$"><code class="overflow-hidden w-full"><%= text %></code></pre>
</div> </div>
@ -84,7 +84,7 @@
<input type="checkbox"> <input type="checkbox">
<div class="collapse-title text-xl font-medium"> <div class="collapse-title text-xl font-medium">
<div> <div>
Template details <%= t('template_details') %>
</div> </div>
<div class="mt-1"> <div class="mt-1">
<div class="badge badge-info badge-lg">GET</div> <div class="badge badge-info badge-lg">GET</div>
@ -96,7 +96,7 @@
<% text = capture do %>curl '<%= api_template_url(current_account.templates&.last || 1) %>' \ <% text = capture do %>curl '<%= api_template_url(current_account.templates&.last || 1) %>' \
--header 'X-Auth-Token: <%= current_user.access_token.token %>'<% end.to_str %> --header 'X-Auth-Token: <%= current_user.access_token.token %>'<% end.to_str %>
<span class="top-0 right-0 absolute"> <span class="top-0 right-0 absolute">
<%= render 'shared/clipboard_copy', icon: 'copy', text:, class: 'btn btn-ghost text-white', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy', copied_title: 'Copied' %> <%= render 'shared/clipboard_copy', icon: 'copy', text:, class: 'btn btn-ghost text-white', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
</span> </span>
<pre data-prefix="$"><code class="overflow-hidden w-full"><%= text %></code></pre> <pre data-prefix="$"><code class="overflow-hidden w-full"><%= text %></code></pre>
</div> </div>
@ -104,7 +104,7 @@
</div> </div>
</div> </div>
<div class="text-center"> <div class="text-center">
<%= link_to 'Open Full API Reference', "#{Docuseal::PRODUCT_URL}/docs/api", class: 'btn btn-warning text-base mt-4 px-8', target: '_blank', rel: 'noopener' %> <%= link_to t('open_full_api_reference'), "#{Docuseal::PRODUCT_URL}/docs/api", class: 'btn btn-warning text-base mt-4 px-8', target: '_blank', rel: 'noopener' %>
</div> </div>
</div> </div>
</div> </div>

@ -1,10 +1,10 @@
<form action="<%= root_path %>" method="get" class="bg-base-200 px-1.5 rounded-xl py-1 whitespace-nowrap"> <form action="<%= root_path %>" method="get" class="bg-base-200 px-1.5 rounded-xl py-1 whitespace-nowrap">
<toggle-cookies data-value="templates" data-key="dashboard_view" class="sm:tooltip tooltip-top" data-tip="Templates"> <toggle-cookies data-value="templates" data-key="dashboard_view" class="sm:tooltip tooltip-top" data-tip="<%= t('templates') %>">
<button class="<%= local_assigns[:selected] == 'submissions' ? 'btn !border !rounded-lg btn-square !p-0 !btn-sm !h-8 !w-9' : 'btn btn-neutral !rounded-lg btn-square !p-0 hover:text-neutral-300 !btn-sm !h-8 !w-9 disabled:btn-neutral' %>"> <button class="<%= local_assigns[:selected] == 'submissions' ? 'btn !border !rounded-lg btn-square !p-0 !btn-sm !h-8 !w-9' : 'btn btn-neutral !rounded-lg btn-square !p-0 hover:text-neutral-300 !btn-sm !h-8 !w-9 disabled:btn-neutral' %>">
<%= svg_icon('layout_grid', class: 'w-6 h-6 stroke-2') %> <%= svg_icon('layout_grid', class: 'w-6 h-6 stroke-2') %>
</button> </button>
</toggle-cookies> </toggle-cookies>
<toggle-cookies data-value="submissions" data-key="dashboard_view" class="sm:tooltip tooltip-top" data-tip="Submissions"> <toggle-cookies data-value="submissions" data-key="dashboard_view" class="sm:tooltip tooltip-top" data-tip="<%= t('submissions') %>">
<button class="<%= local_assigns[:selected] == 'submissions' ? 'btn btn-neutral !rounded-lg btn-square !p-0 hover:text-neutral-300 !btn-sm !h-8 !w-9' : 'btn !border !rounded-lg btn-square !p-0 !btn-sm !h-8 !w-9 disabled:btn-neutral' %>"> <button class="<%= local_assigns[:selected] == 'submissions' ? 'btn btn-neutral !rounded-lg btn-square !p-0 hover:text-neutral-300 !btn-sm !h-8 !w-9' : 'btn !border !rounded-lg btn-square !p-0 !btn-sm !h-8 !w-9 disabled:btn-neutral' %>">
<%= svg_icon('layout_list', class: 'w-6 h-6 stroke-2') %> <%= svg_icon('layout_list', class: 'w-6 h-6 stroke-2') %>
</button> </button>

@ -1,25 +1,27 @@
<div class="max-w-lg mx-auto px-2"> <div class="max-w-lg mx-auto px-2">
<h1 class="text-4xl font-bold text-center mt-8">Change your password</h1> <h1 class="text-4xl font-bold text-center mt-8">
<%= t('change_your_password') %>
</h1>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: 'space-y-6' }) do |f| %> <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: 'space-y-6' }) do |f| %>
<div class="space-y-2"> <div class="space-y-2">
<%= render 'devise/shared/error_messages', resource: %> <%= render 'devise/shared/error_messages', resource: %>
<%= f.hidden_field :reset_password_token %> <%= f.hidden_field :reset_password_token %>
<div class="form-control"> <div class="form-control">
<%= f.label :password, 'New password', class: 'label' %> <%= f.label :password, t('new_password'), class: 'label' %>
<%= f.password_field :password, autofocus: true, autocomplete: 'new-password', class: 'base-input' %> <%= f.password_field :password, autofocus: true, autocomplete: 'new-password', class: 'base-input' %>
<% if @minimum_password_length %> <% if @minimum_password_length %>
<label class="label"> <label class="label">
<span class="label-text">(<%= @minimum_password_length %> characters minimum)</span> <span class="label-text">(<%= t('minimum_password_length_characters_minimum', minimum_password_length: @minimum_password_length) %>)</span>
</label> </label>
<% end %> <% end %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.label :password_confirmation, 'Confirm new password', class: 'label' %> <%= f.label :password_confirmation, t('confirm_new_password'), class: 'label' %>
<%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'base-input' %> <%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'base-input' %>
</div> </div>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.button button_title(title: 'Change my password', disabled_with: 'Changing password'), class: 'base-button' %> <%= f.button button_title(title: t('change_my_password'), disabled_with: t('changing_password')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -1,15 +1,17 @@
<div class="max-w-lg mx-auto px-2"> <div class="max-w-lg mx-auto px-2">
<h1 class="text-4xl font-bold text-center mt-8">Forgot your password?</h1> <h1 class="text-4xl font-bold text-center mt-8">
<%= t('forgot_your_password_') %>
</h1>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: 'space-y-6' }) do |f| %> <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: 'space-y-6' }) do |f| %>
<div class="space-y-2"> <div class="space-y-2">
<%= render 'devise/shared/error_messages', resource: %> <%= render 'devise/shared/error_messages', resource: %>
<div class="form-control"> <div class="form-control">
<%= f.label :email, class: 'label' %> <%= f.label :email, t(:email), class: 'label' %>
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'base-input' %> <%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'base-input' %>
</div> </div>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.button button_title(title: 'Reset password', disabled_with: 'Resetting password'), class: 'base-button' %> <%= f.button button_title(title: t('reset_password'), disabled_with: t('resetting_password')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -1,5 +1,7 @@
<div class="max-w-lg mx-auto px-2"> <div class="max-w-lg mx-auto px-2">
<h1 class="text-4xl font-bold text-center mt-8">Sign In</h1> <h1 class="text-4xl font-bold text-center mt-8">
<%= t('sign_in') %>
</h1>
<%= form_for(resource, as: resource_name, html: { class: 'space-y-6' }, data: { turbo: params[:redir].blank? }, url: session_path(resource_name)) do |f| %> <%= form_for(resource, as: resource_name, html: { class: 'space-y-6' }, data: { turbo: params[:redir].blank? }, url: session_path(resource_name)) do |f| %>
<%= f.hidden_field :email %> <%= f.hidden_field :email %>
<%= f.hidden_field :password %> <%= f.hidden_field :password %>
@ -8,12 +10,12 @@
<% end %> <% end %>
<div class="space-y-2"> <div class="space-y-2">
<div class="form-control"> <div class="form-control">
<%= f.label :otp_attempt, 'Two-Factor Code from Authenticator App', class: 'label' %> <%= f.label :otp_attempt, t('two_factor_code_from_authenticator_app'), class: 'label' %>
<%= f.text_field :otp_attempt, autofocus: true, placeholder: 'XXX-XXX', required: true, class: 'base-input' %> <%= f.text_field :otp_attempt, autofocus: true, placeholder: 'XXX-XXX', required: true, class: 'base-input' %>
</div> </div>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.button button_title(title: 'Sign In', disabled_with: 'Signing In'), class: 'base-button' %> <%= f.button button_title(title: t('sign_in'), disabled_with: t('signing_in')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -3,7 +3,7 @@
<%= link_to t('already_have_an_account'), new_session_path(resource_name, { lang: params[:lang] }.compact_blank), class: 'link link-hover mx-auto' %> <%= link_to t('already_have_an_account'), new_session_path(resource_name, { lang: params[:lang] }.compact_blank), class: 'link link-hover mx-auto' %>
<% end %> <% end %>
<%- if devise_mapping.registerable? && controller_name != 'registrations' %> <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<%= link_to t('create_free_account'), registration_path({ redir: params[:redir], lang: params[:lang] }.compact_blank), class: 'link link-hover' %> <%= link_to t('create_free_account'), registration_path({ redir: params[:redir], lang: params[:lang] }.compact_blank), class: 'link link-hover capitalize' %>
<% end %> <% end %>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
<%= link_to t('forgot_your_password_'), new_password_path(resource_name, { lang: params[:lang] }.compact_blank), class: 'link link-hover' %> <%= link_to t('forgot_your_password_'), new_password_path(resource_name, { lang: params[:lang] }.compact_blank), class: 'link link-hover' %>

@ -7,31 +7,31 @@
<%= f.fields_for :value do |ff| %> <%= f.fields_for :value do |ff| %>
<div class="grid md:grid-cols-2 gap-4"> <div class="grid md:grid-cols-2 gap-4">
<div class="form-control"> <div class="form-control">
<%= ff.label :host, class: 'label' %> <%= ff.label :host, 'Host', class: 'label' %>
<%= ff.text_field :host, value: value['host'], required: true, class: 'base-input' %> <%= ff.text_field :host, value: value['host'], required: true, class: 'base-input' %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= ff.label :port, class: 'label' %> <%= ff.label :port, 'Port', class: 'label' %>
<%= ff.text_field :port, value: value['port'], required: true, class: 'base-input' %> <%= ff.text_field :port, value: value['port'], required: true, class: 'base-input' %>
</div> </div>
</div> </div>
<div class="grid md:grid-cols-2 gap-4"> <div class="grid md:grid-cols-2 gap-4">
<div class="form-control"> <div class="form-control">
<%= ff.label :username, class: 'label' %> <%= ff.label :username, 'Username', class: 'label' %>
<%= ff.text_field :username, value: value['username'], class: 'base-input' %> <%= ff.text_field :username, value: value['username'], class: 'base-input' %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= ff.label :password, class: 'label' %> <%= ff.label :password, 'Password', class: 'label' %>
<%= ff.password_field :password, value: value['password'], class: 'base-input' %> <%= ff.password_field :password, value: value['password'], class: 'base-input' %>
</div> </div>
</div> </div>
<div class="grid md:grid-cols-2 gap-4"> <div class="grid md:grid-cols-2 gap-4">
<div class="form-control"> <div class="form-control">
<%= ff.label :domain, 'Domain (optional)', class: 'label' %> <%= ff.label :domain, "Domain (#{t('optional')})", class: 'label' %>
<%= ff.text_field :domain, value: value['domain'], class: 'base-input' %> <%= ff.text_field :domain, value: value['domain'], class: 'base-input' %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= ff.label :authentication, class: 'label' %> <%= ff.label :authentication, 'Authentication', class: 'label' %>
<%= ff.select :authentication, options_for_select([%w[Plain plain], %w[Login login], %w[CRAM-MD5 cram_md5]], value.fetch('authentication', 'plain')), { prompt: true }, required: true, class: 'base-select' %> <%= ff.select :authentication, options_for_select([%w[Plain plain], %w[Login login], %w[CRAM-MD5 cram_md5]], value.fetch('authentication', 'plain')), { prompt: true }, required: true, class: 'base-select' %>
</div> </div>
</div> </div>
@ -47,12 +47,12 @@
</div> </div>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= ff.label :from_email, 'Send from Email', class: 'label' %> <%= ff.label :from_email, t('send_from_email'), class: 'label' %>
<%= ff.email_field :from_email, value: value['from_email'], required: true, class: 'base-input' %> <%= ff.email_field :from_email, value: value['from_email'], required: true, class: 'base-input' %>
</div> </div>
<% end %> <% end %>
<div class="form-control pt-2"> <div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %> <%= f.button button_title(title: t('save'), disabled_with: t('saving')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -1,19 +1,21 @@
<%= render 'shared/turbo_modal', title: 'Upload Certificate' do %> <%= render 'shared/turbo_modal', title: t('upload_certificate') do %>
<%= form_for @cert_record, url: settings_esign_path, html: { class: 'space-y-4', enctype: 'multipart/form-data' }, data: { turbo_frame: :_top } do |f| %> <%= form_for @cert_record, url: settings_esign_path, html: { class: 'space-y-4', enctype: 'multipart/form-data' }, data: { turbo_frame: :_top } do |f| %>
<div class="space-y-2"> <div class="space-y-2">
<div class="form-control"> <div class="form-control">
<%= f.label :name, class: 'label' %> <%= f.label :name, t('name'), class: 'label' %>
<%= f.text_field :name, required: true, class: 'base-input' %> <%= f.text_field :name, required: true, class: 'base-input' %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.label :file, class: 'label' %> <%= f.label :file, t('file'), class: 'label' %>
<%= f.file_field :file, required: true %> <%= f.file_field :file, required: true %>
<label class="label"> <label class="label">
<span class="label-text-alt">Use a valid .der, .p12 or .pfx file.</span> <span class="label-text-alt">
<%= t('use_a_valid_der_p12_or_pfx_file') %>
</span>
</label> </label>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.label :password, 'Password (optional)', class: 'label' %> <%= f.label :password, "#{t('password')} (#{t('optional')})", class: 'label' %>
<%= f.password_field :password, class: 'base-input' %> <%= f.password_field :password, class: 'base-input' %>
</div> </div>
</div> </div>

@ -2,17 +2,19 @@
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="md:flex-grow"> <div class="md:flex-grow">
<div class="max-w-xl"> <div class="max-w-xl">
<h1 class="text-4xl font-bold mb-4">PDF Signature</h1> <h1 class="text-4xl font-bold mb-4">
<%= t('pdf_signature') %>
</h1>
<div id="result"> <div id="result">
<p class="mb-2"> <p class="mb-2">
Upload signed PDF file to validate its signature: <%= t('upload_signed_pdf_file_to_validate_its_signature_') %>
</p> </p>
</div> </div>
<%= form_for '', url: verify_pdf_signature_index_path, method: :post, html: { enctype: 'multipart/form-data' } do |f| %> <%= form_for '', url: verify_pdf_signature_index_path, method: :post, html: { enctype: 'multipart/form-data' } do |f| %>
<%= f.button type: 'submit', class: 'flex' do %> <%= f.button type: 'submit', class: 'flex' do %>
<div class="disabled mb-3"> <div class="disabled mb-3">
<%= svg_icon('loader', class: 'w-5 h-5 animate-spin inline') %> <%= svg_icon('loader', class: 'w-5 h-5 animate-spin inline') %>
Analyzing... <%= "#{t('analyzing')}..." %>
</div> </div>
<% end %> <% end %>
<file-dropzone data-name="verify_attachments" data-submit-on-upload="true" class="w-full"> <file-dropzone data-name="verify_attachments" data-submit-on-upload="true" class="w-full">
@ -26,10 +28,10 @@
<%= svg_icon('loader', class: 'w-10 h-10 animate-spin') %> <%= svg_icon('loader', class: 'w-10 h-10 animate-spin') %>
</span> </span>
<div class="font-medium mb-1"> <div class="font-medium mb-1">
Verify Signed PDF <%= t('verify_signed_pdf') %>
</div> </div>
<div class="text-xs"> <div class="text-xs">
<span class="font-medium">Click to upload</span> or drag and drop files <%= t('click_to_upload_or_drag_and_drop_files_html') %>
</div> </div>
</div> </div>
<input id="file" name="files[]" class="hidden" data-action="change:file-dropzone#onSelectFiles" data-target="file-dropzone.input" type="file" accept="application/pdf" multiple> <input id="file" name="files[]" class="hidden" data-action="change:file-dropzone#onSelectFiles" data-target="file-dropzone.input" type="file" accept="application/pdf" multiple>
@ -39,11 +41,13 @@
<% end %> <% end %>
</div> </div>
<div class="flex justify-between items-end mb-4 mt-8"> <div class="flex justify-between items-end mb-4 mt-8">
<h2 class="text-3xl font-bold">Signing Certificates</h2> <h2 class="text-3xl font-bold">
<%= t('signing_certificates') %>
</h2>
<% if can?(:create, @encrypted_config) %> <% if can?(:create, @encrypted_config) %>
<%= link_to new_settings_esign_path, class: 'btn btn-primary btn-md', data: { turbo_frame: 'modal' } do %> <%= link_to new_settings_esign_path, class: 'btn btn-primary btn-md', data: { turbo_frame: 'modal' } do %>
<%= svg_icon('plus', class: 'w-6 h-6') %> <%= svg_icon('plus', class: 'w-6 h-6') %>
<span>Upload Cert</span> <span><%= t('upload_cert') %></span>
<% end %> <% end %>
<% end %> <% end %>
</div> </div>
@ -53,13 +57,13 @@
<thead class="bg-base-200"> <thead class="bg-base-200">
<tr class="text-neutral uppercase"> <tr class="text-neutral uppercase">
<th> <th>
Name <%= t('name') %>
</th> </th>
<th> <th>
Valid To <%= t('valid_to') %>
</th> </th>
<th> <th>
Status <%= t('status') %>
</th> </th>
<th class="text-right" width="1px"> <th class="text-right" width="1px">
</th> </th>
@ -77,18 +81,18 @@
<td> <td>
<% if item['status'] == 'default' %> <% if item['status'] == 'default' %>
<span class="badge badge-lg badge-info badge-outline"> <span class="badge badge-lg badge-info badge-outline">
<%= item['status'] %> <%= t('default') %>
</span> </span>
<% elsif can?(:update, @encrypted_config) %> <% elsif can?(:update, @encrypted_config) %>
<%= button_to settings_esign_path, method: :put, params: { name: item['name'] }, class: 'btn btn-outline btn-neutral btn-xs whitespace-nowrap', title: 'Delete', data: { turbo_confirm: 'Are you sure?' } do %> <%= button_to settings_esign_path, method: :put, params: { name: item['name'] }, class: 'btn btn-outline btn-neutral btn-xs whitespace-nowrap', title: t('make_default'), data: { turbo_confirm: t('are_you_sure_') } do %>
Make Default <%= t('make_default') %>
<% end %> <% end %>
<% end %> <% end %>
</td> </td>
<td> <td>
<% if item['name'] != EsignSettingsController::DEFAULT_CERT_NAME && item['status'] != 'default' && can?(:destroy, @encrypted_config) %> <% if item['name'] != EsignSettingsController::DEFAULT_CERT_NAME && item['status'] != 'default' && can?(:destroy, @encrypted_config) %>
<%= button_to settings_esign_path, params: { name: item['name'] }, method: :delete, class: 'btn btn-outline btn-error btn-xs', title: 'Delete', data: { turbo_confirm: 'Are you sure?' } do %> <%= button_to settings_esign_path, params: { name: item['name'] }, method: :delete, class: 'btn btn-outline btn-error btn-xs', title: t('remove'), data: { turbo_confirm: t('are_you_sure_') } do %>
Remove <%= t('remove') %>
<% end %> <% end %>
<% end %> <% end %>
</td> </td>
@ -101,24 +105,26 @@
<% if !Docuseal.multitenant? && can?(:manage, encrypted_config) %> <% if !Docuseal.multitenant? && can?(:manage, encrypted_config) %>
<div class="flex-grow max-w-xl"> <div class="flex-grow max-w-xl">
<div class="flex justify-between items-end mb-4 mt-8"> <div class="flex justify-between items-end mb-4 mt-8">
<h2 class="text-3xl font-bold">Timestamp Server</h2> <h2 class="text-3xl font-bold">
<%= t('timestamp_server') %>
</h2>
</div> </div>
<%= form_for encrypted_config, url: timestamp_server_index_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %> <%= form_for encrypted_config, url: timestamp_server_index_path, method: :post, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<div class="form-control"> <div class="form-control">
<%= f.label :value, class: 'label' do %> <%= f.label :value, class: 'label' do %>
<span class="flex items-center space-x-1"> <span class="flex items-center space-x-1">
<span> <span>
Timeserver URL <%= t('timeserver_url') %>
</span> </span>
<span class="tooltip" data-tip="URL of the trusted RFC 3161 timeserver to be used to generate timestamp signatures."> <span class="tooltip" data-tip="<%= t('url_of_the_trusted_rfc_3161_timeserver_to_be_used_to_generate_timestamp_signatures') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4') %> <%= svg_icon('info_circle', class: 'w-4 h-4') %>
</span> </span>
</span> </span>
<% end %> <% end %>
<%= f.url_field :value, autocomplete: 'off', class: 'base-input', placeholder: 'URL (optional)' %> <%= f.url_field :value, autocomplete: 'off', class: 'base-input', placeholder: "URL (#{t('optional')})" %>
</div> </div>
<div class="form-control pt-2"> <div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Updating'), class: 'base-button' %> <%= f.button button_title(title: t('save'), disabled_with: t('updating')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
</div> </div>
@ -127,14 +133,16 @@
<% if can?(:manage, account_config) %> <% if can?(:manage, account_config) %>
<div class="px-1 mt-8 max-w-xl"> <div class="px-1 mt-8 max-w-xl">
<div class="flex justify-between items-end mb-4 mt-8"> <div class="flex justify-between items-end mb-4 mt-8">
<h2 class="text-3xl font-bold">Preferences</h2> <h2 class="text-3xl font-bold">
<%= t('preferences') %>
</h2>
</div> </div>
<% if can?(:manage, account_config) %> <% if can?(:manage, account_config) %>
<%= form_for account_config, url: account_configs_path, method: :post do |f| %> <%= form_for account_config, url: account_configs_path, method: :post do |f| %>
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<div class="flex items-center justify-between py-2.5"> <div class="flex items-center justify-between py-2.5">
<span> <span>
Apply multiple PDF digital signatures in the document per each signer <%= t('apply_multiple_pdf_digital_signatures_in_the_document_per_each_signer') %>
</span> </span>
<%= f.check_box :value, { class: 'toggle', checked: account_config.value == 'multiple', onchange: 'this.form.requestSubmit()' }, 'multiple', 'single' %> <%= f.check_box :value, { class: 'toggle', checked: account_config.value == 'multiple', onchange: 'this.form.requestSubmit()' }, 'multiple', 'single' %>
</div> </div>
@ -146,7 +154,7 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<div class="flex items-center justify-between py-2.5"> <div class="flex items-center justify-between py-2.5">
<span> <span>
Remove PDF form fillable fields from the signed PDF (flatten form) <%= t('remove_pdf_form_fillable_fields_from_the_signed_pdf_flatten_form') %>
</span> </span>
<%= f.check_box :value, { class: 'toggle', checked: account_config.value != false, onchange: 'this.form.requestSubmit()' } %> <%= f.check_box :value, { class: 'toggle', checked: account_config.value != false, onchange: 'this.form.requestSubmit()' } %>
</div> </div>

@ -1,28 +1,28 @@
<div class="max-w-xl mx-auto px-2"> <div class="max-w-xl mx-auto px-2">
<h1 class="flex text-4xl font-bold items-center justify-center my-8 space-x-2"> <h1 class="flex text-4xl font-bold items-center justify-center my-8 space-x-2">
<%= svg_icon('waving_hand', class: 'h-10 w-10') %> <%= svg_icon('waving_hand', class: 'h-10 w-10') %>
<span>Welcome to <%= Docuseal.product_name %></span> <span><%= t('welcome_to_product_name', product_name: Docuseal.product_name) %></span>
</h1> </h1>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: 'space-y-6' }) do |f| %> <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: 'space-y-6' }) do |f| %>
<div class="space-y-2"> <div class="space-y-2">
<%= render 'devise/shared/error_messages', resource: %> <%= render 'devise/shared/error_messages', resource: %>
<%= f.hidden_field :reset_password_token %> <%= f.hidden_field :reset_password_token %>
<div class="form-control"> <div class="form-control">
<%= f.label :password, 'Set password', class: 'label' %> <%= f.label :password, t('set_password'), class: 'label' %>
<%= f.password_field :password, autofocus: true, autocomplete: 'new-password', class: 'base-input' %> <%= f.password_field :password, autofocus: true, autocomplete: 'new-password', class: 'base-input' %>
<% if @minimum_password_length %> <% if @minimum_password_length %>
<label class="label"> <label class="label">
<span class="label-text">(<%= @minimum_password_length %> characters minimum)</span> <span class="label-text">(<%= t('minimum_password_length_characters_minimum', minimum_password_length: @minimum_password_length) %>)</span>
</label> </label>
<% end %> <% end %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.label :password_confirmation, 'Confirm new password', class: 'label' %> <%= f.label :password_confirmation, t('confirm_password'), class: 'label' %>
<%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'base-input' %> <%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'base-input' %>
</div> </div>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.button button_title(title: 'Save password and Sign in', disabled_with: 'Saving'), class: 'base-button' %> <%= f.button button_title(title: t('save_password_and_sign_in'), disabled_with: t('saving')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -1,6 +1,6 @@
<%= form_for '', url: mfa_setup_path, data: { turbo_frame: :_top }, html: { id: 'mfa_form' } do |f| %> <%= form_for '', url: mfa_setup_path, data: { turbo_frame: :_top }, html: { id: 'mfa_form' } do |f| %>
<p class="text-center"> <p class="text-center">
Use an authenticator mobile app like Google Authenticator or 1Password to scan the QR code below. <%= t('use_an_authenticator_mobile_app_like_google_authenticator_or_1password_to_scan_the_qr_code_below') %>
</p> </p>
<div> <div>
<%== RQRCode::QRCode.new(@provision_url).as_svg(viewbox: true, svg_attributes: { class: 'md:w-80 md:h-80 my-4 mx-auto' }) %> <%== RQRCode::QRCode.new(@provision_url).as_svg(viewbox: true, svg_attributes: { class: 'md:w-80 md:h-80 my-4 mx-auto' }) %>
@ -12,6 +12,6 @@
</span> </span>
</div> </div>
<div class="form-control mt-4"> <div class="form-control mt-4">
<%= f.button button_title(title: 'Save'), class: 'base-button' %> <%= f.button button_title(title: t('save')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>

@ -1,4 +1,4 @@
<%= render 'shared/turbo_modal', title: 'Remove 2FA' do %> <%= render 'shared/turbo_modal', title: t('remove_2fa') do %>
<%= form_for '', url: mfa_setup_path, method: :delete, data: { turbo_frame: :_top } do |f| %> <%= form_for '', url: mfa_setup_path, method: :delete, data: { turbo_frame: :_top } do |f| %>
<div class="form-control my-6 space-y-2"> <div class="form-control my-6 space-y-2">
<%= f.text_field :otp_attempt, required: true, placeholder: 'XXX-XXX', class: 'base-input text-center' %> <%= f.text_field :otp_attempt, required: true, placeholder: 'XXX-XXX', class: 'base-input text-center' %>
@ -7,7 +7,7 @@
</span> </span>
</div> </div>
<div class="form-control mt-4"> <div class="form-control mt-4">
<%= f.button button_title(title: 'Remove 2FA'), class: 'base-button' %> <%= f.button button_title(title: t('remove_2fa')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
<% end %> <% end %>

@ -1,3 +1,3 @@
<%= render 'shared/turbo_modal', title: 'Setup 2FA' do %> <%= render 'shared/turbo_modal', title: t('setup_2fa') do %>
<%= render 'mfa_setup/form' %> <%= render 'mfa_setup/form' %>
<% end %> <% end %>

@ -1,4 +1,6 @@
<div class="max-w-lg mx-auto px-2"> <div class="max-w-lg mx-auto px-2">
<h1 class="text-4xl font-bold text-center mt-8">Setup 2FA</h1> <h1 class="text-4xl font-bold text-center mt-8">
<%= t('setup_2fa') %>
</h1>
<%= render 'mfa_setup/form' %> <%= render 'mfa_setup/form' %>
</div> </div>

@ -1,14 +1,14 @@
<div class="max-w-xl mx-auto px-2"> <div class="max-w-xl mx-auto px-2">
<h1 class="text-4xl font-bold text-center my-8">👨‍💻 Developer Newsletters</h1> <h1 class="text-4xl font-bold text-center my-8">👨‍💻 <%= t('developer_newsletters') %></h1>
<%= form_for current_user, url: newsletter_path do |f| %> <%= form_for current_user, url: newsletter_path do |f| %>
<div class="form-control"> <div class="form-control">
<%= f.email_field :email, placeholder: 'Email', required: true, class: 'base-input' %> <%= f.email_field :email, placeholder: t('email'), required: true, class: 'base-input' %>
</div> </div>
<div class="form-control mt-4"> <div class="form-control mt-4">
<%= f.button button_title, class: 'base-button' %> <%= f.button button_title, class: 'base-button' %>
</div> </div>
<div class="text-center mt-2"> <div class="text-center mt-2">
<a href="/" class="link">Skip</a> <a href="/" class="link"><%= t('skip') %></a>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -4,7 +4,7 @@
<%= f.label :value, class: 'label' do %> <%= f.label :value, class: 'label' do %>
<span class="flex items-center space-x-1"> <span class="flex items-center space-x-1">
<span> <span>
Completed documents notification BCC address <%= t('completed_documents_notification_bcc_address') %>
</span> </span>
<span class="tooltip" data-tip="Send email copy with completed documents to a specified BCC address."> <span class="tooltip" data-tip="Send email copy with completed documents to a specified BCC address.">
<%= svg_icon('info_circle', class: 'w-4 h-4') %> <%= svg_icon('info_circle', class: 'w-4 h-4') %>
@ -14,6 +14,6 @@
<%= tag.input type: 'email', multiple: true, name: 'account_config[value]', autocomplete: 'off', class: 'base-input', value: f.object.value %> <%= tag.input type: 'email', multiple: true, name: 'account_config[value]', autocomplete: 'off', class: 'base-input', value: f.object.value %>
</div> </div>
<div class="form-control pt-2"> <div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Updating'), class: 'base-button' %> <%= f.button button_title(title: t('save'), disabled_with: t('updating')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>

@ -2,29 +2,29 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<div class="form-control"> <div class="form-control">
<% record = Struct.new(:first_duration, :second_duration, :third_duration).new(*(f.object.value || {}).values_at('first_duration', 'second_duration', 'third_duration')) %> <% record = Struct.new(:first_duration, :second_duration, :third_duration).new(*(f.object.value || {}).values_at('first_duration', 'second_duration', 'third_duration')) %>
<% durations = Docuseal.multitenant? ? AccountConfigs::REMINDER_DURATIONS.except('one_hour', 'two_hours').invert : AccountConfigs::REMINDER_DURATIONS.invert %> <% durations = (Docuseal.multitenant? ? AccountConfigs::REMINDER_DURATIONS.except('one_hour', 'two_hours') : AccountConfigs::REMINDER_DURATIONS).keys.map { |v| [t(v.underscore), v] } %>
<div class="flex flex-col md:flex-row gap-4"> <div class="flex flex-col md:flex-row gap-4">
<div class="w-full"> <div class="w-full">
<%= f.fields_for :value, record do |ff| %> <%= f.fields_for :value, record do |ff| %>
<%= ff.label :first_duration, 'First reminder in', class: 'label' %> <%= ff.label :first_duration, t('first_reminder_in'), class: 'label' %>
<%= ff.select :first_duration, durations, { include_blank: 'None' }, class: 'base-select' %> <%= ff.select :first_duration, durations, { include_blank: 'None' }, class: 'base-select' %>
<% end %> <% end %>
</div> </div>
<div class="w-full"> <div class="w-full">
<%= f.fields_for :value, record do |ff| %> <%= f.fields_for :value, record do |ff| %>
<%= ff.label :second_duration, 'Second reminder in', class: 'label' %> <%= ff.label :second_duration, t('second_reminder_in'), class: 'label' %>
<%= ff.select :second_duration, durations, { include_blank: 'None' }, class: 'base-select' %> <%= ff.select :second_duration, durations, { include_blank: 'None' }, class: 'base-select' %>
<% end %> <% end %>
</div> </div>
<div class="w-full"> <div class="w-full">
<%= f.fields_for :value, record do |ff| %> <%= f.fields_for :value, record do |ff| %>
<%= ff.label :third_duration, 'Third reminder in', class: 'label' %> <%= ff.label :third_duration, t('third_reminder_in'), class: 'label' %>
<%= ff.select :third_duration, durations, { include_blank: 'None' }, class: 'base-select' %> <%= ff.select :third_duration, durations, { include_blank: 'None' }, class: 'base-select' %>
<% end %> <% end %>
</div> </div>
</div> </div>
</div> </div>
<div class="form-control pt-4"> <div class="form-control pt-4">
<%= f.button button_title(title: 'Save', disabled_with: 'Updating'), class: 'base-button' %> <%= f.button button_title(title: t('save'), disabled_with: t('updating')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>

@ -1,11 +1,15 @@
<div class="alert my-4"> <div class="alert my-4">
<%= svg_icon('info_circle', class: 'w-6 h-6') %> <%= svg_icon('info_circle', class: 'w-6 h-6') %>
<div> <div>
<p class="font-bold">Unlock with DocuSeal Pro</p> <p class="font-bold">
<%= t('unlock_with_docuseal_pro') %>
</p>
<p> <p>
Send automatic email reminders to your recipients. <%= t('send_automatic_email_reminders_to_your_recipients') %>
<br> <br>
<a class="link font-medium" target="_blank" href="<%= Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}" %>" data-turbo="false">Learn More</a> <a class="link font-medium" target="_blank" href="<%= Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}" %>" data-turbo="false">
<%= t('learn_more') %>
</a>
</p> </p>
</div> </div>
</div> </div>

@ -1,7 +1,9 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0"> <div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0">
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto"> <div class="flex-grow max-w-xl mx-auto">
<h1 class="text-4xl font-bold mb-4">Email Notifications</h1> <h1 class="text-4xl font-bold mb-4">
<%= t('email_notifications') %>
</h1>
<div class="mt-2 mb-1"> <div class="mt-2 mb-1">
<% user_config = UserConfig.find_or_initialize_by(user: current_user, key: UserConfig::RECEIVE_COMPLETED_EMAIL) %> <% user_config = UserConfig.find_or_initialize_by(user: current_user, key: UserConfig::RECEIVE_COMPLETED_EMAIL) %>
<% if can?(:manage, user_config) %> <% if can?(:manage, user_config) %>
@ -9,7 +11,7 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span> <span>
Receive notification emails on completed submission <%= t('receive_notification_emails_on_completed_submission') %>
</span> </span>
<%= f.check_box :value, class: 'toggle', checked: user_config.value != false, onchange: 'this.form.requestSubmit()' %> <%= f.check_box :value, class: 'toggle', checked: user_config.value != false, onchange: 'this.form.requestSubmit()' %>
</div> </div>
@ -18,7 +20,9 @@
</div> </div>
<%= render 'bcc_form', config: @bcc_config %> <%= render 'bcc_form', config: @bcc_config %>
<div class="flex justify-between items-end mb-4 mt-8"> <div class="flex justify-between items-end mb-4 mt-8">
<h2 class="text-3xl font-bold">Sign Request Email Reminders</h2> <h2 class="text-3xl font-bold">
<%= t('sign_request_email_reminders') %>
</h2>
</div> </div>
<%= render 'reminder_banner' %> <%= render 'reminder_banner' %>
<%= render 'reminder_form', config: @reminder_config %> <%= render 'reminder_form', config: @reminder_config %>

@ -1,8 +1,8 @@
<div class="collapse collapse-plus bg-base-200 overflow-visible"> <div class="collapse collapse-plus bg-base-200 overflow-visible">
<input type="checkbox"> <input type="checkbox">
<div class="collapse-title text-xl font-medium"> <div class="collapse-title text-xl font-medium capitalize">
<div> <div>
Documents Copy Email <%= t('documents_copy_email') %>
</div> </div>
</div> </div>
<div class="collapse-content"> <div class="collapse-content">
@ -10,13 +10,13 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<%= f.fields_for :value, Struct.new(:subject, :body, :attach_audit_log).new(*f.object.value.values_at('subject', 'body', 'attach_audit_log')) do |ff| %> <%= f.fields_for :value, Struct.new(:subject, :body, :attach_audit_log).new(*f.object.value.values_at('subject', 'body', 'attach_audit_log')) do |ff| %>
<div class="form-control"> <div class="form-control">
<%= ff.label :subject, class: 'label' %> <%= ff.label :subject, t('subject'), class: 'label' %>
<%= ff.text_field :subject, required: true, class: 'base-input', dir: 'auto' %> <%= ff.text_field :subject, required: true, class: 'base-input', dir: 'auto' %>
</div> </div>
<div class="form-control"> <div class="form-control">
<div class="flex items-center"> <div class="flex items-center">
<%= ff.label :body, class: 'label' %> <%= ff.label :body, t('body'), class: 'label' %>
<span class="tooltip" data-tip="Use following placeholders text: <%= AccountConfig::DEFAULT_VALUES.dig(AccountConfig::SUBMITTER_DOCUMENTS_COPY_EMAIL_KEY, 'body').scan(/{{.*?}}/).join(', ') %>"> <span class="tooltip" data-tip="<%= t('use_following_placeholders_text_') %> <%= AccountConfig::DEFAULT_VALUES.dig(AccountConfig::SUBMITTER_DOCUMENTS_COPY_EMAIL_KEY, 'body').scan(/{{.*?}}/).join(', ') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4') %> <%= svg_icon('info_circle', class: 'w-4 h-4') %>
</span> </span>
</div> </div>
@ -26,13 +26,13 @@
</div> </div>
<div class="flex items-center justify-between py-2.5 mx-1"> <div class="flex items-center justify-between py-2.5 mx-1">
<span> <span>
Attach audit log PDF <%= t('attach_audit_log_pdf') %>
</span> </span>
<%= ff.check_box :attach_audit_log, { checked: ff.object.attach_audit_log != false, class: 'toggle' }, 'true', 'false' %> <%= ff.check_box :attach_audit_log, { checked: ff.object.attach_audit_log != false, class: 'toggle' }, 'true', 'false' %>
</div> </div>
<% end %> <% end %>
<div class="form-control pt-2"> <div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %> <%= f.button button_title(title: t('save'), disabled_with: t('saving')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -2,7 +2,7 @@
<input type="checkbox"> <input type="checkbox">
<div class="collapse-title text-xl font-medium"> <div class="collapse-title text-xl font-medium">
<div> <div>
Completed Form Redirect Button <%= t('completed_form_redirect_button') %>
</div> </div>
</div> </div>
<div class="collapse-content"> <div class="collapse-content">
@ -10,16 +10,16 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<%= f.fields_for :value, Struct.new(:title, :url).new(*(f.object.value || {}).values_at('title', 'url')) do |ff| %> <%= f.fields_for :value, Struct.new(:title, :url).new(*(f.object.value || {}).values_at('title', 'url')) do |ff| %>
<div class="form-control"> <div class="form-control">
<%= ff.label :title, 'Button title', class: 'label' %> <%= ff.label :title, t('button_title'), class: 'label' %>
<%= ff.text_field :title, class: 'base-input', dir: 'auto' %> <%= ff.text_field :title, class: 'base-input', dir: 'auto' %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= ff.label :url, 'Button URL', class: 'label' %> <%= ff.label :url, t('button_url'), class: 'label' %>
<%= ff.url_field :url, class: 'base-input' %> <%= ff.url_field :url, class: 'base-input' %>
</div> </div>
<% end %> <% end %>
<div class="form-control pt-2"> <div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %> <%= f.button button_title(title: t('save'), disabled_with: t('saving')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -2,7 +2,7 @@
<input type="checkbox"> <input type="checkbox">
<div class="collapse-title text-xl font-medium"> <div class="collapse-title text-xl font-medium">
<div> <div>
Completed Form Message <%= t('completed_form_message') %>
</div> </div>
</div> </div>
<div class="collapse-content"> <div class="collapse-content">
@ -10,18 +10,18 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<%= f.fields_for :value, Struct.new(:title, :body).new(*(f.object.value || {}).values_at('title', 'body')) do |ff| %> <%= f.fields_for :value, Struct.new(:title, :body).new(*(f.object.value || {}).values_at('title', 'body')) do |ff| %>
<div class="form-control"> <div class="form-control">
<%= ff.label :title, 'Title', class: 'label' %> <%= ff.label :title, t('title'), class: 'label' %>
<%= ff.text_field :title, class: 'base-input', dir: 'auto' %> <%= ff.text_field :title, class: 'base-input', dir: 'auto' %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= ff.label :body, 'Body', class: 'label' %> <%= ff.label :body, t('body'), class: 'label' %>
<autoresize-textarea> <autoresize-textarea>
<%= ff.text_area :body, class: 'base-input w-full py-2', dir: 'auto' %> <%= ff.text_area :body, class: 'base-input w-full py-2', dir: 'auto' %>
</autoresize-textarea> </autoresize-textarea>
</div> </div>
<% end %> <% end %>
<div class="form-control pt-2"> <div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %> <%= f.button button_title(title: t('save'), disabled_with: t('saving')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -5,7 +5,7 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<div class="flex items-center justify-between py-2.5"> <div class="flex items-center justify-between py-2.5">
<span> <span>
Show confetti on successful completion <%= t('show_confetti_on_successful_completion') %>
</span> </span>
<%= f.check_box :value, { class: 'toggle', checked: account_config.value != false, onchange: 'this.form.requestSubmit()' }, '1', '0' %> <%= f.check_box :value, { class: 'toggle', checked: account_config.value != false, onchange: 'this.form.requestSubmit()' }, '1', '0' %>
</div> </div>

@ -1,11 +1,15 @@
<div class="alert my-4"> <div class="alert my-4">
<%= svg_icon('info_circle', class: 'w-6 h-6') %> <%= svg_icon('info_circle', class: 'w-6 h-6') %>
<div> <div>
<p class="font-bold">Unlock with DocuSeal Pro</p> <p class="font-bold">
<%= t('unlock_with_docuseal_pro') %>
</p>
<p> <p>
Display your company name and logo when signing documents. <%= t('display_your_company_name_and_logo_when_signing_documents') %>
<br> <br>
<a class="link font-medium" target="_blank" href="<%= Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}" %>" data-turbo="false">Learn More</a> <a class="link font-medium" target="_blank" href="<%= Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}" %>" data-turbo="false">
<%= t('learn_more') %>
</a>
</p> </p>
</div> </div>
</div> </div>

@ -1,8 +1,8 @@
<div class="collapse collapse-plus bg-base-200 overflow-visible"> <div class="collapse collapse-plus bg-base-200 overflow-visible">
<input type="checkbox"> <input type="checkbox">
<div class="collapse-title text-xl font-medium"> <div class="collapse-title text-xl font-medium capitalize">
<div> <div>
Signature Request Email <%= t('signature_request_email') %>
</div> </div>
</div> </div>
<div class="collapse-content"> <div class="collapse-content">
@ -10,13 +10,13 @@
<%= f.hidden_field :key %> <%= f.hidden_field :key %>
<%= f.fields_for :value, Struct.new(:subject, :body).new(*f.object.value.values_at('subject', 'body')) do |ff| %> <%= f.fields_for :value, Struct.new(:subject, :body).new(*f.object.value.values_at('subject', 'body')) do |ff| %>
<div class="form-control"> <div class="form-control">
<%= ff.label :subject, class: 'label' %> <%= ff.label :subject, t('subject'), class: 'label' %>
<%= ff.text_field :subject, required: true, class: 'base-input', dir: 'auto' %> <%= ff.text_field :subject, required: true, class: 'base-input', dir: 'auto' %>
</div> </div>
<div class="form-control"> <div class="form-control">
<div class="flex items-center"> <div class="flex items-center">
<%= ff.label :body, class: 'label' %> <%= ff.label :body, t('body'), class: 'label' %>
<span class="tooltip" data-tip="Use following placeholders text: <%= AccountConfig::DEFAULT_VALUES.dig(AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY, 'body').scan(/{{.*?}}/).join(', ') %>"> <span class="tooltip" data-tip="<%= t('use_following_placeholders_text_') %> <%= AccountConfig::DEFAULT_VALUES.dig(AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY, 'body').scan(/{{.*?}}/).join(', ') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4') %> <%= svg_icon('info_circle', class: 'w-4 h-4') %>
</span> </span>
</div> </div>
@ -26,7 +26,7 @@
</div> </div>
<% end %> <% end %>
<div class="form-control pt-2"> <div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %> <%= f.button button_title(title: t('save'), disabled_with: t('saving')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -1,8 +1,8 @@
<div class="collapse collapse-plus bg-base-200 overflow-visible"> <div class="collapse collapse-plus bg-base-200 overflow-visible">
<input type="checkbox"> <input type="checkbox">
<div class="collapse-title text-xl font-medium"> <div class="collapse-title text-xl font-medium capitalize">
<div> <div>
Completed Notification Email <%= t('copleted_notification_email') %>
</div> </div>
</div> </div>
<div class="collapse-content"> <div class="collapse-content">
@ -11,8 +11,8 @@
<%= f.fields_for :value, Struct.new(:subject, :body, :attach_audit_log, :attach_documents).new(*f.object.value.values_at('subject', 'body', 'attach_audit_log', 'attach_documents')) do |ff| %> <%= f.fields_for :value, Struct.new(:subject, :body, :attach_audit_log, :attach_documents).new(*f.object.value.values_at('subject', 'body', 'attach_audit_log', 'attach_documents')) do |ff| %>
<div class="form-control"> <div class="form-control">
<div class="flex items-center"> <div class="flex items-center">
<%= ff.label :subject, class: 'label' %> <%= ff.label :subject, t('subject'), class: 'label' %>
<span class="tooltip" data-tip="Use following placeholders text: <%= AccountConfig::DEFAULT_VALUES.dig(AccountConfig::SUBMITTER_COMPLETED_EMAIL_KEY, 'subject').scan(/{{.*?}}/).join(', ') %>"> <span class="tooltip" data-tip="<%= t('use_following_placeholders_text_') %> <%= AccountConfig::DEFAULT_VALUES.dig(AccountConfig::SUBMITTER_COMPLETED_EMAIL_KEY, 'subject').scan(/{{.*?}}/).join(', ') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4') %> <%= svg_icon('info_circle', class: 'w-4 h-4') %>
</span> </span>
</div> </div>
@ -20,8 +20,8 @@
</div> </div>
<div class="form-control"> <div class="form-control">
<div class="flex items-center"> <div class="flex items-center">
<%= ff.label :body, class: 'label' %> <%= ff.label :body, t('body'), class: 'label' %>
<span class="tooltip" data-tip="Use following placeholders text: <%= AccountConfig::DEFAULT_VALUES.dig(AccountConfig::SUBMITTER_COMPLETED_EMAIL_KEY, 'body').scan(/{{.*?}}/).join(', ') %>"> <span class="tooltip" data-tip="<%= t('use_following_placeholders_text_') %> <%= AccountConfig::DEFAULT_VALUES.dig(AccountConfig::SUBMITTER_COMPLETED_EMAIL_KEY, 'body').scan(/{{.*?}}/).join(', ') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4') %> <%= svg_icon('info_circle', class: 'w-4 h-4') %>
</span> </span>
</div> </div>
@ -31,19 +31,19 @@
</div> </div>
<div class="flex items-center justify-between pt-2.5 mx-1"> <div class="flex items-center justify-between pt-2.5 mx-1">
<span> <span>
Attach documents <%= t('attach_documents') %>
</span> </span>
<%= ff.check_box :attach_documents, { checked: ff.object.attach_documents != false, class: 'toggle' }, 'true', 'false' %> <%= ff.check_box :attach_documents, { checked: ff.object.attach_documents != false, class: 'toggle' }, 'true', 'false' %>
</div> </div>
<div class="flex items-center justify-between pb-2.5 mx-1"> <div class="flex items-center justify-between pb-2.5 mx-1">
<span> <span>
Attach audit log PDF <%= t('attach_audit_log_pdf') %>
</span> </span>
<%= ff.check_box :attach_audit_log, { checked: ff.object.attach_audit_log != false, class: 'toggle' }, 'true', 'false' %> <%= ff.check_box :attach_audit_log, { checked: ff.object.attach_audit_log != false, class: 'toggle' }, 'true', 'false' %>
</div> </div>
<% end %> <% end %>
<div class="form-control pt-2"> <div class="form-control pt-2">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %> <%= f.button button_title(title: t('save'), disabled_with: t('saving')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -1,15 +1,21 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0"> <div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0">
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto"> <div class="flex-grow max-w-xl mx-auto">
<p class="text-4xl font-bold mb-4">Email Templates</p> <p class="text-4xl font-bold mb-4">
<%= t('email_templates') %>
</p>
<div class="space-y-4"> <div class="space-y-4">
<%= render 'signature_request_email_form' %> <%= render 'signature_request_email_form' %>
<%= render 'documents_copy_email_form' %> <%= render 'documents_copy_email_form' %>
<%= render 'submitter_completed_email_form' %> <%= render 'submitter_completed_email_form' %>
</div> </div>
<p class="text-4xl font-bold mb-4 mt-8">Company Logo</p> <p class="text-4xl font-bold mb-4 mt-8">
<%= t('company_logo') %>
</p>
<%= render 'logo_form' %> <%= render 'logo_form' %>
<p class="text-4xl font-bold mb-4 mt-8">Submission Form</p> <p class="text-4xl font-bold mb-4 mt-8">
<%= t('submission_form') %>
</p>
<div class="space-y-4"> <div class="space-y-4">
<%= render 'form_completed_message_form' %> <%= render 'form_completed_message_form' %>
<%= render 'form_completed_button_form' %> <%= render 'form_completed_button_form' %>

@ -1,78 +1,92 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0"> <div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0">
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto"> <div class="flex-grow max-w-xl mx-auto">
<h1 class="text-4xl font-bold mb-4">Profile</h1> <h1 class="text-4xl font-bold mb-4">
<%= t('profile') %>
</h1>
<%= form_for current_user, url: update_contact_settings_profile_index_path, method: :patch, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %> <%= form_for current_user, url: update_contact_settings_profile_index_path, method: :patch, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<div class="grid md:grid-cols-2 gap-4"> <div class="grid md:grid-cols-2 gap-4">
<div class="form-control"> <div class="form-control">
<%= f.label :first_name, class: 'label' %> <%= f.label :first_name, t('first_name'), class: 'label' %>
<%= f.text_field :first_name, required: true, class: 'base-input', dir: 'auto' %> <%= f.text_field :first_name, required: true, class: 'base-input', dir: 'auto' %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.label :last_name, class: 'label' %> <%= f.label :last_name, t('last_name'), class: 'label' %>
<%= f.text_field :last_name, required: true, class: 'base-input', dir: 'auto' %> <%= f.text_field :last_name, required: true, class: 'base-input', dir: 'auto' %>
</div> </div>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.label :email, 'Email', class: 'label' %> <%= f.label :email, t('email'), class: 'label' %>
<%= f.email_field :email, autocomplete: 'off', class: 'base-input' %> <%= f.email_field :email, autocomplete: 'off', class: 'base-input' %>
</div> </div>
<div class="form-control pt-2"> <div class="form-control pt-2">
<%= f.button button_title(title: 'Update', disabled_with: 'Updating'), class: 'base-button' %> <%= f.button button_title(title: t('update'), disabled_with: t('updating')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
<%= render 'email_configs' %> <%= render 'email_configs' %>
<p class="text-2xl font-bold mt-8 mb-4">Signature</p> <p class="text-2xl font-bold mt-8 mb-4">
<%= t('signature') %>
</p>
<% signature = UserConfigs.load_signature(current_user) %> <% signature = UserConfigs.load_signature(current_user) %>
<% if signature %> <% if signature %>
<div class="flex justify-center mb-4 relative"> <div class="flex justify-center mb-4 relative">
<%= button_to button_title(title: 'Remove', disabled_with: 'Removing'), user_signature_path, method: :delete, class: 'right-0 top-0 absolute link' %> <%= button_to button_title(title: t('remove'), disabled_with: t('removing')), user_signature_path, method: :delete, class: 'right-0 top-0 absolute link' %>
<img src="<%= signature.url %>" style="max-height: 200px; width: auto" width="<%= signature.metadata['width'] %>" height="<%= signature.metadata['height'] %>"> <img src="<%= signature.url %>" style="max-height: 200px; width: auto" width="<%= signature.metadata['width'] %>" height="<%= signature.metadata['height'] %>">
</div> </div>
<% end %> <% end %>
<a href="<%= edit_user_signature_path %>" data-turbo-frame="modal" class="base-button w-full">Update Signature</a> <a href="<%= edit_user_signature_path %>" data-turbo-frame="modal" class="base-button w-full">
<p class="text-2xl font-bold mt-8 mb-4">Initials</p> <%= t('update_signature') %>
</a>
<p class="text-2xl font-bold mt-8 mb-4">
<%= t('initials') %>
</p>
<% initials = UserConfigs.load_initials(current_user) %> <% initials = UserConfigs.load_initials(current_user) %>
<% if initials %> <% if initials %>
<div class="flex justify-center mb-4 relative"> <div class="flex justify-center mb-4 relative">
<%= button_to button_title(title: 'Remove', disabled_with: 'Removing'), user_initials_path, method: :delete, class: 'right-0 top-0 absolute link' %> <%= button_to button_title(title: t('remove'), disabled_with: t('removing')), user_initials_path, method: :delete, class: 'right-0 top-0 absolute link' %>
<img src="<%= initials.url %>" style="max-height: 200px; width: auto" width="<%= initials.metadata['width'] %>" height="<%= initials.metadata['height'] %>"> <img src="<%= initials.url %>" style="max-height: 200px; width: auto" width="<%= initials.metadata['width'] %>" height="<%= initials.metadata['height'] %>">
</div> </div>
<% end %> <% end %>
<a href="<%= edit_user_initials_path %>" data-turbo-frame="modal" class="base-button w-full">Update Initials</a> <a href="<%= edit_user_initials_path %>" data-turbo-frame="modal" class="base-button w-full">
<%= t('update_initials') %>
</a>
<% if true_user == current_user && !current_account.testing? %> <% if true_user == current_user && !current_account.testing? %>
<p class="text-2xl font-bold mt-8 mb-4">Change Password</p> <p class="text-2xl font-bold mt-8 mb-4">
<%= t('change_password') %>
</p>
<%= form_for current_user, url: update_password_settings_profile_index_path, method: :patch, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %> <%= form_for current_user, url: update_password_settings_profile_index_path, method: :patch, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<div class="form-control"> <div class="form-control">
<%= f.label :password, 'New password', class: 'label' %> <%= f.label :password, t('new_password'), class: 'label' %>
<%= f.password_field :password, autocomplete: 'off', class: 'base-input' %> <%= f.password_field :password, autocomplete: 'off', class: 'base-input' %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.label :password_confirmation, 'Confirm new password', class: 'label' %> <%= f.label :password_confirmation, t('confirm_password'), class: 'label' %>
<%= f.password_field :password_confirmation, autocomplete: 'off', class: 'base-input' %> <%= f.password_field :password_confirmation, autocomplete: 'off', class: 'base-input' %>
</div> </div>
<div class="form-control pt-2"> <div class="form-control pt-2">
<%= f.button button_title(title: 'Update', disabled_with: 'Updating'), class: 'base-button' %> <%= f.button button_title(title: t('update'), disabled_with: t('updating')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
<p class="text-2xl font-bold mt-8 mb-4">Two-Factor Authentication</p> <p class="text-2xl font-bold mt-8 mb-4">
<%= t('two_factor_authentication') %>
</p>
<div class="space-y-4"> <div class="space-y-4">
<% if current_user.otp_required_for_login %> <% if current_user.otp_required_for_login %>
<p class="flex items-center space-x-1"> <p class="flex items-center space-x-1">
<%= svg_icon('circle_check', class: 'stroke-success inline flex-none w-5 h-5') %> <%= svg_icon('circle_check', class: 'stroke-success inline flex-none w-5 h-5') %>
<span> <span>
2FA has been configured. <%= t('2fa_has_been_configured') %>
</span> </span>
</p> </p>
<a href="<%= edit_mfa_setup_path %>" data-turbo-frame="modal" class="white-button w-full !px-8">🔓 Remove 2FA</a> <a href="<%= edit_mfa_setup_path %>" data-turbo-frame="modal" class="white-button w-full !px-8">🔓 <%= t('remove_2fa') %></a>
<% else %> <% else %>
<p class="flex items-center space-x-1"> <p class="flex items-center space-x-1">
<%= svg_icon('info_circle', class: 'stroke-warning inline flex-none w-5 h-5') %> <%= svg_icon('info_circle', class: 'stroke-warning inline flex-none w-5 h-5') %>
<span> <span>
2FA is not configured. <%= t('2fa_is_not_configured') %>
</span> </span>
</p> </p>
<a href="<%= new_mfa_setup_path %>" data-turbo-frame="modal" class="base-button w-full !px-8">🔒 Set up 2FA</a> <a href="<%= new_mfa_setup_path %>" data-turbo-frame="modal" class="base-button w-full !px-8">🔒 <%= t('set_up_2fa') %></a>
<% end %> <% end %>
</div> </div>
<% end %> <% end %>

@ -5,7 +5,7 @@
<%= render 'start_form/docuseal_logo' %> <%= render 'start_form/docuseal_logo' %>
</div> </div>
<div class="text-center text-4xl font-bold"> <div class="text-center text-4xl font-bold">
Email has been sent <%= t('email_has_been_sent') %>
</div> </div>
</div> </div>
</div> </div>

@ -1,6 +1,6 @@
<div class="max-w-xl mx-auto px-2"> <div class="max-w-xl mx-auto px-2">
<h1 class="flex text-4xl font-bold items-center justify-center my-8 space-x-2"> <h1 class="flex text-4xl font-bold items-center justify-center my-8 space-x-2">
<span>Initial Setup</span> <span><%= t('initial_setup') %></span>
<%= svg_icon('waving_hand', class: 'h-10 w-10') %> <%= svg_icon('waving_hand', class: 'h-10 w-10') %>
</h1> </h1>
<%= form_for '', html: { class: 'space-y-6' }, url: setup_index_path do |f| %> <%= form_for '', html: { class: 'space-y-6' }, url: setup_index_path do |f| %>
@ -8,18 +8,18 @@
<%= fields_for @user do |ff| %> <%= fields_for @user do |ff| %>
<div class="grid gap-2 md:grid-cols-2 md:gap-4"> <div class="grid gap-2 md:grid-cols-2 md:gap-4">
<div class="form-control"> <div class="form-control">
<%= ff.label :first_name, class: 'label' %> <%= ff.label :first_name, t('first_name'), class: 'label' %>
<%= ff.text_field :first_name, required: true, class: 'base-input' %> <%= ff.text_field :first_name, required: true, class: 'base-input' %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= ff.label :last_name, class: 'label' %> <%= ff.label :last_name, t('last_name'), class: 'label' %>
<%= ff.text_field :last_name, required: true, class: 'base-input' %> <%= ff.text_field :last_name, required: true, class: 'base-input' %>
</div> </div>
</div> </div>
<% end %> <% end %>
<%= fields_for @user do |ff| %> <%= fields_for @user do |ff| %>
<div class="form-control"> <div class="form-control">
<%= ff.label :email, class: 'label' %> <%= ff.label :email, t('email'), class: 'label' %>
<%= ff.email_field :email, required: true, class: 'base-input' %> <%= ff.email_field :email, required: true, class: 'base-input' %>
</div> </div>
<% end %> <% end %>
@ -27,13 +27,13 @@
<set-timezone data-input-id="account_timezone"></set-timezone> <set-timezone data-input-id="account_timezone"></set-timezone>
<%= ff.hidden_field :timezone %> <%= ff.hidden_field :timezone %>
<div class="form-control"> <div class="form-control">
<%= ff.label :name, 'Company name', class: 'label' %> <%= ff.label :name, t('company_name'), class: 'label' %>
<%= ff.text_field :name, required: true, class: 'base-input' %> <%= ff.text_field :name, required: true, class: 'base-input' %>
</div> </div>
<% end %> <% end %>
<%= fields_for @user do |ff| %> <%= fields_for @user do |ff| %>
<div class="form-control"> <div class="form-control">
<%= ff.label :password, class: 'label' %> <%= ff.label :password, t('password'), class: 'label' %>
<password-input class="block relative"> <password-input class="block relative">
<%= ff.password_field :password, required: true, placeholder: '************', class: 'base-input w-full pr-10', data: { target: 'password-input.passwordInput' } %> <%= ff.password_field :password, required: true, placeholder: '************', class: 'base-input w-full pr-10', data: { target: 'password-input.passwordInput' } %>
<button data-target="password-input.togglePasswordVisibility" type="button" class="absolute inset-y-0 h-12 right-0 pr-3 flex items-center"> <button data-target="password-input.togglePasswordVisibility" type="button" class="absolute inset-y-0 h-12 right-0 pr-3 flex items-center">
@ -52,7 +52,7 @@
<% if @encrypted_config.value.blank? %> <% if @encrypted_config.value.blank? %>
<set-origin-url data-input-id="encrypted_config_value"></set-origin-url> <set-origin-url data-input-id="encrypted_config_value"></set-origin-url>
<% end %> <% end %>
<%= ff.label :value, 'App URL', class: 'label' %> <%= ff.label :value, t('app_url'), class: 'label' %>
<%= ff.text_field :value, required: true, class: 'base-input' %> <%= ff.text_field :value, required: true, class: 'base-input' %>
</div> </div>
<% end %> <% end %>

@ -1,10 +1,9 @@
<div class="mx-auto mt-1.5"> <div class="mx-auto mt-1.5">
<div class="py-3 rounded-2xl flex items-center justify-between mx-4 md:mx-0"> <div class="py-3 rounded-2xl flex items-center justify-between mx-4 md:mx-0">
<div class="w-full text-center"> <div class="w-full text-center">
<span class="font-bold">Demo Environment</span> <span class="font-bold"><%= t('demo_environment') %></span>
<br> <br>
<a href="<%= new_template_path %>" data-turbo-frame="modal" class="inline underline font-medium">Create a new template</a> document form or <%= t('create_a_new_template_document_form_or_submit_the_existing_one_html', new_template_link: new_template_path, start_form_link: start_form_url(slug: ::Template.first&.slug)) %> 😊
<a href="<%= start_form_url(slug: ::Template.first&.slug) %>" target="_blank" class="inline underline font-medium">submit the existing one</a> 😊
</div> </div>
</div> </div>
</div> </div>

@ -15,5 +15,5 @@
<%= yield %> <%= yield %>
</div> </div>
</div> </div>
<label class="modal-backdrop" for="<%= uuid %>">Close</label> <label class="modal-backdrop" for="<%= uuid %>"><%= t('close') %></label>
</div> </div>

@ -19,7 +19,7 @@
<% else %> <% else %>
<div class="flex items-center justify-center space-x-4 mr-1"> <div class="flex items-center justify-center space-x-4 mr-1">
<%= render 'shared/navbar_buttons' %> <%= render 'shared/navbar_buttons' %>
<%= link_to 'Settings', settings_profile_index_path, class: 'hidden md:inline-flex font-medium text-lg' %> <%= link_to t('settings'), settings_profile_index_path, class: 'hidden md:inline-flex font-medium text-lg' %>
</div> </div>
<% end %> <% end %>
<div class="dropdown dropdown-end"> <div class="dropdown dropdown-end">
@ -30,14 +30,14 @@
<li> <li>
<%= link_to settings_profile_index_path, class: 'flex items-center' do %> <%= link_to settings_profile_index_path, class: 'flex items-center' do %>
<%= svg_icon('adjustments', class: 'w-5 h-5 flex-shrink-0 stroke-2') %> <%= svg_icon('adjustments', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
<span class="mr-1">Profile</span> <span class="mr-1"><%= t('profile') %></span>
<% end %> <% end %>
</li> </li>
<% if !Docuseal.demo? && can?(:manage, EncryptedConfig) %> <% if !Docuseal.demo? && can?(:manage, EncryptedConfig) %>
<li> <li>
<%= link_to Docuseal.multitenant? ? console_redirect_index_path : Docuseal::CONSOLE_URL, data: { prefetch: false }, class: 'flex items-center' do %> <%= link_to Docuseal.multitenant? ? console_redirect_index_path : Docuseal::CONSOLE_URL, data: { prefetch: false }, class: 'flex items-center' do %>
<%= svg_icon('terminal', class: 'w-5 h-5 stroke-2') %> <%= svg_icon('terminal', class: 'w-5 h-5 stroke-2') %>
Console <%= t('console') %>
<% end %> <% end %>
</li> </li>
<% end %> <% end %>
@ -45,14 +45,14 @@
<li> <li>
<%= link_to settings_esign_path, class: 'flex items-center' do %> <%= link_to settings_esign_path, class: 'flex items-center' do %>
<%= svg_icon('zoom_check', class: 'w-5 h-5 stroke-2') %> <%= svg_icon('zoom_check', class: 'w-5 h-5 stroke-2') %>
<span class="mr-1">Verify PDF</span> <span class="mr-1"><%= t('verify_pdf') %></span>
<% end %> <% end %>
</li> </li>
<% end %> <% end %>
<li> <li>
<%= button_to destroy_user_session_path, method: :delete, data: { turbo: false }, class: 'flex items-center' do %> <%= button_to destroy_user_session_path, method: :delete, data: { turbo: false }, class: 'flex items-center' do %>
<%= svg_icon('logout', class: 'w-5 h-5 stroke-2 mr-2 inline') %> <%= svg_icon('logout', class: 'w-5 h-5 stroke-2 mr-2 inline') %>
<span class="mr-1">Sign out</span> <span class="mr-1"><%= t('sign_out') %></span>
<% end %> <% end %>
</li> </li>
</ul> </ul>
@ -71,7 +71,7 @@
<% if Docuseal.multitenant? && !request.path.in?([registration_path, new_registration_path]) %> <% if Docuseal.multitenant? && !request.path.in?([registration_path, new_registration_path]) %>
<%= link_to registration_path({ lang: params[:lang] }.compact_blank), class: 'btn btn-neutral btn-sm btn-outline' do %> <%= link_to registration_path({ lang: params[:lang] }.compact_blank), class: 'btn btn-neutral btn-sm btn-outline' do %>
<span class="flex items-center justify-center space-x-1"> <span class="flex items-center justify-center space-x-1">
<span class="hidden sm:block"><%= t('create_free_account') %></span> <span class="hidden sm:block capitalize"><%= t('create_free_account') %></span>
<span class="md:hidden"><%= t('sign_up') %></span> <span class="md:hidden"><%= t('sign_up') %></span>
</span> </span>
<% end %> <% end %>

@ -1,5 +1,5 @@
<%= link_to Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}", class: 'hidden md:inline-flex btn btn-warning btn-sm', data: { prefetch: false } do %> <%= link_to Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}", class: 'hidden md:inline-flex btn btn-warning btn-sm', data: { prefetch: false } do %>
Upgrade <%= t('upgrade') %>
<% end %> <% end %>
<% if signed_in? && current_user != true_user %> <% if signed_in? && current_user != true_user %>
<span class="hidden md:inline-flex h-3 border-r border-base-content"></span> <span class="hidden md:inline-flex h-3 border-r border-base-content"></span>

@ -12,7 +12,7 @@
<span class="join-item btn btn-disabled !bg-base-200 min-h-full h-10">«</span> <span class="join-item btn btn-disabled !bg-base-200 min-h-full h-10">«</span>
<% end %> <% end %>
<span class="join-item btn uppercase min-h-full h-10"> <span class="join-item btn uppercase min-h-full h-10">
Page <%= @pagy.page %> <%= t('page_number', number: @pagy.page) %>
</span> </span>
<% if @pagy.next %> <% if @pagy.next %>
<%== link.call(@pagy.next, '»', classes: 'join-item btn min-h-full h-10') %> <%== link.call(@pagy.next, '»', classes: 'join-item btn min-h-full h-10') %>

@ -2,12 +2,12 @@
<% if local_assigns[:with_counter] %> <% if local_assigns[:with_counter] %>
<% count = Submitter.where.not(completed_at: nil).distinct.count(:submission_id) %> <% count = Submitter.where.not(completed_at: nil).distinct.count(:submission_id) %>
<% if count > 1 %> <% if count > 1 %>
<b><%= count %></b> documents signed with <%= t('count_documents_signed_with_html', count:) %>
<% else %> <% else %>
Powered by <%= t('powered_by') %>
<% end %> <% end %>
<% else %> <% else %>
Powered by <%= t('powered_by') %>
<% end %> <% end %>
<a href="<%= Docuseal::PRODUCT_URL %><%= local_assigns[:link_path] %>" class="underline"><%= Docuseal.product_name %></a> - open source documents software <a href="<%= Docuseal::PRODUCT_URL %><%= local_assigns[:link_path] %>" class="underline"><%= Docuseal.product_name %></a> - open source documents software
</div> </div>

@ -2,23 +2,25 @@
<menu-active> <menu-active>
<ul class="menu px-0"> <ul class="menu px-0">
<li class="menu-title py-0 !bg-transparent mb-3 -mt-5"><a href="<%= '/' %>" class="!bg-transparent !text-neutral font-medium">&larr; Back</a></li> <li class="menu-title py-0 !bg-transparent mb-3 -mt-5"><a href="<%= '/' %>" class="!bg-transparent !text-neutral font-medium">&larr; Back</a></li>
<li class="menu-title py-0 !bg-transparent"><span class="!bg-transparent">Settings</span></li> <li class="menu-title py-0 !bg-transparent">
<span class="!bg-transparent"><%= t('settings') %></span>
</li>
<li></li> <li></li>
<li> <li>
<%= link_to 'Profile', settings_profile_index_path, class: 'text-base hover:bg-base-300' %> <%= link_to t('profile'), settings_profile_index_path, class: 'text-base hover:bg-base-300' %>
</li> </li>
<li> <li>
<%= link_to 'Account', settings_account_path, class: 'text-base hover:bg-base-300' %> <%= link_to t('account'), settings_account_path, class: 'text-base hover:bg-base-300' %>
</li> </li>
<% unless Docuseal.multitenant? %> <% unless Docuseal.multitenant? %>
<% if can?(:read, EncryptedConfig.new(key: EncryptedConfig::EMAIL_SMTP_KEY, account: current_account)) && ENV['SMTP_ADDRESS'].blank? && true_user == current_user %> <% if can?(:read, EncryptedConfig.new(key: EncryptedConfig::EMAIL_SMTP_KEY, account: current_account)) && ENV['SMTP_ADDRESS'].blank? && true_user == current_user %>
<li> <li>
<%= link_to 'Email', settings_email_index_path, class: 'text-base hover:bg-base-300' %> <%= link_to t('email'), settings_email_index_path, class: 'text-base hover:bg-base-300' %>
</li> </li>
<% end %> <% end %>
<% if can?(:read, EncryptedConfig.new(key: EncryptedConfig::FILES_STORAGE_KEY, account: current_account)) && true_user == current_user %> <% if can?(:read, EncryptedConfig.new(key: EncryptedConfig::FILES_STORAGE_KEY, account: current_account)) && true_user == current_user %>
<li> <li>
<%= link_to 'Storage', settings_storage_index_path, class: 'text-base hover:bg-base-300' %> <%= link_to t('storage'), settings_storage_index_path, class: 'text-base hover:bg-base-300' %>
</li> </li>
<% end %> <% end %>
<% if can?(:read, EncryptedConfig.new(key: 'submitter_invitation_sms', account: current_account)) && true_user == current_user %> <% if can?(:read, EncryptedConfig.new(key: 'submitter_invitation_sms', account: current_account)) && true_user == current_user %>
@ -29,22 +31,22 @@
<% end %> <% end %>
<% if can?(:read, AccountConfig) %> <% if can?(:read, AccountConfig) %>
<li> <li>
<%= link_to 'Notifications', settings_notifications_path, class: 'text-base hover:bg-base-300' %> <%= link_to t('notifications'), settings_notifications_path, class: 'text-base hover:bg-base-300' %>
</li> </li>
<% end %> <% end %>
<% if can?(:read, EncryptedConfig.new(key: EncryptedConfig::ESIGN_CERTS_KEY, account: current_account)) %> <% if can?(:read, EncryptedConfig.new(key: EncryptedConfig::ESIGN_CERTS_KEY, account: current_account)) %>
<li> <li>
<%= link_to 'E-Signature', settings_esign_path, class: 'text-base hover:bg-base-300' %> <%= link_to t('e_signature'), settings_esign_path, class: 'text-base hover:bg-base-300' %>
</li> </li>
<% end %> <% end %>
<% if can?(:read, AccountConfig) %> <% if can?(:read, AccountConfig) %>
<li> <li>
<%= link_to 'Personalization', settings_personalization_path, class: 'text-base hover:bg-base-300' %> <%= link_to t('personalization'), settings_personalization_path, class: 'text-base hover:bg-base-300' %>
</li> </li>
<% end %> <% end %>
<% if can?(:read, User) %> <% if can?(:read, User) %>
<li> <li>
<%= link_to 'Users', settings_users_path, class: 'text-base hover:bg-base-300' %> <%= link_to t('users'), settings_users_path, class: 'text-base hover:bg-base-300' %>
</li> </li>
<% end %> <% end %>
<%= render 'shared/settings_nav_extra' %> <%= render 'shared/settings_nav_extra' %>
@ -63,21 +65,21 @@
<% if !Docuseal.demo? && can?(:manage, EncryptedConfig) && (current_user != true_user || !current_account.linked_account_account) %> <% if !Docuseal.demo? && can?(:manage, EncryptedConfig) && (current_user != true_user || !current_account.linked_account_account) %>
<li> <li>
<%= link_to Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}", class: 'text-base hover:bg-base-300', data: { prefetch: false } do %> <%= link_to Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}", class: 'text-base hover:bg-base-300', data: { prefetch: false } do %>
Plans <%= t('plans') %>
<span class="badge badge-warning">New</span> <span class="badge badge-warning"><%= t('new') %></span>
<% end %> <% end %>
</li> </li>
<% end %> <% end %>
<% if !Docuseal.demo? && can?(:manage, EncryptedConfig) && (current_user != true_user || !current_account.testing?) %> <% if !Docuseal.demo? && can?(:manage, EncryptedConfig) && (current_user != true_user || !current_account.testing?) %>
<li> <li>
<%= link_to Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/api") : "#{Docuseal::CONSOLE_URL}/on_premise", class: 'text-base hover:bg-base-300', data: { prefetch: false } do %> <%= link_to Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/api") : "#{Docuseal::CONSOLE_URL}/on_premise", class: 'text-base hover:bg-base-300', data: { prefetch: false } do %>
<% if Docuseal.multitenant? %> API <% else %> Console <% end %> <% if Docuseal.multitenant? %> API <% else %> <%= t('console') %> <% end %>
<% end %> <% end %>
</li> </li>
<% if Docuseal.multitenant? %> <% if Docuseal.multitenant? %>
<li> <li>
<%= link_to console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/embedding/form"), class: 'text-base hover:bg-base-300', data: { prefetch: false } do %> <%= link_to console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/embedding/form"), class: 'text-base hover:bg-base-300', data: { prefetch: false } do %>
Embedding <%= t('embedding') %>
<% end %> <% end %>
</li> </li>
<% end %> <% end %>
@ -92,7 +94,7 @@
<%= form_for '', url: testing_account_path, method: current_account.testing? ? :delete : :get, html: { class: 'flex w-full' } do |f| %> <%= form_for '', url: testing_account_path, method: current_account.testing? ? :delete : :get, html: { class: 'flex w-full' } do |f| %>
<label class="flex items-center text-base hover:bg-base-300 w-full justify-between" for="testing_toggle"> <label class="flex items-center text-base hover:bg-base-300 w-full justify-between" for="testing_toggle">
<span class="mr-2 w-full"> <span class="mr-2 w-full">
Test Environment <%= t('test_environment') %>
</span> </span>
<%= f.check_box :testing_toggle, class: 'toggle toggle-sm', checked: current_account.testing?, onchange: 'this.form.requestSubmit()' %> <%= f.check_box :testing_toggle, class: 'toggle toggle-sm', checked: current_account.testing?, onchange: 'this.form.requestSubmit()' %>
</label> </label>
@ -105,7 +107,7 @@
<% if Docuseal.multitenant? || cannot?(:manage, :tenants) %> <% if Docuseal.multitenant? || cannot?(:manage, :tenants) %>
<div class="mx-4 border-t border-base-300 hidden md:block"> <div class="mx-4 border-t border-base-300 hidden md:block">
<div class="text-sm mt-3"> <div class="text-sm mt-3">
Need help? Ask a question: <%= t('need_help_ask_a_question_') %>
</div> </div>
<div class="flex mt-3 space-x-3"> <div class="flex mt-3 space-x-3">
<div class="tooltip" data-tip="GitHub"> <div class="tooltip" data-tip="GitHub">
@ -113,13 +115,13 @@
<%= svg_icon('brand_github', class: 'w-8 h-8') %> <%= svg_icon('brand_github', class: 'w-8 h-8') %>
</a> </a>
</div> </div>
<div class="tooltip" data-tip="Discord Community"> <div class="tooltip" data-tip="<%= t('discord_community') %>">
<a href="<%= Docuseal::DISCORD_URL %>" target="_blank" class="btn btn-circle btn-primary btn-md"> <a href="<%= Docuseal::DISCORD_URL %>" target="_blank" class="btn btn-circle btn-primary btn-md">
<%= svg_icon('brand_discord', class: 'w-8 h-8') %> <%= svg_icon('brand_discord', class: 'w-8 h-8') %>
</a> </a>
</div> </div>
<%= capture do %> <%= capture do %>
<div class="tooltip" data-tip="AI Assistant"> <div class="tooltip" data-tip="<%= t('ai_assistant') %>">
<a href="<%= Docuseal::CHATGPT_URL %>" target="_blank" class="btn btn-circle btn-primary btn-md"> <a href="<%= Docuseal::CHATGPT_URL %>" target="_blank" class="btn btn-circle btn-primary btn-md">
<%= svg_icon('brand_openai', class: 'w-8 h-8') %> <%= svg_icon('brand_openai', class: 'w-8 h-8') %>
</a> </a>

@ -1,17 +1,18 @@
<% if signed_in? && current_user != true_user && current_account.testing? %> <% if signed_in? && current_user != true_user && current_account.testing? %>
<div class="alert py-1 text-sm font-medium gap-x-2 flex whitespace-nowrap"> <div class="alert py-1 text-sm font-medium gap-x-2 flex whitespace-nowrap">
<a href="<%= testing_api_settings_path %>" data-turbo-frame="modal" class="link font-semibold flex"> <a href="<%= testing_api_settings_path %>" data-turbo-frame="modal" class="link font-semibold flex">
<%= svg_icon('code_circle', class: 'w-5 h-5 mr-1') %>Testing Environment <%= svg_icon('code_circle', class: 'w-5 h-5 mr-1') %>
<span><%= t('testing_environment') %></span>
</a> </a>
<span> <span>
| |
</span> </span>
<%= button_to testing_account_path, method: :delete, class: 'inline flex' do %> <%= button_to testing_account_path, method: :delete, class: 'inline flex' do %>
<% title = capture do %> <% title = capture do %>
<span class="link">Exit</span> <span class="link"><%= t('exit') %></span>
<span>&times;</span> <span>&times;</span>
<% end %> <% end %>
<%= button_title(title:, disabled_with: 'Leave') %> <%= button_title(title:, disabled_with: t('leave')) %>
<% end %> <% end %>
</div> </div>
<% end %> <% end %>

@ -2,7 +2,7 @@
<%= form_for '', url: testing_account_path, method: current_account.testing? ? :delete : :get, html: { class: 'flex' } do |f| %> <%= form_for '', url: testing_account_path, method: current_account.testing? ? :delete : :get, html: { class: 'flex' } do |f| %>
<label class="flex items-center justify-between py-2.5" for="testing_toggle"> <label class="flex items-center justify-between py-2.5" for="testing_toggle">
<span class="mr-2 text-lg"> <span class="mr-2 text-lg">
Test Environment <%= t('test_environment') %>
</span> </span>
<%= f.check_box :testing_toggle, class: 'toggle', checked: current_account.testing?, onchange: 'this.form.requestSubmit()' %> <%= f.check_box :testing_toggle, class: 'toggle', checked: current_account.testing?, onchange: 'this.form.requestSubmit()' %>
</label> </label>

@ -1,11 +1,15 @@
<div class="alert"> <div class="alert">
<%= svg_icon('info_circle', class: 'w-6 h-6') %> <%= svg_icon('info_circle', class: 'w-6 h-6') %>
<div> <div>
<p class="font-bold">Send signature requests via SMS</p> <p class="font-bold">
<%= t('send_signature_requests_via_sms') %>
</p>
<p class="text-gray-700"> <p class="text-gray-700">
Unlock with DocuSeal Pro <%= t('unlock_with_docuseal_pro') %>
<br> <br>
<a class="link font-medium" target="_blank" href="<%= Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}" %>" data-turbo="false">Learn More</a> <a class="link font-medium" target="_blank" href="<%= Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}" %>" data-turbo="false">
<%= t('learn_more') %>
</a>
</p> </p>
</div> </div>
</div> </div>

@ -1,11 +1,15 @@
<div class="alert"> <div class="alert">
<%= svg_icon('info_circle', class: 'w-6 h-6') %> <%= svg_icon('info_circle', class: 'w-6 h-6') %>
<div> <div>
<p class="font-bold">Single Sign On with SAML 2.0</p> <p class="font-bold">
<%= t('single_sign_on_with_saml_2_0') %>
</p>
<p class="text-gray-700"> <p class="text-gray-700">
Unlock with DocuSeal Pro <%= t('unlock_with_docuseal_pro') %>
<br> <br>
<a class="link font-medium" target="_blank" href="<%= Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}" %>" data-turbo="false">Learn More</a> <a class="link font-medium" target="_blank" href="<%= Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}" %>" data-turbo="false">
<%= t('learn_more') %>
</a>
</p> </p>
</div> </div>
</div> </div>

@ -22,10 +22,10 @@
</div> </div>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= fff.label :endpoint, 'Endpoint (optional)', class: 'label' %> <%= fff.label :endpoint, "Endpoint (#{t('optional')})", class: 'label' %>
<%= fff.text_field :endpoint, value: configs['endpoint'], type: :url, class: 'base-input' %> <%= fff.text_field :endpoint, value: configs['endpoint'], type: :url, class: 'base-input' %>
<label class="label"> <label class="label">
<span class="label-text-alt">For AWS S3 compatible APIs like Minio.</span> <span class="label-text-alt"><%= t('for_aws_s3_compatible_apis_like_minio') %></span>
</label> </label>
</div> </div>
<% end %> <% end %>

@ -4,11 +4,13 @@
<div class="alert my-4"> <div class="alert my-4">
<%= svg_icon('info_circle', class: 'stroke-current flex-shrink-0 w-6 h-6') %> <%= svg_icon('info_circle', class: 'stroke-current flex-shrink-0 w-6 h-6') %>
<div> <div>
<p class="font-bold">Store all files on disk</p> <p class="font-bold">
<%= t('store_all_files_on_disk') %>
</p>
<p class="text-gray-700"> <p class="text-gray-700">
No configs are needed but make sure your disk is persistent <%= t('no_configs_are_needed_but_make_sure_your_disk_is_persistent') %>
<br> <br>
<i>(not suitable for Heroku and other PaaS)</i> <i>(<%= t('not_suitable_for_heroku_and_other_paas') %>)</i>
</p> </p>
</div> </div>
</div> </div>

@ -1,7 +1,9 @@
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0"> <div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0">
<%= render 'shared/settings_nav' %> <%= render 'shared/settings_nav' %>
<div class="flex-grow max-w-xl mx-auto"> <div class="flex-grow max-w-xl mx-auto">
<h1 class="text-4xl font-bold mb-4">Storage</h1> <h1 class="text-4xl font-bold mb-4">
<%= t('storage') %>
</h1>
<% value = @encrypted_config.value || { 'service' => 'disk' } %> <% value = @encrypted_config.value || { 'service' => 'disk' } %>
<% configs = value['configs'] || {} %> <% configs = value['configs'] || {} %>
<%= form_for @encrypted_config, url: settings_storage_index_path, method: :post, html: { autocomplete: 'off', class: 'w-full' } do |f| %> <%= form_for @encrypted_config, url: settings_storage_index_path, method: :post, html: { autocomplete: 'off', class: 'w-full' } do |f| %>
@ -29,7 +31,7 @@
<%= render 'azure_form', f:, configs:, value: %> <%= render 'azure_form', f:, configs:, value: %>
</disable-hidden> </disable-hidden>
<div class="form-control"> <div class="form-control">
<%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button' %> <%= f.button button_title(title: t('save'), disabled_with: t('saving')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -1,11 +1,15 @@
<div class="alert"> <div class="alert">
<%= svg_icon('info_circle', class: 'w-6 h-6') %> <%= svg_icon('info_circle', class: 'w-6 h-6') %>
<div> <div>
<p class="font-bold">Bulk send from Excel XLSX or CSV</p> <p class="font-bold">
<%= t('bulk_send_from_excel_xlsx_or_csv') %>
</p>
<p class="text-gray-700"> <p class="text-gray-700">
Unlock with DocuSeal Pro <%= t('unlock_with_docuseal_pro') %>
<br> <br>
<a class="link font-medium" target="_blank" href="<%= Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}" %>" data-turbo="false">Learn More</a> <a class="link font-medium" target="_blank" href="<%= Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}" %>" data-turbo="false">
<%= t('learn_more') %>
</a>
</p> </p>
</div> </div>
</div> </div>

@ -44,7 +44,7 @@
<% if params[:selfsign].blank? %> <% if params[:selfsign].blank? %>
<a href="#" class="btn btn-primary btn-sm w-full flex items-center justify-center" data-action="click:dynamic-list#addItem"> <a href="#" class="btn btn-primary btn-sm w-full flex items-center justify-center" data-action="click:dynamic-list#addItem">
<%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %> <%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %>
<span>Add New</span> <span><%= t('add_new') %></span>
</a> </a>
<% end %> <% end %>
</dynamic-list> </dynamic-list>
@ -54,6 +54,6 @@
<%= render 'send_sms', f: %> <%= render 'send_sms', f: %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.button button_title(title: 'Add Recipients'), class: 'base-button' %> <%= f.button button_title(title: t('add_recipients')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>

@ -5,7 +5,7 @@
<emails-textarea data-bulk-enabled="<%= Docuseal.demo? || !Docuseal.multitenant? || can?(:manage, :bulk_send) %>" data-limit="<%= Docuseal.multitenant? ? (can?(:manage, :bulk_send) ? 40 : 1) : nil %>"> <emails-textarea data-bulk-enabled="<%= Docuseal.demo? || !Docuseal.multitenant? || can?(:manage, :bulk_send) %>" data-limit="<%= Docuseal.multitenant? ? (can?(:manage, :bulk_send) ? 40 : 1) : nil %>">
<submitters-autocomplete data-field="email" class="block relative"> <submitters-autocomplete data-field="email" class="block relative">
<autoresize-textarea> <autoresize-textarea>
<%= f.text_area :emails, required: true, class: 'base-textarea w-full !text-lg', placeholder: 'Type emails here...', rows: 2 %> <%= f.text_area :emails, required: true, class: 'base-textarea w-full !text-lg', placeholder: "#{t('type_emails_here')}...", rows: 2 %>
</autoresize-textarea> </autoresize-textarea>
</submitters-autocomplete> </submitters-autocomplete>
</emails-textarea> </emails-textarea>
@ -29,7 +29,7 @@
<input type="hidden" name="submission[1][submitters][][uuid]" value="<%= item['uuid'] %>"> <input type="hidden" name="submission[1][submitters][][uuid]" value="<%= item['uuid'] %>">
<submitters-autocomplete data-field="email"> <submitters-autocomplete data-field="email">
<linked-input data-target-id="<%= "email_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>"> <linked-input data-target-id="<%= "email_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>">
<%= tag.input type: 'email', multiple: true, name: 'submission[1][submitters][][email]', autocomplete: 'off', class: 'base-input-slim w-full', placeholder: 'Email', required: index.zero?, value: item['email'].presence || ((params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.email : ''), id: "email_#{item['uuid']}" %> <%= tag.input type: 'email', multiple: true, name: 'submission[1][submitters][][email]', autocomplete: 'off', class: 'base-input-slim w-full', placeholder: t('email'), required: index.zero?, value: item['email'].presence || ((params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.email : ''), id: "email_#{item['uuid']}" %>
</linked-input> </linked-input>
</submitters-autocomplete> </submitters-autocomplete>
</submitter-item> </submitter-item>
@ -41,7 +41,7 @@
<% if params[:selfsign].blank? %> <% if params[:selfsign].blank? %>
<a href="#" class="btn btn-primary btn-sm w-full flex items-center justify-center" data-action="click:dynamic-list#addItem"> <a href="#" class="btn btn-primary btn-sm w-full flex items-center justify-center" data-action="click:dynamic-list#addItem">
<%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %> <%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %>
<span>Add New</span> <span><%= t('add_new') %></span>
</a> </a>
<% end %> <% end %>
</dynamic-list> </dynamic-list>
@ -52,7 +52,7 @@
</div> </div>
<div class="form-control"> <div class="form-control">
<%= content_for(:submit_button) || capture do %> <%= content_for(:submit_button) || capture do %>
<%= f.button button_title(title: 'Add Recipients'), class: 'base-button' %> <%= f.button button_title(title: t('add_recipients')), class: 'base-button' %>
<% end %> <% end %>
</div> </div>
<% end %> <% end %>

@ -2,12 +2,16 @@
<div class=""> <div class="">
<input id="api_key" type="text" value="<%= start_form_url(slug: template.slug) %>" class="input font-mono input-bordered w-full bg-white" autocomplete="off" readonly> <input id="api_key" type="text" value="<%= start_form_url(slug: template.slug) %>" class="input font-mono input-bordered w-full bg-white" autocomplete="off" readonly>
<div class="mt-4"> <div class="mt-4">
<%= render 'shared/clipboard_copy', icon: 'copy', text: start_form_url(slug: template.slug), class: 'base-button w-full block', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy', copied_title: 'Copied' %> <%= render 'shared/clipboard_copy', icon: 'copy', text: start_form_url(slug: template.slug), class: 'base-button w-full block', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
</div> </div>
</div> </div>
<div class="divider uppercase normal-case mt-5 mb-6 text-neutral-700">Or embed on your website</div> <div class="divider uppercase normal-case mt-5 mb-6 text-neutral-700">
<%= t('or_embed_on_your_website') %>
</div>
<%= render 'templates/embedding', template: %> <%= render 'templates/embedding', template: %>
<div class="mt-4"> <div class="mt-4">
<a href="#" class="link text-center block" data-action="click:turbo-modal#close">Close</a> <a href="#" class="link text-center block" data-action="click:turbo-modal#close">
<%= t('close') %>
</a>
</div> </div>
</div> </div>

@ -21,13 +21,13 @@
<input type="hidden" name="submission[1][submitters][][uuid]" value="<%= item['uuid'] %>"> <input type="hidden" name="submission[1][submitters][][uuid]" value="<%= item['uuid'] %>">
<submitters-autocomplete data-field="phone"> <submitters-autocomplete data-field="phone">
<linked-input data-target-id="<%= "phone_phone_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>"> <linked-input data-target-id="<%= "phone_phone_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>">
<%= tag.input type: 'tel', pattern: '^\+[0-9\s\-]+$', oninvalid: "this.value ? this.setCustomValidity('Use internatioanl format: +1xxx...') : ''", oninput: "this.setCustomValidity('')", name: 'submission[1][submitters][][phone]', autocomplete: 'off', class: 'base-input-slim w-full', placeholder: 'Phone', required: index.zero?, id: "phone_phone_#{item['uuid']}" %> <%= tag.input type: 'tel', pattern: '^\+[0-9\s\-]+$', oninvalid: "this.value ? this.setCustomValidity('Use internatioanl format: +1xxx...') : ''", oninput: "this.setCustomValidity('')", name: 'submission[1][submitters][][phone]', autocomplete: 'off', class: 'base-input-slim w-full', placeholder: t('phone'), required: index.zero?, id: "phone_phone_#{item['uuid']}" %>
</linked-input> </linked-input>
</submitters-autocomplete> </submitters-autocomplete>
<% if submitters.size > 1 %> <% if submitters.size > 1 %>
<submitters-autocomplete data-field="name"> <submitters-autocomplete data-field="name">
<linked-input data-target-id="<%= "phone_name_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>"> <linked-input data-target-id="<%= "phone_name_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>">
<input type="text" name="submission[1][submitters][][name]" autocomplete="off" class="base-input-slim mt-1.5 w-full" placeholder="Name (optional)" value="<%= (params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.full_name : '' %>" dir="auto" id="phone_name_<%= item['uuid'] %>"> <input type="text" name="submission[1][submitters][][name]" autocomplete="off" class="base-input-slim mt-1.5 w-full" placeholder="<%= "#{t('name')} (#{t('optional')})" %>" value="<%= (params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.full_name : '' %>" dir="auto" id="phone_name_<%= item['uuid'] %>">
</linked-input> </linked-input>
</submitters-autocomplete> </submitters-autocomplete>
<% end %> <% end %>
@ -36,7 +36,7 @@
<div class="form-control flex"> <div class="form-control flex">
<submitters-autocomplete data-field="name"> <submitters-autocomplete data-field="name">
<linked-input data-target-id="<%= "phone_name_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>"> <linked-input data-target-id="<%= "phone_name_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>">
<input type="text" name="submission[1][submitters][][name]" autocomplete="off" class="base-input-slim w-full" placeholder="Name (optional)" value="<%= (params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.full_name : '' %>" dir="auto" id="phone_name_<%= item['uuid'] %>"> <input type="text" name="submission[1][submitters][][name]" autocomplete="off" class="base-input-slim w-full" placeholder="<%= "#{t('name')} (#{t('optional')})" %>" value="<%= (params[:selfsign] && index.zero?) || item['is_requester'] ? current_user.full_name : '' %>" dir="auto" id="phone_name_<%= item['uuid'] %>">
</linked-input> </linked-input>
</submitters-autocomplete> </submitters-autocomplete>
</div> </div>
@ -50,7 +50,7 @@
<% if params[:selfsign].blank? %> <% if params[:selfsign].blank? %>
<a href="#" class="btn btn-primary btn-sm w-full flex items-center justify-center" data-action="click:dynamic-list#addItem"> <a href="#" class="btn btn-primary btn-sm w-full flex items-center justify-center" data-action="click:dynamic-list#addItem">
<%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %> <%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %>
<span>Add New</span> <span><%= t('add_new') %></span>
</a> </a>
<% end %> <% end %>
</dynamic-list> </dynamic-list>
@ -59,6 +59,6 @@
<%= render 'send_sms', f: %> <%= render 'send_sms', f: %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.button button_title(title: 'Add Recipients'), class: 'base-button' %> <%= f.button button_title(title: t('add_recipients')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>

@ -3,13 +3,13 @@
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<%= f.label :send_email, for: uuid = SecureRandom.uuid, class: 'flex items-center cursor-pointer' do %> <%= f.label :send_email, for: uuid = SecureRandom.uuid, class: 'flex items-center cursor-pointer' do %>
<%= f.check_box :send_email, id: uuid, class: 'base-checkbox', disabled: !can_send_emails, checked: can_send_emails %> <%= f.check_box :send_email, id: uuid, class: 'base-checkbox', disabled: !can_send_emails, checked: can_send_emails %>
<span class="label">Send emails</span> <span class="label"><%= t('send_emails') %></span>
<% end %> <% end %>
<div> <div>
<% if can_send_emails %> <% if can_send_emails %>
<label> <label>
<%= f.check_box :is_custom_message, onchange: "[this.form.querySelector('#message_field').classList.toggle('hidden', !event.currentTarget.checked)]", checked: false, class: 'hidden peer' %> <%= f.check_box :is_custom_message, onchange: "[this.form.querySelector('#message_field').classList.toggle('hidden', !event.currentTarget.checked)]", checked: false, class: 'hidden peer' %>
<span class="link peer-checked:hidden">Edit message</span> <span class="link peer-checked:hidden"><%= t('edit_message') %></span>
</label> </label>
<% end %> <% end %>
<%= render 'email_stats' %> <%= render 'email_stats' %>
@ -19,11 +19,13 @@
<div class="alert my-4"> <div class="alert my-4">
<%= svg_icon('info_circle', class: 'w-6 h-6') %> <%= svg_icon('info_circle', class: 'w-6 h-6') %>
<div> <div>
<p class="font-bold">SMTP not Configured</p> <p class="font-bold"><%= t('smtp_not_configured') %></p>
<p class="text-gray-700"> <p class="text-gray-700">
Configure SMTP settings in order to send emails: <%= t('configure_smtp_settings_in_order_to_send_emails_') %>
<br> <br>
<a class="link font-medium" data-turbo-frame="_top" href="<%= settings_email_index_path %>">Go to SMTP settings</a> <a class="link font-medium" data-turbo-frame="_top" href="<%= settings_email_index_path %>">
<%= t('go_to_smtp_setting') %>
</a>
</p> </p>
</div> </div>
</div> </div>
@ -34,13 +36,13 @@
<div class="card-body"> <div class="card-body">
<div class="form-control space-y-2"> <div class="form-control space-y-2">
<div class="form-control"> <div class="form-control">
<%= f.label :subject, class: 'label' %> <%= f.label :subject, t('subject'), class: 'label' %>
<%= f.text_field :subject, value: @template.preferences['request_email_subject'].presence || config.value['subject'], required: true, class: '!text-sm base-input w-full', dir: 'auto' %> <%= f.text_field :subject, value: @template.preferences['request_email_subject'].presence || config.value['subject'], required: true, class: '!text-sm base-input w-full', dir: 'auto' %>
</div> </div>
<div class="form-control"> <div class="form-control">
<div class="flex items-center"> <div class="flex items-center">
<%= f.label :message, 'Body', class: 'label' %> <%= f.label :message, t('body'), class: 'label' %>
<span class="tooltip tooltip-right" data-tip="Use following placeholders text: <%= AccountConfig::DEFAULT_VALUES.dig(AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY, 'body').scan(/{{.*?}}/).join(', ') %>"> <span class="tooltip tooltip-right" data-tip="<%= t('use_following_placeholders_text_') %> <%= AccountConfig::DEFAULT_VALUES.dig(AccountConfig::SUBMITTER_INVITATION_EMAIL_KEY, 'body').scan(/{{.*?}}/).join(', ') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4') %> <%= svg_icon('info_circle', class: 'w-4 h-4') %>
</span> </span>
</div> </div>
@ -49,7 +51,7 @@
</autoresize-textarea> </autoresize-textarea>
<label for="<%= uuid = SecureRandom.uuid %>" class="flex items-center cursor-pointer"> <label for="<%= uuid = SecureRandom.uuid %>" class="flex items-center cursor-pointer">
<%= check_box_tag :save_message, id: uuid, class: 'base-checkbox', checked: false %> <%= check_box_tag :save_message, id: uuid, class: 'base-checkbox', checked: false %>
<span class="label">Save as default template message</span> <span class="label"><%= t('save_as_default_template_message') %></span>
</label> </label>
</div> </div>
<%= render 'message_fields' %> <%= render 'message_fields' %>

@ -1,5 +1,5 @@
<div class="mt-2 mb-1"> <div class="mt-2 mb-1">
<div class="tooltip w-full" data-tip="Unlock with Enterpise"> <div class="tooltip w-full" data-tip="<%= t('unlock_with_docuseal_pro') %>">
<%= link_to submitter.sent_at? ? 'Re-send SMS' : 'Send SMS', Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}", class: 'btn btn-sm btn-primary text-gray-400 w-full' %> <%= link_to submitter.sent_at? ? t('re_send_sms') : t('send_sms'), Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premise" }.to_query}", class: 'btn btn-sm btn-primary text-gray-400 w-full' %>
</div> </div>
</div> </div>

@ -2,8 +2,8 @@
<div class="form-control"> <div class="form-control">
<%= f.label :preserve_order, for: uuid = SecureRandom.uuid, class: 'flex items-center cursor-pointer' do %> <%= f.label :preserve_order, for: uuid = SecureRandom.uuid, class: 'flex items-center cursor-pointer' do %>
<%= f.check_box :preserve_order, id: uuid, class: 'base-checkbox', checked: template.submissions.last&.submitters_order.in?(['preserved', nil]) %> <%= f.check_box :preserve_order, id: uuid, class: 'base-checkbox', checked: template.submissions.last&.submitters_order.in?(['preserved', nil]) %>
<span class="label">Preserve order</span> <span class="label"><%= t('preserve_order') %></span>
<span class="tooltip" data-tip="When checked, notifications will be sent to the second party once the form is completed by the previous party. Uncheck this option to send notifications to all parties simultaneously right away."> <span class="tooltip" data-tip="<%= t('when_checked_notifications_will_be_sent_to_the_second_party_once_the_form_is_completed_by_the_previous_party') %>">
<%= svg_icon('info_circle', class: 'w-4 h-4') %> <%= svg_icon('info_circle', class: 'w-4 h-4') %>
</span> </span>
<% end %> <% end %>

@ -1,4 +1,4 @@
<%= render 'shared/turbo_modal_large', title: params[:selfsign] ? 'Add Recipients' : 'Add New Recipients' do %> <%= render 'shared/turbo_modal_large', title: params[:selfsign] ? t('add_recipients') : 'Add New Recipients' do %>
<% options = [['via Email', 'email'], ['via Phone', 'phone'], %w[Detailed detailed], ['Upload List', 'list']].compact %> <% options = [['via Email', 'email'], ['via Phone', 'phone'], %w[Detailed detailed], ['Upload List', 'list']].compact %>
<toggle-visible data-element-ids="<%= options.map(&:last).to_json %>" class="relative text-center px-2 mt-4 block"> <toggle-visible data-element-ids="<%= options.map(&:last).to_json %>" class="relative text-center px-2 mt-4 block">
<div class="join"> <div class="join">

@ -12,9 +12,7 @@
<% if @submission.audit_trail.present? %> <% if @submission.audit_trail.present? %>
<a href="<%= ActiveStorage::Blob.proxy_url(@submission.audit_trail.blob, expires_at: 4.hours.from_now) %>" class="white-button" target="_blank"> <a href="<%= ActiveStorage::Blob.proxy_url(@submission.audit_trail.blob, expires_at: 4.hours.from_now) %>" class="white-button" target="_blank">
<%= svg_icon('external_link', class: 'w-6 h-6') %> <%= svg_icon('external_link', class: 'w-6 h-6') %>
<span class="hidden md:inline"> <span class="hidden md:inline"><%= t('audit_log') %></span>
Audit Log
</span>
</a> </a>
<% end %> <% end %>
<% if last_submitter = @submission.submitters.to_a.select(&:completed_at?).max_by(&:completed_at) %> <% if last_submitter = @submission.submitters.to_a.select(&:completed_at?).max_by(&:completed_at) %>
@ -23,11 +21,11 @@
<download-button data-src="<%= submitter_download_index_path(last_submitter.slug, { sig: params[:sig], combined: is_combined_enabled }.compact) %>" class="base-button <%= '!rounded-r-none !pr-2' if is_all_completed && !is_combined_enabled %>"> <download-button data-src="<%= submitter_download_index_path(last_submitter.slug, { sig: params[:sig], combined: is_combined_enabled }.compact) %>" class="base-button <%= '!rounded-r-none !pr-2' if is_all_completed && !is_combined_enabled %>">
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton"> <span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
<%= svg_icon('download', class: 'w-6 h-6') %> <%= svg_icon('download', class: 'w-6 h-6') %>
<span class="hidden md:inline">Download</span> <span class="hidden md:inline"><%= t('download') %></span>
</span> </span>
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton"> <span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
<%= svg_icon('loader', class: 'w-6 h-6 animate-spin') %> <%= svg_icon('loader', class: 'w-6 h-6 animate-spin') %>
<span class="hidden md:inline">Downloading</span> <span class="hidden md:inline"><%= t('downloading') %></span>
</span> </span>
</download-button> </download-button>
<% if is_all_completed && !is_combined_enabled %> <% if is_all_completed && !is_combined_enabled %>
@ -42,11 +40,11 @@
<download-button data-src="<%= submitter_download_index_path(last_submitter.slug, { sig: params[:sig], combined: true }.compact) %>" class="flex items-center"> <download-button data-src="<%= submitter_download_index_path(last_submitter.slug, { sig: params[:sig], combined: true }.compact) %>" class="flex items-center">
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton"> <span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
<%= svg_icon('download', class: 'w-6 h-6 flex-shrink-0') %> <%= svg_icon('download', class: 'w-6 h-6 flex-shrink-0') %>
<span class="whitespace-nowrap">Download combined PDF</span> <span class="whitespace-nowrap"><%= t('download_combined_pdf') %></span>
</span> </span>
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton"> <span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
<%= svg_icon('loader', class: 'w-6 h-6 animate-spin') %> <%= svg_icon('loader', class: 'w-6 h-6 animate-spin') %>
<span>Downloading</span> <span><%= t('downloading') %></span>
</span> </span>
</download-button> </download-button>
</li> </li>
@ -144,25 +142,25 @@
<%= svg_icon('writing', class: 'w-5 h-5') %> <%= svg_icon('writing', class: 'w-5 h-5') %>
<span> <span>
<% if submitter&.declined_at? %> <% if submitter&.declined_at? %>
Declined on <%= l(submitter.declined_at.in_time_zone(@submission.account.timezone), format: :short, locale: @submission.account.locale) %> <%= t('declined_on_time', time: l(submitter.declined_at.in_time_zone(@submission.account.timezone), format: :short, locale: @submission.account.locale)) %>
<% elsif submitter %> <% elsif submitter %>
<%= submitter.completed_at? ? l(submitter.completed_at.in_time_zone(@submission.account.timezone), format: :long, locale: @submission.account.locale) : 'Not completed yet' %> <%= submitter.completed_at? ? l(submitter.completed_at.in_time_zone(@submission.account.timezone), format: :long, locale: @submission.account.locale) : t('not_copleted_yet') %>
<% else %> <% else %>
Not invited yet <%= t('not_invited_yet') %>
<% end %> <% end %>
</span> </span>
</div> </div>
<% if submitter&.declined_at? %> <% if submitter&.declined_at? %>
<div class="flex items-center space-x-1 mt-1"> <div class="flex items-center space-x-1 mt-1">
<span> <span>
Reason: <%= t('Reason') %>:
<%= simple_format(h(submitter.submission_events.find_by(event_type: :decline_form).data['reason'])) %> <%= simple_format(h(submitter.submission_events.find_by(event_type: :decline_form).data['reason'])) %>
</span> </span>
</div> </div>
<% end %> <% end %>
<% if signed_in? && submitter && submitter.email && !submitter.completed_at && !@submission.archived_at? && can?(:update, submitter) && Accounts.can_send_emails?(current_account) && !@submission.expired? && !submitter.declined_at? %> <% if signed_in? && submitter && submitter.email && !submitter.completed_at && !@submission.archived_at? && can?(:update, submitter) && Accounts.can_send_emails?(current_account) && !@submission.expired? && !submitter.declined_at? %>
<div class="mt-2 mb-1"> <div class="mt-2 mb-1">
<%= button_to button_title(title: submitter.sent_at? ? 'Re-send Email' : 'Send Email', disabled_with: 'Sending'), submitter_send_email_index_path(submitter_slug: submitter.slug), class: 'btn btn-sm btn-primary w-full' %> <%= button_to button_title(title: submitter.sent_at? ? 'Re-send Email' : 'Send Email', disabled_with: t('sending')), submitter_send_email_index_path(submitter_slug: submitter.slug), class: 'btn btn-sm btn-primary w-full' %>
</div> </div>
<% end %> <% end %>
<% if signed_in? && submitter && submitter.phone && !submitter.completed_at && !@submission.archived_at? && can?(:update, submitter) && !@submission.expired? && !submitter.declined_at? %> <% if signed_in? && submitter && submitter.phone && !submitter.completed_at && !@submission.archived_at? && can?(:update, submitter) && !@submission.expired? && !submitter.declined_at? %>
@ -171,7 +169,7 @@
<% if signed_in? && submitter && !submitter.completed_at? && !@submission.archived_at? && can?(:create, submitter) && !@submission.expired? && !submitter.declined_at? %> <% if signed_in? && submitter && !submitter.completed_at? && !@submission.archived_at? && can?(:create, submitter) && !@submission.expired? && !submitter.declined_at? %>
<div class="mt-2 mb-1"> <div class="mt-2 mb-1">
<a class="btn btn-sm btn-primary w-full" target="_blank" href="<%= submit_form_path(slug: submitter.slug) %>"> <a class="btn btn-sm btn-primary w-full" target="_blank" href="<%= submit_form_path(slug: submitter.slug) %>">
Sign In-person <%= t('sign_in_person') %>
</a> </a>
</div> </div>
<% end %> <% end %>
@ -222,11 +220,11 @@
<input type="checkbox" class="peer hidden" onclick="[document_view.classList.toggle('hidden'), parties_view.classList.toggle('hidden')]"> <input type="checkbox" class="peer hidden" onclick="[document_view.classList.toggle('hidden'), parties_view.classList.toggle('hidden')]">
<span class="peer-checked:hidden flex items-center space-x-2"> <span class="peer-checked:hidden flex items-center space-x-2">
<%= svg_icon('users', class: 'w-8 h-8') %> <%= svg_icon('users', class: 'w-8 h-8') %>
<span>Signers</span> <span><%= t('signers') %></span>
</span> </span>
<span class="hidden peer-checked:flex items-center"> <span class="hidden peer-checked:flex items-center">
<%= svg_icon('chevron_left', class: 'w-8 h-8') %> <%= svg_icon('chevron_left', class: 'w-8 h-8') %>
<span>Back</span> <span><%= t('back') %></span>
</span> </span>
</label> </label>
</div> </div>

@ -1,15 +1,15 @@
<div> <div>
<%= link_to root_path do %> <%= link_to root_path do %>
&larr; &larr;
<span>Back to Active</span> <span><%= t('back_to_active') %></span>
<% end %> <% end %>
</div> </div>
<div class="flex justify-between mb-4 items-center"> <div class="flex justify-between mb-4 items-center">
<div> <div>
<h1 class="text-4xl font-bold md:block <%= 'hidden' if params[:q].present? %>">Submissions <span class="badge badge-outline badge-lg align-middle">Archived</span></h1> <h1 class="text-4xl font-bold md:block <%= 'hidden' if params[:q].present? %>"><%= t('submissions') %> <span class="badge badge-outline badge-lg align-middle"><%= t('archived') %></span></h1>
</div> </div>
<% if params[:q].present? || @pagy.pages > 1 %> <% if params[:q].present? || @pagy.pages > 1 %>
<%= render 'shared/search_input', placeholder: 'Search...' %> <%= render 'shared/search_input', placeholder: "#{t('search')}..." %>
<% end %> <% end %>
</div> </div>
<% if @pagy.count > 0 %> <% if @pagy.count > 0 %>
@ -19,7 +19,7 @@
<% elsif params[:q].present? %> <% elsif params[:q].present? %>
<div class="text-center"> <div class="text-center">
<div class="mt-16 text-3xl font-semibold"> <div class="mt-16 text-3xl font-semibold">
Submissions not Found <%= t('submissions_not_found') %>
</div> </div>
</div> </div>
<% end %> <% end %>

@ -5,7 +5,9 @@
<div class="mr-2"> <div class="mr-2">
<%= render 'dashboard/toggle_view', selected: 'submissions' %> <%= render 'dashboard/toggle_view', selected: 'submissions' %>
</div> </div>
<h1 class="text-2xl md:text-3xl sm:text-4xl font-bold md:block <%= 'hidden' if params[:q].present? %>">Submissions</h1> <h1 class="text-2xl md:text-3xl sm:text-4xl font-bold md:block <%= 'hidden' if params[:q].present? %>">
<%= t('submissions') %>
</h1>
</div> </div>
<div class="flex space-x-2"> <div class="flex space-x-2">
<% if params[:q].present? || @pagy.pages > 1 %> <% if params[:q].present? || @pagy.pages > 1 %>
@ -17,7 +19,7 @@
</span> </span>
<%= link_to new_template_path, class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %> <%= link_to new_template_path, class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %>
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %> <%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
<span class="hidden md:block">Create</span> <span class="hidden md:block"><%= t('create') %></span>
<% end %> <% end %>
<% end %> <% end %>
</div> </div>
@ -25,7 +27,7 @@
<% view_archived_html = capture do %> <% view_archived_html = capture do %>
<% if current_account.submissions.where.not(archived_at: nil).exists? %> <% if current_account.submissions.where.not(archived_at: nil).exists? %>
<div> <div>
<a href="<%= submissions_archived_index_path %>" class="link text-sm">View Archived</a> <a href="<%= submissions_archived_index_path %>" class="link text-sm"><%= t('view_archived') %></a>
</div> </div>
<% end %> <% end %>
<% end %> <% end %>
@ -34,19 +36,19 @@
<a href="<%= url_for(params.to_unsafe_h.except(:status)) %>" class="<%= params[:status].blank? ? 'border-neutral-700' : 'border-neutral-300' %> flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-48 hover:border-neutral-600"> <a href="<%= url_for(params.to_unsafe_h.except(:status)) %>" class="<%= params[:status].blank? ? 'border-neutral-700' : 'border-neutral-300' %> flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-48 hover:border-neutral-600">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<%= svg_icon('list', class: 'w-5 h-5') %> <%= svg_icon('list', class: 'w-5 h-5') %>
<span class="font-normal">All</span> <span class="font-normal"><%= t('all') %></span>
</div> </div>
</a> </a>
<a href="<%= url_for(params.to_unsafe_h.merge(status: :pending)) %>" class="<%= params[:status] == 'pending' ? 'border-neutral-700' : 'border-neutral-300' %> flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-48 hover:border-neutral-600"> <a href="<%= url_for(params.to_unsafe_h.merge(status: :pending)) %>" class="<%= params[:status] == 'pending' ? 'border-neutral-700' : 'border-neutral-300' %> flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-48 hover:border-neutral-600">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<%= svg_icon('clock', class: 'w-5 h-5') %> <%= svg_icon('clock', class: 'w-5 h-5') %>
<span class="font-normal">Pending</span> <span class="font-normal"><%= t('pending') %></span>
</div> </div>
</a> </a>
<a href="<%= url_for(params.to_unsafe_h.merge(status: :completed)) %>" class="<%= params[:status] == 'completed' ? 'border-neutral-700' : 'border-neutral-300' %> flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-48 hover:border-neutral-600"> <a href="<%= url_for(params.to_unsafe_h.merge(status: :completed)) %>" class="<%= params[:status] == 'completed' ? 'border-neutral-700' : 'border-neutral-300' %> flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-48 hover:border-neutral-600">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<%= svg_icon('circle_check', class: 'w-5 h-5') %> <%= svg_icon('circle_check', class: 'w-5 h-5') %>
<span class="font-normal">Completed</span> <span class="font-normal"><%= t('completed') %></span>
</div> </div>
</a> </a>
</div> </div>
@ -70,7 +72,7 @@
<% elsif params[:q].present? %> <% elsif params[:q].present? %>
<div class="text-center"> <div class="text-center">
<div class="mt-16 text-3xl font-semibold"> <div class="mt-16 text-3xl font-semibold">
Submissions not Found <%= t('submissions_not_found') %>
</div> </div>
</div> </div>
<% end %> <% end %>

@ -10,7 +10,7 @@
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="mb-1 text-lg font-semibold">XLSX</span> <span class="mb-1 text-lg font-semibold">XLSX</span>
<p class="text-sm"> Primarily opened with Microsoft Excel. Other options include Google Sheets, LibreOffice Calc, and OpenOffice Calc.</p> <p class="text-sm"><%= t('primarily_opened_with_microsoft_excel_other_options_include_google_sheets_libreoffice_calc_and_openoffice_calc') %></p>
</div> </div>
</div> </div>
<% end %> <% end %>
@ -24,7 +24,7 @@
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="mb-1 text-lg font-semibold">CSV</span> <span class="mb-1 text-lg font-semibold">CSV</span>
<p class="text-sm">Can be opened with Microsoft Excel, Google Sheets, or any text editor like Notepad.</p> <p class="text-sm"><%= t('can_be_opened_with_microsoft_excel_google_sheets_or_any_text_editor_like_notepad') %></p>
</div> </div>
</div> </div>
<% end %> <% end %>

@ -41,7 +41,7 @@
<% end %> <% end %>
<% if Docuseal.multitenant? %> <% if Docuseal.multitenant? %>
<div> <div>
<%= link_to 'Create free account', registration_path, class: 'white-button w-full' %> <%= link_to t('create_free_account'), registration_path, class: 'white-button w-full' %>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -19,31 +19,31 @@
<%= form_for '', url: submit_form_path(params[:slug]), html: { style: 'max-width: 900px; width: 100%; margin-bottom: 120px' }, method: :put do |f| %> <%= form_for '', url: submit_form_path(params[:slug]), html: { style: 'max-width: 900px; width: 100%; margin-bottom: 120px' }, method: :put do |f| %>
<input value="" type="hidden" name="values[<%= (@submitter.submission.template_fields || @submitter.template.fields).find { |f| f['type'] == 'signature' && f['uuid'].starts_with?(params[:f]) }['uuid'] %>]"> <input value="" type="hidden" name="values[<%= (@submitter.submission.template_fields || @submitter.template.fields).find { |f| f['type'] == 'signature' && f['uuid'].starts_with?(params[:f]) }['uuid'] %>]">
<div class="font-semibold text-4xl text-center w-full mb-2"> <div class="font-semibold text-4xl text-center w-full mb-2">
Draw Signature <%= t('draw_signature') %>
</div> </div>
<div class="w-full bg-white rounded-2xl border relative" style="height: 300px"> <div class="w-full bg-white rounded-2xl border relative" style="height: 300px">
<canvas class="w-full"></canvas> <canvas class="w-full"></canvas>
<button aria-label="Clear" class="btn btn-ghost btn-sm font-medium top-0 right-0 absolute mt-1 mr-1"> <button aria-label="<%= t('clear') %>" class="btn btn-ghost btn-sm font-medium top-0 right-0 absolute mt-1 mr-1">
<%= svg_icon('reload', class: 'w-5 h-5') %> <%= svg_icon('reload', class: 'w-5 h-5') %>
<span class="inline">Clear</span> <span class="inline"><%= t('clear') %></span>
</button> </button>
</div> </div>
<div class="mt-4"> <div class="mt-4">
<button disabled class="base-button w-full"> <button disabled class="base-button w-full">
Submit <%= t('submit') %>
</button> </button>
<%= f.button button_title(title: 'Submit'), class: 'base-button w-full', style: 'display: none' %> <%= f.button button_title(title: t('submit')), class: 'base-button w-full', style: 'display: none' %>
</div> </div>
<% end %> <% end %>
<div id="success" class="text-center p-2 hidden" style="margin-bottom: 100px"> <div id="success" class="text-center p-2 hidden" style="margin-bottom: 100px">
<div class="flex items-center space-x-1 items-center justify-center text-2xl font-semibold mb-2"> <div class="flex items-center space-x-1 items-center justify-center text-2xl font-semibold mb-2">
<%= svg_icon('circle_check', class: 'text-green-600') %> <%= svg_icon('circle_check', class: 'text-green-600') %>
<span> <span>
Signature Uploaded <%= t('signature_uploaded') %>
</span> </span>
</div> </div>
<div> <div>
Return back to your desktop device to complete the form or <a class="link" href="<%= submit_form_path(params[:slug]) %>">continue on mobile</a> <%= t('return_back_to_your_desktop_device_to_complete_the_form_or_continue_on_mobile_html', link: submit_form_path(params[:slug])) %>
</div> </div>
</div> </div>
</draw-signature> </draw-signature>

@ -1,10 +1,10 @@
<%= render 'shared/turbo_modal', title: 'Rename Folder' do %> <%= render 'shared/turbo_modal', title: t('rename_folder') do %>
<%= form_for @template_folder, url: folder_path(@template_folder), data: { turbo_frame: :_top }, html: { autocomplete: :off } do |f| %> <%= form_for @template_folder, url: folder_path(@template_folder), data: { turbo_frame: :_top }, html: { autocomplete: :off } do |f| %>
<div class="form-control my-6"> <div class="form-control my-6">
<%= f.text_field :name, required: true, placeholder: 'Folder Name...', class: 'base-input w-full', autofocus: true, dir: 'auto' %> <%= f.text_field :name, required: true, placeholder: "#{t('folder_name')}...", class: 'base-input w-full', autofocus: true, dir: 'auto' %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.button button_title(title: 'Rename', disabled_with: 'Saving'), class: 'base-button' %> <%= f.button button_title(title: t('rename'), disabled_with: t('saving')), class: 'base-button' %>
</div> </div>
<% end %> <% end %>
<% end %> <% end %>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save