mirror of
https://github.com/docusealco/docuseal
synced 2026-11-04 22:42:32 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08c3b04511 | ||
|
|
7779eb880d | ||
|
|
21b9ca8ea5 | ||
|
|
db7e3794d9 | ||
|
|
df1d78897a | ||
|
|
e1eb2da6f9 | ||
|
|
a92d1d82c1 | ||
|
|
2dcfc41365 | ||
|
|
15cbdfb20c | ||
|
|
a2cf8fc5b5 | ||
|
|
f94ef3afdc | ||
|
|
9e3a9f0eeb | ||
|
|
1280572e6f | ||
|
|
cba2e84338 | ||
|
|
17ca8f013a | ||
|
|
96ef7ffaf7 | ||
|
|
1bbcdb8073 | ||
|
|
da130c3f4f | ||
|
|
c15f3ecd83 | ||
|
|
df0e0e6a1b |
No files matched your search
@@ -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
|
||||||
@@ -26,6 +26,8 @@ DocuSeal is an open source platform that provides secure and efficient digital d
|
|||||||
</p>
|
</p>
|
||||||
<h2 align="center">
|
<h2 align="center">
|
||||||
<a href="https://demo.docuseal.co">✨ Live Demo</a>
|
<a href="https://demo.docuseal.co">✨ Live Demo</a>
|
||||||
|
<span>|</span>
|
||||||
|
<a href="https://docuseal.co/sign_up">☁️ Try in Cloud</a>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
[](https://demo.docuseal.co)
|
[](https://demo.docuseal.co)
|
||||||
@@ -44,9 +46,11 @@ DocuSeal is an open source platform that provides secure and efficient digital d
|
|||||||
|
|
||||||
## Deploy
|
## Deploy
|
||||||
|
|
||||||
| Heroku | Railway | DigitalOcean |
|
|Heroku|Railway|
|
||||||
| ---------------------------------------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
|
|:--:|:---:|
|
||||||
| [<img alt="Deploy on Heroku" src="https://www.herokucdn.com/deploy/button.svg" height="40">](https://heroku.com/deploy?template=https://github.com/docusealco/docuseal-heroku) | [<img alt="Deploy on Railway" src="https://railway.app/button.svg" height="40">](https://railway.app/template/IGoDnc?referralCode=ruU7JR) | [<img alt="Deploy on DigitalOcean" src="https://www.deploytodo.com/do-btn-blue.svg" height="40">](https://cloud.digitalocean.com/apps/new?repo=https://github.com/docusealco/docuseal-digitalocean/tree/master&refcode=421d50f53990) |
|
| [<img alt="Deploy on Heroku" src="https://www.herokucdn.com/deploy/button.svg" height="40">](https://heroku.com/deploy?template=https://github.com/docusealco/docuseal-heroku) | [<img alt="Deploy on Railway" src="https://railway.app/button.svg" height="40">](https://railway.app/template/IGoDnc?referralCode=ruU7JR)|
|
||||||
|
|**DigitalOcean**|**Render**|
|
||||||
|
| [<img alt="Deploy on DigitalOcean" src="https://www.deploytodo.com/do-btn-blue.svg" height="40">](https://cloud.digitalocean.com/apps/new?repo=https://github.com/docusealco/docuseal-digitalocean/tree/master&refcode=421d50f53990) | [<img alt="Deploy to Render" src="https://render.com/images/deploy-to-render-button.svg" height="40">](https://render.com/deploy?repo=https://github.com/docusealco/docuseal-render)
|
||||||
|
|
||||||
|
|
||||||
#### Docker
|
#### Docker
|
||||||
@@ -69,6 +73,13 @@ Run the app under a custom domain over https using docker compose (make sure you
|
|||||||
HOST=your-domain-name.com docker-compose up
|
HOST=your-domain-name.com docker-compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## For Companies
|
||||||
|
### Integrate seamless document signing into your web or mobile apps with DocuSeal!
|
||||||
|
|
||||||
|
At DocuSeal we have expertise and technologies to make documents creation, filling, signing and processing seamlessly integrated with your product. We specialize in working with various industries, including **Banking, Healthcare, Transport, eCommerce, KYC, CRM, and other software products** that require bulk document signing. By leveraging DocuSeal, we can assist in reducing the overall cost of developing and processing electronic documents while ensuring security and compliance with local electronic document laws.
|
||||||
|
|
||||||
|
[](https://cal.com/docuseal)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
DocuSeal is released under the GNU Affero General Public License v3.0.
|
DocuSeal is released under the GNU Affero General Public License v3.0.
|
||||||
@@ -22,14 +22,16 @@ class SubmissionsController < ApplicationController
|
|||||||
create_submissions_from_submitters
|
create_submissions_from_submitters
|
||||||
end
|
end
|
||||||
|
|
||||||
|
submitters = submissions.flat_map(&:submitters)
|
||||||
|
|
||||||
if params[:send_email] == '1'
|
if params[:send_email] == '1'
|
||||||
submissions.flat_map(&:submitters).each do |submitter|
|
submitters.each do |submitter|
|
||||||
SubmitterMailer.invitation_email(submitter, message: params[:message]).deliver_later!
|
SubmitterMailer.invitation_email(submitter, message: params[:message]).deliver_later!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
redirect_to template_path(@template),
|
redirect_to template_path(@template),
|
||||||
notice: "#{submissions.size} #{'recipient'.pluralize(submissions.size)} added"
|
notice: "#{submitters.size} #{'recipient'.pluralize(submitters.size)} added"
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,10 @@
|
|||||||
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] }}
|
||||||
<template v-if="field.type === 'checkbox'">
|
<template v-if="field.type === 'checkbox' && !field.name">
|
||||||
{{ fieldIndex + 1 }}
|
{{ fieldIndex + 1 }}
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="!field.required">
|
<template v-else-if="!field.required && field.type !== 'checkbox'">
|
||||||
(optional)
|
(optional)
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,7 +11,10 @@
|
|||||||
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
|
||||||
|
v-if="attachmentsIndex[area.attachment_uuid]"
|
||||||
|
:to="`#page-${area.attachment_uuid}-${area.page}`"
|
||||||
|
>
|
||||||
<FieldArea
|
<FieldArea
|
||||||
:ref="setAreaRef"
|
:ref="setAreaRef"
|
||||||
v-model="values[field.uuid]"
|
v-model="values[field.uuid]"
|
||||||
|
|||||||
@@ -51,6 +51,16 @@
|
|||||||
Star on Github
|
Star on Github
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
v-if="isDemo"
|
||||||
|
href="https://docuseal.co/sign_up"
|
||||||
|
class="white-button flex items-center space-x-1 w-full"
|
||||||
|
>
|
||||||
|
<IconLogin />
|
||||||
|
<span>
|
||||||
|
Create a Free Account
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center mt-4">
|
<div class="text-center mt-4">
|
||||||
Signed with
|
Signed with
|
||||||
@@ -64,7 +74,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { IconCircleCheck, IconBrandGithub, IconMail, IconDownload, IconInnerShadowTop } from '@tabler/icons-vue'
|
import { IconCircleCheck, IconBrandGithub, IconMail, IconDownload, IconInnerShadowTop, IconLogin } from '@tabler/icons-vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FormCompleted',
|
name: 'FormCompleted',
|
||||||
@@ -73,6 +83,7 @@ export default {
|
|||||||
IconInnerShadowTop,
|
IconInnerShadowTop,
|
||||||
IconBrandGithub,
|
IconBrandGithub,
|
||||||
IconMail,
|
IconMail,
|
||||||
|
IconLogin,
|
||||||
IconDownload
|
IconDownload
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
:values="values"
|
:values="values"
|
||||||
:attachments-index="attachmentsIndex"
|
:attachments-index="attachmentsIndex"
|
||||||
:current-step="currentStepFields"
|
:current-step="currentStepFields"
|
||||||
@focus-step="[saveStep(), goToStep($event, false, true)]"
|
@focus-step="[saveStep(), goToStep($event, false, true), currentField.type !== 'checkbox' ? isFormVisible = true : '']"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
v-if="!isFormVisible"
|
v-if="!isFormVisible"
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-show="isFormVisible"
|
||||||
id="form_container"
|
id="form_container"
|
||||||
class="shadow-md bg-base-100 absolute bottom-0 md:bottom-4 w-full border-base-200 border p-4 rounded"
|
class="shadow-md bg-base-100 absolute bottom-0 md:bottom-4 w-full border-base-200 border p-4 rounded"
|
||||||
>
|
>
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
@click="[$refs.areas.scrollIntoField(field), values[field.uuid] = !values[field.uuid]]"
|
@click="[$refs.areas.scrollIntoField(field), values[field.uuid] = !values[field.uuid]]"
|
||||||
>
|
>
|
||||||
<span class="text-xl">
|
<span class="text-xl">
|
||||||
{{ currentField.name || currentField.type + ' ' + (index + 1) }}
|
{{ field.name || field.type + ' ' + (index + 1) }}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -430,7 +430,7 @@ 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]) {
|
if (clickUpload && !this.values[this.currentField.uuid] && ['file', 'image'].includes(this.currentField.type)) {
|
||||||
this.$refs.form.querySelector('input[type="file"]')?.click()
|
this.$refs.form.querySelector('input[type="file"]')?.click()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
@blur="onNameBlur"
|
@blur="onNameBlur"
|
||||||
>{{ field.name || defaultName }}</span>
|
>{{ field.name || defaultName }}</span>
|
||||||
<div
|
<div
|
||||||
v-if="isNameFocus"
|
v-if="isNameFocus && field.type !== 'checkbox'"
|
||||||
class="flex items-center ml-1.5"
|
class="flex items-center ml-1.5"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -314,7 +317,7 @@ export default {
|
|||||||
const field = {
|
const field = {
|
||||||
name: '',
|
name: '',
|
||||||
uuid: v4(),
|
uuid: v4(),
|
||||||
required: true,
|
required: type !== 'checkbox',
|
||||||
type,
|
type,
|
||||||
submitter_uuid: this.selectedSubmitter.uuid,
|
submitter_uuid: this.selectedSubmitter.uuid,
|
||||||
areas: [area]
|
areas: [area]
|
||||||
@@ -333,7 +336,7 @@ export default {
|
|||||||
type: this.dragFieldType,
|
type: this.dragFieldType,
|
||||||
uuid: v4(),
|
uuid: v4(),
|
||||||
submitter_uuid: this.selectedSubmitter.uuid,
|
submitter_uuid: this.selectedSubmitter.uuid,
|
||||||
required: true
|
required: this.dragFieldType !== 'checkbox'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['select', 'multiple', 'radio'].includes(this.dragFieldType)) {
|
if (['select', 'multiple', 'radio'].includes(this.dragFieldType)) {
|
||||||
@@ -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) {
|
||||||
|
|||||||
@@ -31,19 +31,21 @@
|
|||||||
v-if="isNameFocus"
|
v-if="isNameFocus"
|
||||||
class="flex items-center relative"
|
class="flex items-center relative"
|
||||||
>
|
>
|
||||||
<input
|
<template v-if="field.type !== 'checkbox'">
|
||||||
:id="`required-checkbox-${field.uuid}`"
|
<input
|
||||||
v-model="field.required"
|
:id="`required-checkbox-${field.uuid}`"
|
||||||
type="checkbox"
|
v-model="field.required"
|
||||||
class="checkbox checkbox-xs no-animation rounded"
|
type="checkbox"
|
||||||
@mousedown.prevent
|
class="checkbox checkbox-xs no-animation rounded"
|
||||||
>
|
@mousedown.prevent
|
||||||
<label
|
>
|
||||||
:for="`required-checkbox-${field.uuid}`"
|
<label
|
||||||
class="label text-xs"
|
:for="`required-checkbox-${field.uuid}`"
|
||||||
@click.prevent="field.required = !field.required"
|
class="label text-xs"
|
||||||
@mousedown.prevent
|
@click.prevent="field.required = !field.required"
|
||||||
>Required</label>
|
@mousedown.prevent
|
||||||
|
>Required</label>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
|
|||||||
@@ -171,7 +171,8 @@ export default {
|
|||||||
const field = {
|
const field = {
|
||||||
name: '',
|
name: '',
|
||||||
uuid: v4(),
|
uuid: v4(),
|
||||||
required: true,
|
required: type !== 'checkbox',
|
||||||
|
areas: [],
|
||||||
submitter_uuid: this.selectedSubmitter.uuid,
|
submitter_uuid: this.selectedSubmitter.uuid,
|
||||||
type
|
type
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 })"
|
||||||
×
|
/>
|
||||||
</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)"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="withArrows"
|
||||||
|
class="flex flex-col space-y-1"
|
||||||
>
|
>
|
||||||
↑
|
<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)"
|
↑
|
||||||
>
|
</button>
|
||||||
↓
|
<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)"
|
||||||
|
>
|
||||||
|
↓
|
||||||
|
</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>
|
||||||
@@ -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
|
||||||
@@ -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) }
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
DocuSeal
|
DocuSeal
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<h2 class="mb-16 text-lg text-center text-gray-500">
|
<h2 class="mb-16 text-lg text-center text-gray-600">
|
||||||
A self-hosted and open-source web platform that provides secure and efficient digital document signing and processing.
|
A self-hosted and open-source web platform that provides secure and efficient digital document signing and processing.
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<h3 class="mb-4 text-2xl font-semibold">Easy to Start</h3>
|
<h3 class="mb-4 text-2xl font-semibold">Easy to Start</h3>
|
||||||
<p class="text-base text-gray-500">
|
<p class="text-base text-gray-500">
|
||||||
Run on your own host using <a href="https://hub.docker.com/r/docuseal/docuseal" class="link link-neutral font-bold" target="_blank">Docker</a> container, or deploy on your favorite managed PaaS with a single <a href="https:///www.docuseal.co/install" class="link link-neutral font-bold">click</a>.
|
Run on your own host using <a href="https://hub.docker.com/r/docuseal/docuseal" class="link link-neutral font-bold" target="_blank">Docker</a> container, or deploy on your favorite managed PaaS with a single <a href="https://www.docuseal.co/install" class="link link-neutral font-bold">click</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,3 +70,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<%= render 'shared/attribution' %>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<turbo-frame id="modal">
|
<turbo-frame id="modal">
|
||||||
<turbo-modal class="modal modal-open items-start !animate-none">
|
<turbo-modal class="modal modal-open items-start !animate-none overflow-y-auto">
|
||||||
<div class="absolute top-0 bottom-0 right-0 left-0" data-action="click:turbo-modal#close"></div>
|
<div class="absolute top-0 bottom-0 right-0 left-0" data-action="click:turbo-modal#close"></div>
|
||||||
<div class="modal-box pt-4 pb-6 px-6 mt-20">
|
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none">
|
||||||
<% if local_assigns[:title] %>
|
<% if local_assigns[:title] %>
|
||||||
<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>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<h1 class="text-5xl font-bold text-center">DocuSeal</h1>
|
<h1 class="text-5xl font-bold text-center">DocuSeal</h1>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center bg-base-300 rounded-xl p-4 mb-4">
|
<div class="flex items-center bg-base-200 rounded-xl p-4 mb-4">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="mr-3">
|
<div class="mr-3">
|
||||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="space-y-6 mx-auto">
|
<div class="space-y-6 mx-auto">
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div class="text-center w-full space-y-6">
|
<div class="text-center w-full space-y-6">
|
||||||
<a href="/" class="flex justify-center">
|
<a href="/" class="flex justify-center items-center">
|
||||||
<span class="mr-3">
|
<span class="mr-3">
|
||||||
<%= render 'shared/logo', width: '50px', height: '50px' %>
|
<%= render 'shared/logo', width: '50px', height: '50px' %>
|
||||||
</span>
|
</span>
|
||||||
@@ -10,14 +10,14 @@
|
|||||||
</a>
|
</a>
|
||||||
<p class="text-xl font-semibold text-center">You have been invited to submit a form</p>
|
<p class="text-xl font-semibold text-center">You have been invited to submit a form</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center bg-base-300 rounded-xl p-4 mb-4">
|
<div class="flex items-center bg-base-200 rounded-xl p-4 mb-4">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="mr-3">
|
<div class="mr-3">
|
||||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-lg font-bold mb-1"><%= @template.name %></p>
|
<p class="text-lg font-bold mb-1"><%= @template.name %></p>
|
||||||
<p class="text-sm">Invited by <%= @template.account.name %></p>
|
<p class="text-sm">Invited by <span class="font-semibold"><%= @template.account.name %></span></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<h1 class="text-5xl font-bold text-center">DocuSeal</h1>
|
<h1 class="text-5xl font-bold text-center">DocuSeal</h1>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center bg-base-300 rounded-xl p-4 mb-4">
|
<div class="flex items-center bg-base-200 rounded-xl p-4 mb-4">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="mr-3">
|
<div class="mr-3">
|
||||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<% if Accounts.can_send_emails?(@submitter.submission.template.account) %>
|
<% if Accounts.can_send_emails?(@submitter.submission.template.account) %>
|
||||||
<%= button_to button_title(title: 'Send copy to Email', disabled_with: 'Sending', icon: svg_icon('mail_forward', class: 'w-6 h-6')), send_submission_email_index_path, params: { submitter_slug: @submitter.slug }, form: { onsubmit: 'event.submitter.disabled = true' }, class: 'base-button w-full' %>
|
<%= button_to button_title(title: 'Send copy to Email', disabled_with: 'Sending', icon: svg_icon('mail_forward', class: 'w-6 h-6')), send_submission_email_index_path, params: { submitter_slug: @submitter.slug }, form: { onsubmit: 'event.submitter.disabled = true' }, class: 'white-button w-full' %>
|
||||||
<div class="divider">OR</div>
|
<div class="divider">OR</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<download-button data-src="<%= submitter_download_index_path(@submitter.slug) %>" class="base-button w-full">
|
<download-button data-src="<%= submitter_download_index_path(@submitter.slug) %>" class="base-button w-full">
|
||||||
|
|||||||
@@ -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 @@
|
|||||||
<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>
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ Rails.application.configure do
|
|||||||
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
|
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
|
||||||
config.action_mailer.raise_delivery_errors = false
|
config.action_mailer.raise_delivery_errors = false
|
||||||
|
|
||||||
if ENV['SMTP_USERNAME']
|
if ENV['SMTP_ADDRESS']
|
||||||
config.action_mailer.delivery_method = :smtp
|
config.action_mailer.delivery_method = :smtp
|
||||||
config.action_mailer.smtp_settings = {
|
config.action_mailer.smtp_settings = {
|
||||||
address: ENV.fetch('SMTP_ADDRESS', nil),
|
address: ENV.fetch('SMTP_ADDRESS', nil),
|
||||||
@@ -86,7 +86,7 @@ Rails.application.configure do
|
|||||||
password: ENV.fetch('SMTP_PASSWORD', nil),
|
password: ENV.fetch('SMTP_PASSWORD', nil),
|
||||||
authentication: ENV.fetch('SMTP_AUTHENTICATION', 'plain'),
|
authentication: ENV.fetch('SMTP_AUTHENTICATION', 'plain'),
|
||||||
enable_starttls_auto: ENV['SMTP_ENABLE_STARTTLS_AUTO'] == 'true'
|
enable_starttls_auto: ENV['SMTP_ENABLE_STARTTLS_AUTO'] == 'true'
|
||||||
}
|
}.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
|
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
|
||||||
|
|||||||
@@ -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
@@ -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,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
@@ -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']] ||= [])
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@
|
|||||||
"compression-webpack-plugin": "10.0.0",
|
"compression-webpack-plugin": "10.0.0",
|
||||||
"css-loader": "^6.7.3",
|
"css-loader": "^6.7.3",
|
||||||
"css-minimizer-webpack-plugin": "^5.0.0",
|
"css-minimizer-webpack-plugin": "^5.0.0",
|
||||||
"daisyui": "^3.1.5",
|
"daisyui": "^3.2.1",
|
||||||
"mini-css-extract-plugin": "^2.7.5",
|
"mini-css-extract-plugin": "^2.7.5",
|
||||||
"postcss": "^8.4.23",
|
"postcss": "^8.4.23",
|
||||||
"postcss-import": "^15.1.0",
|
"postcss-import": "^15.1.0",
|
||||||
|
|||||||
@@ -2300,13 +2300,14 @@ csstype@^3.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
|
||||||
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
|
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
|
||||||
|
|
||||||
daisyui@^3.1.5:
|
daisyui@^3.2.1:
|
||||||
version "3.1.5"
|
version "3.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/daisyui/-/daisyui-3.1.5.tgz#49cb961d03504beebd238d58f0169dbe81103c95"
|
resolved "https://registry.yarnpkg.com/daisyui/-/daisyui-3.2.1.tgz#7fac542b03023adbde2afc869518840dbef509ee"
|
||||||
integrity sha512-3nk5L//IU31ISzsonKEFG8ris8hS/MRAc+PrOTOyRbssjv1fYmNKSdIrOhG6op2ED3RyJL4p6I81JUwd7RWqTw==
|
integrity sha512-gIqE6wiqoJt9G8+n3R/SwLeUnpNCE2eDhT73rP0yZYVaM7o6zVcakBH3aEW5RGpx3UkonPiLuvcgxRcb2lE8TA==
|
||||||
dependencies:
|
dependencies:
|
||||||
colord "^2.9"
|
colord "^2.9"
|
||||||
css-selector-tokenizer "^0.8"
|
css-selector-tokenizer "^0.8"
|
||||||
|
postcss "^8"
|
||||||
postcss-js "^4"
|
postcss-js "^4"
|
||||||
tailwindcss "^3"
|
tailwindcss "^3"
|
||||||
|
|
||||||
@@ -4448,6 +4449,15 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^
|
|||||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
|
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
|
||||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||||
|
|
||||||
|
postcss@^8:
|
||||||
|
version "8.4.26"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.26.tgz#1bc62ab19f8e1e5463d98cf74af39702a00a9e94"
|
||||||
|
integrity sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==
|
||||||
|
dependencies:
|
||||||
|
nanoid "^3.3.6"
|
||||||
|
picocolors "^1.0.0"
|
||||||
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
postcss@^8.1.10, postcss@^8.4.21, postcss@^8.4.23:
|
postcss@^8.1.10, postcss@^8.4.21, postcss@^8.4.23:
|
||||||
version "8.4.23"
|
version "8.4.23"
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab"
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab"
|
||||||
|
|||||||
Reference in new issue
Block a user