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