document conditions
This commit is contained in:
@@ -109,7 +109,7 @@ class TemplatesController < ApplicationController
|
|||||||
def template_params
|
def template_params
|
||||||
params.require(:template).permit(
|
params.require(:template).permit(
|
||||||
:name,
|
:name,
|
||||||
{ schema: [%i[attachment_uuid name]],
|
{ schema: [[:attachment_uuid, :name, { conditions: [%i[field_uuid value action operation]] }]],
|
||||||
submitters: [%i[name uuid is_requester linked_to_uuid invite_by_uuid email]],
|
submitters: [%i[name uuid is_requester linked_to_uuid invite_by_uuid email]],
|
||||||
fields: [[:uuid, :submitter_uuid, :name, :type,
|
fields: [[:uuid, :submitter_uuid, :name, :type,
|
||||||
:required, :readonly, :default_value,
|
:required, :readonly, :default_value,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ safeRegisterElement('submission-form', class extends HTMLElement {
|
|||||||
this.app = createApp(Form, {
|
this.app = createApp(Form, {
|
||||||
submitter: JSON.parse(this.dataset.submitter),
|
submitter: JSON.parse(this.dataset.submitter),
|
||||||
inviteSubmitters: JSON.parse(this.dataset.inviteSubmitters),
|
inviteSubmitters: JSON.parse(this.dataset.inviteSubmitters),
|
||||||
|
schema: JSON.parse(this.dataset.schema),
|
||||||
canSendEmail: this.dataset.canSendEmail === 'true',
|
canSendEmail: this.dataset.canSendEmail === 'true',
|
||||||
previousSignatureValue: this.dataset.previousSignatureValue,
|
previousSignatureValue: this.dataset.previousSignatureValue,
|
||||||
goToLast: this.dataset.goToLast === 'true',
|
goToLast: this.dataset.goToLast === 'true',
|
||||||
|
|||||||
@@ -652,6 +652,11 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
schema: {
|
||||||
|
type: Array,
|
||||||
|
required: false,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
attachments: {
|
attachments: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: false,
|
required: false,
|
||||||
@@ -841,6 +846,21 @@ export default {
|
|||||||
isMobile () {
|
isMobile () {
|
||||||
return /android|iphone|ipad/i.test(navigator.userAgent)
|
return /android|iphone|ipad/i.test(navigator.userAgent)
|
||||||
},
|
},
|
||||||
|
attachmentConditionsIndex () {
|
||||||
|
return this.schema.reduce((acc, item) => {
|
||||||
|
if (item.conditions?.length) {
|
||||||
|
if (item.conditions.every((c) => this.fieldsUuidIndex[c.field_uuid])) {
|
||||||
|
acc[item.attachment_uuid] = this.checkFieldConditions(item)
|
||||||
|
} else {
|
||||||
|
acc[item.attachment_uuid] = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc[item.attachment_uuid] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
},
|
||||||
emptyValueRequiredStep () {
|
emptyValueRequiredStep () {
|
||||||
return this.stepFields.find((fields, index) => {
|
return this.stepFields.find((fields, index) => {
|
||||||
return fields.some((f) => {
|
return fields.some((f) => {
|
||||||
@@ -921,7 +941,7 @@ export default {
|
|||||||
return this.currentStepFields[0]
|
return this.currentStepFields[0]
|
||||||
},
|
},
|
||||||
readonlyConditionalFields () {
|
readonlyConditionalFields () {
|
||||||
return this.fields.filter((f) => f.readonly && f.conditions?.length && this.checkFieldConditions(f))
|
return this.fields.filter((f) => f.readonly && f.conditions?.length && this.checkFieldConditions(f) && this.checkFieldDocumentsConditions(f))
|
||||||
},
|
},
|
||||||
stepFields () {
|
stepFields () {
|
||||||
const verificationFields = []
|
const verificationFields = []
|
||||||
@@ -943,7 +963,7 @@ export default {
|
|||||||
return sortedFields.reduce((acc, f) => {
|
return sortedFields.reduce((acc, f) => {
|
||||||
const prevStep = acc[acc.length - 1]
|
const prevStep = acc[acc.length - 1]
|
||||||
|
|
||||||
if (this.checkFieldConditions(f)) {
|
if (this.checkFieldConditions(f) && this.checkFieldDocumentsConditions(f)) {
|
||||||
if (f.type === 'checkbox' && Array.isArray(prevStep) && prevStep[0].type === 'checkbox' && !f.description) {
|
if (f.type === 'checkbox' && Array.isArray(prevStep) && prevStep[0].type === 'checkbox' && !f.description) {
|
||||||
prevStep.push(f)
|
prevStep.push(f)
|
||||||
} else {
|
} else {
|
||||||
@@ -976,6 +996,23 @@ export default {
|
|||||||
if (isEmpty(value) && this.currentStep > 0) {
|
if (isEmpty(value) && this.currentStep > 0) {
|
||||||
this.currentStep -= 1
|
this.currentStep -= 1
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
attachmentConditionsIndex: {
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
handler (value) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const root = this.$root.$el.parentNode.getRootNode()
|
||||||
|
|
||||||
|
for (const key in value) {
|
||||||
|
const doc = root.querySelector(`[id="document-${key}"`)
|
||||||
|
|
||||||
|
if (doc) {
|
||||||
|
doc.classList.toggle('hidden', !value[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeUnmount () {
|
beforeUnmount () {
|
||||||
@@ -1057,6 +1094,15 @@ export default {
|
|||||||
onOrientationChange (event) {
|
onOrientationChange (event) {
|
||||||
this.orientation = event.target.type
|
this.orientation = event.target.type
|
||||||
},
|
},
|
||||||
|
checkFieldDocumentsConditions (field) {
|
||||||
|
if (field.areas?.length) {
|
||||||
|
return field.areas.some((area) => {
|
||||||
|
return this.attachmentConditionsIndex[area.attachment_uuid]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
},
|
||||||
checkFieldConditions (field) {
|
checkFieldConditions (field) {
|
||||||
if (field.conditions?.length) {
|
if (field.conditions?.length) {
|
||||||
const result = field.conditions.reduce((acc, cond) => {
|
const result = field.conditions.reduce((acc, cond) => {
|
||||||
|
|||||||
@@ -257,7 +257,7 @@
|
|||||||
:to="modalContainerEl"
|
:to="modalContainerEl"
|
||||||
>
|
>
|
||||||
<ConditionsModal
|
<ConditionsModal
|
||||||
:field="field"
|
:item="field"
|
||||||
:build-default-name="buildDefaultName"
|
:build-default-name="buildDefaultName"
|
||||||
@close="isShowConditionsModal = false"
|
@close="isShowConditionsModal = false"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -362,6 +362,7 @@
|
|||||||
:default-submitters="defaultSubmitters"
|
:default-submitters="defaultSubmitters"
|
||||||
:draw-field-type="drawFieldType"
|
:draw-field-type="drawFieldType"
|
||||||
:default-fields="[...defaultRequiredFields, ...defaultFields]"
|
:default-fields="[...defaultRequiredFields, ...defaultFields]"
|
||||||
|
:template="template"
|
||||||
:default-required-fields="defaultRequiredFields"
|
:default-required-fields="defaultRequiredFields"
|
||||||
:field-types="fieldTypes"
|
:field-types="fieldTypes"
|
||||||
:with-sticky-submitters="withStickySubmitters"
|
:with-sticky-submitters="withStickySubmitters"
|
||||||
@@ -1023,6 +1024,22 @@ export default {
|
|||||||
|
|
||||||
if (!field.areas.length) {
|
if (!field.areas.length) {
|
||||||
this.template.fields.splice(this.template.fields.indexOf(field), 1)
|
this.template.fields.splice(this.template.fields.indexOf(field), 1)
|
||||||
|
|
||||||
|
this.template.fields.forEach((f) => {
|
||||||
|
(f.conditions || []).forEach((c) => {
|
||||||
|
if (c.field_uuid === field.uuid) {
|
||||||
|
f.conditions.splice(f.conditions.indexOf(c), 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.template.schema.forEach((item) => {
|
||||||
|
(item.conditions || []).forEach((c) => {
|
||||||
|
if (c.field_uuid === field.uuid) {
|
||||||
|
item.conditions.splice(item.conditions.indexOf(c), 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.save()
|
this.save()
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl">
|
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl">
|
||||||
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
||||||
<span>
|
<span>
|
||||||
{{ t('condition') }} - {{ field.name || buildDefaultName(field, template.fields) }}
|
{{ t('condition') }} - {{ item.name || buildDefaultName(item, template.fields) }}
|
||||||
</span>
|
</span>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
@@ -138,12 +138,12 @@
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<div
|
<div
|
||||||
v-if="field.conditions?.[0]?.field_uuid"
|
v-if="item.conditions?.[0]?.field_uuid"
|
||||||
class="text-center w-full mt-4"
|
class="text-center w-full mt-4"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="link"
|
class="link"
|
||||||
@click="[conditions = [], delete field.conditions, validateSaveAndClose()]"
|
@click="[conditions = [], delete item.conditions, validateSaveAndClose()]"
|
||||||
>
|
>
|
||||||
{{ t('remove_condition') }}
|
{{ t('remove_condition') }}
|
||||||
</button>
|
</button>
|
||||||
@@ -158,7 +158,7 @@ export default {
|
|||||||
name: 'ConditionModal',
|
name: 'ConditionModal',
|
||||||
inject: ['t', 'save', 'template', 'withConditions'],
|
inject: ['t', 'save', 'template', 'withConditions'],
|
||||||
props: {
|
props: {
|
||||||
field: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
@@ -170,22 +170,26 @@ export default {
|
|||||||
emits: ['close'],
|
emits: ['close'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
conditions: this.field.conditions?.[0] ? JSON.parse(JSON.stringify(this.field.conditions)) : [{}]
|
conditions: this.item.conditions?.[0] ? JSON.parse(JSON.stringify(this.item.conditions)) : [{}]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
fields () {
|
fields () {
|
||||||
return this.template.fields.reduce((acc, f) => {
|
if (this.item.submitter_uuid) {
|
||||||
if (f !== this.field && f.submitter_uuid === this.field.submitter_uuid) {
|
return this.template.fields.reduce((acc, f) => {
|
||||||
acc.push(f)
|
if (f !== this.item && f.submitter_uuid === this.item.submitter_uuid) {
|
||||||
}
|
acc.push(f)
|
||||||
|
}
|
||||||
|
|
||||||
return acc
|
return acc
|
||||||
}, [])
|
}, [])
|
||||||
|
} else {
|
||||||
|
return this.template.fields
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.field.conditions ||= []
|
this.item.conditions ||= []
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
conditionField (condition) {
|
conditionField (condition) {
|
||||||
@@ -219,9 +223,9 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.conditions.find((f) => f.field_uuid)) {
|
if (this.conditions.find((f) => f.field_uuid)) {
|
||||||
this.field.conditions = this.conditions
|
this.item.conditions = this.conditions
|
||||||
} else {
|
} else {
|
||||||
delete this.field.conditions
|
delete this.item.conditions
|
||||||
}
|
}
|
||||||
|
|
||||||
this.save()
|
this.save()
|
||||||
|
|||||||
@@ -236,7 +236,7 @@
|
|||||||
:to="modalContainerEl"
|
:to="modalContainerEl"
|
||||||
>
|
>
|
||||||
<ConditionsModal
|
<ConditionsModal
|
||||||
:field="field"
|
:item="field"
|
||||||
:build-default-name="buildDefaultName"
|
:build-default-name="buildDefaultName"
|
||||||
@close="isShowConditionsModal = false"
|
@close="isShowConditionsModal = false"
|
||||||
/>
|
/>
|
||||||
@@ -365,7 +365,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
const typeIndex = fields.filter((f) => f.type === field.type).indexOf(field)
|
const typeIndex = fields.filter((f) => f.type === field.type).indexOf(field)
|
||||||
|
|
||||||
if (this.field.type === 'heading') {
|
if (field.type === 'heading') {
|
||||||
return `${this.fieldNames[field.type]} ${typeIndex + 1}`
|
return `${this.fieldNames[field.type]} ${typeIndex + 1}`
|
||||||
} else {
|
} else {
|
||||||
return `${this.fieldLabels[field.type]} ${typeIndex + 1}`
|
return `${this.fieldLabels[field.type]} ${typeIndex + 1}`
|
||||||
|
|||||||
@@ -229,6 +229,10 @@ export default {
|
|||||||
type: Array,
|
type: Array,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
template: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
withHelp: {
|
withHelp: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
@@ -380,6 +384,14 @@ export default {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.template.schema.forEach((item) => {
|
||||||
|
(item.conditions || []).forEach((c) => {
|
||||||
|
if (c.field_uuid === field.uuid) {
|
||||||
|
item.conditions.splice(item.conditions.indexOf(c), 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
this.save()
|
this.save()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,30 @@
|
|||||||
loading="lazy"
|
loading="lazy"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="group flex justify-end cursor-pointer top-0 bottom-0 left-0 right-0 absolute p-1"
|
class="group flex justify-end cursor-pointer top-0 bottom-0 left-0 right-0 absolute p-1 hover:bg-black/10 transition-colors"
|
||||||
@click="$emit('scroll-to', item)"
|
@click="$emit('scroll-to', item)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="editable"
|
v-if="editable"
|
||||||
class="flex justify-between w-full"
|
class="flex justify-between w-full"
|
||||||
>
|
>
|
||||||
<div style="width: 26px" />
|
<div
|
||||||
|
style="width: 26px"
|
||||||
|
class="flex flex-col justify-between group-hover:opacity-100"
|
||||||
|
:class="{'opacity-0': !item.conditions?.length }"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<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 p-0"
|
||||||
|
@click.stop="isShowConditionsModal = true"
|
||||||
|
>
|
||||||
|
<IconRouteAltLeft
|
||||||
|
:width="14"
|
||||||
|
:stroke-width="1.6"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="">
|
<div class="">
|
||||||
<ReplaceButton
|
<ReplaceButton
|
||||||
v-if="withReplaceButton"
|
v-if="withReplaceButton"
|
||||||
@@ -73,19 +89,36 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Teleport
|
||||||
|
v-if="isShowConditionsModal"
|
||||||
|
:to="modalContainerEl"
|
||||||
|
>
|
||||||
|
<ConditionsModal
|
||||||
|
:item="item"
|
||||||
|
:build-default-name="buildDefaultName"
|
||||||
|
@close="isShowConditionsModal = false"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Contenteditable from './contenteditable'
|
import Contenteditable from './contenteditable'
|
||||||
import Upload from './upload'
|
import Upload from './upload'
|
||||||
|
import { IconRouteAltLeft } from '@tabler/icons-vue'
|
||||||
|
import ConditionsModal from './conditions_modal'
|
||||||
import ReplaceButton from './replace'
|
import ReplaceButton from './replace'
|
||||||
|
import Field from './field'
|
||||||
|
import FieldType from './field_type'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DocumentPreview',
|
name: 'DocumentPreview',
|
||||||
components: {
|
components: {
|
||||||
Contenteditable,
|
Contenteditable,
|
||||||
|
IconRouteAltLeft,
|
||||||
|
ConditionsModal,
|
||||||
ReplaceButton
|
ReplaceButton
|
||||||
},
|
},
|
||||||
|
inject: ['t'],
|
||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -121,13 +154,24 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['scroll-to', 'change', 'remove', 'up', 'down', 'replace'],
|
emits: ['scroll-to', 'change', 'remove', 'up', 'down', 'replace'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isShowConditionsModal: false
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
fieldNames: FieldType.computed.fieldNames,
|
||||||
|
fieldLabels: FieldType.computed.fieldLabels,
|
||||||
previewImage () {
|
previewImage () {
|
||||||
return [...this.document.preview_images].sort((a, b) => parseInt(a.filename) - parseInt(b.filename))[0]
|
return [...this.document.preview_images].sort((a, b) => parseInt(a.filename) - parseInt(b.filename))[0]
|
||||||
|
},
|
||||||
|
modalContainerEl () {
|
||||||
|
return this.$el.getRootNode().querySelector('#docuseal_modal_container')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
upload: Upload.methods.upload,
|
upload: Upload.methods.upload,
|
||||||
|
buildDefaultName: Field.methods.buildDefaultName,
|
||||||
onUpdateName (value) {
|
onUpdateName (value) {
|
||||||
this.item.name = value
|
this.item.name = value
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,9 @@
|
|||||||
</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 || @submission.template.schema).each do |item| %>
|
<% values = @submission.submitters.reduce({}) { |acc, sub| acc.merge(sub.values) } %>
|
||||||
|
<% schema = Submissions.filtered_conditions_schema(@submission, values:) %>
|
||||||
|
<% schema.each do |item| %>
|
||||||
<% document = @submission.template_schema_documents.find { |a| item['attachment_uuid'] == a.uuid } %>
|
<% document = @submission.template_schema_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="<%= Docuseal::URL_CACHE.fetch([document.id, document.uuid, 0].join(':'), expires_in: 10.minutes) { 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="<%= Docuseal::URL_CACHE.fetch([document.id, document.uuid, 0].join(':'), expires_in: 10.minutes) { document.preview_images.first.url } %>" width="<%= document.preview_images.first.metadata['width'] %>" height="<%= document.preview_images.first.metadata['height'] %>" class="rounded border" loading="lazy">
|
||||||
@@ -79,10 +81,9 @@
|
|||||||
<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 || @submission.template.fields) %>
|
<% fields_index = Templates.build_field_areas_index(@submission.template_fields || @submission.template.fields) %>
|
||||||
<% submitters_index = @submission.submitters.index_by(&:uuid) %>
|
<% submitters_index = @submission.submitters.index_by(&:uuid) %>
|
||||||
<% 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) %>
|
||||||
<% page_blob_struct = Struct.new(:url, :metadata, keyword_init: true) %>
|
<% page_blob_struct = Struct.new(:url, :metadata, keyword_init: true) %>
|
||||||
<% (@submission.template_schema || @submission.template.schema).each do |item| %>
|
<% schema.each do |item| %>
|
||||||
<% document = @submission.template_schema_documents.find { |e| e.uuid == item['attachment_uuid'] } %>
|
<% document = @submission.template_schema_documents.find { |e| e.uuid == item['attachment_uuid'] } %>
|
||||||
<% document_annots_index = document.metadata.dig('pdf', 'annotations')&.group_by { |e| e['page'] } || {} %>
|
<% document_annots_index = document.metadata.dig('pdf', 'annotations')&.group_by { |e| e['page'] } || {} %>
|
||||||
<% preview_images_index = document.preview_images.loaded? ? document.preview_images.index_by { |e| e.filename.base.to_i } : {} %>
|
<% preview_images_index = document.preview_images.loaded? ? document.preview_images.index_by { |e| e.filename.base.to_i } : {} %>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<% data_attachments = attachments_index.values.select { |e| e.record_id == submitter.id }.to_json(only: %i[uuid created_at], methods: %i[url filename content_type]) %>
|
<% data_attachments = attachments_index.values.select { |e| e.record_id == submitter.id }.to_json(only: %i[uuid created_at], 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_fields = (submitter.submission.template_fields || submitter.submission.template.fields).select { |f| f['submitter_uuid'] == submitter.uuid }.to_json %>
|
||||||
<% invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
|
<% invite_submitters = (submitter.submission.template_submitters || submitter.submission.template.submitters).select { |s| s['invite_by_uuid'] == submitter.uuid && submitter.submission.submitters.none? { |e| e.uuid == s['uuid'] } }.to_json %>
|
||||||
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-reuse-signature="<%= configs[:reuse_signature] %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'].presence || submitter.submission.template.preferences['completed_redirect_url'] %>" data-completed-message="<%= (configs[:completed_message]&.compact_blank.presence || submitter.submission.template.preferences['completed_message'] || {}).to_json %>" data-completed-button="<%= configs[:completed_button].to_json %>" data-go-to-last="<%= submitter.preferences.key?('go_to_last') ? submitter.preferences['go_to_last'] : submitter.opened_at? %>" data-submitter="<%= submitter.to_json(only: %i[uuid slug name phone email]) %>" data-can-send-email="<%= Accounts.can_send_emails?(submitter.submission.account) %>" data-invite-submitters="<%= invite_submitters %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-previous-signature-value="<%= local_assigns[:signature_attachment]&.uuid %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-expand="<%= local_assigns[:expand] %>" data-scroll-padding="<%= local_assigns[:scroll_padding] %>" data-language="<%= I18n.locale.to_s.split('-').first %>"></submission-form>
|
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-schema="<%= schema.to_json %>" data-reuse-signature="<%= configs[:reuse_signature] %>" data-require-signing-reason="<%= configs[:require_signing_reason] %>" data-with-signature-id="<%= configs[:with_signature_id] %>" data-with-confetti="<%= configs[:with_confetti] %>" data-completed-redirect-url="<%= submitter.preferences['completed_redirect_url'].presence || submitter.submission.template.preferences['completed_redirect_url'] %>" data-completed-message="<%= (configs[:completed_message]&.compact_blank.presence || submitter.submission.template.preferences['completed_message'] || {}).to_json %>" data-completed-button="<%= configs[:completed_button].to_json %>" data-go-to-last="<%= submitter.preferences.key?('go_to_last') ? submitter.preferences['go_to_last'] : submitter.opened_at? %>" data-submitter="<%= submitter.to_json(only: %i[uuid slug name phone email]) %>" data-can-send-email="<%= Accounts.can_send_emails?(submitter.submission.account) %>" data-invite-submitters="<%= invite_submitters %>" data-attachments="<%= data_attachments %>" data-fields="<%= data_fields %>" data-values="<%= submitter.values.to_json %>" data-with-typed-signature="<%= configs[:with_typed_signature] %>" data-previous-signature-value="<%= local_assigns[:signature_attachment]&.uuid %>" data-remember-signature="<%= configs[:prefill_signature] %>" data-dry-run="<%= local_assigns[:dry_run] %>" data-expand="<%= local_assigns[:expand] %>" data-scroll-padding="<%= local_assigns[:scroll_padding] %>" data-language="<%= I18n.locale.to_s.split('-').first %>"></submission-form>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<% values = @submitter.submission.submitters.reduce({}) { |acc, sub| acc.merge(sub.values) } %>
|
<% values = @submitter.submission.submitters.reduce({}) { |acc, sub| acc.merge(sub.values) } %>
|
||||||
<% submitters_index = @submitter.submission.submitters.index_by(&:uuid) %>
|
<% submitters_index = @submitter.submission.submitters.index_by(&:uuid) %>
|
||||||
<% page_blob_struct = Struct.new(:url, :metadata, keyword_init: true) %>
|
<% page_blob_struct = Struct.new(:url, :metadata, keyword_init: true) %>
|
||||||
|
<% schema = Submissions.filtered_conditions_schema(@submitter.submission, values:, include_submitter_uuid: @submitter.uuid) %>
|
||||||
<div style="max-height: -webkit-fill-available;">
|
<div style="max-height: -webkit-fill-available;">
|
||||||
<div id="scrollbox">
|
<div id="scrollbox">
|
||||||
<div class="mx-auto block pb-72" style="max-width: 1000px">
|
<div class="mx-auto block pb-72" style="max-width: 1000px">
|
||||||
@@ -35,32 +36,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% (@submitter.submission.template_schema || @submitter.submission.template.schema).each do |item| %>
|
<% schema.each do |item| %>
|
||||||
<% document = @submitter.submission.template_schema_documents.find { |a| a.uuid == item['attachment_uuid'] } %>
|
<% document = @submitter.submission.template_schema_documents.find { |a| a.uuid == item['attachment_uuid'] } %>
|
||||||
<% document_annots_index = document.metadata.dig('pdf', 'annotations')&.group_by { |e| e['page'] } || {} %>
|
<div id="document-<%= document.uuid %>">
|
||||||
<% preview_images_index = document.preview_images.loaded? ? document.preview_images.index_by { |e| e.filename.base.to_i } : {} %>
|
<% document_annots_index = document.metadata.dig('pdf', 'annotations')&.group_by { |e| e['page'] } || {} %>
|
||||||
<% lazyload_metadata = document.preview_images.last.metadata %>
|
<% preview_images_index = document.preview_images.loaded? ? document.preview_images.index_by { |e| e.filename.base.to_i } : {} %>
|
||||||
<% (document.metadata.dig('pdf', 'number_of_pages') || (document.preview_images.loaded? ? preview_images_index.size : document.preview_images.size)).times do |index| %>
|
<% lazyload_metadata = document.preview_images.last.metadata %>
|
||||||
<% page = preview_images_index[index] || page_blob_struct.new(metadata: lazyload_metadata, url: preview_document_page_path(document.signed_uuid, "#{index}.jpg")) %>
|
<% (document.metadata.dig('pdf', 'number_of_pages') || (document.preview_images.loaded? ? preview_images_index.size : document.preview_images.size)).times do |index| %>
|
||||||
<div class="relative my-4 shadow-md">
|
<% page = preview_images_index[index] || page_blob_struct.new(metadata: lazyload_metadata, url: preview_document_page_path(document.signed_uuid, "#{index}.jpg")) %>
|
||||||
<img loading="lazy" src="<%= page.url %>" width="<%= page.metadata['width'] %>" height="<%= page.metadata['height'] %>">
|
<div class="relative my-4 shadow-md">
|
||||||
<div id="page-<%= [document.uuid, index].join('-') %>" class="top-0 bottom-0 left-0 right-0 absolute">
|
<img loading="lazy" src="<%= page.url %>" width="<%= page.metadata['width'] %>" height="<%= page.metadata['height'] %>">
|
||||||
<% document_annots_index[index]&.each do |annot| %>
|
<div id="page-<%= [document.uuid, index].join('-') %>" class="top-0 bottom-0 left-0 right-0 absolute">
|
||||||
<%= render 'submissions/annotation', annot: %>
|
<% document_annots_index[index]&.each do |annot| %>
|
||||||
<% end %>
|
<%= render 'submissions/annotation', annot: %>
|
||||||
<% fields_index.dig(document.uuid, index)&.each do |(area, field)| %>
|
<% end %>
|
||||||
<% value = values[field['uuid']].presence || (field['default_value'].present? ? Submitters::SubmitValues.template_default_value_for_submitter(field['default_value'], @submitter.submission.submitters.find { |e| e.uuid == field['submitter_uuid'] }, with_time: false) : nil) %>
|
<% fields_index.dig(document.uuid, index)&.each do |(area, field)| %>
|
||||||
<% next if value.blank? %>
|
<% value = values[field['uuid']].presence || (field['default_value'].present? ? Submitters::SubmitValues.template_default_value_for_submitter(field['default_value'], @submitter.submission.submitters.find { |e| e.uuid == field['submitter_uuid'] }, with_time: false) : nil) %>
|
||||||
<% next if !field['readonly'] && field['submitter_uuid'] == @submitter.uuid %>
|
<% next if value.blank? %>
|
||||||
<% next if value == '{{date}}' && field['submitter_uuid'] != @submitter.uuid %>
|
<% next if !field['readonly'] && field['submitter_uuid'] == @submitter.uuid %>
|
||||||
<% next if field['conditions'].present? && values[field['uuid']].blank? && field['submitter_uuid'] != @submitter.uuid %>
|
<% next if value == '{{date}}' && field['submitter_uuid'] != @submitter.uuid %>
|
||||||
<% next if field['conditions'].present? && field['submitter_uuid'] == @submitter.uuid %>
|
<% next if field['conditions'].present? && values[field['uuid']].blank? && field['submitter_uuid'] != @submitter.uuid %>
|
||||||
<% next if field.dig('preferences', 'formula').present? && field['submitter_uuid'] == @submitter.uuid %>
|
<% next if field['conditions'].present? && field['submitter_uuid'] == @submitter.uuid %>
|
||||||
<%= render 'submissions/value', area:, field:, attachments_index: @attachments_index, value:, locale: @submitter.account.locale, timezone: @submitter.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: @form_configs[:with_signature_id] %>
|
<% next if field.dig('preferences', 'formula').present? && field['submitter_uuid'] == @submitter.uuid %>
|
||||||
<% end %>
|
<%= render 'submissions/value', area:, field:, attachments_index: @attachments_index, value:, locale: @submitter.account.locale, timezone: @submitter.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: @form_configs[:with_signature_id] %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<% end %>
|
||||||
<% end %>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= render 'shared/attribution', link_path: '/start', account: @submitter.account, with_style: false %>
|
<%= render 'shared/attribution', link_path: '/start', account: @submitter.account, with_style: false %>
|
||||||
<% if @form_configs[:policy_links].present? %>
|
<% if @form_configs[:policy_links].present? %>
|
||||||
@@ -76,7 +79,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">
|
||||||
<%= render 'submit_form/submission_form', attachments_index: @attachments_index, submitter: @submitter, signature_attachment: @signature_attachment, configs: @form_configs, dry_run: local_assigns[:dry_run], expand: local_assigns[:expand], scroll_padding: local_assigns.fetch(:scroll_padding, '-110px') %>
|
<%= render 'submit_form/submission_form', attachments_index: @attachments_index, submitter: @submitter, signature_attachment: @signature_attachment, configs: @form_configs, dry_run: local_assigns[:dry_run], expand: local_assigns[:expand], scroll_padding: local_assigns.fetch(:scroll_padding, '-110px'), schema: %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -124,4 +124,42 @@ module Submissions
|
|||||||
|
|
||||||
fixed_email
|
fixed_email
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filtered_conditions_schema(submission, values: nil, include_submitter_uuid: nil)
|
||||||
|
fields_uuid_index = nil
|
||||||
|
|
||||||
|
(submission.template_schema || submission.template.schema).filter_map do |item|
|
||||||
|
if item['conditions'].present?
|
||||||
|
fields_uuid_index ||=
|
||||||
|
(submission.template_fields || submission.template.fields).index_by { |f| f['uuid'] }
|
||||||
|
|
||||||
|
values ||= submission.submitters.reduce({}) { |acc, sub| acc.merge(sub.values) }
|
||||||
|
|
||||||
|
next unless check_document_conditions(item, values, fields_uuid_index, include_submitter_uuid:)
|
||||||
|
end
|
||||||
|
|
||||||
|
item
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_document_conditions(item, values, fields_index, include_submitter_uuid: nil)
|
||||||
|
return true if item['conditions'].blank?
|
||||||
|
|
||||||
|
item['conditions'].all? do |condition|
|
||||||
|
result =
|
||||||
|
if fields_index[condition['field_uuid']]['submitter_uuid'] == include_submitter_uuid
|
||||||
|
true
|
||||||
|
else
|
||||||
|
Submitters::SubmitValues.check_field_condition(condition, values, fields_index)
|
||||||
|
end
|
||||||
|
|
||||||
|
item['conditions'].each_with_object([]) do |c, acc|
|
||||||
|
if c['operation'] == 'or'
|
||||||
|
acc.push(acc.pop || result)
|
||||||
|
else
|
||||||
|
acc.push(result)
|
||||||
|
end
|
||||||
|
end.exclude?(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ module Submissions
|
|||||||
submitter.submission.template_schema.each do |item|
|
submitter.submission.template_schema.each do |item|
|
||||||
pdf = pdfs_index[item['attachment_uuid']]
|
pdf = pdfs_index[item['attachment_uuid']]
|
||||||
|
|
||||||
|
next unless pdf
|
||||||
|
|
||||||
pdf.dispatch_message(:complete_objects)
|
pdf.dispatch_message(:complete_objects)
|
||||||
|
|
||||||
pdf.pages.each { |page| result.pages << result.import(page) }
|
pdf.pages.each { |page| result.pages << result.import(page) }
|
||||||
|
|||||||
@@ -37,9 +37,11 @@ module Submissions
|
|||||||
original_documents = template.documents.preload(:blob)
|
original_documents = template.documents.preload(:blob)
|
||||||
|
|
||||||
result_attachments =
|
result_attachments =
|
||||||
(submission.template_schema || template.schema).map do |item|
|
(submission.template_schema || template.schema).filter_map do |item|
|
||||||
pdf = pdfs_index[item['attachment_uuid']]
|
pdf = pdfs_index[item['attachment_uuid']]
|
||||||
|
|
||||||
|
next if pdf.nil?
|
||||||
|
|
||||||
if original_documents.find { |a| a.uuid == item['attachment_uuid'] }.image?
|
if original_documents.find { |a| a.uuid == item['attachment_uuid'] }.image?
|
||||||
pdf = GenerateResultAttachments.normalize_image_pdf(pdf)
|
pdf = GenerateResultAttachments.normalize_image_pdf(pdf)
|
||||||
|
|
||||||
|
|||||||
@@ -56,9 +56,11 @@ module Submissions
|
|||||||
original_documents = template.documents.preload(:blob)
|
original_documents = template.documents.preload(:blob)
|
||||||
|
|
||||||
result_attachments =
|
result_attachments =
|
||||||
submitter.submission.template_schema.map do |item|
|
submitter.submission.template_schema.filter_map do |item|
|
||||||
pdf = pdfs_index[item['attachment_uuid']]
|
pdf = pdfs_index[item['attachment_uuid']]
|
||||||
|
|
||||||
|
next if pdf.nil?
|
||||||
|
|
||||||
if original_documents.find { |a| a.uuid == item['attachment_uuid'] }.image?
|
if original_documents.find { |a| a.uuid == item['attachment_uuid'] }.image?
|
||||||
pdf = normalize_image_pdf(pdf)
|
pdf = normalize_image_pdf(pdf)
|
||||||
|
|
||||||
@@ -545,7 +547,15 @@ module Submissions
|
|||||||
documents = latest_submitter&.documents&.preload(:blob).to_a.presence
|
documents = latest_submitter&.documents&.preload(:blob).to_a.presence
|
||||||
documents ||= submission.template_schema_documents.preload(:blob)
|
documents ||= submission.template_schema_documents.preload(:blob)
|
||||||
|
|
||||||
documents.to_h do |attachment|
|
attachment_uuids = Submissions.filtered_conditions_schema(submission).pluck('attachment_uuid')
|
||||||
|
attachments_index = documents.index_by { |a| a.metadata['original_uuid'] || a.uuid }
|
||||||
|
|
||||||
|
attachment_uuids.each_with_object({}) do |uuid, acc|
|
||||||
|
attachment = attachments_index[uuid]
|
||||||
|
attachment ||= submission.template_schema_documents.preload(:blob).find { |a| a.uuid == uuid }
|
||||||
|
|
||||||
|
next unless attachment
|
||||||
|
|
||||||
pdf =
|
pdf =
|
||||||
if attachment.image?
|
if attachment.image?
|
||||||
build_pdf_from_image(attachment)
|
build_pdf_from_image(attachment)
|
||||||
@@ -555,23 +565,23 @@ module Submissions
|
|||||||
|
|
||||||
pdf = maybe_rotate_pdf(pdf)
|
pdf = maybe_rotate_pdf(pdf)
|
||||||
|
|
||||||
if flatten
|
maybe_flatten_pdf(pdf) if flatten
|
||||||
begin
|
|
||||||
pdf.acro_form.create_appearances(force: true) if pdf.acro_form && pdf.acro_form[:NeedAppearances]
|
|
||||||
pdf.acro_form&.flatten
|
|
||||||
rescue HexaPDF::MissingGlyphError
|
|
||||||
nil
|
|
||||||
rescue StandardError => e
|
|
||||||
Rollbar.error(e) if defined?(Rollbar)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
pdf.config['font.on_missing_glyph'] = method(:on_missing_glyph).to_proc
|
pdf.config['font.on_missing_glyph'] = method(:on_missing_glyph).to_proc
|
||||||
|
|
||||||
[attachment.metadata['original_uuid'] || attachment.uuid, pdf]
|
acc[uuid] = pdf
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def maybe_flatten_pdf(pdf)
|
||||||
|
pdf.acro_form.create_appearances(force: true) if pdf.acro_form && pdf.acro_form[:NeedAppearances]
|
||||||
|
pdf.acro_form&.flatten
|
||||||
|
rescue HexaPDF::MissingGlyphError
|
||||||
|
nil
|
||||||
|
rescue StandardError => e
|
||||||
|
Rollbar.error(e) if defined?(Rollbar)
|
||||||
|
end
|
||||||
|
|
||||||
def maybe_rotate_pdf(pdf)
|
def maybe_rotate_pdf(pdf)
|
||||||
return pdf if pdf.pages.size > MAX_PAGE_ROTATE
|
return pdf if pdf.pages.size > MAX_PAGE_ROTATE
|
||||||
|
|
||||||
|
|||||||
@@ -183,10 +183,17 @@ module Submitters
|
|||||||
def maybe_remove_condition_values(submitter)
|
def maybe_remove_condition_values(submitter)
|
||||||
fields_uuid_index = submitter.submission.template_fields.index_by { |e| e['uuid'] }
|
fields_uuid_index = submitter.submission.template_fields.index_by { |e| e['uuid'] }
|
||||||
|
|
||||||
|
attachments_index =
|
||||||
|
Submissions.filtered_conditions_schema(submitter.submission).index_by { |i| i['attachment_uuid'] }
|
||||||
|
|
||||||
submitter.submission.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
|
||||||
|
|
||||||
submitter.values.delete(field['uuid']) unless check_field_conditions(submitter, field, fields_uuid_index)
|
submitter.values.delete(field['uuid']) unless check_field_conditions(submitter, field, fields_uuid_index)
|
||||||
|
|
||||||
|
if field['areas'].present? && field['areas'].none? { |area| attachments_index[area['attachment_uuid']] }
|
||||||
|
submitter.values.delete(field['uuid'])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
submitter.values
|
submitter.values
|
||||||
|
|||||||
Reference in New Issue
Block a user