implement submission page

pull/105/head
Alex Turchyn 2 years ago
parent 587be1bb67
commit 1da67011c4

@ -12,6 +12,8 @@ class SubmissionsController < ApplicationController
Submission.joins(:template).where(template: { account_id: current_account.id }) Submission.joins(:template).where(template: { account_id: current_account.id })
.preload(template: { documents_attachments: { preview_images_attachments: :blob } }) .preload(template: { documents_attachments: { preview_images_attachments: :blob } })
.find(params[:id]) .find(params[:id])
render :show, layout: 'plain'
end end
def new; end def new; end

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class TemplatesController < ApplicationController class TemplatesController < ApplicationController
layout false layout 'plain'
before_action :load_base_template, only: %i[new create] before_action :load_base_template, only: %i[new create]

@ -10,6 +10,7 @@ import MenuActive from './elements/menu_active'
import ClipboardCopy from './elements/clipboard_copy' import ClipboardCopy from './elements/clipboard_copy'
import TemplateBuilder from './template_builder/builder' import TemplateBuilder from './template_builder/builder'
import DynamicList from './elements/dynamic_list' import DynamicList from './elements/dynamic_list'
import DownloadButton from './elements/download_button'
document.addEventListener('turbo:before-cache', () => { document.addEventListener('turbo:before-cache', () => {
window.flash?.remove() window.flash?.remove()
@ -28,6 +29,7 @@ window.customElements.define('file-dropzone', FileDropzone)
window.customElements.define('menu-active', MenuActive) window.customElements.define('menu-active', MenuActive)
window.customElements.define('clipboard-copy', ClipboardCopy) window.customElements.define('clipboard-copy', ClipboardCopy)
window.customElements.define('dynamic-list', DynamicList) window.customElements.define('dynamic-list', DynamicList)
window.customElements.define('download-button', DownloadButton)
window.customElements.define('template-builder', class extends HTMLElement { window.customElements.define('template-builder', class extends HTMLElement {
connectedCallback () { connectedCallback () {

@ -1,21 +1,4 @@
<template> <template>
<template
v-for="field in otherSubmitterFields"
:key="field.uuid"
>
<Teleport
v-for="(area, index) in field.areas"
:key="index"
:to="`#page-${area.attachment_uuid}-${area.page}`"
>
<FieldArea
:model-value="values[field.uuid]"
:field="field"
:area="area"
:attachments-index="attachmentsIndex"
/>
</Teleport>
</template>
<FieldAreas <FieldAreas
ref="areas" ref="areas"
:steps="stepFields" :steps="stepFields"
@ -273,7 +256,6 @@
<script> <script>
import FieldAreas from './areas' import FieldAreas from './areas'
import FieldArea from './area'
import ImageStep from './image_step' import ImageStep from './image_step'
import SignatureStep from './signature_step' import SignatureStep from './signature_step'
import AttachmentStep from './attachment_step' import AttachmentStep from './attachment_step'
@ -285,7 +267,6 @@ export default {
name: 'SubmissionForm', name: 'SubmissionForm',
components: { components: {
FieldAreas, FieldAreas,
FieldArea,
ImageStep, ImageStep,
SignatureStep, SignatureStep,
AttachmentStep, AttachmentStep,
@ -336,14 +317,8 @@ export default {
currentField () { currentField () {
return this.currentStepFields[0] return this.currentStepFields[0]
}, },
currentSubmitterFields () {
return this.fields.filter((f) => f.submitter_uuid === this.submitterUuid)
},
otherSubmitterFields () {
return this.fields.filter((f) => f.submitter_uuid !== this.submitterUuid)
},
stepFields () { stepFields () {
return this.currentSubmitterFields.reduce((acc, f) => { return this.fields.reduce((acc, f) => {
const prevStep = acc[acc.length - 1] const prevStep = acc[acc.length - 1]
if (f.type === 'checkbox' && Array.isArray(prevStep) && prevStep[0].type === 'checkbox') { if (f.type === 'checkbox' && Array.isArray(prevStep) && prevStep[0].type === 'checkbox') {

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M5 12l5 5l10 -10"></path>
</svg>

After

Width:  |  Height:  |  Size: 327 B

@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/> <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M9 5h-2a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-12a2 2 0 0 0 -2 -2h-2" /> <path d="M9 5h-2a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-12a2 2 0 0 0 -2 -2h-2" />
<path d="M9 3m0 2a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v0a2 2 0 0 1 -2 2h-2a2 2 0 0 1 -2 -2z" /> <path d="M9 3m0 2a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v0a2 2 0 0 1 -2 2h-2a2 2 0 0 1 -2 -2z" />

Before

Width:  |  Height:  |  Size: 471 B

After

Width:  |  Height:  |  Size: 470 B

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M3 7a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-10z"></path>
<path d="M3 7l9 6l9 -6"></path>
</svg>

After

Width:  |  Height:  |  Size: 422 B

@ -1,3 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> <svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path fill-rule="evenodd" d="M15.621 4.379a3 3 0 00-4.242 0l-7 7a3 3 0 004.241 4.243h.001l.497-.5a.75.75 0 011.064 1.057l-.498.501-.002.002a4.5 4.5 0 01-6.364-6.364l7-7a4.5 4.5 0 016.368 6.36l-3.455 3.553A2.625 2.625 0 119.52 9.52l3.45-3.451a.75.75 0 111.061 1.06l-3.45 3.451a1.125 1.125 0 001.587 1.595l3.454-3.553a3 3 0 000-4.242z" clip-rule="evenodd" /> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M15 7l-6.5 6.5a1.5 1.5 0 0 0 3 3l6.5 -6.5a3 3 0 0 0 -6 -6l-6.5 6.5a4.5 4.5 0 0 0 9 9l6.5 -6.5"></path>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 504 B

After

Width:  |  Height:  |  Size: 404 B

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M20 17v-12c0 -1.121 -.879 -2 -2 -2s-2 .879 -2 2v12l2 2l2 -2z"></path>
<path d="M16 7h4"></path>
<path d="M18 19h-13a2 2 0 1 1 0 -4h4a2 2 0 1 0 0 -4h-3"></path>
</svg>

After

Width:  |  Height:  |  Size: 467 B

@ -14,19 +14,7 @@
<body> <body>
<turbo-frame id="modal"></turbo-frame> <turbo-frame id="modal"></turbo-frame>
<%= render 'shared/navbar' %> <%= render 'shared/navbar' %>
<% if flash.present? %> <% if flash.present? %><% render 'shared/flash' %><% end %>
<div id="flash" class="absolute top-0 w-full h-0">
<div class="max-w-xl mx-auto mt-1.5">
<div class="alert py-3">
<%= svg_icon('info_circle', class: 'stroke-info flex-shrink-0 w-6 h-6') %>
<div>
<span><%= flash[:notice] || flash[:alert] %></span>
</div>
<a href="#" class="w-6 h-6 rounded-lg text-center hover:bg-base-300" onclick="[event.preventDefault(), window.flash.remove()]">&times;</a>
</div>
</div>
</div>
<% end %>
<div class="max-w-6xl mx-auto px-4 md:px-2"> <div class="max-w-6xl mx-auto px-4 md:px-2">
<%= yield %> <%= yield %>
</div> </div>

@ -0,0 +1,16 @@
<html data-theme="docuseal">
<head>
<title>
Docuseal
</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<%= javascript_pack_tag 'application', defer: true %>
<%= stylesheet_pack_tag 'application', media: 'all' %>
</head>
<body>
<% if flash.present? %><% render 'shared/flash' %><% end %>
<%= yield %>
</body>
</html>

@ -0,0 +1,11 @@
<div id="flash" class="absolute top-0 w-full h-0">
<div class="max-w-xl mx-auto mt-1.5">
<div class="alert py-3">
<%= svg_icon('info_circle', class: 'stroke-info flex-shrink-0 w-6 h-6') %>
<div>
<span><%= flash[:notice] || flash[:alert] %></span>
</div>
<a href="#" class="w-6 h-6 rounded-lg text-center hover:bg-base-300" onclick="[event.preventDefault(), window.flash.remove()]">&times;</a>
</div>
</div>
</div>

@ -0,0 +1,26 @@
<div class="flex absolute text-[1.5vw] lg:text-base" style="width: <%= area['w'] * 100 %>%; height: <%= area['h'] * 100 %>%; left: <%= area['x'] * 100 %>%; top: <%= area['y'] * 100 %>%">
<% if field['type'].in?(['signature', 'image']) %>
<img class="object-contain mx-auto" src="<%= attachments_index[value].url %>" loading="lazy">
<% elsif field['type'] == 'file' %>
<div class="px-0.5 flex flex-col justify-center">
<% Array.wrap(value).each do |val| %>
<a target="_blank" href="<%= attachments_index[val].url %>">
<%= svg_icon('paperclip', class: "inline w-[1.5vw] h-[1.5vw] lg:w-4 lg:h-4") %>
<%= attachments_index[val].filename %>
</a>
<% end %>
</div>
<% elsif field['type'] == 'checkbox' %>
<div class="w-full p-[0.2vw] flex items-center justify-center">
<%= svg_icon('check', class: "aspect-square #{area['w'] > area['h'] ? '!w-auto !h-full' : '!w-full !h-auto'}") %>
</div>
<% elsif field['type'] == 'date' %>
<div class="flex items-center px-0.5">
<%= l(Date.parse(value)) %>
</div>
<% else %>
<div class="flex items-center px-0.5">
<%= Array.wrap(value).join(', ') %>
</div>
<% end %>
</div>

@ -1,73 +1,131 @@
<div class="card card-compact bg-base-200 md:card-normal"> <div style="max-width: 1600px" class="mx-auto pl-4">
<div class="card-body"> <div class="flex justify-between py-1.5 items-center pr-4">
<div> <div class="flex space-x-3">
<h3 class="text-4xl font-bold leading-7 text-gray-900">Submission Information</h3> <a href="/"><%= render 'shared/logo' %></a>
<p class="mt-2 max-w-2xl text-sm leading-6 text-gray-500">Personal details</p> <span class="text-3xl font-semibold focus:text-clip"><%= @submission.template.name %></span>
</div> </div>
<div class="mt-2 border-t border-gray-300"> <div class="space-x-3 flex items-center">
<dl class="divide-y divide-gray-300"> <% if last_submitter = @submission.submitters.select(&:completed_at?).max_by(&:completed_at) %>
<div class="py-6 sm:grid sm:grid-cols-3 sm:gap-4"> <download-button data-src="<%= submitter_download_index_path(last_submitter.slug) %>" class="base-button">
<dt class="text-sm font-medium leading-6 text-gray-900">Template</dt> <span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"> <%= svg_icon('download', class: 'w-6 h-6') %>
<%= link_to @submission.template.name, template_submissions_path(@submission.template), class: 'link link-hover' %> <span>Download</span>
</dd> </span>
</div> <span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
<% @submission.submitters.each do |submitter| %> <%= svg_icon('loader', class: 'w-6 h-6 animate-spin') %>
<div class="py-6 sm:grid sm:grid-cols-3 sm:gap-4"> <span>Downloading</span>
<dt class="text-sm font-medium leading-6 text-gray-900"><%= submitter.email %></dt> </span>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"> </download-button>
<%= submitter.status %> <% end %>
</dd> </div>
</div>
<div class="flex" style="max-height: calc(100vh - 60px)">
<div class="overflow-y-auto overflow-x-hidden w-52 flex-none pr-3 mt-0.5 pt-0.5">
<% @submission.template.documents.each do |document| %>
<a href="#<%= "page-#{document.uuid}-0" %>" onclick="[event.preventDefault(), window[event.target.closest('a').href.split('#')[1]].scrollIntoView({ behavior: 'smooth', block: 'start' })]" class="block cursor-pointer">
<img src="<%= document.preview_images.first.url %>" width="<%= document.preview_images.first.metadata['width'] %>" height="<%= document.preview_images.first.metadata['height'] %>" class="rounded border" loading="lazy" >
<div class="pb-2 pt-1.5 text-center">
<%= @submission.template.schema.find { |e| e['attachment_uuid'] == document.uuid }&.dig('name').presence || attachment.filename.base %>
</div> </div>
</a>
<% end %>
</div>
<div class="w-full overflow-y-auto overflow-x-hidden mt-0.5 pt-0.5">
<div class="pr-3.5 pl-0.5">
<% fields_index = Templates.build_field_areas_index(@submission.template) %>
<% values = @submission.submitters.reduce({}) { |acc, sub| acc.merge(sub.values) } %>
<% attachments_index = ActiveStorage::Attachment.where(record: @submission.submitters, name: :attachments).preload(:blob).index_by(&:uuid) %>
<% @submission.template.schema.each do |item| %>
<% document = @submission.template.documents.find { |e| e.uuid == item['attachment_uuid'] } %>
<% document.preview_images.sort_by { |a| a.filename.base.to_i }.each_with_index do |page, index| %>
<div id="<%= "page-#{document.uuid}-#{index}" %>" class="relative">
<img src="<%= page.url %>" width="<%= page.metadata['width'] %>" class="shadow-md mb-4" height="<%= page.metadata['height'] %>" loading="lazy">
<div class="top-0 bottom-0 left-0 right-0 absolute">
<% fields_index.dig(document.uuid, index)&.each do |(area, field)| %>
<% value = values[field['uuid']] %>
<% next if value.blank? %>
<%= render 'submissions/value', area:, field:, attachments_index:, value: %>
<% end %>
</div>
</div>
<% end %>
<% end %> <% end %>
<% @submission.template.fields.each do |field| %> </div>
<div class="py-6 sm:grid sm:grid-cols-3 sm:gap-4"> </div>
<dt class="text-sm font-medium leading-6 text-gray-900"> <div class="relative w-80 flex-none pt-0.5 pr-4 pl-0.5 overflow-auto space">
<%= field['name'].presence || "[FIELD NAME]" %> <% colors = ['bg-red-500', 'bg-sky-500', 'bg-emerald-500', 'bg-yellow-300', 'bg-purple-600'] %>
</dt> <% submitter_fields_index = @submission.template.fields.group_by { |f| f['submitter_uuid'] } %>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"> <% @submission.template.submitters.each_with_index do |item, index| %>
<% if ['image', 'signature'].include?(field['type']) %> <% submitter = @submission.submitters.find { |e| e.uuid == item['uuid'] } %>
<ul role="list" class="divide-y divide-gray-100 rounded-md border border-gray-200"> <div class="sticky -top-1 bg-base-100 pt-1 -mt-1 z-10">
<% @submission.submitters.each do |submitter| %> <div class="border border-base-300 rounded-md px-2 py-1 mb-1">
<% Array.wrap(submitter.values[field['uuid']]).each do |uuid| %> <div class="flex items-center space-x-1 ">
<li class="py-4 pl-4 pr-5 text-sm leading-6"> <span class="mx-1 w-3 h-3 rounded-full <%= colors[index] %>"></span>
<div class="flex w-0 flex-1 items-center"> <span class="text-lg">
<%= image_tag ActiveStorage::Attachment.find_by(uuid:).url %> <%= @submission.template.submitters.find { |e| e['uuid'] == submitter.uuid }&.dig('name') || "#{(index + 1).ordinalize} Submitter" %>
</div> </span>
</li> </div>
<% end %> <div class="flex items-center space-x-1 mt-1">
<% end %> <%= svg_icon('mail', class: 'w-5 h-5') %>
</ul> <span>
<% elsif ['attachment'].include?(field['type']) %> <%= submitter.email %>
<ul role="list" class="divide-y divide-gray-100 rounded-md border border-gray-200"> </span>
<% @submission.submitters.each do |submitter| %> </div>
<% Array.wrap(submitter.values[field['uuid']]).each do |uuid| %> <div class="flex items-center space-x-1 mt-1">
<% attachment = ActiveStorage::Attachment.find_by(uuid:) %> <%= svg_icon('writing', class: 'w-5 h-5') %>
<li class="flex items-center justify-between py-4 pl-4 pr-5 text-sm leading-6"> <span>
<div class="flex w-0 flex-1 items-center"> <%= submitter.completed_at? ? l(submitter.completed_at, format: :long) : 'Not completed yet' %>
<%= svg_icon('paperclip', class: 'h-5 w-5 flex-shrink-0 text-gray-300') %> </span>
<div class="ml-4 flex min-w-0 flex-1 gap-2"> </div>
<span class="truncate font-medium"> <% unless submitter.completed_at? %>
<%= attachment.filename %> <div class="mt-2 mb-1">
</span> <a class="btn btn-xs btn-primary w-full" target="_blank" href="<%= submit_form_url(slug: submitter.slug) %>">
</div> Submit Form
</div> </a>
<div class="ml-4 flex-shrink-0"> </div>
<%= link_to 'Download', attachment.url, class: "font-medium text-indigo-600 hover:text-indigo-500" %> <% end %>
</div> </div>
</li> </div>
<div class="px-1.5 mb-4">
<% submitter_field_counters = Hash.new { 0 } %>
<% submitter_fields_index[submitter.uuid].each_with_index do |field, index| %>
<% submitter_field_counters[field['type']] += 1 %>
<% value = values[field['uuid']] %>
<% next if value.blank? %>
<div class="pt-2.5 border-b border-base-300">
<div class="text-xs font-medium uppercase mb-0.5">
<%= field['name'].presence || "#{field['type'].titleize} Field #{submitter_field_counters[field['type']]}" %>
</div>
<div>
<% if field['type'] == 'signature' %>
<div class="w-full bg-base-300">
<img class="object-contain mx-auto" height="<%= attachments_index[value].metadata['height'] %>" width="<%= attachments_index[value].metadata['width'] %>" src="<%= attachments_index[value].url %>" loading="lazy">
</div>
<% elsif field['type'] == 'image' %>
<img class="object-contain mx-auto max-h-28" height="<%= attachments_index[value].metadata['height'] %>" width="<%= attachments_index[value].metadata['width'] %>" src="<%= attachments_index[value].url %>" loading="lazy">
<% elsif field['type'] == 'file' %>
<div class="flex flex-col justify-center">
<% Array.wrap(value).each do |val| %>
<a target="_blank" class="flex items-center space-x-1" href="<%= attachments_index[val].url %>">
<%= svg_icon('paperclip', class: "w-4 h-4") %>
<span>
<%= attachments_index[val].filename %>
</span>
</a>
<% end %> <% end %>
<% end %> </div>
</ul> <% elsif field['type'] == 'checkbox' %>
<% else %> <%= svg_icon('check', class: "w-6 h-6") %>
<% @submission.submitters.each do |submitter| %> <% elsif field['type'] == 'date' %>
<%= submitter.values[field['uuid']] %> <%= l(Date.parse(value)) %>
<% else %>
<%= Array.wrap(value).join(', ') %>
<% end %> <% end %>
<% end %> </div>
</dt> </div>
</div> <% end %>
<% end %> </div>
</dl> <% end %>
</div> </div>
</div> </div>
</div> </div>

@ -1,6 +1,6 @@
<% attachment_field_uuids = @submitter.submission.template.fields.select { |f| f['type'].in?(%w[image signature file]) }.pluck('uuid') %> <% fields_index = Templates.build_field_areas_index(@submitter.submission.template) %>
<% values = @submitter.submission.submitters.reduce({}) { |acc, e| acc.merge(e.values) } %> <% values = @submitter.submission.submitters.reduce({}) { |acc, sub| acc.merge(sub.values) } %>
<% attachments = ActiveStorage::Attachment.where(uuid: values.values_at(*attachment_field_uuids).flatten).preload(:blob) %> <% attachments_index = ActiveStorage::Attachment.where(record: @submitter.submission.submitters, name: :attachments).preload(:blob).index_by(&:uuid) %>
<div class="mx-auto block pb-72" style="max-width: 1000px"> <div class="mx-auto block pb-72" style="max-width: 1000px">
<div class="mt-4 flex"> <div class="mt-4 flex">
<a href="<%= root_path %>" class="mx-auto text-2xl md:text-3xl font-bold items-center flex space-x-3"> <a href="<%= root_path %>" class="mx-auto text-2xl md:text-3xl font-bold items-center flex space-x-3">
@ -13,7 +13,13 @@
<% document.preview_images.sort_by { |a| a.filename.base.to_i }.each_with_index do |page, index| %> <% document.preview_images.sort_by { |a| a.filename.base.to_i }.each_with_index do |page, index| %>
<div class="relative my-4 shadow-md"> <div class="relative my-4 shadow-md">
<img src="<%= page.url %>" width="<%= page.metadata['width'] %>" height="<%= page.metadata['height'] %>" loading="lazy"> <img src="<%= page.url %>" width="<%= page.metadata['width'] %>" height="<%= page.metadata['height'] %>" loading="lazy">
<div id="page-<%= [document.uuid, index].join('-') %>" class="top-0 bottom-0 left-0 right-0 absolute"></div> <div id="page-<%= [document.uuid, index].join('-') %>" class="top-0 bottom-0 left-0 right-0 absolute">
<% fields_index.dig(document.uuid, index)&.each do |(area, field)| %>
<% value = values[field['uuid']] %>
<% next if value.blank? %>
<%= render 'submissions/value', area:, field:, attachments_index:, value: %>
<% end %>
</div>
</div> </div>
<% end %> <% end %>
<% end %> <% end %>
@ -26,7 +32,7 @@
<div class="mx-auto" style="max-width: 1000px"> <div class="mx-auto" style="max-width: 1000px">
<div class="relative md:mx-32"> <div class="relative md:mx-32">
<div class="shadow-md bg-base-100 absolute bottom-0 md:bottom-4 w-full border border-base-200 p-4 rounded"> <div class="shadow-md bg-base-100 absolute bottom-0 md:bottom-4 w-full border border-base-200 p-4 rounded">
<submission-form data-submitter-uuid="<%= @submitter.uuid %>" data-submitter-slug="<%= @submitter.slug %>" data-attachments="<%= attachments.to_json(only: %i[uuid], methods: %i[url filename content_type]) %>" data-fields="<%= @submitter.submission.template.fields.to_json %>" data-values="<%= values.to_json %>" data-authenticity-token="<%= form_authenticity_token %>"></submission-form> <submission-form data-submitter-uuid="<%= @submitter.uuid %>" data-submitter-slug="<%= @submitter.slug %>" data-attachments="<%= attachments_index.values.select { |e| e.record_id == @submitter.id }.to_json(only: %i[uuid], methods: %i[url filename content_type]) %>" data-fields="<%= @submitter.submission.template.fields.select { |f| f['submitter_uuid'] == @submitter.uuid }.to_json %>" data-values="<%= @submitter.values.to_json %>" data-authenticity-token="<%= form_authenticity_token %>"></submission-form>
</div> </div>
</div> </div>
</div> </div>

@ -1,16 +1 @@
<!DOCTYPE html> <template-builder data-template="<%= @template.to_json(include: { documents: { include: { preview_images: { methods: %i[url metadata filename] } } } }) %>"></template-builder>
<html data-theme="docuseal" class="h-full">
<head>
<title>
Docuseal
</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<%= javascript_pack_tag 'application', defer: true %>
<%= stylesheet_pack_tag 'application', media: 'all' %>
</head>
<body class="h-full" data>
<template-builder data-template="<%= @template.to_json(include: { documents: { include: { preview_images: { methods: %i[url metadata filename] } } } }) %>"></template-builder>
</body>
</html>

@ -1,4 +1,20 @@
# frozen_string_literal: true # frozen_string_literal: true
module Templates module Templates
module_function
def build_field_areas_index(template)
hash = {}
template.fields.each do |field|
(field['areas'] || []).each do |area|
hash[area['attachment_uuid']] ||= {}
acc = (hash[area['attachment_uuid']][area['page']] ||= [])
acc << [area, field]
end
end
hash
end
end end

Loading…
Cancel
Save