Compare commits

...
11 Commits
27 changed files with 302 additions and 92 deletions

No files matched your search

+3
View File
@@ -61,5 +61,8 @@ RSpec/ExampleLength:
Rails/I18nLocaleTexts: Rails/I18nLocaleTexts:
Enabled: false Enabled: false
Rails/SkipsModelValidations:
Enabled: false
Rails/ApplicationController: Rails/ApplicationController:
Enabled: false Enabled: false
@@ -23,6 +23,10 @@ class SubmitFormController < ApplicationController
submitter.save! submitter.save!
Submissions.update_template_fields!(submitter.submission) if submitter.submission.template_fields.blank?
submitter.submission.save!
if submitter.completed_at? if submitter.completed_at?
GenerateSubmitterResultAttachmentsJob.perform_later(submitter) GenerateSubmitterResultAttachmentsJob.perform_later(submitter)
+1 -1
View File
@@ -13,7 +13,7 @@ export default actionable(class extends HTMLElement {
document.body.classList.remove('overflow-hidden') document.body.classList.remove('overflow-hidden')
document.removeEventListener('keyup', this.onEscKey) document.removeEventListener('keyup', this.onEscKey)
document.removeEventListener('turbo:submit-end', this.handleSubmit) document.removeEventListener('turbo:submit-end', this.onSubmit)
document.removeEventListener('turbo:before-cache', this.close) document.removeEventListener('turbo:before-cache', this.close)
} }
+7 -2
View File
@@ -21,7 +21,7 @@
</span> </span>
</div> </div>
<div <div
v-if="isActive" v-if="isActive && withLabel"
class="absolute -top-7 rounded bg-base-content text-base-100 px-2 text-sm whitespace-nowrap" class="absolute -top-7 rounded bg-base-content text-base-100 px-2 text-sm whitespace-nowrap"
> >
{{ field.name || fieldNames[field.type] }} {{ field.name || fieldNames[field.type] }}
@@ -146,6 +146,11 @@ export default {
required: false, required: false,
default: false default: false
}, },
withLabel: {
type: Boolean,
required: false,
default: true
},
fieldIndex: { fieldIndex: {
type: Number, type: Number,
required: false, required: false,
@@ -207,7 +212,7 @@ export default {
}, },
formattedDate () { formattedDate () {
if (this.field.type === 'date' && this.modelValue) { if (this.field.type === 'date' && this.modelValue) {
return new Intl.DateTimeFormat([], { year: 'numeric', month: 'long', day: 'numeric' }).format(new Date(this.modelValue)) return new Intl.DateTimeFormat([], { year: 'numeric', month: 'long', day: 'numeric', timeZone: 'UTC' }).format(new Date(this.modelValue))
} else { } else {
return '' return ''
} }
+9 -1
View File
@@ -11,7 +11,9 @@
v-for="(area, areaIndex) in field.areas" v-for="(area, areaIndex) in field.areas"
:key="areaIndex" :key="areaIndex"
> >
<Teleport :to="`#page-${area.attachment_uuid}-${area.page}`"> <Teleport
:to="`#page-${area.attachment_uuid}-${area.page}`"
>
<FieldArea <FieldArea
:ref="setAreaRef" :ref="setAreaRef"
v-model="values[field.uuid]" v-model="values[field.uuid]"
@@ -20,6 +22,7 @@
:submittable="true" :submittable="true"
:field-index="fieldIndex" :field-index="fieldIndex"
:is-active="currentStep === step" :is-active="currentStep === step"
:with-label="withLabel"
:is-value-set="step.some((f) => f.uuid in values)" :is-value-set="step.some((f) => f.uuid in values)"
:attachments-index="attachmentsIndex" :attachments-index="attachmentsIndex"
@click="$emit('focus-step', step)" @click="$emit('focus-step', step)"
@@ -54,6 +57,11 @@ export default {
required: false, required: false,
default: () => ({}) default: () => ({})
}, },
withLabel: {
type: Boolean,
required: false,
default: true
},
currentStep: { currentStep: {
type: Array, type: Array,
required: false, required: false,
+33 -24
View File
@@ -4,6 +4,7 @@
:steps="stepFields" :steps="stepFields"
:values="values" :values="values"
:attachments-index="attachmentsIndex" :attachments-index="attachmentsIndex"
:with-label="!isAnonymousChecboxes"
:current-step="currentStepFields" :current-step="currentStepFields"
@focus-step="[saveStep(), goToStep($event, false, true), currentField.type !== 'checkbox' ? isFormVisible = true : '']" @focus-step="[saveStep(), goToStep($event, false, true), currentField.type !== 'checkbox' ? isFormVisible = true : '']"
/> />
@@ -182,7 +183,7 @@
/> />
<div <div
v-else-if="currentField.type === 'checkbox'" v-else-if="currentField.type === 'checkbox'"
class="flex w-full" class="flex w-full max-h-44 overflow-y-auto"
> >
<input <input
type="hidden" type="hidden"
@@ -192,31 +193,36 @@
<div <div
class="space-y-3.5 mx-auto" class="space-y-3.5 mx-auto"
> >
<div <template v-if="isAnonymousChecboxes">
v-for="(field, index) in currentStepFields" Complete hightlighted checkboxes and click <span class="font-semibold">{{ stepFields.length === currentStep + 1 ? 'submit' : 'next' }}</span>.
:key="field.uuid" </template>
> <template v-else>
<label <div
:for="field.uuid" v-for="(field, index) in currentStepFields"
class="flex items-center space-x-3" :key="field.uuid"
> >
<input <label
type="hidden" :for="field.uuid"
:name="`values[${field.uuid}]`" class="flex items-center space-x-3"
:value="!!values[field.uuid]"
> >
<input <input
:id="field.uuid" type="hidden"
type="checkbox" :name="`values[${field.uuid}]`"
class="base-checkbox !h-7 !w-7" :value="!!values[field.uuid]"
:checked="!!values[field.uuid]" >
@click="[$refs.areas.scrollIntoField(field), values[field.uuid] = !values[field.uuid]]" <input
> :id="field.uuid"
<span class="text-xl"> type="checkbox"
{{ field.name || field.type + ' ' + (index + 1) }} class="base-checkbox !h-7 !w-7"
</span> :checked="!!values[field.uuid]"
</label> @click="[$refs.areas.scrollIntoField(field), values[field.uuid] = !values[field.uuid]]"
</div> >
<span class="text-xl">
{{ field.name || field.type + ' ' + (index + 1) }}
</span>
</label>
</div>
</template>
</div> </div>
</div> </div>
<ImageStep <ImageStep
@@ -374,6 +380,9 @@ export default {
currentStepFields () { currentStepFields () {
return this.stepFields[this.currentStep] return this.stepFields[this.currentStep]
}, },
isAnonymousChecboxes () {
return this.currentField.type === 'checkbox' && this.currentStepFields.every((e) => !e.name) && this.currentStepFields.length > 4
},
isButtonDisabled () { isButtonDisabled () {
return this.isSubmitting || return this.isSubmitting ||
(this.currentField.required && ['image', 'file'].includes(this.currentField.type) && !this.values[this.currentField.uuid]?.length) || (this.currentField.required && ['image', 'file'].includes(this.currentField.type) && !this.values[this.currentField.uuid]?.length) ||
+26 -2
View File
@@ -64,8 +64,11 @@
:with-arrows="template.schema.length > 1" :with-arrows="template.schema.length > 1"
:item="item" :item="item"
:document="sortedDocuments[index]" :document="sortedDocuments[index]"
:template="template"
:is-direct-upload="isDirectUpload"
@scroll-to="scrollIntoDocument(item)" @scroll-to="scrollIntoDocument(item)"
@remove="onDocumentRemove" @remove="onDocumentRemove"
@replace="onDocumentReplace"
@up="moveDocument(item, -1)" @up="moveDocument(item, -1)"
@down="moveDocument(item, 1)" @down="moveDocument(item, 1)"
@change="save" @change="save"
@@ -133,7 +136,7 @@
<p> <p>
<button <button
class="base-button" class="base-button"
@click="drawField = false" @click="drawField = null"
> >
Cancel Cancel
</button> </button>
@@ -353,7 +356,7 @@ export default {
if (this.selectedField?.type === this.dragFieldType) { if (this.selectedField?.type === this.dragFieldType) {
baseArea = this.selectedAreaRef.value baseArea = this.selectedAreaRef.value
} else if (previousField?.areas) { } else if (previousField?.areas?.length) {
baseArea = previousField.areas[previousField.areas.length - 1] baseArea = previousField.areas[previousField.areas.length - 1]
} else { } else {
if (['checkbox'].includes(this.dragFieldType)) { if (['checkbox'].includes(this.dragFieldType)) {
@@ -417,6 +420,27 @@ export default {
this.template.schema.splice(this.template.schema.indexOf(item), 1) this.template.schema.splice(this.template.schema.indexOf(item), 1)
} }
this.template.fields.forEach((field) => {
[...(field.areas || [])].forEach((area) => {
if (area.attachment_uuid === item.attachment_uuid) {
field.areas.splice(field.areas.indexOf(area), 1)
}
})
})
this.save()
},
onDocumentReplace ({ replaceSchemaItem, schema, documents }) {
this.template.schema.splice(this.template.schema.indexOf(replaceSchemaItem), 1, schema[0])
this.template.documents.push(...documents)
this.template.fields.forEach((field) => {
(field.areas || []).forEach((area) => {
if (area.attachment_uuid === replaceSchemaItem.attachment_uuid) {
area.attachment_uuid = schema[0].attachment_uuid
}
})
})
this.save() this.save()
}, },
moveDocument (item, direction) { moveDocument (item, direction) {
@@ -93,6 +93,11 @@ export default {
} }
} }
}, },
mounted () {
if (this.isDirectUpload) {
import('@rails/activestorage')
}
},
methods: { methods: {
upload: Upload.methods.upload, upload: Upload.methods.upload,
onDropFiles (e) { onDropFiles (e) {
@@ -172,6 +172,7 @@ export default {
name: '', name: '',
uuid: v4(), uuid: v4(),
required: type !== 'checkbox', required: type !== 'checkbox',
areas: [],
submitter_uuid: this.selectedSubmitter.uuid, submitter_uuid: this.selectedSubmitter.uuid,
type type
} }
+59 -29
View File
@@ -9,39 +9,51 @@
loading="lazy" loading="lazy"
> >
<div <div
class="group flex justify-end cursor-pointer top-0 bottom-0 left-0 right-0 absolute" class="group flex justify-end cursor-pointer top-0 bottom-0 left-0 right-0 absolute p-1"
@click="$emit('scroll-to', item)" @click="$emit('scroll-to', item)"
> >
<div <div class="flex justify-between w-full">
class="flex flex-col justify-between opacity-0 group-hover:opacity-100" <div style="width: 26px" />
> <div class="">
<div class="rounded-bl rounded-tr bg-white border"> <ReplaceButton
<button :is-direct-upload="isDirectUpload"
class="rounded-bl rounded-tr hover:text-base-100 hover:bg-base-content w-full transition-colors" :template-id="template.id"
style="width: 24px" class="opacity-0 group-hover:opacity-100"
@click.stop="$emit('remove', item)" @click.stop
> @success="$emit('replace', { replaceSchemaItem: item, ...$event })"
&times; />
</button>
</div> </div>
<div <div
v-if="withArrows" class="flex flex-col justify-between opacity-0 group-hover:opacity-100"
class="flex flex-col border rounded-br rounded-tl bg-white divide-y"
> >
<button <div>
class="rounded-tl hover:text-base-100 hover:bg-base-content w-full transition-colors" <button
style="width: 24px" class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors"
@click.stop="$emit('up', item)" style="width: 24px; height: 24px"
@click.stop="$emit('remove', item)"
>
&times;
</button>
</div>
<div
v-if="withArrows"
class="flex flex-col space-y-1"
> >
&uarr; <button
</button> class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors"
<button style="width: 24px; height: 24px"
class="rounded-br hover:text-base-100 hover:bg-base-content w-full transition-colors" @click.stop="$emit('up', item)"
style="width: 24px" >
@click.stop="$emit('down', item)" &uarr;
> </button>
&darr; <button
</button> class="btn border-base-200 bg-white text-base-content btn-xs rounded hover:text-base-100 hover:bg-base-content hover:border-base-content w-full transition-colors"
style="width: 24px; height: 24px"
@click.stop="$emit('down', item)"
>
&darr;
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -60,34 +72,52 @@
<script> <script>
import Contenteditable from './contenteditable' import Contenteditable from './contenteditable'
import Upload from './upload'
import ReplaceButton from './replace'
export default { export default {
name: 'DocumentPreview', name: 'DocumentPreview',
components: { components: {
Contenteditable Contenteditable,
ReplaceButton
}, },
props: { props: {
item: { item: {
type: Object, type: Object,
required: true required: true
}, },
template: {
type: Object,
required: true
},
document: { document: {
type: Object, type: Object,
required: true required: true
}, },
isDirectUpload: {
type: Boolean,
required: true,
default: false
},
withArrows: { withArrows: {
type: Boolean, type: Boolean,
required: false, required: false,
default: true default: true
} }
}, },
emits: ['scroll-to', 'change', 'remove', 'up', 'down'], emits: ['scroll-to', 'change', 'remove', 'up', 'down', 'replace'],
computed: { computed: {
previewImage () { previewImage () {
return this.document.preview_images[0] return this.document.preview_images[0]
} }
}, },
mounted () {
if (this.isDirectUpload) {
import('@rails/activestorage')
}
},
methods: { methods: {
upload: Upload.methods.upload,
onUpdateName (value) { onUpdateName (value) {
this.item.name = value this.item.name = value
@@ -0,0 +1,70 @@
<template>
<label
:for="inputId"
class="btn btn-neutral btn-xs text-white transition-none"
:class="{ 'opacity-100': isLoading || isProcessing }"
>
{{ message }}
<form
ref="form"
class="hidden"
>
<input
:id="inputId"
ref="input"
name="files[]"
type="file"
accept="image/*, application/pdf"
@change="upload"
>
</form>
</label>
</template>
<script>
import Upload from './upload'
export default {
name: 'ReplaceDocument',
props: {
templateId: {
type: [Number, String],
required: true
},
isDirectUpload: {
type: Boolean,
required: true,
default: false
}
},
emits: ['success'],
data () {
return {
isLoading: false,
isProcessing: false
}
},
computed: {
inputId () {
return 'el' + Math.random().toString(32).split('.')[1]
},
message () {
if (this.isLoading) {
return 'Uploading...'
} else if (this.isProcessing) {
return 'Processing...'
} else {
return 'Replace'
}
}
},
mounted () {
if (this.isDirectUpload) {
import('@rails/activestorage')
}
},
methods: {
upload: Upload.methods.upload
}
}
</script>
+13 -6
View File
@@ -4,12 +4,15 @@
# #
# Table name: submissions # Table name: submissions
# #
# id :bigint not null, primary key # id :bigint not null, primary key
# deleted_at :datetime # deleted_at :datetime
# created_at :datetime not null # template_fields :text
# updated_at :datetime not null # template_schema :text
# created_by_user_id :bigint # template_submitters :text
# template_id :bigint not null # created_at :datetime not null
# updated_at :datetime not null
# created_by_user_id :bigint
# template_id :bigint not null
# #
# Indexes # Indexes
# #
@@ -27,5 +30,9 @@ class Submission < ApplicationRecord
has_many :submitters, dependent: :destroy has_many :submitters, dependent: :destroy
serialize :template_fields, JSON
serialize :template_schema, JSON
serialize :template_submitters, JSON
scope :active, -> { where(deleted_at: nil) } scope :active, -> { where(deleted_at: nil) }
end end
+3
View File
@@ -44,6 +44,9 @@ class Template < ApplicationRecord
has_many_attached :documents has_many_attached :documents
has_many :schema_documents, ->(e) { where(uuid: e.schema.pluck('attachment_uuid')) },
class_name: 'ActiveStorage::Attachment', dependent: :destroy, as: :record, inverse_of: :record
has_many :submissions, dependent: :destroy has_many :submissions, dependent: :destroy
scope :active, -> { where(deleted_at: nil) } scope :active, -> { where(deleted_at: nil) }
+4
View File
@@ -56,6 +56,10 @@ class User < ApplicationRecord
!deleted_at? !deleted_at?
end end
def remember_me
true
end
def initials def initials
[first_name.first, last_name.first].join.upcase [first_name.first, last_name.first].join.upcase
end end
-8
View File
@@ -10,14 +10,6 @@
<%= f.label :password, class: 'label' %> <%= f.label :password, class: 'label' %>
<%= f.password_field :password, autocomplete: 'current-password', class: 'base-input' %> <%= f.password_field :password, autocomplete: 'current-password', class: 'base-input' %>
</div> </div>
<% if devise_mapping.rememberable? %>
<div class="form-control">
<%= f.label :remember_me, class: 'flex items-center cursor-pointer' do %>
<%= f.check_box :remember_me, checked: true, class: 'base-checkbox' %>
<span class="label">Remember me</span>
<% end %>
</div>
<% end %>
</div> </div>
<div class="form-control"> <div class="form-control">
<%= f.button button_title(title: 'Log In', disabled_with: 'Logging In'), class: 'base-button' %> <%= f.button button_title(title: 'Log In', disabled_with: 'Logging In'), class: 'base-button' %>
+6 -6
View File
@@ -23,7 +23,7 @@
</div> </div>
<div class="flex md:max-h-[calc(100vh-60px)]"> <div class="flex md:max-h-[calc(100vh-60px)]">
<div class="overflow-y-auto overflow-x-hidden hidden lg:block w-52 flex-none pr-3 mt-0.5 pt-0.5"> <div class="overflow-y-auto overflow-x-hidden hidden lg:block w-52 flex-none pr-3 mt-0.5 pt-0.5">
<% @submission.template.schema.each do |item| %> <% (@submission.template_schema || @submission.template.schema).each do |item| %>
<% document = @submission.template.documents.find { |a| item['attachment_uuid'] == a.uuid } %> <% document = @submission.template.documents.find { |a| item['attachment_uuid'] == a.uuid } %>
<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"> <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"> <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">
@@ -35,10 +35,10 @@
</div> </div>
<div class="w-full overflow-y-auto overflow-x-hidden mt-0.5 pt-0.5"> <div class="w-full overflow-y-auto overflow-x-hidden mt-0.5 pt-0.5">
<div class="pr-3.5 pl-0.5"> <div class="pr-3.5 pl-0.5">
<% fields_index = Templates.build_field_areas_index(@submission.template) %> <% fields_index = Templates.build_field_areas_index(@submission.template_fields || @submission.template.fields) %>
<% values = @submission.submitters.reduce({}) { |acc, sub| acc.merge(sub.values) } %> <% 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) %> <% attachments_index = ActiveStorage::Attachment.where(record: @submission.submitters, name: :attachments).preload(:blob).index_by(&:uuid) %>
<% @submission.template.schema.each do |item| %> <% (@submission.template_schema || @submission.template.schema).each do |item| %>
<% document = @submission.template.documents.find { |e| e.uuid == item['attachment_uuid'] } %> <% 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| %> <% 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"> <div id="<%= "page-#{document.uuid}-#{index}" %>" class="relative">
@@ -57,15 +57,15 @@
</div> </div>
<div class="hidden md:block relative w-80 flex-none pt-0.5 pr-4 pl-0.5 overflow-auto space"> <div class="hidden md:block relative w-80 flex-none pt-0.5 pr-4 pl-0.5 overflow-auto space">
<% colors = %w[bg-red-500 bg-sky-500 bg-emerald-500 bg-yellow-300 bg-purple-600] %> <% colors = %w[bg-red-500 bg-sky-500 bg-emerald-500 bg-yellow-300 bg-purple-600] %>
<% submitter_fields_index = @submission.template.fields.group_by { |f| f['submitter_uuid'] } %> <% submitter_fields_index = (@submission.template_fields || @submission.template.fields).group_by { |f| f['submitter_uuid'] } %>
<% @submission.template.submitters.each_with_index do |item, index| %> <% (@submission.template_submitters || @submission.template.submitters).each_with_index do |item, index| %>
<% submitter = @submission.submitters.find { |e| e.uuid == item['uuid'] } %> <% submitter = @submission.submitters.find { |e| e.uuid == item['uuid'] } %>
<div class="sticky -top-1 bg-base-100 pt-1 -mt-1 z-10"> <div class="sticky -top-1 bg-base-100 pt-1 -mt-1 z-10">
<div class="border border-base-300 rounded-md px-2 py-1 mb-1"> <div class="border border-base-300 rounded-md px-2 py-1 mb-1">
<div class="flex items-center space-x-1"> <div class="flex items-center space-x-1">
<span class="mx-1 w-3 h-3 rounded-full <%= colors[index] %>"></span> <span class="mx-1 w-3 h-3 rounded-full <%= colors[index] %>"></span>
<span class="text-lg"> <span class="text-lg">
<%= @submission.template.submitters.find { |e| e['uuid'] == submitter&.uuid }&.dig('name') || "#{(index + 1).ordinalize} Submitter" %> <%= (@submission.template_submitters || @submission.template.submitters).find { |e| e['uuid'] == submitter&.uuid }&.dig('name') || "#{(index + 1).ordinalize} Submitter" %>
</span> </span>
</div> </div>
<div class="flex items-center space-x-1 mt-1"> <div class="flex items-center space-x-1 mt-1">
+3 -3
View File
@@ -1,4 +1,4 @@
<% fields_index = Templates.build_field_areas_index(@submitter.submission.template) %> <% fields_index = Templates.build_field_areas_index(@submitter.submission.template_fields || @submitter.submission.template.fields) %>
<% values = @submitter.submission.submitters.where.not(id: @submitter.id).reduce({}) { |acc, sub| acc.merge(sub.values) } %> <% values = @submitter.submission.submitters.where.not(id: @submitter.id).reduce({}) { |acc, sub| acc.merge(sub.values) } %>
<% attachments_index = ActiveStorage::Attachment.where(record: @submitter.submission.submitters, name: :attachments).preload(:blob).index_by(&:uuid) %> <% attachments_index = ActiveStorage::Attachment.where(record: @submitter.submission.submitters, name: :attachments).preload(:blob).index_by(&:uuid) %>
<div style="max-height: -webkit-fill-available;"> <div style="max-height: -webkit-fill-available;">
@@ -10,7 +10,7 @@
<span>DocuSeal</span> <span>DocuSeal</span>
</a> </a>
</div> </div>
<% @submitter.submission.template.schema.each do |item| %> <% (@submitter.submission.template_schema || @submitter.submission.template.schema).each do |item| %>
<% document = @submitter.submission.template.documents.find { |a| a.uuid == item['attachment_uuid'] } %> <% document = @submitter.submission.template.documents.find { |a| a.uuid == item['attachment_uuid'] } %>
<% 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">
@@ -32,7 +32,7 @@
<div class="fixed bottom-0 w-full h-0 z-20"> <div class="fixed bottom-0 w-full h-0 z-20">
<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">
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-is-direct-upload="<%= Docuseal.active_storage_public? %>" data-submitter-uuid="<%= @submitter.uuid %>" data-submitter-slug="<%= @submitter.slug %>" data-can-send-email="<%= Accounts.can_send_emails?(Struct.new(:id).new(@submitter.submission.template.account_id)) %>" 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> <submission-form data-is-demo="<%= Docuseal.demo? %>" data-is-direct-upload="<%= Docuseal.active_storage_public? %>" data-submitter-uuid="<%= @submitter.uuid %>" data-submitter-slug="<%= @submitter.slug %>" data-can-send-email="<%= Accounts.can_send_emails?(Struct.new(:id).new(@submitter.submission.template.account_id)) %>" 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 || @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 -1
View File
@@ -1 +1 @@
<template-builder data-is-direct-upload="<%= Docuseal.active_storage_public? %>" data-template="<%= @template.to_json(include: { documents: { include: { preview_images: { methods: %i[url metadata filename] } } } }) %>"></template-builder> <template-builder data-is-direct-upload="<%= Docuseal.active_storage_public? %>" data-template="<%= @template.as_json.merge(documents: @template.schema_documents.as_json(include: { preview_images: { methods: %i[url metadata filename] } })).to_json %>"></template-builder>
+1 -1
View File
@@ -37,7 +37,7 @@
<div class="space-y-4"> <div class="space-y-4">
<% @submissions.each do |submission| %> <% @submissions.each do |submission| %>
<a href="<%= submission_path(submission) %>" class="bg-base-200 w-full flex flex-col md:flex-row space-y-4 md:space-y-0 md:justify-between rounded-2xl px-6 py-5 md:items-center"> <a href="<%= submission_path(submission) %>" class="bg-base-200 w-full flex flex-col md:flex-row space-y-4 md:space-y-0 md:justify-between rounded-2xl px-6 py-5 md:items-center">
<% submitters = submission.template.submitters.filter_map { |item| submission.submitters.find { |e| e.uuid == item['uuid'] } } %> <% submitters = (submission.template_submitters || submission.template.submitters).filter_map { |item| submission.submitters.find { |e| e.uuid == item['uuid'] } } %>
<% is_submission_complated = submitters.all?(&:completed_at?) %> <% is_submission_complated = submitters.all?(&:completed_at?) %>
<% if submitters.size == 1 %> <% if submitters.size == 1 %>
<div> <div>
+1 -1
View File
@@ -159,7 +159,7 @@ Devise.setup do |config|
# ==> Configuration for :rememberable # ==> Configuration for :rememberable
# The time the user will be remembered without asking for credentials again. # The time the user will be remembered without asking for credentials again.
# config.remember_for = 2.weeks config.remember_for = 2.years
# Invalidates all the remember me tokens when the user signs out. # Invalidates all the remember me tokens when the user signs out.
config.expire_all_remember_me_on_sign_out = true config.expire_all_remember_me_on_sign_out = true
+3 -1
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
ActiveRecord::Tasks::DatabaseTasks.migrate if ENV['RAILS_ENV'] == 'production' Rails.configuration.to_prepare do
ActiveRecord::Tasks::DatabaseTasks.migrate if ENV['RAILS_ENV'] == 'production'
end
@@ -0,0 +1,31 @@
# frozen_string_literal: true
class AddTemplateFieldsToSubmission < ActiveRecord::Migration[7.0]
class MigrationTemplate < ApplicationRecord
self.table_name = 'templates'
end
class MigrationSubmission < ApplicationRecord
self.table_name = 'submissions'
end
def up
add_column :submissions, :template_fields, :text
add_column :submissions, :template_schema, :text
add_column :submissions, :template_submitters, :text
MigrationTemplate.all.each do |template|
MigrationSubmission.where(template_id: template.id).each do |submission|
submission.update_columns(template_fields: template.fields,
template_schema: template.schema,
template_submitters: template.submitters)
end
end
end
def down
remove_column :submissions, :template_fields
remove_column :submissions, :template_schema
remove_column :submissions, :template_submitters
end
end
+4 -1
View File
@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_07_01_075115) do ActiveRecord::Schema[7.0].define(version: 2023_07_26_062428) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@@ -77,6 +77,9 @@ ActiveRecord::Schema[7.0].define(version: 2023_07_01_075115) do
t.datetime "deleted_at" t.datetime "deleted_at"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.text "template_fields"
t.text "template_schema"
t.text "template_submitters"
t.index ["created_by_user_id"], name: "index_submissions_on_created_by_user_id" t.index ["created_by_user_id"], name: "index_submissions_on_created_by_user_id"
t.index ["template_id"], name: "index_submissions_on_template_id" t.index ["template_id"], name: "index_submissions_on_template_id"
end end
+1 -1
View File
@@ -25,7 +25,7 @@ module ActionMailerConfigsInterceptor
port: email_configs.value['port'], port: email_configs.value['port'],
tls: email_configs.value['port'].to_s == '465') tls: email_configs.value['port'].to_s == '465')
message.from = email_configs.value['from_email'] message.from = "#{email_configs.account.name} <#{email_configs.value['from_email']}>"
else else
message.delivery_method(:test) message.delivery_method(:test)
end end
+9
View File
@@ -1,4 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
module Submissions module Submissions
module_function
def update_template_fields!(submission)
submission.template_fields = submission.template.fields
submission.template_schema = submission.template.schema
submission.template_submitters = submission.template.submitters
submission.save!
end
end end
@@ -27,7 +27,7 @@ module Submissions
pdfs_index = build_pdfs_index(submitter) pdfs_index = build_pdfs_index(submitter)
template.fields.each do |field| submitter.submission.template_fields.each do |field|
next if field['submitter_uuid'] != submitter.uuid next if field['submitter_uuid'] != submitter.uuid
field.fetch('areas', []).each do |area| field.fetch('areas', []).each do |area|
@@ -159,7 +159,7 @@ module Submissions
original_documents = template.documents.preload(:blob) original_documents = template.documents.preload(:blob)
results = results =
template.schema.map do |item| submitter.submission.template_schema.map do |item|
pdf = pdfs_index[item['attachment_uuid']] pdf = pdfs_index[item['attachment_uuid']]
attachment = save_signed_pdf(pdf:, submitter:, certs:, uuid: item['attachment_uuid'], name: item['name']) attachment = save_signed_pdf(pdf:, submitter:, certs:, uuid: item['attachment_uuid'], name: item['name'])
+2 -2
View File
@@ -3,10 +3,10 @@
module Templates module Templates
module_function module_function
def build_field_areas_index(template) def build_field_areas_index(fields)
hash = {} hash = {}
template.fields.each do |field| fields.each do |field|
(field['areas'] || []).each do |area| (field['areas'] || []).each do |area|
hash[area['attachment_uuid']] ||= {} hash[area['attachment_uuid']] ||= {}
acc = (hash[area['attachment_uuid']][area['page']] ||= []) acc = (hash[area['attachment_uuid']][area['page']] ||= [])