style submitter form

pull/105/head
Alex Turchyn 2 years ago
parent 199ccf082b
commit fef54859bf

@ -31,20 +31,8 @@ button[disabled] .enabled {
display: none; display: none;
} }
button .disabled { select:required:invalid {
display: none; @apply text-gray-300;
}
button[disabled] .disabled {
display: initial;
}
button .enabled {
display: initial;
}
button[disabled] .enabled {
display: none;
} }
.btn { .btn {
@ -54,6 +42,15 @@ button[disabled] .enabled {
.base-input { .base-input {
@apply input input-bordered bg-white; @apply input input-bordered bg-white;
} }
.base-button { .base-button {
@apply btn btn-neutral text-white text-base; @apply btn btn-neutral text-white text-base;
} }
.base-checkbox {
@apply checkbox rounded bg-white checkbox-sm;
}
.base-radio {
@apply radio bg-white radio-sm;
}

@ -1,8 +1,36 @@
<template> <template>
<div <div
class="flex cursor-pointer bg-red-100 bg-opacity-60 absolute" class="flex cursor-pointer bg-red-100 absolute border"
:style="computedStyle" :style="computedStyle"
:class="{ 'border-red-100 bg-opacity-70': !isActive, 'border-red-500 border-dashed bg-opacity-30 z-10': isActive }"
> >
<div
v-if="!isActive && !modelValue"
class="absolute top-0 bottom-0 right-0 left-0 items-center justify-center h-full w-full"
>
<span
v-if="field"
class="flex justify-center items-center h-full opacity-50"
>
<component
:is="fieldIcons[field.type]"
width="100%"
height="100%"
class="max-h-10 text-base-content"
/>
</span>
</div>
<div
v-if="isActive"
class="absolute -top-7 rounded bg-base-content text-base-100 px-2 text-sm whitespace-nowrap"
>
{{ field.name || fieldNames[field.type] }}
</div>
<div
v-if="isActive"
ref="scrollToElem"
class="absolute -top-20"
/>
<img <img
v-if="field.type === 'image' && image" v-if="field.type === 'image' && image"
class="object-contain" class="object-contain"
@ -23,12 +51,14 @@
</a> </a>
</div> </div>
<span v-else> <span v-else>
{{ value }} {{ modelValue }}
</span> </span>
</div> </div>
</template> </template>
<script> <script>
import { IconTextSize, IconWriting, IconCalendarEvent, IconPhoto, IconCheckbox, IconPaperclip, IconSelect, IconCircleDot } from '@tabler/icons-vue'
export default { export default {
name: 'FieldArea', name: 'FieldArea',
props: { props: {
@ -36,11 +66,16 @@ export default {
type: Object, type: Object,
required: true required: true
}, },
value: { modelValue: {
type: [Array, String, Number, Object], type: [Array, String, Number, Object, Boolean],
required: false, required: false,
default: '' default: ''
}, },
isActive: {
type: Boolean,
required: false,
default: false
},
attachmentsIndex: { attachmentsIndex: {
type: Object, type: Object,
required: false, required: false,
@ -51,24 +86,49 @@ export default {
required: true required: true
} }
}, },
emits: ['update:model-value'],
computed: { computed: {
fieldNames () {
return {
text: 'Text',
signature: 'Signature',
date: 'Date',
image: 'Image',
file: 'File',
select: 'Select',
checkbox: 'Checkbox',
radio: 'Radio'
}
},
fieldIcons () {
return {
text: IconTextSize,
signature: IconWriting,
date: IconCalendarEvent,
image: IconPhoto,
file: IconPaperclip,
select: IconSelect,
checkbox: IconCheckbox,
radio: IconCircleDot
}
},
image () { image () {
if (this.field.type === 'image') { if (this.field.type === 'image') {
return this.attachmentsIndex[this.value] return this.attachmentsIndex[this.modelValue]
} else { } else {
return null return null
} }
}, },
signature () { signature () {
if (this.field.type === 'signature') { if (this.field.type === 'signature') {
return this.attachmentsIndex[this.value] return this.attachmentsIndex[this.modelValue]
} else { } else {
return null return null
} }
}, },
attachments () { attachments () {
if (this.field.type === 'attachment') { if (this.field.type === 'attachment') {
return (this.value || []).map((uuid) => this.attachmentsIndex[uuid]) return (this.modelValue || []).map((uuid) => this.attachmentsIndex[uuid])
} else { } else {
return [] return []
} }

@ -10,10 +10,11 @@
<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]"
:field="field" :field="field"
:area="area" :area="area"
:is-active="currentField === field"
:attachments-index="attachmentsIndex" :attachments-index="attachmentsIndex"
:value="values[field.uuid]"
@click="$emit('focus-field', field)" @click="$emit('focus-field', field)"
/> />
</Teleport> </Teleport>
@ -44,6 +45,11 @@ export default {
type: Object, type: Object,
required: false, required: false,
default: () => ({}) default: () => ({})
},
currentField: {
type: Object,
required: false,
default: () => ({})
} }
}, },
emits: ['focus-field'], emits: ['focus-field'],
@ -59,7 +65,7 @@ export default {
scrollIntoField (field) { scrollIntoField (field) {
this.areaRefs.find((area) => { this.areaRefs.find((area) => {
if (area.field === field) { if (area.field === field) {
area.$el.scrollIntoView({ behavior: 'smooth', block: 'center' }) area.$refs.scrollToElem.scrollIntoView({ behavior: 'smooth', block: 'start' })
return true return true
} else { } else {

@ -1,29 +1,42 @@
<template> <template>
<div> <div>
<template v-if="modelValue.length"> <div v-if="modelValue.length">
<div <div
v-for="(val, index) in modelValue" v-for="(val, index) in modelValue"
:key="index" :key="index"
class="flex mb-2"
>
<input
:value="val"
type="hidden"
:name="`values[${field.uuid}][]`"
> >
<a <a
v-if="val" v-if="val"
class="flex items-center space-x-1.5 w-full"
:href="attachmentsIndex[val].url" :href="attachmentsIndex[val].url"
target="_blank"
> >
<IconPaperclip
:width="16"
class="flex-none"
:heigh="16"
/>
<span>
{{ attachmentsIndex[val].filename }} {{ attachmentsIndex[val].filename }}
</span>
</a> </a>
<input
:value="val"
type="hidden"
:name="`values[${field.uuid}][]`"
>
<button <button
v-if="modelValue" v-if="modelValue"
@click.prevent="removeAttachment(val)" @click.prevent="removeAttachment(val)"
> >
Remove <IconTrashX
:width="18"
:heigh="19"
/>
</button> </button>
</div> </div>
</template> </div>
<template v-else> <template v-else>
<input <input
value="" value=""
@ -32,7 +45,7 @@
> >
</template> </template>
<FileDropzone <FileDropzone
:message="'Attachments'" :message="`Upload ${field.name || 'Attachments'}`"
:submitter-slug="submitterSlug" :submitter-slug="submitterSlug"
@upload="onUpload" @upload="onUpload"
/> />
@ -41,11 +54,14 @@
<script> <script>
import FileDropzone from './dropzone' import FileDropzone from './dropzone'
import { IconPaperclip, IconTrashX } from '@tabler/icons-vue'
export default { export default {
name: 'AttachmentStep', name: 'AttachmentStep',
components: { components: {
FileDropzone FileDropzone,
IconPaperclip,
IconTrashX
}, },
props: { props: {
field: { field: {

@ -1,22 +1,36 @@
<template> <template>
<label
v-if="field.name"
:for="field.uuid"
class="label text-2xl mb-2"
>{{ field.name }}</label>
<div class="space-y-3.5">
<div <div
v-for="(option, index) in field.options" v-for="(option, index) in field.options"
:key="index" :key="index"
> >
<label :for="field.uuid + option"> <label
:for="field.uuid + option"
class="flex items-center space-x-3"
>
<input <input
:id="field.uuid + option" :id="field.uuid + option"
:ref="setInputRef" :ref="setInputRef"
type="checkbox" type="checkbox"
:name="`values[${field.uuid}][]`" :name="`values[${field.uuid}][]`"
:value="option" :value="option"
class="base-checkbox !h-7 !w-7"
:checked="modelValue.includes(option)" :checked="modelValue.includes(option)"
@change="onChange" @change="onChange"
> >
<span class="text-xl">
{{ option }} {{ option }}
</span>
</label> </label>
</div> </div>
</div>
</template> </template>
<script> <script>
export default { export default {
name: 'SheckboxStep', name: 'SheckboxStep',

@ -1,16 +1,30 @@
<template> <template>
<div <div
class="flex h-20 w-full" class="flex h-32 w-full"
@dragover.prevent @dragover.prevent
@drop.prevent="onDropFiles" @drop.prevent="onDropFiles"
> >
<label <label
:for="inputId" :for="inputId"
class="w-full" class="w-full relative bg-base-300 hover:bg-base-200 rounded-md border border-base-content border-dashed"
>
<div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center">
<div class="flex flex-col items-center">
<IconCloudUpload
:width="30"
:height="30"
/>
<div
v-if="message"
class="font-medium mb-1"
> >
Upload
{{ message }} {{ message }}
</label> </div>
<div class="text-xs">
<span class="font-medium">Click to upload</span> or drag and drop
</div>
</div>
</div>
<input <input
:id="inputId" :id="inputId"
ref="input" ref="input"
@ -20,14 +34,19 @@
class="hidden" class="hidden"
@change="onSelectFiles" @change="onSelectFiles"
> >
</label>
</div> </div>
</template> </template>
<script> <script>
import { DirectUpload } from '@rails/activestorage' import { DirectUpload } from '@rails/activestorage'
import { IconCloudUpload } from '@tabler/icons-vue'
export default { export default {
name: 'FileDropzone', name: 'FileDropzone',
components: {
IconCloudUpload
},
props: { props: {
message: { message: {
type: String, type: String,
@ -62,7 +81,9 @@ export default {
e.preventDefault() e.preventDefault()
this.uploadFiles(this.$refs.input.files).then(() => { this.uploadFiles(this.$refs.input.files).then(() => {
if (this.$refs.input) {
this.$refs.input.value = '' this.$refs.input.value = ''
}
}) })
}, },
async uploadFiles (files) { async uploadFiles (files) {

@ -4,20 +4,15 @@
:fields="submitterFields" :fields="submitterFields"
:values="values" :values="values"
:attachments-index="attachmentsIndex" :attachments-index="attachmentsIndex"
@focus-field="goToField" :current-field="currentField"
@focus-field="goToField($event, false, true)"
/> />
<button
v-if="currentStep !== 0"
@click="goToField(submitterFields[currentStep - 1], true)"
>
Back
</button>
{{ currentField.type }}
<form <form
v-if="!isCompleted" v-if="!isCompleted"
ref="form" ref="form"
:action="submitPath" :action="submitPath"
method="post" method="post"
class="md:mx-16"
@submit.prevent="submitStep" @submit.prevent="submitStep"
> >
<input <input
@ -36,46 +31,59 @@
name="_method" name="_method"
type="hidden" type="hidden"
> >
<div> <div class="mt-4">
<template v-if="currentField.type === 'text'"> <div v-if="currentField.type === 'text'">
<label :for="currentField.uuid">{{ currentField.name || 'Text' }}</label> <label
v-if="currentField.name"
:for="currentField.uuid"
class="label text-2xl mb-2"
>{{ currentField.name }}</label>
<div> <div>
<input <input
:id="currentField.uuid" :id="currentField.uuid"
v-model="values[currentField.uuid]" v-model="values[currentField.uuid]"
autofocus autofocus
class="text-xl" class="base-input !text-2xl w-full"
:required="currentField.required" :required="currentField.required"
placeholder="Type here..."
type="text" type="text"
:name="`values[${currentField.uuid}]`" :name="`values[${currentField.uuid}]`"
> >
</div> </div>
</template> </div>
<template v-else-if="currentField.type === 'date'"> <div v-else-if="currentField.type === 'date'">
<label :for="currentField.uuid">{{ currentField.name || 'Date' }}</label> <label
v-if="currentField.name"
:for="currentField.uuid"
class="label text-2xl mb-2"
>{{ currentField.name }}</label>
<div> <div>
<input <input
:id="currentField.uuid" :id="currentField.uuid"
v-model="values[currentField.uuid]" v-model="values[currentField.uuid]"
class="text-xl" class="base-input !text-2xl w-full text-center"
autofocus autofocus
:required="currentField.required" :required="currentField.required"
type="date" type="date"
:name="`values[${currentField.uuid}]`" :name="`values[${currentField.uuid}]`"
> >
</div> </div>
</template> </div>
<template v-else-if="currentField.type === 'select'"> <div v-else-if="currentField.type === 'select'">
<label :for="currentField.uuid">{{ currentField.name || 'Date' }}</label> <label
v-if="currentField.name"
:for="currentField.uuid"
class="label text-2xl mb-2"
>{{ currentField.name }}</label>
<select <select
:id="currentField.uuid" :id="currentField.uuid"
v-model="values[currentField.uuid]" :required="true"
:required="currentField.required" class="select base-input !text-2xl w-full text-center font-normal"
:name="`values[${currentField.uuid}]`" :name="`values[${currentField.uuid}]`"
@change="values[currentField.uuid] = $event.target.value"
> >
<option <option
value="" value=""
disabled
:selected="!values[currentField.uuid]" :selected="!values[currentField.uuid]"
> >
Select your option Select your option
@ -83,35 +91,72 @@
<option <option
v-for="(option, index) in currentField.options" v-for="(option, index) in currentField.options"
:key="index" :key="index"
:select="values[currentField.uuid] == option" :selected="values[currentField.uuid] == option"
:value="option" :value="option"
> >
{{ option }} {{ option }}
</option> </option>
</select> </select>
</template> </div>
<template v-else-if="currentField.type === 'radio'"> <div v-else-if="currentField.type === 'radio' && currentField.options?.length">
<label
v-if="currentField.name"
:for="currentField.uuid"
class="label text-2xl mb-2"
>{{ currentField.name }}</label>
<div class="space-y-3.5">
<div <div
v-for="(option, index) in currentField.options" v-for="(option, index) in currentField.options"
:key="index" :key="index"
> >
<label :for="currentField.uuid + option"> <label
:for="currentField.uuid + option"
class="flex items-center space-x-3"
>
<input <input
:id="currentField.uuid + option" :id="currentField.uuid + option"
v-model="values[currentField.uuid]" v-model="values[currentField.uuid]"
type="radio" type="radio"
class="base-radio !h-7 !w-7"
:name="`values[${currentField.uuid}]`" :name="`values[${currentField.uuid}]`"
:value="option" :value="option"
required
> >
<span class="text-xl">
{{ option }} {{ option }}
</span>
</label> </label>
</div> </div>
</template> </div>
</div>
<CheckboxStep <CheckboxStep
v-else-if="currentField.type === 'checkbox'" v-else-if="currentField.type === 'checkbox' && currentField.options?.length"
v-model="values[currentField.uuid]" v-model="values[currentField.uuid]"
:field="currentField" :field="currentField"
/> />
<div v-else-if="['radio', 'checkbox'].includes(currentField.type)">
<div class="flex justify-center">
<label
:for="currentField.uuid"
class="flex items-center space-x-3"
>
<input
:id="currentField.uuid"
:model-value="values[currentField.uuid]"
:type="currentField.type"
:name="`values[${currentField.uuid}]`"
:value="true"
class="!h-7 !w-7"
:class="{'base-radio' : currentField.type === 'radio', 'base-checkbox': currentField.type === 'checkbox'}"
:checked="!!values[currentField.uuid]"
@click="values[currentField.uuid] = !values[currentField.uuid]"
>
<span class="text-xl">
{{ currentField.name || currentField.type }}
</span>
</label>
</div>
</div>
<ImageStep <ImageStep
v-else-if="currentField.type === 'image'" v-else-if="currentField.type === 'image'"
v-model="values[currentField.uuid]" v-model="values[currentField.uuid]"
@ -138,8 +183,11 @@
@attached="attachments.push($event)" @attached="attachments.push($event)"
/> />
</div> </div>
<div> <div class="mt-8">
<button type="submit"> <button
type="submit"
class="base-button w-full"
>
<span v-if="isSubmitting"> <span v-if="isSubmitting">
Submitting... Submitting...
</span> </span>
@ -148,6 +196,18 @@
</span> </span>
</button> </button>
</div> </div>
<div class="flex justify-center">
<div class="flex items-center mt-5 mb-1">
<a
v-for="(field, index) in submitterFields"
:key="field.uuid"
href="#"
class="inline border border-base-300 h-3 w-3 rounded-full mx-1"
:class="{ 'bg-base-200': index === currentStep, 'bg-base-content': index < currentStep, 'bg-white': index > currentStep }"
@click.prevent="goToField(field, true)"
/>
</div>
</div>
</form> </form>
<FormCompleted <FormCompleted
v-else v-else
@ -234,7 +294,7 @@ export default {
) )
}, },
methods: { methods: {
goToField (field, scrollToArea = false) { goToField (field, scrollToArea = false, clickUpload = false) {
this.currentStep = this.submitterFields.indexOf(field) this.currentStep = this.submitterFields.indexOf(field)
this.$nextTick(() => { this.$nextTick(() => {
@ -243,6 +303,10 @@ export default {
} }
this.$refs.form.querySelector('input[type="date"], input[type="text"], select')?.focus() this.$refs.form.querySelector('input[type="date"], input[type="text"], select')?.focus()
if (clickUpload && !this.values[this.currentField.uuid]) {
this.$refs.form.querySelector('input[type="file"]')?.click()
}
}) })
}, },
async submitStep () { async submitStep () {

@ -1,23 +1,33 @@
<template> <template>
<div> <div v-if="modelValue">
<img <div class="flex justify-between items-center w-full mb-2">
v-if="modelValue" <label
class="w-80" class="label text-2xl"
:src="attachmentsIndex[modelValue].url" >{{ field.name || 'Image' }}</label>
>
<button <button
v-if="modelValue" class="btn btn-outline btn-sm"
@click.prevent="remove" @click.prevent="remove"
> >
Remove <IconReload :width="16" />
Reupload
</button> </button>
</div>
<div>
<img
:src="attachmentsIndex[modelValue].url"
class="h-52 border border-base-300 rounded mx-auto"
>
</div>
<input <input
:value="modelValue" :value="modelValue"
type="hidden" type="hidden"
:name="`values[${field.uuid}]`" :name="`values[${field.uuid}]`"
> >
</div>
<div>
<FileDropzone <FileDropzone
:message="'Image'" v-if="!modelValue"
:message="`Upload ${field.name || 'Image'}`"
:submitter-slug="submitterSlug" :submitter-slug="submitterSlug"
:accept="'image/*'" :accept="'image/*'"
@upload="onImageUpload" @upload="onImageUpload"
@ -27,10 +37,13 @@
<script> <script>
import FileDropzone from './dropzone' import FileDropzone from './dropzone'
import { IconReload } from '@tabler/icons-vue'
export default { export default {
name: 'ImageStep', name: 'ImageStep',
components: { components: {
FileDropzone FileDropzone,
IconReload
}, },
props: { props: {
field: { field: {

@ -1,5 +1,26 @@
<template> <template>
<div> <div>
<div class="flex justify-between items-center w-full mb-2">
<label
class="label text-2xl"
>{{ field.name || 'Signature' }}</label>
<button
v-if="modelValue"
class="btn btn-outline btn-sm"
@click.prevent="remove"
>
<IconReload :width="16" />
Redraw
</button>
<button
v-else
class="btn btn-outline btn-sm"
@click.prevent="clear"
>
<IconReload :width="16" />
Clear
</button>
</div>
<input <input
:value="modelValue" :value="modelValue"
type="hidden" type="hidden"
@ -8,32 +29,26 @@
<img <img
v-if="modelValue" v-if="modelValue"
:src="attachmentsIndex[modelValue].url" :src="attachmentsIndex[modelValue].url"
class="w-full bg-white border border-base-300 rounded"
> >
<canvas <canvas
v-show="!modelValue" v-show="!modelValue"
ref="canvas" ref="canvas"
class="bg-white border border-base-300 rounded"
/> />
<button
v-if="modelValue"
@click.prevent="remove"
>
Redraw
</button>
<button
v-else
@click.prevent="clear"
>
Clear
</button>
</div> </div>
</template> </template>
<script> <script>
import SignaturePad from 'signature_pad' import SignaturePad from 'signature_pad'
import { DirectUpload } from '@rails/activestorage' import { DirectUpload } from '@rails/activestorage'
import { IconReload } from '@tabler/icons-vue'
export default { export default {
name: 'SignatureStep', name: 'SignatureStep',
components: {
IconReload
},
props: { props: {
field: { field: {
type: Object, type: Object,
@ -56,6 +71,9 @@ export default {
}, },
emits: ['attached', 'update:model-value'], emits: ['attached', 'update:model-value'],
mounted () { mounted () {
this.$refs.canvas.width = this.$refs.canvas.parentNode.clientWidth
this.$refs.canvas.height = this.$refs.canvas.parentNode.clientWidth / 3
this.pad = new SignaturePad(this.$refs.canvas) this.pad = new SignaturePad(this.$refs.canvas)
}, },
methods: { methods: {

@ -291,6 +291,10 @@ export default {
required: true required: true
} }
if (this.dragFieldType === 'select') {
field.options = ['']
}
const fieldArea = { const fieldArea = {
x: (area.x - 6) / area.maskW, x: (area.x - 6) / area.maskW,
y: area.y / area.maskH, y: area.y / area.maskH,

@ -12,8 +12,6 @@
<%= yield :head %> <%= yield :head %>
</head> </head>
<body class="h-full"> <body class="h-full">
<div class="max-w-6xl mx-auto px-4 md:px-2">
<%= yield %> <%= yield %>
</div>
</body> </body>
</html> </html>

@ -1,4 +1,4 @@
<svg height="<%= local_assigns.fetch(:height, '37px') %>" width="<%= local_assigns.fetch(:width, '37px') %>" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 511.998 511.998" xml:space="preserve"> <svg height="<%= local_assigns.fetch(:height, '37px') %>" width="<%= local_assigns.fetch(:width, '37px') %>" class="<%= local_assigns[:class] %>" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 511.998 511.998" xml:space="preserve">
<path style="fill:#C8AF9B;" d="M503.999,247.999c0,128.13-111.033,240-248,240S8,376.129,8,247.999s111.033-224,248-224 <path style="fill:#C8AF9B;" d="M503.999,247.999c0,128.13-111.033,240-248,240S8,376.129,8,247.999s111.033-224,248-224
S503.999,119.869,503.999,247.999z" /> S503.999,119.869,503.999,247.999z" />
<path style="fill:#AA968C;" d="M255.999,23.999C119.033,23.999,8,119.868,8,247.999c0,24.631,4.138,48.647,11.74,71.397 <path style="fill:#AA968C;" d="M255.999,23.999C119.033,23.999,8,119.868,8,247.999c0,24.631,4.138,48.647,11.74,71.397

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

@ -1,18 +1,32 @@
<% attachment_field_uuids = @submitter.submission.template.fields.select { |f| f['type'].in?(%w[image signature attachment]) }.pluck('uuid') %> <% attachment_field_uuids = @submitter.submission.template.fields.select { |f| f['type'].in?(%w[image signature file]) }.pluck('uuid') %>
<% attachments = ActiveStorage::Attachment.where(uuid: @submitter.values.values_at(*attachment_field_uuids).flatten).preload(:blob) %> <% attachments = ActiveStorage::Attachment.where(uuid: @submitter.values.values_at(*attachment_field_uuids).flatten).preload(:blob) %>
<div class="mx-auto block" style="max-width: 1000px"> <div class="mx-auto block pb-72" style="max-width: 1000px">
<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">
<%= render 'shared/logo', class: 'w-9 h-9 md:w-12 md:h-12' %>
<span>DocuSeal</span>
</a>
</div>
<% @submitter.submission.template.schema.each do |item| %> <% @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"> <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"></div>
</div> </div>
<% end %> <% end %>
<% end %> <% end %>
<div class="sticky bottom-8 w-full"> <div class="text-center">
<div class="bg-white mx-8 md:mx-32 border p-4 rounded"> Powered by
<a href="https://www.docuseal.co" class="underline">DocuSeal</a> - An open source documents software
</div>
</div>
<div class="fixed bottom-0 w-full h-0 z-20">
<div class="mx-auto" style="max-width: 1000px">
<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">
<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="<%= @submitter.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.to_json(only: %i[uuid], methods: %i[url filename content_type]) %>" data-fields="<%= @submitter.submission.template.fields.to_json %>" data-values="<%= @submitter.values.to_json %>" data-authenticity-token="<%= form_authenticity_token %>"></submission-form>
</div> </div>
</div> </div>
</div>
</div> </div>

Loading…
Cancel
Save