diff --git a/.rubocop.yml b/.rubocop.yml index e40ff7c2..aad5509e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -42,6 +42,12 @@ Metrics/CyclomaticComplexity: Metrics/PerceivedComplexity: Max: 15 +Style/MultipleComparison: + Enabled: false + +Naming/PredicateMethod: + Enabled: false + Layout/LineLength: AllowedPatterns: ['\A\s*#'] diff --git a/Gemfile.lock b/Gemfile.lock index b3300f1f..84d527e6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -76,7 +76,7 @@ GEM public_suffix (>= 2.0.2, < 7.0) annotaterb (4.14.0) arabic-letter-connector (0.1.1) - ast (2.4.2) + ast (2.4.3) aws-eventstream (1.3.0) aws-partitions (1.1027.0) aws-sdk-core (3.214.0) @@ -291,10 +291,10 @@ GEM rdoc (>= 4.0.0) reline (>= 0.4.2) jmespath (1.6.2) - json (2.13.2) + json (2.15.0) jwt (2.9.3) base64 - language_server-protocol (3.17.0.3) + language_server-protocol (3.17.0.5) launchy (3.0.1) addressable (~> 2.8) childprocess (~> 5.0) @@ -305,6 +305,7 @@ GEM letter_opener (~> 1.9) railties (>= 6.1) rexml + lint_roller (1.1.0) logger (1.7.0) lograge (0.14.0) actionpack (>= 4) @@ -365,8 +366,8 @@ GEM ostruct (0.6.3) package_json (0.1.0) pagy (9.3.3) - parallel (1.26.3) - parser (3.3.6.0) + parallel (1.27.0) + parser (3.3.9.0) ast (~> 2.4.1) racc pg (1.5.9) @@ -383,6 +384,7 @@ GEM pretender (0.5.0) actionpack (>= 6.1) prettyprint (0.2.0) + prism (1.5.1) pry (0.15.0) coderay (~> 1.1) method_source (~> 1.0) @@ -395,7 +397,7 @@ GEM puma (6.5.0) nio4r (~> 2.0) racc (1.8.1) - rack (3.2.0) + rack (3.2.3) rack-proxy (0.7.7) rack rack-session (2.1.1) @@ -448,7 +450,7 @@ GEM psych (>= 4.0.0) redis-client (0.23.0) connection_pool - regexp_parser (2.9.3) + regexp_parser (2.11.3) reline (0.6.2) io-console (~> 0.5) representable (3.2.0) @@ -485,18 +487,20 @@ GEM rspec-mocks (~> 3.13) rspec-support (~> 3.13) rspec-support (3.13.2) - rubocop (1.69.2) + rubocop (1.81.1) json (~> 2.3) - language_server-protocol (>= 3.17.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.36.2, < 2.0) + rubocop-ast (>= 1.47.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.37.0) - parser (>= 3.3.1.0) + rubocop-ast (1.47.1) + parser (>= 3.3.7.2) + prism (~> 1.4) rubocop-performance (1.23.0) rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) @@ -567,9 +571,9 @@ GEM tzinfo-data (1.2024.2) tzinfo (>= 1.0.0) uber (0.1.0) - unicode-display_width (3.1.2) - unicode-emoji (~> 4.0, >= 4.0.4) - unicode-emoji (4.0.4) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.1.0) uniform_notifier (1.16.0) uri (1.0.3) useragent (0.16.11) diff --git a/README.md b/README.md index 50b20b12..9753da2a 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ DocuSeal is an open source platform that provides secure and efficient digital d - PDF signature verification - Users management - Mobile-optimized -- 6 UI languages with signing available in 14 languages +- 7 UI languages with signing available in 14 languages - API and Webhooks for integrations - Easy to deploy in minutes diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 6409946e..7a6587a5 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -8,7 +8,8 @@ class AccountsController < ApplicationController 'es-ES' => 'Español', 'pt-PT' => 'Português', 'de-DE' => 'Deutsch', - 'it-IT' => 'Italiano' + 'it-IT' => 'Italiano', + 'nl-NL' => 'Nederlands' }.freeze before_action :load_account diff --git a/app/controllers/api/submissions_controller.rb b/app/controllers/api/submissions_controller.rb index 8388f704..15223276 100644 --- a/app/controllers/api/submissions_controller.rb +++ b/app/controllers/api/submissions_controller.rb @@ -147,7 +147,7 @@ module Api is_send_email = !params[:send_email].in?(['false', false]) if (emails = (params[:emails] || params[:email]).presence) && - (params[:submission].blank? && params[:submitters].blank?) + params[:submission].blank? && params[:submitters].blank? Submissions.create_from_emails(template:, user: current_user, source: :api, diff --git a/app/controllers/templates_controller.rb b/app/controllers/templates_controller.rb index d86a5ad8..c96360d9 100644 --- a/app/controllers/templates_controller.rb +++ b/app/controllers/templates_controller.rb @@ -118,7 +118,8 @@ class TemplatesController < ApplicationController def template_params params.require(:template).permit( :name, - { schema: [[:attachment_uuid, :name, { conditions: [%i[field_uuid value action operation]] }]], + { schema: [[:attachment_uuid, :google_drive_file_id, :name, + { conditions: [%i[field_uuid value action operation]] }]], submitters: [%i[name uuid is_requester linked_to_uuid invite_by_uuid optional_invite_by_uuid email order]], fields: [[:uuid, :submitter_uuid, :name, :type, :required, :readonly, :default_value, diff --git a/app/javascript/application.js b/app/javascript/application.js index a9fb02d0..31e28407 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -49,6 +49,8 @@ import ShowOnValue from './elements/show_on_value' import CustomValidation from './elements/custom_validation' import ToggleClasses from './elements/toggle_classes' import AutosizeField from './elements/autosize_field' +import GoogleDriveFilePicker from './elements/google_drive_file_picker' +import OpenModal from './elements/open_modal' import * as TurboInstantClick from './lib/turbo_instant_click' @@ -136,6 +138,8 @@ safeRegisterElement('show-on-value', ShowOnValue) safeRegisterElement('custom-validation', CustomValidation) safeRegisterElement('toggle-classes', ToggleClasses) safeRegisterElement('autosize-field', AutosizeField) +safeRegisterElement('google-drive-file-picker', GoogleDriveFilePicker) +safeRegisterElement('open-modal', OpenModal) safeRegisterElement('template-builder', class extends HTMLElement { connectedCallback () { @@ -160,6 +164,7 @@ safeRegisterElement('template-builder', class extends HTMLElement { withSendButton: this.dataset.withSendButton !== 'false', withSignYourselfButton: this.dataset.withSignYourselfButton !== 'false', withConditions: this.dataset.withConditions === 'true', + withGoogleDrive: this.dataset.withGoogleDrive === 'true', withReplaceAndCloneUpload: true, currencies: (this.dataset.currencies || '').split(',').filter(Boolean), acceptFileTypes: this.dataset.acceptFileTypes, diff --git a/app/javascript/elements/google_drive_file_picker.js b/app/javascript/elements/google_drive_file_picker.js new file mode 100644 index 00000000..9aae712d --- /dev/null +++ b/app/javascript/elements/google_drive_file_picker.js @@ -0,0 +1,55 @@ +export default class extends HTMLElement { + connectedCallback () { + const iframeTemplate = this.querySelector('template') + + this.observer = new IntersectionObserver((entries) => { + if (entries.some(e => e.isIntersecting)) { + iframeTemplate.parentElement.prepend(iframeTemplate.content) + + this.observer.disconnect() + } + }) + + this.observer.observe(this) + + window.addEventListener('message', this.messageHandler) + } + + messageHandler = (event) => { + if (event.data.type === 'google-drive-files-picked') { + this.form.querySelectorAll('input[name="google_drive_file_ids[]"]').forEach(el => el.remove()) + + const files = event.data.files || [] + + files.forEach((file) => { + const input = document.createElement('input') + input.type = 'hidden' + input.name = 'google_drive_file_ids[]' + input.value = file.id + this.form.appendChild(input) + }) + + this.form.querySelector('button[type="submit"]').click() + this.loader.classList.remove('hidden') + } else if (event.data.type === 'google-drive-picker-loaded') { + this.loader.classList.add('hidden') + this.form.classList.remove('hidden') + } else if (event.data.type === 'google-drive-picker-request-oauth') { + document.getElementById(this.dataset.oauthButtonId).classList.remove('hidden') + this.classList.add('hidden') + } + } + + disconnectedCallback () { + this.observer?.unobserve(this) + window.removeEventListener('message', this.messageHandler) + } + + get form () { + return this.querySelector('form') + } + + get loader () { + return document.getElementById('google_drive_loader') + } +} diff --git a/app/javascript/elements/open_modal.js b/app/javascript/elements/open_modal.js new file mode 100644 index 00000000..b7b30787 --- /dev/null +++ b/app/javascript/elements/open_modal.js @@ -0,0 +1,16 @@ +export default class extends HTMLElement { + connectedCallback () { + const src = this.getAttribute('src') + const link = document.createElement('a') + + link.href = src + link.setAttribute('data-turbo-frame', 'modal') + link.style.display = 'none' + + this.appendChild(link) + + link.click() + + window.history.replaceState({}, document.title, window.location.pathname) + } +} diff --git a/app/javascript/submission_form/i18n.js b/app/javascript/submission_form/i18n.js index 41af4c7a..b5f02eec 100644 --- a/app/javascript/submission_form/i18n.js +++ b/app/javascript/submission_form/i18n.js @@ -91,7 +91,7 @@ const en = { type_text: 'Type text', email_has_been_sent: 'Email has been sent', processing: 'Processing', - pay_with_strip: 'Pay with Stripe', + pay_with_stripe: 'Pay with Stripe', reupload: 'Reupload', upload: 'Upload', files: 'Files', @@ -192,7 +192,7 @@ const es = { type_text: 'Escribir texto', email_has_been_sent: 'El correo electrónico ha sido enviado', processing: 'Procesando', - pay_with_strip: 'Pagar con Stripe', + pay_with_stripe: 'Pagar con Stripe', reupload: 'Volver a subir', upload: 'Subir', files: 'Archivos', @@ -222,7 +222,7 @@ const it = { value_is_invalid: 'Il valore non è valido', verification_code_is_invalid: 'Il codice di verifica non è valido', drawn_signature_on_a_touchscreen_device: 'Firma disegnata su un dispositivo con schermo tattile', - scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: 'Scansiona il codice QR con l\'app della fotocamera per aprire il modulo sul cellulare e disegnare la tua firma', + scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: "Scansiona il codice QR con l'app della fotocamera per aprire il modulo sul cellulare e disegnare la tua firma", by_clicking_you_agree_to_the: 'Cliccando su "{button}", accetti il', electronic_signature_disclosure: 'Divulgazione della Firma Elettronica', esignature_disclosure: 'Divulgazione della eFirma', @@ -293,7 +293,7 @@ const it = { toggle_multiline_text: 'Attiva Testo Multilinea', email_has_been_sent: "L'email è stata inviata", processing: 'Elaborazione', - pay_with_strip: 'Paga con Stripe', + pay_with_stripe: 'Paga con Stripe', reupload: 'Ricarica', upload: 'Carica', files: 'File', @@ -304,64 +304,64 @@ const it = { const de = { please_upload_an_image_file: 'Bitte laden Sie eine Bilddatei hoch', must_be_characters_length: 'Muss {number} Zeichen lang sein', - complete_all_required_fields_to_proceed_with_identity_verification: 'Vervollständigen Sie alle erforderlichen Felder, um mit der Identitätsverifizierung fortzufahren.', + complete_all_required_fields_to_proceed_with_identity_verification: 'Füllen Sie alle Pflichtfelder aus, um mit der Identitätsprüfung fortzufahren.', verify_id: 'ID überprüfen', - identity_verification: 'Identitätsüberprüfung', + identity_verification: 'Identitätsprüfung', complete: 'Abschließen', - fill_all_required_fields_to_complete: 'Alle erforderlichen Felder ausfüllen, um abzuschließen', - sign_and_complete: 'Signieren und Abschließen', + fill_all_required_fields_to_complete: 'Füllen Sie alle Pflichtfelder aus, um abzuschließen', + sign_and_complete: 'Unterzeichnen und abschließen', + text: 'Text', + by_clicking_you_agree_to_the: 'Durch Klicken auf "{button}" stimmen Sie dem', + electronic_signature_disclosure: 'Hinweis zur E-Signatur', + esignature_disclosure: 'Hinweis zur eSignatur', + signature: 'Unterschrift', + initials: 'Initialen', + drawn_signature_on_a_touchscreen_device: 'Auf einem Touchscreen-Gerät gezeichnete Unterschrift', + approved: 'Genehmigt', + reviewed: 'Geprüft', + other: 'Sonstiges', + authored_by_me: 'Von mir erstellt', invite: 'Einladen', email: 'E-Mail', - approved: 'Genehmigt', - reviewed: 'Überprüft', - other: 'Andere', - authored_by_me: 'Von mir verfasst', approved_by: 'Genehmigt von', - reviewed_by: 'Überprüft von', - authored_by: 'Verfasst von', + reviewed_by: 'Geprüft von', + authored_by: 'Erstellt von', select_a_reason: 'Grund auswählen', + scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: 'Scannen Sie den QR-Code mit der Kamera-App, um das Formular auf dem Mobilgerät zu öffnen und Ihre Unterschrift zu zeichnen', + date: 'Datum', + number: 'Zahl', value_is_invalid: 'Wert ist ungültig', - verification_code_is_invalid: 'Bestätigungscode ist ungültig', - drawn_signature_on_a_touchscreen_device: 'Gezeichnete Unterschrift auf einem Touchscreen-Gerät', - scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: 'Scannen Sie den QR-Code mit der Kamera-App, um das Formular auf dem Handy zu öffnen und Ihre Unterschrift zu zeichnen', - by_clicking_you_agree_to_the: 'Durch Klicken auf "{button}" stimmen Sie zu, dass Sie die', - electronic_signature_disclosure: 'Elektronische Unterschriftenoffenlegung', - esignature_disclosure: 'eSignatur Offenlegung', + verification_code_is_invalid: 'Verifizierungscode ist ungültig', already_paid: 'Bereits bezahlt', - minimize: 'Minimieren', - text: 'Text', - signature: 'Unterschrift', - digitally_signed_by: 'Digital unterschrieben von', - reason: 'Grund', - initials: 'Initialen', - date: 'Datum', - number: 'Nummer', image: 'Bild', pay: 'Bezahlen', + take_photo: 'Foto aufnehmen', + number_phone_is_invalid: 'Telefonnummer ({number}) ist ungültig', file: 'Datei', - select: 'Auswählen', + digitally_signed_by: 'Elektronisch signiert von', + reason: 'Grund', + select: 'Auswahl', checkbox: 'Checkbox', - multiple: 'Mehrere', + multiple: 'Mehrfach', radio: 'Radio', cells: 'Zellen', stamp: 'Stempel', + minimize: 'Minimieren', payment: 'Zahlung', - number_phone_is_invalid: '{number} Telefonnummer ist ungültig', phone: 'Telefon', start_now: 'Jetzt starten', - continue: 'Fortsetzen', - sign_now: 'Jetzt unterschreiben', + continue: 'Weiter', + sign_now: 'Jetzt unterzeichnen', type_here_: 'Hier eingeben...', optional: 'optional', option: 'Option', appears_on: 'Erscheint auf', page: 'Seite', - take_photo: 'Foto aufnehmen', - select_your_option: 'Wähle deine Option', - complete_hightlighted_checkboxes_and_click: 'Markierte Kontrollkästchen ausfüllen und klicken', - submit: 'absenden', - next: 'weiter', - click_to_upload: 'Klicken zum Hochladen', + select_your_option: 'Option auswählen', + complete_hightlighted_checkboxes_and_click: 'Füllen Sie die markierten Kontrollkästchen aus und klicken Sie auf', + submit: 'Einreichen', + next: 'Weiter', + click_to_upload: 'Zum Hochladen klicken', or_drag_and_drop_files: 'oder Dateien hierher ziehen und ablegen', send_copy_via_email: 'Kopie per E-Mail senden', download: 'Herunterladen', @@ -370,137 +370,137 @@ const de = { draw_initials: 'Initialen zeichnen', type_signature_here: 'Unterschrift hier eingeben', type_initial_here: 'Initialen hier eingeben', - form_has_been_completed: 'Formular wurde ausgefüllt!', - document_has_been_signed: 'Dokument wurde unterschrieben!', - documents_have_been_signed: 'Dokumente wurden unterschrieben!', + form_has_been_completed: 'Formular wurde abgeschlossen!', + document_has_been_signed: 'Dokument wurde unterzeichnet!', + documents_have_been_signed: 'Dokumente wurden unterzeichnet!', create_a_free_account: 'Kostenloses Konto erstellen', powered_by: 'Bereitgestellt von', - please_check_the_box_to_continue: 'Bitte setzen Sie das Häkchen, um fortzufahren.', + please_check_the_box_to_continue: 'Bitte aktivieren Sie das Kontrollkästchen, um fortzufahren.', open_source_documents_software: 'Open-Source-Dokumentensoftware', - verified_phone_number: 'Telefonnummer überprüfen', + verified_phone_number: 'Telefonnummer verifizieren', use_international_format: 'Internationales Format verwenden: +1xxx', six_digits_code: '6-stelliger Code', change_phone_number: 'Telefonnummer ändern', sending: 'Senden...', resend_code: 'Code erneut senden', - verification_code_has_been_resent: 'Die Verifizierungscode wurde erneut per SMS gesendet', - please_fill_all_required_fields: 'Bitte füllen Sie alle erforderlichen Felder aus', - set_today: 'Heute einstellen', + verification_code_has_been_resent: 'Der Verifizierungscode wurde per SMS erneut gesendet', + please_fill_all_required_fields: 'Bitte füllen Sie alle Pflichtfelder aus', + set_today: 'Heute', + toggle_multiline_text: 'Mehrzeiligen Text umschalten', draw_signature: 'Unterschrift zeichnen', type_initial: 'Initialen eingeben', draw: 'Zeichnen', type: 'Eingeben', type_text: 'Text eingeben', - toggle_multiline_text: 'Mehrzeiligen Text umschalten', - email_has_been_sent: 'Die E-Mail wurde gesendet', + email_has_been_sent: 'E-Mail wurde gesendet', processing: 'Verarbeitung', - pay_with_strip: 'Mit Stripe bezahlen', + pay_with_stripe: 'Mit Stripe bezahlen', reupload: 'Erneut hochladen', upload: 'Hochladen', files: 'Dateien', - signature_is_too_small_or_simple_please_redraw: 'Die Unterschrift ist zu klein oder zu einfach. Bitte erneut zeichnen.', - wait_countdown_seconds: 'Warte {countdown} Sekunden' + signature_is_too_small_or_simple_please_redraw: 'Die Unterschrift ist zu klein oder zu einfach. Bitte neu zeichnen.', + wait_countdown_seconds: 'Bitte {countdown} Sekunden warten' } const fr = { - please_upload_an_image_file: 'Veuillez télécharger un fichier image', - must_be_characters_length: 'Doit contenir {number} caractères', - complete_all_required_fields_to_proceed_with_identity_verification: "Veuillez remplir tous les champs obligatoires pour continuer la vérification de l'identité.", - verify_id: "Vérification de l'ID", - identity_verification: "Vérification de l'identité", + please_upload_an_image_file: 'Veuillez téléverser un fichier image', + must_be_characters_length: 'Doit comporter {number} caractères', + complete_all_required_fields_to_proceed_with_identity_verification: "Veuillez remplir tous les champs obligatoires pour poursuivre la vérification d'identité.", + verify_id: "Vérifier l'ID", + identity_verification: "Vérification d'identité", complete: 'Terminer', - fill_all_required_fields_to_complete: 'Veuillez remplir tous les champs obligatoires pour compléter', - sign_and_complete: 'Signer et Terminer', - invite: 'Inviter', - email: 'Courriel', + fill_all_required_fields_to_complete: 'Remplissez tous les champs obligatoires pour terminer', + sign_and_complete: 'Signer et terminer', + text: 'Texte', + by_clicking_you_agree_to_the: 'En cliquant sur "{button}", vous acceptez la', + electronic_signature_disclosure: 'Déclaration relative à la signature électronique', + esignature_disclosure: 'Déclaration eSignature', + signature: 'Signature', + initials: 'Initiales', + drawn_signature_on_a_touchscreen_device: 'Signature dessinée sur un appareil à écran tactile', approved: 'Approuvé', reviewed: 'Révisé', other: 'Autre', authored_by_me: 'Rédigé par moi', + invite: 'Inviter', + email: 'E-mail', approved_by: 'Approuvé par', reviewed_by: 'Révisé par', authored_by: 'Rédigé par', select_a_reason: 'Sélectionnez une raison', + scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: 'Scannez le QR avec l’appareil photo pour ouvrir le formulaire et signer sur mobile', + date: 'Date', + number: 'Nombre', value_is_invalid: 'La valeur est invalide', verification_code_is_invalid: 'Le code de vérification est invalide', - drawn_signature_on_a_touchscreen_device: 'Signature dessinée sur un appareil à écran tactile', - scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: 'Scannez le code QR avec l\'application de l\'appareil photo pour ouvrir le formulaire sur mobile et dessiner votre signature', - by_clicking_you_agree_to_the: 'En cliquant sur "{button}", vous acceptez la', - electronic_signature_disclosure: 'Divulgation de Signature Électronique', - esignature_disclosure: 'Divulgation de la eSignature', - minimize: 'Réduire', - text: 'Texte', already_paid: 'Déjà payé', - signature: 'Signature', - initials: 'Initiales', - digitally_signed_by: 'Signé numériquement par', - reason: 'Raison', - pay: 'Payer', - date: 'Date', - number: 'Numéro', image: 'Image', + pay: 'Payer', + take_photo: 'Prendre une photo', + number_phone_is_invalid: "Le numéro de téléphone {number} n'est pas valide", file: 'Fichier', - select: 'Choisir', + digitally_signed_by: 'Signé électroniquement par', + reason: 'Motif', + select: 'Sélectionner', checkbox: 'Coche', multiple: 'Multiple', radio: 'Radio', cells: 'Cellules', stamp: 'Tampon', + minimize: 'Réduire', payment: 'Paiement', - number_phone_is_invalid: '{number} téléphone est invalide', phone: 'Téléphone', start_now: 'Commencer maintenant', continue: 'Continuer', sign_now: 'Signer maintenant', - type_here_: 'Tapez ici...', + type_here_: 'Saisissez ici...', optional: 'facultatif', option: 'Option', appears_on: 'Apparaît sur', page: 'Page', - take_photo: 'Prendre une photo', select_your_option: 'Sélectionnez votre option', - complete_hightlighted_checkboxes_and_click: 'Complétez les cases à cocher en surbrillance et cliquez', - submit: 'envoyer', + complete_hightlighted_checkboxes_and_click: 'Cochez les cases mises en évidence puis cliquez', + submit: 'soumettre', next: 'suivant', - click_to_upload: 'Cliquez pour télécharger', - or_drag_and_drop_files: 'ou faites glisser-déposer les fichiers', + click_to_upload: 'Cliquez pour téléverser', + or_drag_and_drop_files: 'ou faites glisser et déposez des fichiers', send_copy_via_email: 'Envoyer une copie par e-mail', download: 'Télécharger', clear: 'Effacer', redraw: 'Redessiner', draw_initials: 'Dessiner les initiales', - type_signature_here: 'Tapez la signature ici', - type_initial_here: 'Tapez les initiales ici', - form_has_been_completed: 'Le formulaire a été complété !', - document_has_been_signed: 'Le document a été signé!', - documents_have_been_signed: 'Les documents ont été signés!', - create_a_free_account: 'Créer un Compte Gratuit', + type_signature_here: 'Saisissez la signature ici', + type_initial_here: 'Saisissez les initiales ici', + form_has_been_completed: 'Le formulaire a été rempli !', + document_has_been_signed: 'Le document a été signé !', + documents_have_been_signed: 'Les documents ont été signés !', + create_a_free_account: 'Créer un compte gratuit', powered_by: 'Propulsé par', please_check_the_box_to_continue: 'Veuillez cocher la case pour continuer.', open_source_documents_software: 'logiciel de documents open source', verified_phone_number: 'Vérifier le numéro de téléphone', - use_international_format: 'Utiliser le format international : +1xxx', + use_international_format: 'Utilisez le format international : +1xxx', six_digits_code: 'Code à 6 chiffres', - change_phone_number: 'Changer le numéro de téléphone', - sending: 'Envoi en cours...', + change_phone_number: 'Changer de numéro de téléphone', + sending: 'Envoi...', resend_code: 'Renvoyer le code', verification_code_has_been_resent: 'Le code de vérification a été renvoyé par SMS', please_fill_all_required_fields: 'Veuillez remplir tous les champs obligatoires', - set_today: "Définir Aujourd'hui", - draw_signature: 'Dessiner une signature', + set_today: "Définir à aujourd'hui", + toggle_multiline_text: 'Basculer le texte multiligne', + draw_signature: 'Dessiner la signature', type_initial: 'Saisir les initiales', draw: 'Dessiner', type: 'Saisir', type_text: 'Saisir du texte', - toggle_multiline_text: 'Basculer le Texte Multiligne', - email_has_been_sent: "L'email a été envoyé", - processing: 'Traitement', - pay_with_strip: 'Paiement avec Stripe', - reupload: 'Recharger', - upload: 'Télécharger', + email_has_been_sent: "L'e-mail a été envoyé", + processing: 'Traitement en cours', + pay_with_stripe: 'Payer avec Stripe', + reupload: 'Retéléverser', + upload: 'Téléverser', files: 'Fichiers', signature_is_too_small_or_simple_please_redraw: 'La signature est trop petite ou trop simple. Veuillez la redessiner.', - wait_countdown_seconds: 'Attendez {countdown} secondes' + wait_countdown_seconds: 'Veuillez patienter {countdown} secondes' } const pl = { @@ -596,7 +596,7 @@ const pl = { toggle_multiline_text: 'Przełącz Tekst Wielolinijkowy', email_has_been_sent: 'E-mail został wysłany', processing: 'Przetwarzanie', - pay_with_strip: 'Płatność za pomocą Stripe', + pay_with_stripe: 'Płatność za pomocą Stripe', reupload: 'Ponowne przesłanie', upload: 'Przesyłanie', files: 'Pliki', @@ -798,7 +798,7 @@ const cs = { toggle_multiline_text: 'Přepnout Víceřádkový Text', email_has_been_sent: 'E-mail byl odeslán', processing: 'Zpracování', - pay_with_strip: 'Zaplacení přes Stripe', + pay_with_stripe: 'Zaplacení přes Stripe', reupload: 'Znovu nahrát', upload: 'Nahrát', files: 'Soubory', @@ -899,7 +899,7 @@ const pt = { toggle_multiline_text: 'Alternar Texto Multilinha', email_has_been_sent: 'Email enviado', processing: 'Processamento', - pay_with_strip: 'Pagar com Stripe', + pay_with_stripe: 'Pagar com Stripe', reupload: 'Reenviar', upload: 'Carregar', files: 'Arquivos', @@ -1000,7 +1000,7 @@ const he = { type_text: 'הקלד טקסט', email_has_been_sent: 'האימייל נשלח', processing: 'מעבד', - pay_with_strip: 'שלם עם סטרייפ', + pay_with_stripe: 'שלם עם סטרייפ', reupload: 'העלה שוב', upload: 'העלאה', files: 'קבצים', @@ -1101,7 +1101,7 @@ const nl = { type_text: 'Typ tekst', email_has_been_sent: 'E-mail is verzonden', processing: 'Verwerken', - pay_with_strip: 'Betalen met Stripe', + pay_with_stripe: 'Betalen met Stripe', reupload: 'Opnieuw uploaden', upload: 'Uploaden', files: 'Bestanden', @@ -1303,7 +1303,7 @@ const ko = { type_text: '텍스트 입력', email_has_been_sent: '이메일이 전송되었습니다', processing: '처리 중', - pay_with_strip: '스트라이프로 결제', + pay_with_stripe: '스트라이프로 결제', reupload: '다시 업로드', upload: '업로드', files: '파일', @@ -1404,7 +1404,7 @@ const ja = { type_text: 'テキストを入力', email_has_been_sent: 'メールが送信されました', processing: '処理中', - pay_with_strip: 'Stripeで支払う', + pay_with_stripe: 'Stripeで支払う', reupload: '再アップロード', upload: 'アップロード', files: 'ファイル', diff --git a/app/javascript/submission_form/payment_step.vue b/app/javascript/submission_form/payment_step.vue index d262d3a6..adf4e19c 100644 --- a/app/javascript/submission_form/payment_step.vue +++ b/app/javascript/submission_form/payment_step.vue @@ -62,7 +62,7 @@ width="22" /> - {{ t('pay_with_strip') }} + {{ t('pay_with_stripe') }} diff --git a/app/javascript/template_builder/builder.vue b/app/javascript/template_builder/builder.vue index c679a37e..07abcba9 100644 --- a/app/javascript/template_builder/builder.vue +++ b/app/javascript/template_builder/builder.vue @@ -260,8 +260,12 @@ :style="{ backgroundColor }" > @@ -297,6 +301,8 @@ v-if="withUploadButton" :template-id="template.id" :accept-file-types="acceptFileTypes" + :with-google-drive="withGoogleDrive" + @click-google-drive="$refs.upload.openGoogleDriveModal()" @success="updateFromUpload" />
import Upload from './upload' -import { IconCloudUpload, IconFilePlus, IconFileSymlink, IconFiles, IconInnerShadowTop } from '@tabler/icons-vue' +import { IconCloudUpload, IconFilePlus, IconFileSymlink, IconFiles, IconInnerShadowTop, IconBrandGoogleDrive } from '@tabler/icons-vue' export default { name: 'FileDropzone', @@ -71,7 +80,8 @@ export default { IconCloudUpload, IconInnerShadowTop, IconFileSymlink, - IconFiles + IconFiles, + IconBrandGoogleDrive }, inject: ['baseFetch', 't'], props: { @@ -99,6 +109,11 @@ export default { required: false, default: true }, + withGoogleDrive: { + type: Boolean, + required: false, + default: false + }, title: { type: String, required: false, @@ -110,7 +125,7 @@ export default { default: 'image/*, application/pdf, application/zip' } }, - emits: ['success', 'error', 'loading'], + emits: ['success', 'error', 'loading', 'click-google-drive'], data () { return { isLoading: false, diff --git a/app/javascript/template_builder/field_submitter.vue b/app/javascript/template_builder/field_submitter.vue index a3151288..b22d3036 100644 --- a/app/javascript/template_builder/field_submitter.vue +++ b/app/javascript/template_builder/field_submitter.vue @@ -52,7 +52,7 @@ > @@ -100,7 +100,7 @@ class="cursor-pointer text-base-100 flex h-full items-center justify-center" >