mirror of https://github.com/docusealco/docuseal
				
				
				
			Merge c97c5879e9 into ef05f030de
	
		
	
				
					
				
			
						commit
						d1462f87f0
					
				| @ -0,0 +1,21 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class SubmitFormTakePhotoController < ApplicationController | ||||||
|  |   layout false | ||||||
|  | 
 | ||||||
|  |   around_action :with_browser_locale, only: %i[show] | ||||||
|  |   skip_before_action :authenticate_user! | ||||||
|  |   skip_authorization_check | ||||||
|  | 
 | ||||||
|  |   def show | ||||||
|  |     @submitter = Submitter.find_by!(slug: params[:slug]) | ||||||
|  | 
 | ||||||
|  |     return redirect_to submit_form_completed_path(@submitter.slug) if @submitter.completed_at? | ||||||
|  | 
 | ||||||
|  |     if @submitter.submission.template.archived_at? || @submitter.submission.archived_at? | ||||||
|  |       return redirect_to submit_form_path(@submitter.slug) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     render :show | ||||||
|  |   end | ||||||
|  | end | ||||||
| @ -0,0 +1,142 @@ | |||||||
|  | window.customElements.define('file-photo', class extends HTMLElement { | ||||||
|  |   connectedCallback () { | ||||||
|  | 
 | ||||||
|  |     this.clearButton.addEventListener('click', (e) => { | ||||||
|  |       e.preventDefault() | ||||||
|  |       this.valueInput.value = null | ||||||
|  |       this.inputFile.click() | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     this.inputFile.addEventListener('change', (e) => { | ||||||
|  |       e.preventDefault() | ||||||
|  |       this.updateSubmitButtonVisibility() | ||||||
|  |       this.uploadFiles(this.inputFile.files) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     this.form.addEventListener('submit', (e) => { | ||||||
|  |       e.preventDefault(); | ||||||
|  |       this.submitButton.disabled = true | ||||||
|  |       fetch(this.form.action, { | ||||||
|  |         method: 'PUT', | ||||||
|  |         body: new FormData(this.form) | ||||||
|  |       }).then((response) => { | ||||||
|  |         this.form.classList.add('hidden') | ||||||
|  |         this.success.classList.remove('hidden') | ||||||
|  |         return response | ||||||
|  |       }).finally(() => { | ||||||
|  |         this.submitButton.disabled = false | ||||||
|  |       }) | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   toggleLoading = (e) => { | ||||||
|  |     this.updateSubmitButtonVisibility() | ||||||
|  |     if (e && e.target && !e.target.contains(this)) { | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |     this.loading.classList.toggle('hidden') | ||||||
|  |     this.icon.classList.toggle('hidden') | ||||||
|  |     this.classList.toggle('opacity-50') | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async uploadFiles (files) { | ||||||
|  |     this.toggleLoading() | ||||||
|  |     return await Promise.all( | ||||||
|  |         Array.from(files).map(async (file) => { | ||||||
|  |           const formData = new FormData() | ||||||
|  |           if (file.type === 'image/bmp') { | ||||||
|  |             file = await this.convertBmpToPng(file) | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           formData.append('file', file) | ||||||
|  |           formData.append('submitter_slug', this.dataset.slug) | ||||||
|  |           formData.append('name', 'attachments') | ||||||
|  | 
 | ||||||
|  |           return fetch('/api/attachments', { | ||||||
|  |             method: 'POST', | ||||||
|  |             body: formData | ||||||
|  |           }).then(resp => resp.json()).then((data) => { | ||||||
|  |             return data | ||||||
|  |           }) | ||||||
|  |         })).then((result) => { | ||||||
|  |         this.valueInput.value = result[0].uuid | ||||||
|  |         return result[0] | ||||||
|  |     }).finally(() => { | ||||||
|  |       this.toggleLoading() | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   convertBmpToPng (bmpFile) { | ||||||
|  |     return new Promise((resolve, reject) => { | ||||||
|  |       const reader = new FileReader() | ||||||
|  | 
 | ||||||
|  |       reader.onload = function (event) { | ||||||
|  |         const img = new Image() | ||||||
|  | 
 | ||||||
|  |         img.onload = function () { | ||||||
|  |           const canvas = document.createElement('canvas') | ||||||
|  |           const ctx = canvas.getContext('2d') | ||||||
|  | 
 | ||||||
|  |           canvas.width = img.width | ||||||
|  |           canvas.height = img.height | ||||||
|  |           ctx.drawImage(img, 0, 0) | ||||||
|  |           canvas.toBlob(function (blob) { | ||||||
|  |             const newFile = new File([blob], bmpFile.name.replace(/\.\w+$/, '.png'), { type: 'image/png' }) | ||||||
|  |             resolve(newFile) | ||||||
|  |           }, 'image/png') | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         img.src = event.target.result | ||||||
|  |       } | ||||||
|  |       reader.onerror = reject | ||||||
|  |       reader.readAsDataURL(bmpFile) | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   updateSubmitButtonVisibility () { | ||||||
|  |     if (!this.valueInput.value) { | ||||||
|  |         this.submitButton.style.display = 'none' | ||||||
|  |         this.placeholderButton.style.display = 'block' | ||||||
|  |     } else { | ||||||
|  |         this.submitButton.style.display = 'block' | ||||||
|  |         this.placeholderButton.style.display = 'none' | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get submitButton () { | ||||||
|  |     return this.querySelector('button[type="submit"]') | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get clearButton () { | ||||||
|  |     return this.querySelector('button[aria-label="Clear"]') | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get placeholderButton () { | ||||||
|  |     return this.querySelector('button[disabled]') | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get valueInput () { | ||||||
|  |     return this.querySelector('input[name^="values"]') | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get inputFile () { | ||||||
|  |     return this.querySelector('input[id="file"]') | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get icon () { | ||||||
|  |     return this.querySelector('#file-photo-icon') | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get loading () { | ||||||
|  |     return this.querySelector('#file-photo-loading') | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get form () { | ||||||
|  |     return this.querySelector('form') | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get success () { | ||||||
|  |     return this.querySelector('#success') | ||||||
|  |   } | ||||||
|  | }) | ||||||
| After Width: | Height: | Size: 446 B | 
| @ -0,0 +1,67 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html data-theme="docuseal" lang="en"> | ||||||
|  |   <head> | ||||||
|  |     <%= render 'layouts/head_tags' %> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|  |     <%= csrf_meta_tags %> | ||||||
|  |     <%= csp_meta_tag %> | ||||||
|  |     <% if ENV['ROLLBAR_CLIENT_TOKEN'] %> | ||||||
|  |       <meta name="rollbar-token" content="<%= ENV.fetch('ROLLBAR_CLIENT_TOKEN', nil) %>"> | ||||||
|  |       <%= javascript_pack_tag 'rollbar', 'photo', defer: true %> | ||||||
|  |     <% else %> | ||||||
|  |       <%= javascript_pack_tag 'photo', defer: true %> | ||||||
|  |     <% end %> | ||||||
|  |     <%= stylesheet_pack_tag 'form', media: 'all' %> | ||||||
|  |     <%= render 'shared/posthog' if ENV['POSTHOG_TOKEN'] %> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <% field = (@submitter.submission.template_fields || @submitter.template.fields).find { |f| f['type'] == 'image' && f['uuid'].starts_with?(params[:f]) } %> | ||||||
|  |     <file-photo data-slug="<%= params[:slug] %>" class="flex items-center h-screen p-2 justify-center"> | ||||||
|  |       <%= 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[<%= field['uuid'] %>]"> | ||||||
|  |         <% if field['description'] %> | ||||||
|  |           <div class="w-full mb-2"> | ||||||
|  |             <%= field['description'] %> | ||||||
|  |           </div> | ||||||
|  |         <% end %> | ||||||
|  |         <label for="file" class="w-full block h-32 relative bg-base-200 hover:bg-base-200/70 rounded-md border border-base-content border-dashed"> | ||||||
|  |             <div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center p-2"> | ||||||
|  |               <div class="flex flex-col items-center text-center"> | ||||||
|  |               <span id="file-photo-icon"> | ||||||
|  |                 <%= svg_icon('camera', class: 'w-10 h-10') %> | ||||||
|  |               </span> | ||||||
|  |                 <span id="file-photo-loading" class="hidden"> | ||||||
|  |                 <%= svg_icon('loader', class: 'w-10 h-10 animate-spin') %> | ||||||
|  |               </span> | ||||||
|  |                 <div class="font-medium mb-1"> | ||||||
|  |                   <%= t('take_photo') %> | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |               <button aria-label="Clear" class="hidden btn btn-ghost btn-sm font-medium top-0 right-0 absolute mt-1 mr-1"> | ||||||
|  |                 <%= svg_icon('reload', class: 'w-5 h-5') %> | ||||||
|  |                 <span class="inline"><%= t('clear') %></span> | ||||||
|  |               </button> | ||||||
|  |               <input id="file" class="hidden" name="files[]" type="file" accept="image/*" capture="camera"> | ||||||
|  |             </div> | ||||||
|  |           </label> | ||||||
|  |         <div class="mt-4"> | ||||||
|  |           <button disabled class="base-button w-full"> | ||||||
|  |             <%= t('submit') %> | ||||||
|  |           </button> | ||||||
|  |           <%= f.button button_title(title: t('submit')), class: 'base-button w-full', style: 'display: none' %> | ||||||
|  |         </div> | ||||||
|  |       <% end %> | ||||||
|  |       <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"> | ||||||
|  |           <%= svg_icon('circle_check', class: 'text-green-600') %> | ||||||
|  |           <span> | ||||||
|  |             <%= t('photo_uploaded') %> | ||||||
|  |           </span> | ||||||
|  |         </div> | ||||||
|  |         <div> | ||||||
|  |           <%= t('return_back_to_your_desktop_device_to_complete_the_form_or_continue_on_mobile_html', link: submit_form_path(params[:slug])) %> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </file-photo> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
					Loading…
					
					
				
		Reference in new issue