fix refactor dropzone
This commit is contained in:
+7
-8
@@ -1,15 +1,10 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class TemplateReplaceDocumentsController < ApplicationController
|
class TemplatesCloneAndReplaceController < ApplicationController
|
||||||
load_and_authorize_resource :template
|
load_and_authorize_resource :template
|
||||||
|
|
||||||
def create
|
def create
|
||||||
if params[:blobs].blank? && params[:files].blank?
|
return head :unprocessable_entity if params[:files].blank?
|
||||||
return respond_to do |f|
|
|
||||||
f.html { redirect_back fallback_location: template_path(@template), alert: I18n.t('file_is_missing') }
|
|
||||||
f.json { render json: { error: I18n.t('file_is_missing') }, status: :unprocessable_entity }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ActiveRecord::Associations::Preloader.new(
|
ActiveRecord::Associations::Preloader.new(
|
||||||
records: [@template],
|
records: [@template],
|
||||||
@@ -17,6 +12,7 @@ class TemplateReplaceDocumentsController < ApplicationController
|
|||||||
).call
|
).call
|
||||||
|
|
||||||
cloned_template = Templates::Clone.call(@template, author: current_user)
|
cloned_template = Templates::Clone.call(@template, author: current_user)
|
||||||
|
cloned_template.name = File.basename(params[:files].first.original_filename, '.*')
|
||||||
cloned_template.save!
|
cloned_template.save!
|
||||||
|
|
||||||
documents = Templates::ReplaceAttachments.call(cloned_template, params, extract_fields: true)
|
documents = Templates::ReplaceAttachments.call(cloned_template, params, extract_fields: true)
|
||||||
@@ -31,6 +27,9 @@ class TemplateReplaceDocumentsController < ApplicationController
|
|||||||
f.json { render json: { id: cloned_template.id } }
|
f.json { render json: { id: cloned_template.id } }
|
||||||
end
|
end
|
||||||
rescue Templates::CreateAttachments::PdfEncrypted
|
rescue Templates::CreateAttachments::PdfEncrypted
|
||||||
render json: { error: 'PDF encrypted', status: 'pdf_encrypted' }, status: :unprocessable_entity
|
respond_to do |f|
|
||||||
|
f.html { render turbo_stream: turbo_stream.append(params[:form_id], html: helpers.tag.prompt_password) }
|
||||||
|
f.json { render json: { error: 'PDF encrypted', status: 'pdf_encrypted' }, status: :unprocessable_entity }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -127,7 +127,7 @@ safeRegisterElement('template-builder', class extends HTMLElement {
|
|||||||
withSendButton: this.dataset.withSendButton !== 'false',
|
withSendButton: this.dataset.withSendButton !== 'false',
|
||||||
withSignYourselfButton: this.dataset.withSignYourselfButton !== 'false',
|
withSignYourselfButton: this.dataset.withSignYourselfButton !== 'false',
|
||||||
withConditions: this.dataset.withConditions === 'true',
|
withConditions: this.dataset.withConditions === 'true',
|
||||||
withReplaceAndCloneUpload: this.dataset.withReplaceAndCloneUpload !== 'false',
|
withReplaceAndCloneUpload: true,
|
||||||
currencies: (this.dataset.currencies || '').split(',').filter(Boolean),
|
currencies: (this.dataset.currencies || '').split(',').filter(Boolean),
|
||||||
acceptFileTypes: this.dataset.acceptFileTypes,
|
acceptFileTypes: this.dataset.acceptFileTypes,
|
||||||
showTourStartForm: this.dataset.showTourStartForm === 'true'
|
showTourStartForm: this.dataset.showTourStartForm === 'true'
|
||||||
|
|||||||
@@ -1,74 +1,106 @@
|
|||||||
import { actionable } from '@github/catalyst/lib/actionable'
|
|
||||||
import { target, targets, targetable } from '@github/catalyst/lib/targetable'
|
import { target, targets, targetable } from '@github/catalyst/lib/targetable'
|
||||||
|
|
||||||
export default actionable(targetable(class extends HTMLElement {
|
export default targetable(class extends HTMLElement {
|
||||||
static [targets.static] = ['hiddenOnHover']
|
static [targets.static] = [
|
||||||
|
'hiddenOnDrag',
|
||||||
|
'folderCards',
|
||||||
|
'templateCards'
|
||||||
|
]
|
||||||
|
|
||||||
static [target.static] = [
|
static [target.static] = [
|
||||||
'loading',
|
'form',
|
||||||
'icon',
|
'fileDropzone',
|
||||||
'input',
|
'fileDropzoneLoading'
|
||||||
'fileDropzone'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
connectedCallback () {
|
connectedCallback () {
|
||||||
this.showOnlyOnWindowHover = this.dataset.showOnlyOnWindowHover === 'true'
|
|
||||||
|
|
||||||
document.addEventListener('drop', this.onWindowDragdrop)
|
document.addEventListener('drop', this.onWindowDragdrop)
|
||||||
document.addEventListener('dragover', this.onWindowDropover)
|
document.addEventListener('dragover', this.onWindowDropover)
|
||||||
|
|
||||||
window.addEventListener('dragleave', this.onWindowDragleave)
|
window.addEventListener('dragleave', this.onWindowDragleave)
|
||||||
|
|
||||||
this.addEventListener('dragover', this.onDragover)
|
this.fileDropzone?.addEventListener('drop', this.onDropFile)
|
||||||
this.addEventListener('dragleave', this.onDragleave)
|
|
||||||
|
|
||||||
this.fileDropzone.addEventListener('drop', this.onDrop)
|
this.folderCards.forEach((el) => el.addEventListener('drop', this.onDropFolder))
|
||||||
this.fileDropzone.addEventListener('turbo:submit-start', this.showDraghover)
|
this.templateCards.forEach((el) => el.addEventListener('drop', this.onDropTemplate))
|
||||||
this.fileDropzone.addEventListener('turbo:submit-end', this.hideDraghover)
|
|
||||||
|
return [this.fileDropzone, ...this.folderCards, ...this.templateCards].forEach((el) => {
|
||||||
|
el?.addEventListener('dragover', this.onDragover)
|
||||||
|
el?.addEventListener('dragleave', this.onDragleave)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback () {
|
disconnectedCallback () {
|
||||||
document.removeEventListener('drop', this.onWindowDragdrop)
|
document.removeEventListener('drop', this.onWindowDragdrop)
|
||||||
document.removeEventListener('dragover', this.onWindowDropover)
|
document.removeEventListener('dragover', this.onWindowDropover)
|
||||||
|
|
||||||
window.removeEventListener('dragleave', this.onWindowDragleave)
|
window.removeEventListener('dragleave', this.onWindowDragleave)
|
||||||
|
|
||||||
this.removeEventListener('dragover', this.onDragover)
|
this.fileDropzone?.removeEventListener('drop', this.onDropFile)
|
||||||
this.removeEventListener('dragleave', this.onDragleave)
|
|
||||||
|
|
||||||
this.fileDropzone.removeEventListener('drop', this.onDrop)
|
this.folderCards.forEach((el) => el.removeEventListener('drop', this.onDropFolder))
|
||||||
this.fileDropzone.removeEventListener('turbo:submit-start', this.showDraghover)
|
this.templateCards.forEach((el) => el.removeEventListener('drop', this.onDropTemplate))
|
||||||
this.fileDropzone.removeEventListener('turbo:submit-end', this.hideDraghover)
|
|
||||||
|
return [this.fileDropzone, ...this.folderCards, ...this.templateCards].forEach((el) => {
|
||||||
|
el?.removeEventListener('dragover', this.onDragover)
|
||||||
|
el?.removeEventListener('dragleave', this.onDragleave)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onDrop = (e) => {
|
onDropFile = (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
this.input.files = e.dataTransfer.files
|
this.fileDropzoneLoading.classList.remove('hidden')
|
||||||
|
this.fileDropzoneLoading.previousElementSibling.classList.add('hidden')
|
||||||
|
this.fileDropzoneLoading.classList.add('opacity-50')
|
||||||
|
|
||||||
this.uploadFiles(e.dataTransfer.files)
|
this.uploadFiles(e.dataTransfer.files, '/templates_upload')
|
||||||
|
}
|
||||||
|
|
||||||
|
onDropFolder = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const loading = document.createElement('div')
|
||||||
|
const svg = e.target.querySelector('svg')
|
||||||
|
|
||||||
|
loading.innerHTML = this.loadingIconHtml
|
||||||
|
loading.children[0].classList.add(...svg.classList)
|
||||||
|
|
||||||
|
e.target.replaceChild(loading.children[0], svg)
|
||||||
|
e.target.classList.add('opacity-50')
|
||||||
|
|
||||||
|
const params = new URLSearchParams({ folder_name: e.target.innerText }).toString()
|
||||||
|
|
||||||
|
this.uploadFiles(e.dataTransfer.files, `/templates_upload?${params}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
onDropTemplate = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const loading = document.createElement('div')
|
||||||
|
loading.classList.add('bottom-5', 'left-0', 'flex', 'justify-center', 'w-full', 'absolute')
|
||||||
|
loading.innerHTML = this.loadingIconHtml
|
||||||
|
|
||||||
|
e.target.appendChild(loading)
|
||||||
|
e.target.classList.add('opacity-50')
|
||||||
|
|
||||||
|
const id = e.target.href.split('/').pop()
|
||||||
|
|
||||||
|
this.uploadFiles(e.dataTransfer.files, `/templates/${id}/clone_and_replace`)
|
||||||
}
|
}
|
||||||
|
|
||||||
onWindowDragdrop = () => {
|
onWindowDragdrop = () => {
|
||||||
if (!this.hovered) this.hideDraghover()
|
if (!this.isLoading) this.hideDraghover()
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectFiles (e) {
|
uploadFiles (files, url) {
|
||||||
e.preventDefault()
|
this.isLoading = true
|
||||||
|
|
||||||
this.uploadFiles(this.input.files)
|
this.form.action = url
|
||||||
}
|
|
||||||
|
|
||||||
toggleLoading = (e) => {
|
this.form.querySelector('[type="file"]').files = files
|
||||||
if (e && e.target && (!e.target.contains(this) || !e.detail?.formSubmission?.formElement?.contains(this))) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading?.classList?.toggle('hidden')
|
this.form.querySelector('[type="submit"]').click()
|
||||||
this.icon?.classList?.toggle('hidden')
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadFiles () {
|
|
||||||
this.toggleLoading()
|
|
||||||
|
|
||||||
this.fileDropzone.querySelector('button[type="submit"]').click()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onWindowDropover = (e) => {
|
onWindowDropover = (e) => {
|
||||||
@@ -79,45 +111,50 @@ export default actionable(targetable(class extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDragover () {
|
||||||
|
this.style.backgroundColor = '#F7F3F0'
|
||||||
|
}
|
||||||
|
|
||||||
|
onDragleave () {
|
||||||
|
this.style.backgroundColor = null
|
||||||
|
}
|
||||||
|
|
||||||
onWindowDragleave = (e) => {
|
onWindowDragleave = (e) => {
|
||||||
if (e.clientX <= 0 || e.clientY <= 0 || e.clientX >= window.innerWidth || e.clientY >= window.innerHeight) {
|
if (e.clientX <= 0 || e.clientY <= 0 || e.clientX >= window.innerWidth || e.clientY >= window.innerHeight) {
|
||||||
this.hideDraghover()
|
this.hideDraghover()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDragover (e) {
|
|
||||||
e.preventDefault()
|
|
||||||
|
|
||||||
this.hovered = true
|
|
||||||
this.style.backgroundColor = '#F7F3F0'
|
|
||||||
}
|
|
||||||
|
|
||||||
onDragleave (e) {
|
|
||||||
e.preventDefault()
|
|
||||||
|
|
||||||
this.hovered = false
|
|
||||||
this.style.backgroundColor = null
|
|
||||||
}
|
|
||||||
|
|
||||||
showDraghover = () => {
|
showDraghover = () => {
|
||||||
if (this.showOnlyOnWindowHover) {
|
if (this.isDrag) return
|
||||||
this.classList.remove('hidden')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.classList.remove('bg-base-200', 'border-transparent')
|
this.isDrag = true
|
||||||
this.classList.add('bg-base-100', 'border-base-300', 'border-dashed')
|
|
||||||
this.fileDropzone.classList.remove('hidden')
|
this.fileDropzone?.classList?.remove('hidden')
|
||||||
this.hiddenOnHover.forEach((el) => { el.style.display = 'none' })
|
|
||||||
|
this.hiddenOnDrag.forEach((el) => { el.style.display = 'none' })
|
||||||
|
|
||||||
|
return [...this.folderCards, ...this.templateCards].forEach((el) => {
|
||||||
|
el.classList.remove('bg-base-200', 'before:hidden')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
hideDraghover = () => {
|
hideDraghover = () => {
|
||||||
if (this.showOnlyOnWindowHover) {
|
this.isDrag = false
|
||||||
this.classList.add('hidden')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.classList.add('bg-base-200', 'border-transparent')
|
this.fileDropzone?.classList?.add('hidden')
|
||||||
this.classList.remove('bg-base-100', 'border-base-300', 'border-dashed')
|
|
||||||
this.fileDropzone.classList.add('hidden')
|
this.hiddenOnDrag.forEach((el) => { el.style.display = null })
|
||||||
this.hiddenOnHover.forEach((el) => { el.style.display = null })
|
|
||||||
|
return [...this.folderCards, ...this.templateCards].forEach((el) => {
|
||||||
|
el.classList.add('bg-base-200', 'before:hidden')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}))
|
|
||||||
|
get loadingIconHtml () {
|
||||||
|
return `<svg xmlns="http://www.w3.org/2000/svg" class="animate-spin" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M12 3a9 9 0 1 0 9 9" />
|
||||||
|
</svg>`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<HoverDropzone
|
|
||||||
v-if="sortedDocuments.length && (withUploadButton || withAddPageButton)"
|
|
||||||
:is-dragging="isDragging"
|
|
||||||
:template-id="template.id"
|
|
||||||
:accept-file-types="acceptFileTypes"
|
|
||||||
:with-replace-and-clone="withReplaceAndCloneUpload"
|
|
||||||
hover-class="bg-base-200/50"
|
|
||||||
@add="[updateFromUpload($event), isDragging = false]"
|
|
||||||
@replace="[onDocumentsReplace($event), isDragging = false]"
|
|
||||||
@replace-and-clone="[onDocumentsReplaceAndTemplateClone($event), isDragging = false]"
|
|
||||||
@error="onUploadFailed"
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
ref="dragContainer"
|
ref="dragContainer"
|
||||||
style="max-width: 1600px"
|
style="max-width: 1600px"
|
||||||
class="mx-auto pl-3 h-full"
|
class="mx-auto pl-3 h-full"
|
||||||
:class="isMobile ? 'pl-4' : 'md:pl-4'"
|
:class="isMobile ? 'pl-4' : 'md:pl-4'"
|
||||||
@dragover="onDragover"
|
@dragover="onDragover"
|
||||||
@drop="isDragging = false"
|
@drop="isDragFile = false"
|
||||||
>
|
>
|
||||||
|
<HoverDropzone
|
||||||
|
v-if="sortedDocuments.length && withUploadButton && editable"
|
||||||
|
:is-dragging="isDragFile"
|
||||||
|
:template-id="template.id"
|
||||||
|
:accept-file-types="acceptFileTypes"
|
||||||
|
:with-replace-and-clone="withReplaceAndCloneUpload"
|
||||||
|
hover-class="bg-base-200/50"
|
||||||
|
@add="[updateFromUpload($event), isDragFile = false]"
|
||||||
|
@replace="[onDocumentsReplace($event), isDragFile = false]"
|
||||||
|
@replace-and-clone="onDocumentsReplaceAndTemplateClone($event)"
|
||||||
|
@error="[onUploadFailed($event), isDragFile = false]"
|
||||||
|
/>
|
||||||
<DragPlaceholder
|
<DragPlaceholder
|
||||||
ref="dragPlaceholder"
|
ref="dragPlaceholder"
|
||||||
:field="fieldsDragFieldRef.value || toRaw(dragField)"
|
:field="fieldsDragFieldRef.value || toRaw(dragField)"
|
||||||
@@ -683,7 +683,7 @@ export default {
|
|||||||
withReplaceAndCloneUpload: {
|
withReplaceAndCloneUpload: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: true
|
default: false
|
||||||
},
|
},
|
||||||
withPhone: {
|
withPhone: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -745,7 +745,7 @@ export default {
|
|||||||
drawFieldType: null,
|
drawFieldType: null,
|
||||||
drawOption: null,
|
drawOption: null,
|
||||||
dragField: null,
|
dragField: null,
|
||||||
isDragging: false
|
isDragFile: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -892,12 +892,12 @@ export default {
|
|||||||
ref.x = e.clientX - ref.offsetX
|
ref.x = e.clientX - ref.offsetX
|
||||||
ref.y = e.clientY - ref.offsetY
|
ref.y = e.clientY - ref.offsetY
|
||||||
} else if (e.dataTransfer?.types?.includes('Files')) {
|
} else if (e.dataTransfer?.types?.includes('Files')) {
|
||||||
this.isDragging = true
|
this.isDragFile = true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onWindowDragLeave (event) {
|
onWindowDragLeave (event) {
|
||||||
if (event.clientX <= 0 || event.clientY <= 0 || event.clientX >= window.innerWidth || event.clientY >= window.innerHeight) {
|
if (event.clientX <= 0 || event.clientY <= 0 || event.clientX >= window.innerWidth || event.clientY >= window.innerHeight) {
|
||||||
this.isDragging = false
|
this.isDragFile = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
reorderFields (item) {
|
reorderFields (item) {
|
||||||
@@ -1560,8 +1560,6 @@ export default {
|
|||||||
}, 'image/png')
|
}, 'image/png')
|
||||||
},
|
},
|
||||||
onUploadFailed (error) {
|
onUploadFailed (error) {
|
||||||
this.isDragging = false
|
|
||||||
|
|
||||||
if (error) alert(error)
|
if (error) alert(error)
|
||||||
},
|
},
|
||||||
updateFromUpload (data) {
|
updateFromUpload (data) {
|
||||||
@@ -1685,7 +1683,7 @@ export default {
|
|||||||
this.save()
|
this.save()
|
||||||
},
|
},
|
||||||
onDocumentsReplace (data) {
|
onDocumentsReplace (data) {
|
||||||
data.schema.forEach((schemaItem , index) => {
|
data.schema.forEach((schemaItem, index) => {
|
||||||
const existingSchemaItem = this.template.schema[index]
|
const existingSchemaItem = this.template.schema[index]
|
||||||
|
|
||||||
if (this.template.schema[index]) {
|
if (this.template.schema[index]) {
|
||||||
|
|||||||
@@ -10,12 +10,12 @@
|
|||||||
id="document_dropzone"
|
id="document_dropzone"
|
||||||
class="w-full relative rounded-md border-2 border-base-content/10 border-dashed"
|
class="w-full relative rounded-md border-2 border-base-content/10 border-dashed"
|
||||||
:for="inputId"
|
:for="inputId"
|
||||||
:class="[{ 'opacity-50': isLoading || isProcessing, 'hover:bg-base-200': !hoverClass }, isDragEntering && hoverClass ? hoverClass : '']"
|
:class="[{ 'opacity-50': isLoading, 'hover:bg-base-200/30': !hoverClass }, isDragEntering && hoverClass ? hoverClass : '']"
|
||||||
>
|
>
|
||||||
<div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center pointer-events-none">
|
<div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center pointer-events-none">
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
<IconInnerShadowTop
|
<IconInnerShadowTop
|
||||||
v-if="isLoading || isProcessing"
|
v-if="isLoading"
|
||||||
class="animate-spin"
|
class="animate-spin"
|
||||||
:width="40"
|
:width="40"
|
||||||
:height="40"
|
:height="40"
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="message"
|
v-if="message"
|
||||||
class="font-medium text-lg mb-1"
|
class="font-medium text-lg mb-1"
|
||||||
|
:class="{ 'mt-1': !withDescription }"
|
||||||
>
|
>
|
||||||
{{ message }}
|
{{ message }}
|
||||||
</div>
|
</div>
|
||||||
@@ -97,10 +98,10 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
header: {
|
title: {
|
||||||
type: Object,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: () => ({})
|
default: ''
|
||||||
},
|
},
|
||||||
acceptFileTypes: {
|
acceptFileTypes: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -108,11 +109,10 @@ export default {
|
|||||||
default: 'image/*, application/pdf'
|
default: 'image/*, application/pdf'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['success', 'error', 'loading', 'processing'],
|
emits: ['success', 'error', 'loading'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isProcessing: false,
|
|
||||||
isDragEntering: false
|
isDragEntering: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -122,7 +122,7 @@ export default {
|
|||||||
},
|
},
|
||||||
uploadUrl () {
|
uploadUrl () {
|
||||||
if (this.cloneTemplateOnUpload) {
|
if (this.cloneTemplateOnUpload) {
|
||||||
return `/templates/${this.templateId}/replace_documents`
|
return `/templates/${this.templateId}/clone_and_replace`
|
||||||
} else {
|
} else {
|
||||||
return `/templates/${this.templateId}/documents`
|
return `/templates/${this.templateId}/documents`
|
||||||
}
|
}
|
||||||
@@ -130,21 +130,16 @@ export default {
|
|||||||
message () {
|
message () {
|
||||||
if (this.isLoading) {
|
if (this.isLoading) {
|
||||||
return this.t('uploading')
|
return this.t('uploading')
|
||||||
} else if (this.isProcessing) {
|
|
||||||
return this.t('processing_')
|
|
||||||
} else if (this.acceptFileTypes === 'image/*, application/pdf') {
|
} else if (this.acceptFileTypes === 'image/*, application/pdf') {
|
||||||
return this.header.pdf_documents_or_images || this.header.documents_or_images || this.t('add_pdf_documents_or_images')
|
return this.title || this.t('add_pdf_documents_or_images')
|
||||||
} else {
|
} else {
|
||||||
return this.header.documents_or_images || this.t('add_documents_or_images')
|
return this.title || this.t('add_documents_or_images')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
isLoading (value) {
|
isLoading (value) {
|
||||||
this.$emit('loading', value)
|
this.$emit('loading', value)
|
||||||
},
|
|
||||||
isProcessing (value) {
|
|
||||||
this.$emit('processing', value)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="isDragging || isLoading || isProcessing"
|
v-if="isDragging || isLoading"
|
||||||
class="modal modal-open"
|
class="modal modal-open"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col gap-2 p-4 items-center bg-base-100 h-full max-h-[85vh] max-w-6xl rounded-2xl w-full">
|
<div class="flex flex-col gap-2 p-4 items-center bg-base-100 h-full max-h-[85vh] max-w-6xl rounded-2xl w-full">
|
||||||
@@ -11,10 +11,9 @@
|
|||||||
:template-id="templateId"
|
:template-id="templateId"
|
||||||
:accept-file-types="acceptFileTypes"
|
:accept-file-types="acceptFileTypes"
|
||||||
:with-description="false"
|
:with-description="false"
|
||||||
:header="{ documents_or_images: t('upload_new_document') }"
|
:title="t('upload_a_new_document')"
|
||||||
type="add_files"
|
type="add_files"
|
||||||
@loading="isLoading = $event"
|
@loading="isLoading = $event"
|
||||||
@processing="isProcessing = $event"
|
|
||||||
@success="$emit('add', $event)"
|
@success="$emit('add', $event)"
|
||||||
@error="$emit('error', $event)"
|
@error="$emit('error', $event)"
|
||||||
/>
|
/>
|
||||||
@@ -26,9 +25,8 @@
|
|||||||
:template-id="templateId"
|
:template-id="templateId"
|
||||||
:accept-file-types="acceptFileTypes"
|
:accept-file-types="acceptFileTypes"
|
||||||
:with-description="false"
|
:with-description="false"
|
||||||
:header="{ documents_or_images: t('replace_existing_document') }"
|
:title="t('replace_existing_document')"
|
||||||
@loading="isLoading = $event"
|
@loading="isLoading = $event"
|
||||||
@processing="isProcessing = $event"
|
|
||||||
@success="$emit('replace', $event)"
|
@success="$emit('replace', $event)"
|
||||||
@error="$emit('error', $event)"
|
@error="$emit('error', $event)"
|
||||||
/>
|
/>
|
||||||
@@ -41,9 +39,8 @@
|
|||||||
:accept-file-types="acceptFileTypes"
|
:accept-file-types="acceptFileTypes"
|
||||||
:with-description="false"
|
:with-description="false"
|
||||||
:clone-template-on-upload="true"
|
:clone-template-on-upload="true"
|
||||||
:header="{ documents_or_images: t('clone_template_and_replace_documents') }"
|
:title="t('clone_and_replace_documents')"
|
||||||
@loading="isLoading = $event"
|
@loading="isLoading = $event"
|
||||||
@processing="isProcessing = $event"
|
|
||||||
@success="$emit('replace-and-clone', $event)"
|
@success="$emit('replace-and-clone', $event)"
|
||||||
@error="$emit('error', $event)"
|
@error="$emit('error', $event)"
|
||||||
/>
|
/>
|
||||||
@@ -85,8 +82,7 @@ export default {
|
|||||||
emits: ['add', 'replace', 'replace-and-clone', 'error'],
|
emits: ['add', 'replace', 'replace-and-clone', 'error'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
isLoading: false,
|
isLoading: false
|
||||||
isProcessing: false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,9 +63,9 @@ const en = {
|
|||||||
processing_: 'Processing...',
|
processing_: 'Processing...',
|
||||||
add_pdf_documents_or_images: 'Add PDF documents or images',
|
add_pdf_documents_or_images: 'Add PDF documents or images',
|
||||||
add_documents_or_images: 'Add documents or images',
|
add_documents_or_images: 'Add documents or images',
|
||||||
upload_new_document: 'Upload new document',
|
upload_a_new_document: 'Upload a new document',
|
||||||
replace_existing_document: 'Replace existing document',
|
replace_existing_document: 'Replace existing document',
|
||||||
clone_template_and_replace_documents: 'Clone template and replace documents',
|
clone_and_replace_documents: 'Clone and replace documents',
|
||||||
required: 'Required',
|
required: 'Required',
|
||||||
default_value: 'Default value',
|
default_value: 'Default value',
|
||||||
format: 'Format',
|
format: 'Format',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<label
|
<label
|
||||||
:for="inputId"
|
:for="inputId"
|
||||||
class="btn btn-neutral btn-xs text-white transition-none replace-document-button"
|
class="btn btn-neutral btn-xs text-white transition-none replace-document-button"
|
||||||
:class="{ 'opacity-100': isLoading || isProcessing }"
|
:class="{ 'opacity-100': isLoading }"
|
||||||
>
|
>
|
||||||
{{ message }}
|
{{ message }}
|
||||||
<form
|
<form
|
||||||
@@ -41,8 +41,7 @@ export default {
|
|||||||
emits: ['success'],
|
emits: ['success'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
isLoading: false,
|
isLoading: false
|
||||||
isProcessing: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -52,8 +51,6 @@ export default {
|
|||||||
message () {
|
message () {
|
||||||
if (this.isLoading) {
|
if (this.isLoading) {
|
||||||
return this.t('uploading_')
|
return this.t('uploading_')
|
||||||
} else if (this.isProcessing) {
|
|
||||||
return this.t('processing_')
|
|
||||||
} else {
|
} else {
|
||||||
return this.t('replace')
|
return this.t('replace')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
id="add_document_button"
|
id="add_document_button"
|
||||||
:for="inputId"
|
:for="inputId"
|
||||||
class="btn btn-outline w-full add-document-button"
|
class="btn btn-outline w-full add-document-button"
|
||||||
:class="{ 'btn-disabled': isLoading || isProcessing }"
|
:class="{ 'btn-disabled': isLoading }"
|
||||||
>
|
>
|
||||||
<IconInnerShadowTop
|
<IconInnerShadowTop
|
||||||
v-if="isLoading || isProcessing"
|
v-if="isLoading"
|
||||||
width="20"
|
width="20"
|
||||||
class="animate-spin"
|
class="animate-spin"
|
||||||
/>
|
/>
|
||||||
@@ -18,9 +18,6 @@
|
|||||||
<span v-if="isLoading">
|
<span v-if="isLoading">
|
||||||
{{ t('uploading_') }}
|
{{ t('uploading_') }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="isProcessing">
|
|
||||||
{{ t('processing_') }}
|
|
||||||
</span>
|
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ t('add_document') }}
|
{{ t('add_document') }}
|
||||||
</span>
|
</span>
|
||||||
@@ -66,8 +63,7 @@ export default {
|
|||||||
emits: ['success', 'error'],
|
emits: ['success', 'error'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
isLoading: false,
|
isLoading: false
|
||||||
isProcessing: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -91,6 +87,7 @@ export default {
|
|||||||
resp.json().then((data) => {
|
resp.json().then((data) => {
|
||||||
this.$emit('success', data)
|
this.$emit('success', data)
|
||||||
this.$refs.input.value = ''
|
this.$refs.input.value = ''
|
||||||
|
this.isLoading = false
|
||||||
})
|
})
|
||||||
} else if (resp.status === 422) {
|
} else if (resp.status === 422) {
|
||||||
resp.json().then((data) => {
|
resp.json().then((data) => {
|
||||||
@@ -106,22 +103,26 @@ export default {
|
|||||||
if (resp.ok) {
|
if (resp.ok) {
|
||||||
this.$emit('success', await resp.json())
|
this.$emit('success', await resp.json())
|
||||||
this.$refs.input.value = ''
|
this.$refs.input.value = ''
|
||||||
|
this.isLoading = false
|
||||||
} else {
|
} else {
|
||||||
alert(this.t('wrong_password'))
|
alert(this.t('wrong_password'))
|
||||||
|
|
||||||
this.$emit('error', await resp.json().error)
|
this.$emit('error', await resp.json().error)
|
||||||
|
this.isLoading = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.$emit('error', data.error)
|
this.$emit('error', data.error)
|
||||||
|
this.isLoading = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
resp.json().then((data) => {
|
resp.json().then((data) => {
|
||||||
this.$emit('error', data.error)
|
this.$emit('error', data.error)
|
||||||
|
this.isLoading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).finally(() => {
|
}).catch(() => {
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M14 3v4a1 1 0 0 0 1 1h4" /><path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z" /><path d="M12 11v6" /><path d="M9.5 13.5l2.5 -2.5l2.5 2.5" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 462 B |
@@ -1,15 +1,12 @@
|
|||||||
<% is_long = folder.name.size > 32 %>
|
<% is_long = folder.name.size > 32 %>
|
||||||
<dashboard-dropzone class="relative rounded-2xl bg-base-200 border-2 border-transparent">
|
<a href="<%= folder_path(folder) %>" class="flex h-full flex-col justify-between rounded-2xl py-5 px-6 w-full bg-base-200 before:border-2 before:border-base-300 before:border-dashed before:absolute before:left-0 before:right-0 before:top-0 before:bottom-0 before:hidden before:rounded-2xl relative" data-targets="dashboard-dropzone.folderCards">
|
||||||
<a href="<%= folder_path(folder) %>" class="flex h-full flex-col justify-between py-5 px-6 w-full">
|
<% if !is_long %>
|
||||||
<% if !is_long %>
|
<%= svg_icon('folder', class: 'w-6 h-6') %>
|
||||||
<%= svg_icon('folder', class: 'w-6 h-6') %>
|
<% end %>
|
||||||
|
<div class="text-lg font-semibold mt-1" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: <%= is_long ? 2 : 1 %>;">
|
||||||
|
<% if is_long %>
|
||||||
|
<%= svg_icon('folder', class: 'w-6 h-6 inline') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="text-lg font-semibold mt-1" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: <%= is_long ? 2 : 1 %>;">
|
<%= folder.name %>
|
||||||
<% if is_long %>
|
</div>
|
||||||
<%= svg_icon('folder', class: 'w-6 h-6 inline') %>
|
</a>
|
||||||
<% end %>
|
|
||||||
<%= folder.name %>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<%= render 'templates/dashboard_dropzone_form', url: templates_upload_path(folder_name: folder.name) %>
|
|
||||||
</dashboard-dropzone>
|
|
||||||
|
|||||||
@@ -4,50 +4,56 @@
|
|||||||
<span><%= t('home') %></span>
|
<span><%= t('home') %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative flex justify-between items-center w-full mb-4">
|
<dashboard-dropzone>
|
||||||
<dashboard-dropzone data-show-only-on-window-hover="true" class="absolute bottom-0 h-36 w-full rounded-xl bg-base-200 border-2 border-dashed z-10 hidden">
|
<div class="relative flex justify-between items-center w-full mb-4">
|
||||||
<%= render 'templates/dashboard_dropzone_form', url: templates_upload_path(folder_name: @template_folder.name), title: t('upload_new_document'), icon: 'cloud_upload' %>
|
<%= form_for '', url: '', id: form_id = SecureRandom.uuid, method: :post, class: 'hidden', data: { target: 'dashboard-dropzone.form' }, html: { enctype: 'multipart/form-data' } do %>
|
||||||
</dashboard-dropzone>
|
<input name="form_id" value="<%= form_id %>">
|
||||||
<h1 class="text-4xl font-bold flex flex-grow min-w-0 space-x-2 md:flex <%= 'hidden' if params[:q].present? %>">
|
<input name="folder_name" value="<%= @template_folder.name %>">
|
||||||
<%= svg_icon('folder', class: 'w-9 h-9 flex-shrink-0') %>
|
<button type="submit"></button>
|
||||||
<span class="peer truncate">
|
<input id="dashboard_dropzone_input" name="files[]" type="file" multiple>
|
||||||
<%= @template_folder.name %>
|
<% end %>
|
||||||
</span>
|
<%= render 'templates/dashboard_dropzone', style: 'height: 137px' %>
|
||||||
<% if can?(:update, @template_folder) && @template_folder.name != TemplateFolder::DEFAULT_NAME %>
|
<h1 class="text-2xl truncate md:text-3xl font-bold flex items-center flex-grow min-w-0 space-x-2 md:flex <%= 'hidden' if params[:q].present? %>">
|
||||||
<span class="pl-1 opacity-0 hover:opacity-100 peer-hover:opacity-100">
|
<%= svg_icon('folder', class: 'w-9 h-9 flex-shrink-0') %>
|
||||||
<a href="<%= edit_folder_path(@template_folder) %>" data-turbo-frame="modal">
|
<span class="peer truncate">
|
||||||
<%= svg_icon('pencil', class: 'w-7 h-7') %>
|
<%= @template_folder.name %>
|
||||||
</a>
|
|
||||||
</span>
|
</span>
|
||||||
<% end %>
|
<% if can?(:update, @template_folder) && @template_folder.name != TemplateFolder::DEFAULT_NAME %>
|
||||||
</h1>
|
<span class="opacity-0 hover:opacity-100 peer-hover:opacity-100">
|
||||||
<div class="flex space-x-2">
|
<a href="<%= edit_folder_path(@template_folder) %>" data-turbo-frame="modal">
|
||||||
<% if params[:q].present? || @pagy.pages > 1 %>
|
<%= svg_icon('pencil', class: 'w-7 h-7') %>
|
||||||
<%= render 'shared/search_input' %>
|
</a>
|
||||||
<% end %>
|
</span>
|
||||||
<% if can?(:create, ::Template) %>
|
<% end %>
|
||||||
<%= render 'templates/upload_button', folder_name: @template_folder.name %>
|
</h1>
|
||||||
<%= link_to new_template_path(folder_name: @template_folder.name), class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %>
|
<div class="flex space-x-2">
|
||||||
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
<% if params[:q].present? || @pagy.pages > 1 %>
|
||||||
<span class="hidden md:block"><%= t('create') %></span>
|
<%= render 'shared/search_input' %>
|
||||||
|
<% end %>
|
||||||
|
<% if can?(:create, ::Template) %>
|
||||||
|
<%= render 'templates/upload_button', folder_name: @template_folder.name %>
|
||||||
|
<%= link_to new_template_path(folder_name: @template_folder.name), class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %>
|
||||||
|
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
||||||
|
<span class="hidden md:block"><%= t('create') %></span>
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% if @pagy.count > 0 %>
|
|
||||||
<div class="grid gap-4 md:grid-cols-3">
|
|
||||||
<%= render partial: 'templates/template', collection: @templates %>
|
|
||||||
</div>
|
|
||||||
<% templates_order_select_html = capture do %>
|
|
||||||
<% if params[:q].blank? && @pagy.pages > 1 %>
|
|
||||||
<%= render('shared/templates_order_select', with_recently_used: @pagy.count < 10_000) %>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
<%= render 'shared/pagination', pagy: @pagy, items_name: 'templates', right_additional_html: templates_order_select_html %>
|
|
||||||
<% elsif params[:q].present? %>
|
|
||||||
<div class="text-center">
|
|
||||||
<div class="mt-16 text-3xl font-semibold">
|
|
||||||
<%= t('templates_not_found') %>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% if @pagy.count > 0 %>
|
||||||
|
<div class="grid gap-4 md:grid-cols-3">
|
||||||
|
<%= render partial: 'templates/template', collection: @templates %>
|
||||||
|
</div>
|
||||||
|
<% templates_order_select_html = capture do %>
|
||||||
|
<% if params[:q].blank? && @pagy.pages > 1 %>
|
||||||
|
<%= render('shared/templates_order_select', with_recently_used: @pagy.count < 10_000) %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<%= render 'shared/pagination', pagy: @pagy, items_name: 'templates', right_additional_html: templates_order_select_html %>
|
||||||
|
<% elsif params[:q].present? %>
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="mt-16 text-3xl font-semibold">
|
||||||
|
<%= t('templates_not_found') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</dashboard-dropzone>
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<div class="absolute bottom-0 w-full cursor-pointer rounded-xl bg-base-100 border-2 border-base-300 border-dashed hidden z-50" data-target="dashboard-dropzone.fileDropzone" style="<%= local_assigns[:style] %>">
|
||||||
|
<div class="absolute top-0 right-0 left-0 bottom-0 flex justify-center p-2 items-center pointer-events-none">
|
||||||
|
<div class="flex flex-col items-center text-center" data-target="dashboard-dropzone.toggleLoading">
|
||||||
|
<span class="flex flex-col items-center">
|
||||||
|
<span>
|
||||||
|
<%= svg_icon('cloud_upload', class: 'w-9 h-9') %>
|
||||||
|
</span>
|
||||||
|
<div class="font-medium mb-1">
|
||||||
|
<%= t('upload_new_document') %>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span class="flex flex-col items-center hidden" data-target="dashboard-dropzone.fileDropzoneLoading">
|
||||||
|
<%= svg_icon('loader', class: 'w-9 h-9 animate-spin') %>
|
||||||
|
<div class="font-medium mb-1">
|
||||||
|
<%= t('uploading') %>...
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<%= form_for '', url: local_assigns.fetch(:url, templates_upload_path), id: form_id = SecureRandom.uuid, method: :post, class: 'block hidden', html: { enctype: 'multipart/form-data' }, data: { target: 'dashboard-dropzone.fileDropzone' } do %>
|
|
||||||
<input type="hidden" name="form_id" value="<%= form_id %>">
|
|
||||||
<button type="submit" class="hidden"></button>
|
|
||||||
<label for="dashboard_dropzone_input_<%= form_id %>">
|
|
||||||
<div class="absolute top-0 right-0 left-0 bottom-0 flex justify-center p-2 items-<%= local_assigns.fetch(:position, 'center') %>">
|
|
||||||
<div class="flex flex-col items-center text-center">
|
|
||||||
<% if local_assigns[:icon] %>
|
|
||||||
<span data-target="dashboard-dropzone.icon" class="flex flex-col items-center">
|
|
||||||
<span>
|
|
||||||
<%= svg_icon(local_assigns[:icon], class: 'w-10 h-10') %>
|
|
||||||
</span>
|
|
||||||
<% if local_assigns[:title] %>
|
|
||||||
<div class="font-medium mb-1">
|
|
||||||
<%= local_assigns[:title] %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
</span>
|
|
||||||
<% end %>
|
|
||||||
<span data-target="dashboard-dropzone.loading" class="flex flex-col items-center hidden">
|
|
||||||
<%= svg_icon('loader', class: 'w-10 h-10 animate-spin') %>
|
|
||||||
<div class="font-medium mb-1">
|
|
||||||
<%= t('uploading') %>...
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<input id="dashboard_dropzone_input_<%= form_id %>" name="files[]" class="hidden" data-action="change:dashboard-dropzone#onSelectFiles" data-target="dashboard-dropzone.input" type="file" accept="image/*, application/pdf<%= ', .docx, .doc, .xlsx, .xls, .odt, .rtf' if Docuseal.multitenant? %>" multiple>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
<% end %>
|
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
<dashboard-dropzone class="block h-36 relative group bg-base-200 border-2 rounded-2xl border-transparent">
|
<div class="h-36 relative group">
|
||||||
<a href="<%= template_path(template) %>" class="flex h-full flex-col justify-between pt-6 px-7 w-full peer">
|
<a href="<%= template_path(template) %>" class="flex h-full flex-col justify-between rounded-2xl pt-6 px-7 w-full bg-base-200 before:border-2 before:border-base-300 before:border-dashed before:absolute before:left-0 before:right-0 before:top-0 before:bottom-0 before:hidden before:rounded-2xl" data-targets="dashboard-dropzone.templateCards">
|
||||||
<div class="pb-4 text-xl font-semibold" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;">
|
<div class="text-xl font-semibold" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;">
|
||||||
<% if template.template_accesses.present? %>
|
<% if template.template_accesses.present? %>
|
||||||
<%= svg_icon('lock', class: 'w-6 h-6 inline -translate-y-[4px]') %>
|
<%= svg_icon('lock', class: 'w-6 h-6 inline -translate-y-[4px]') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% template.name.split(/(_)/).each do |item| %><%= item %><wbr><% end %>
|
<% template.name.split(/(_)/).each do |item| %><%= item %><wbr><% end %>
|
||||||
</div>
|
</div>
|
||||||
<div class="pb-6 pt-1 space-y-1" data-targets="dashboard-dropzone.hiddenOnHover">
|
<div class="pb-6 pt-1 space-y-1" data-targets="dashboard-dropzone.hiddenOnDrag">
|
||||||
<p class="flex items-center space-x-1 text-xs text-base-content/60">
|
<p class="flex items-center space-x-1 text-xs text-base-content/60">
|
||||||
<%= svg_icon('user', class: 'w-4 h-4') %>
|
<%= svg_icon('user', class: 'w-4 h-4') %>
|
||||||
<span><%= template.author.full_name.presence || template.author.email.to_s.sub(/\+\w+@/, '@') %></span>
|
<span><%= template.author.full_name.presence || template.author.email.to_s.sub(/\+\w+@/, '@') %></span>
|
||||||
@@ -28,8 +28,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<%= render 'templates/dashboard_dropzone_form', url: template_replace_documents_path(template), title: t('upload_new_based_on_template'), icon: 'files', position: 'end' %>
|
<div class="absolute top-0 bottom-0 w-0 items-center hidden md:group-hover:flex" style="right: 37px" data-targets="dashboard-dropzone.hiddenOnDrag">
|
||||||
<div class="absolute top-0 bottom-0 w-0 flex items-center hidden md:group-hover:flex" style="right: 37px" data-targets="dashboard-dropzone.hiddenOnHover">
|
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<% if can?(:update, template) && !template.archived_at? && template.account_id == current_account.id %>
|
<% if can?(:update, template) && !template.archived_at? && template.account_id == current_account.id %>
|
||||||
<span class="tooltip tooltip-left" data-tip="<%= t('move') %>">
|
<span class="tooltip tooltip-left" data-tip="<%= t('move') %>">
|
||||||
@@ -69,4 +68,4 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</dashboard-dropzone>
|
</div>
|
||||||
|
|||||||
@@ -1,81 +1,86 @@
|
|||||||
<% has_archived = current_account.templates.where.not(archived_at: nil).exists? %>
|
<% has_archived = current_account.templates.where.not(archived_at: nil).exists? %>
|
||||||
<% show_dropzone = params[:q].blank? && @pagy.pages == 1 && ((@template_folders.size < 10 && @templates.size.zero?) || (@template_folders.size < 7 && @templates.size < 4) || (@template_folders.size < 4 && @templates.size < 7)) %>
|
<% show_dropzone = params[:q].blank? && @pagy.pages == 1 && ((@template_folders.size < 10 && @templates.size.zero?) || (@template_folders.size < 7 && @templates.size < 4) || (@template_folders.size < 4 && @templates.size < 7)) %>
|
||||||
<% if Docuseal.demo? %><%= render 'shared/demo_alert' %><% end %>
|
<% if Docuseal.demo? %><%= render 'shared/demo_alert' %><% end %>
|
||||||
<div class="flex justify-between items-center w-full mb-4 relative">
|
<dashboard-dropzone>
|
||||||
<% unless show_dropzone %>
|
<%= form_for '', url: '', id: form_id = SecureRandom.uuid, method: :post, class: 'hidden', data: { target: 'dashboard-dropzone.form' }, html: { enctype: 'multipart/form-data' } do %>
|
||||||
<dashboard-dropzone data-show-only-on-window-hover="true" class="absolute bottom-0 w-full rounded-xl bg-base-200 border-2 border-dashed z-10 hidden" style="height: 7.5rem;">
|
<input name="form_id" value="<%= form_id %>">
|
||||||
<%= render 'templates/dashboard_dropzone_form', title: t('upload_new_document'), icon: 'cloud_upload' %>
|
<button type="submit"></button>
|
||||||
</dashboard-dropzone>
|
<input id="dashboard_dropzone_input" name="files[]" type="file" multiple>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="flex items-center flex-grow min-w-0">
|
<div class="flex justify-between items-center w-full mb-4 relative">
|
||||||
<% if has_archived || @pagy.count > 0 || @template_folders.present? %>
|
<% unless show_dropzone %>
|
||||||
<div class="mr-2">
|
<%= render 'templates/dashboard_dropzone', style: 'height: 114px' %>
|
||||||
<%= render 'dashboard/toggle_view', selected: 'templates' %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
<h1 class="text-2xl truncate md:text-3xl sm:text-4xl font-bold md:block <%= 'hidden' if params[:q].present? %>">
|
<div class="flex items-center flex-grow min-w-0">
|
||||||
<%= t('document_templates_html') %>
|
<% if has_archived || @pagy.count > 0 || @template_folders.present? %>
|
||||||
</h1>
|
<div class="mr-2">
|
||||||
</div>
|
<%= render 'dashboard/toggle_view', selected: 'templates' %>
|
||||||
<div class="flex space-x-2">
|
|
||||||
<% if params[:q].present? || @pagy.pages > 1 || @template_folders.present? %>
|
|
||||||
<%= render 'shared/search_input' %>
|
|
||||||
<% end %>
|
|
||||||
<% if can?(:create, ::Template) %>
|
|
||||||
<span class="hidden sm:block">
|
|
||||||
<%= render 'templates/upload_button' %>
|
|
||||||
</span>
|
|
||||||
<%= link_to new_template_path, class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %>
|
|
||||||
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
|
||||||
<span class="hidden md:block"><%= t('create') %></span>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% view_archived_html = capture do %>
|
|
||||||
<% if has_archived %>
|
|
||||||
<div>
|
|
||||||
<a href="<%= templates_archived_index_path %>" class="link text-sm"><%= t('view_archived') %></a>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
<% templates_order_select_html = capture do %>
|
|
||||||
<% if params[:q].blank? && @pagy.pages > 1 %>
|
|
||||||
<%= render('shared/templates_order_select', with_recently_used: @pagy.count < 10_000) %>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
<% if @template_folders.present? %>
|
|
||||||
<div class="grid gap-4 md:grid-cols-3 <%= 'mb-6' if @templates.present? %>">
|
|
||||||
<%= render partial: 'template_folders/folder', collection: @template_folders, as: :folder %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<% if @templates.present? %>
|
|
||||||
<div class="grid gap-4 md:grid-cols-3">
|
|
||||||
<%= render partial: 'templates/template', collection: @templates %>
|
|
||||||
<% if show_dropzone && current_user.created_at > 2.weeks.ago || params[:tour] == 'true' %>
|
|
||||||
<% user_config = current_user.user_configs.find_or_initialize_by(key: UserConfig::SHOW_APP_TOUR) %>
|
|
||||||
<% if user_config.new_record? || user_config.value || params[:tour] == 'true' %>
|
|
||||||
<div class="hidden md:block">
|
|
||||||
<app-tour id="app_tour" data-show-tour="<%= params[:tour] == 'true' || user_config.value %>" data-type="dashboard" data-next-page-path="<%= @templates.first && can?(:edit, @templates.first) ? edit_template_path(@templates.first, params.permit(:tour)) : settings_account_path %>" data-i18n="<%= t('app_tour').to_json %>"></app-tour>
|
|
||||||
<% if user_config.new_record? && !params.key?(:tour) %>
|
|
||||||
<div class="h-36 rounded-2xl pt-3 px-7 w-full border border-dashed border-base-300">
|
|
||||||
<div class="text-xl text-center font-semibold text-base-content">
|
|
||||||
<%= t('welcome_to_docuseal') %>
|
|
||||||
</div>
|
|
||||||
<div class="my-2 text-center text-xs text-base-content/70">
|
|
||||||
<%= t('start_a_quick_tour_to_learn_how_to_create_an_send_your_first_document') %>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2 mt-3 w-full">
|
|
||||||
<%= button_to button_title(title: t('skip'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), user_configs_path, params: { user_config: { key: UserConfig::SHOW_APP_TOUR, value: false } }, class: 'btn btn-sm btn-outline w-full', form_class: 'flex-1', method: :post, form: { onsubmit: 'window.app_tour.parentNode.remove()' } %>
|
|
||||||
<%= button_to t('start_tour'), user_configs_path, params: { user_config: { key: UserConfig::SHOW_APP_TOUR, value: true } }, class: 'btn btn-sm btn-warning w-full', form_class: 'flex-1', method: :post, form: { onsubmit: 'window.app_tour.start()' } %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<h1 class="text-2xl truncate md:text-3xl sm:text-4xl font-bold md:block <%= 'hidden' if params[:q].present? %>">
|
||||||
|
<%= t('document_templates_html') %>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
<% if params[:q].present? || @pagy.pages > 1 || @template_folders.present? %>
|
||||||
|
<%= render 'shared/search_input' %>
|
||||||
|
<% end %>
|
||||||
|
<% if can?(:create, ::Template) %>
|
||||||
|
<span class="hidden sm:block">
|
||||||
|
<%= render 'templates/upload_button' %>
|
||||||
|
</span>
|
||||||
|
<%= link_to new_template_path, class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %>
|
||||||
|
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
||||||
|
<span class="hidden md:block"><%= t('create') %></span>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% view_archived_html = capture do %>
|
||||||
|
<% if has_archived %>
|
||||||
|
<div>
|
||||||
|
<a href="<%= templates_archived_index_path %>" class="link text-sm"><%= t('view_archived') %></a>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% templates_order_select_html = capture do %>
|
||||||
|
<% if params[:q].blank? && @pagy.pages > 1 %>
|
||||||
|
<%= render('shared/templates_order_select', with_recently_used: @pagy.count < 10_000) %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% if @template_folders.present? %>
|
||||||
|
<div class="grid gap-4 md:grid-cols-3 <%= 'mb-6' if @templates.present? %>">
|
||||||
|
<%= render partial: 'template_folders/folder', collection: @template_folders, as: :folder %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% if @templates.present? %>
|
||||||
|
<div class="grid gap-4 md:grid-cols-3">
|
||||||
|
<%= render partial: 'templates/template', collection: @templates %>
|
||||||
|
<% if show_dropzone && current_user.created_at > 2.weeks.ago || params[:tour] == 'true' %>
|
||||||
|
<% user_config = current_user.user_configs.find_or_initialize_by(key: UserConfig::SHOW_APP_TOUR) %>
|
||||||
|
<% if user_config.new_record? || user_config.value || params[:tour] == 'true' %>
|
||||||
|
<div class="hidden md:block">
|
||||||
|
<app-tour id="app_tour" data-show-tour="<%= params[:tour] == 'true' || user_config.value %>" data-type="dashboard" data-next-page-path="<%= @templates.first && can?(:edit, @templates.first) ? edit_template_path(@templates.first, params.permit(:tour)) : settings_account_path %>" data-i18n="<%= t('app_tour').to_json %>"></app-tour>
|
||||||
|
<% if user_config.new_record? && !params.key?(:tour) %>
|
||||||
|
<div class="h-36 rounded-2xl pt-3 px-7 w-full border border-dashed border-base-300">
|
||||||
|
<div class="text-xl text-center font-semibold text-base-content">
|
||||||
|
<%= t('welcome_to_docuseal') %>
|
||||||
|
</div>
|
||||||
|
<div class="my-2 text-center text-xs text-base-content/70">
|
||||||
|
<%= t('start_a_quick_tour_to_learn_how_to_create_an_send_your_first_document') %>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 mt-3 w-full">
|
||||||
|
<%= button_to button_title(title: t('skip'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), user_configs_path, params: { user_config: { key: UserConfig::SHOW_APP_TOUR, value: false } }, class: 'btn btn-sm btn-outline w-full', form_class: 'flex-1', method: :post, form: { onsubmit: 'window.app_tour.parentNode.remove()' } %>
|
||||||
|
<%= button_to t('start_tour'), user_configs_path, params: { user_config: { key: UserConfig::SHOW_APP_TOUR, value: true } }, class: 'btn btn-sm btn-warning w-full', form_class: 'flex-1', method: :post, form: { onsubmit: 'window.app_tour.start()' } %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</dashboard-dropzone>
|
||||||
<% if show_dropzone %>
|
<% if show_dropzone %>
|
||||||
<%= render 'templates/dropzone' %>
|
<%= render 'templates/dropzone' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -717,7 +717,7 @@ en: &en
|
|||||||
name_a_z: Name A-Z
|
name_a_z: Name A-Z
|
||||||
recently_used: Recently used
|
recently_used: Recently used
|
||||||
newest_first: Newest first
|
newest_first: Newest first
|
||||||
upload_new_based_on_template: Upload new based on template
|
create_base_on_template: Create based on template
|
||||||
submission_sources:
|
submission_sources:
|
||||||
api: API
|
api: API
|
||||||
bulk: Bulk Send
|
bulk: Bulk Send
|
||||||
|
|||||||
+1
-1
@@ -97,7 +97,7 @@ Rails.application.routes.draw do
|
|||||||
resources :templates, only: %i[new create edit update show destroy] do
|
resources :templates, only: %i[new create edit update show destroy] do
|
||||||
resource :debug, only: %i[show], controller: 'templates_debug' if Rails.env.development?
|
resource :debug, only: %i[show], controller: 'templates_debug' if Rails.env.development?
|
||||||
resources :documents, only: %i[create], controller: 'template_documents'
|
resources :documents, only: %i[create], controller: 'template_documents'
|
||||||
resources :replace_documents, only: %i[create], controller: 'template_replace_documents'
|
resources :clone_and_replace, only: %i[create], controller: 'templates_clone_and_replace'
|
||||||
resources :restore, only: %i[create], controller: 'templates_restore'
|
resources :restore, only: %i[create], controller: 'templates_restore'
|
||||||
resources :archived, only: %i[index], controller: 'templates_archived_submissions'
|
resources :archived, only: %i[index], controller: 'templates_archived_submissions'
|
||||||
resources :submissions, only: %i[new create]
|
resources :submissions, only: %i[new create]
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ module Templates
|
|||||||
module Clone
|
module Clone
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
|
# rubocop:disable Metrics, Style/CombinableLoops
|
||||||
def call(original_template, author:, external_id: nil, name: nil, folder_name: nil)
|
def call(original_template, author:, external_id: nil, name: nil, folder_name: nil)
|
||||||
template = original_template.account.templates.new
|
template = original_template.account.templates.new
|
||||||
|
|
||||||
@@ -29,10 +30,13 @@ module Templates
|
|||||||
template.schema.first['name'] = template.name
|
template.schema.first['name'] = template.name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
original_template.template_accesses.each do |template_access|
|
||||||
|
template.template_accesses.new(user_id: template_access.user_id)
|
||||||
|
end
|
||||||
|
|
||||||
template
|
template
|
||||||
end
|
end
|
||||||
|
|
||||||
# rubocop:disable Metrics, Style/CombinableLoops
|
|
||||||
def update_submitters_and_fields_and_schema(cloned_submitters, cloned_fields, cloned_schema, cloned_preferences)
|
def update_submitters_and_fields_and_schema(cloned_submitters, cloned_fields, cloned_schema, cloned_preferences)
|
||||||
submitter_uuids_replacements = {}
|
submitter_uuids_replacements = {}
|
||||||
field_uuids_replacements = {}
|
field_uuids_replacements = {}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ module Templates
|
|||||||
end
|
end
|
||||||
|
|
||||||
next if template.fields.any? { |f| f['areas']&.any? { |a| a['attachment_uuid'] == document.uuid } }
|
next if template.fields.any? { |f| f['areas']&.any? { |a| a['attachment_uuid'] == document.uuid } }
|
||||||
next unless submitter && document.metadata.dig('pdf', 'fields').present?
|
next if submitter.blank? || document.metadata.dig('pdf', 'fields').blank?
|
||||||
|
|
||||||
pdf_fields = document.metadata['pdf'].delete('fields').to_a
|
pdf_fields = document.metadata['pdf'].delete('fields').to_a
|
||||||
pdf_fields.each { |f| f['submitter_uuid'] = submitter['uuid'] }
|
pdf_fields.each { |f| f['submitter_uuid'] = submitter['uuid'] }
|
||||||
|
|||||||
Reference in New Issue
Block a user