Merge from docusealco/wip
This commit is contained in:
@@ -11,8 +11,9 @@ module Api
|
|||||||
params[:before] = Time.zone.at(params[:before].to_i) if params[:before].present?
|
params[:before] = Time.zone.at(params[:before].to_i) if params[:before].present?
|
||||||
|
|
||||||
submitters = paginate(
|
submitters = paginate(
|
||||||
submitters.preload(template: :folder, submission: [:submitters, { audit_trail_attachment: :blob,
|
submitters.preload(template: { folder: :parent_folder },
|
||||||
combined_document_attachment: :blob }],
|
submission: [:submitters, { audit_trail_attachment: :blob,
|
||||||
|
combined_document_attachment: :blob }],
|
||||||
documents_attachments: :blob, attachments_attachments: :blob),
|
documents_attachments: :blob, attachments_attachments: :blob),
|
||||||
field: :completed_at
|
field: :completed_at
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ module Api
|
|||||||
submissions = filter_submissions(submissions, params)
|
submissions = filter_submissions(submissions, params)
|
||||||
|
|
||||||
submissions = paginate(submissions.preload(:created_by_user, :submitters,
|
submissions = paginate(submissions.preload(:created_by_user, :submitters,
|
||||||
template: :folder,
|
template: { folder: :parent_folder },
|
||||||
combined_document_attachment: :blob,
|
combined_document_attachment: :blob,
|
||||||
audit_trail_attachment: :blob))
|
audit_trail_attachment: :blob))
|
||||||
|
|
||||||
@@ -104,9 +104,10 @@ module Api
|
|||||||
submissions = submissions.where(slug: params[:slug]) if params[:slug].present?
|
submissions = submissions.where(slug: params[:slug]) if params[:slug].present?
|
||||||
|
|
||||||
if params[:template_folder].present?
|
if params[:template_folder].present?
|
||||||
folder_ids = TemplateFolder.accessible_by(current_ability).where(name: params[:template_folder]).pluck(:id)
|
folders =
|
||||||
|
TemplateFolders.filter_by_full_name(TemplateFolder.accessible_by(current_ability), params[:template_folder])
|
||||||
|
|
||||||
submissions = submissions.joins(:template).where(template: { folder_id: folder_ids })
|
submissions = submissions.joins(:template).where(template: { folder_id: folders.pluck(:id) })
|
||||||
end
|
end
|
||||||
|
|
||||||
if params.key?(:archived)
|
if params.key?(:archived)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ module Api
|
|||||||
def index
|
def index
|
||||||
templates = filter_templates(@templates, params)
|
templates = filter_templates(@templates, params)
|
||||||
|
|
||||||
templates = paginate(templates.preload(:author, :folder))
|
templates = paginate(templates.preload(:author, folder: :parent_folder))
|
||||||
|
|
||||||
schema_documents =
|
schema_documents =
|
||||||
ActiveStorage::Attachment.where(record_id: templates.map(&:id),
|
ActiveStorage::Attachment.where(record_id: templates.map(&:id),
|
||||||
@@ -92,9 +92,9 @@ module Api
|
|||||||
templates = templates.where(slug: params[:slug]) if params[:slug].present?
|
templates = templates.where(slug: params[:slug]) if params[:slug].present?
|
||||||
|
|
||||||
if params[:folder].present?
|
if params[:folder].present?
|
||||||
folder_ids = TemplateFolder.accessible_by(current_ability).where(name: params[:folder]).pluck(:id)
|
folders = TemplateFolders.filter_by_full_name(TemplateFolder.accessible_by(current_ability), params[:folder])
|
||||||
|
|
||||||
templates = templates.where(folder_id: folder_ids)
|
templates = templates.where(folder_id: folders.pluck(:id))
|
||||||
end
|
end
|
||||||
|
|
||||||
templates
|
templates
|
||||||
|
|||||||
@@ -3,14 +3,31 @@
|
|||||||
class TemplateFoldersAutocompleteController < ApplicationController
|
class TemplateFoldersAutocompleteController < ApplicationController
|
||||||
load_and_authorize_resource :template_folder, parent: false
|
load_and_authorize_resource :template_folder, parent: false
|
||||||
|
|
||||||
LIMIT = 100
|
LIMIT = 30
|
||||||
|
|
||||||
def index
|
def index
|
||||||
templates_query = Template.accessible_by(current_ability).where(archived_at: nil)
|
parent_name, name =
|
||||||
|
if params[:parent_name].present?
|
||||||
|
[params[:parent_name], params[:q]]
|
||||||
|
else
|
||||||
|
params[:q].to_s.split(' /', 2).map(&:squish)
|
||||||
|
end
|
||||||
|
|
||||||
template_folders = @template_folders.where(id: templates_query.select(:folder_id))
|
if name
|
||||||
template_folders = TemplateFolders.search(template_folders, params[:q]).limit(LIMIT)
|
parent_folder = @template_folders.find_by(name: parent_name, parent_folder_id: nil)
|
||||||
|
else
|
||||||
|
name = parent_name
|
||||||
|
end
|
||||||
|
|
||||||
render json: template_folders.as_json(only: %i[name archived_at])
|
template_folders = TemplateFolders.filter_active_folders(@template_folders.where(parent_folder:),
|
||||||
|
Template.accessible_by(current_ability))
|
||||||
|
|
||||||
|
name = name.to_s.downcase
|
||||||
|
|
||||||
|
template_folders = TemplateFolders.search(template_folders, name).order(id: :desc).limit(LIMIT)
|
||||||
|
|
||||||
|
render json: template_folders.preload(:parent_folder)
|
||||||
|
.sort_by { |e| e.name.downcase.index(name) || Float::MAX }
|
||||||
|
.as_json(only: %i[name archived_at], methods: %i[full_name])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,13 +5,39 @@ class TemplateFoldersController < ApplicationController
|
|||||||
|
|
||||||
helper_method :selected_order
|
helper_method :selected_order
|
||||||
|
|
||||||
def show
|
TEMPLATES_PER_PAGE = 12
|
||||||
@templates = @template_folder.templates.active.accessible_by(current_ability)
|
FOLDERS_PER_PAGE = 18
|
||||||
.preload(:author, :template_accesses)
|
|
||||||
@templates = Templates.search(current_user, @templates, params[:q])
|
|
||||||
@templates = Templates::Order.call(@templates, current_user, selected_order)
|
|
||||||
|
|
||||||
@pagy, @templates = pagy_auto(@templates, limit: 12)
|
def show
|
||||||
|
@templates = Template.active.accessible_by(current_ability)
|
||||||
|
.where(folder: [@template_folder, *(params[:q].present? ? @template_folder.subfolders : [])])
|
||||||
|
.preload(:author, :template_accesses)
|
||||||
|
|
||||||
|
@template_folders =
|
||||||
|
@template_folder.subfolders.where(id: Template.accessible_by(current_ability).active.select(:folder_id))
|
||||||
|
|
||||||
|
@template_folders = TemplateFolders.search(@template_folders, params[:q])
|
||||||
|
@template_folders = TemplateFolders.sort(@template_folders, current_user, selected_order)
|
||||||
|
|
||||||
|
if @templates.exists?
|
||||||
|
@templates = Templates.search(current_user, @templates, params[:q])
|
||||||
|
@templates = Templates::Order.call(@templates, current_user, selected_order)
|
||||||
|
|
||||||
|
limit =
|
||||||
|
if @template_folders.size < 4
|
||||||
|
TEMPLATES_PER_PAGE
|
||||||
|
else
|
||||||
|
(@template_folders.size < 7 ? 9 : 6)
|
||||||
|
end
|
||||||
|
|
||||||
|
@pagy, @templates = pagy_auto(@templates, limit:)
|
||||||
|
|
||||||
|
load_related_submissions if params[:q].present? && @templates.blank?
|
||||||
|
else
|
||||||
|
@pagy, @template_folders = pagy(@template_folders, limit: FOLDERS_PER_PAGE)
|
||||||
|
|
||||||
|
@templates = @templates.none
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit; end
|
def edit; end
|
||||||
@@ -40,4 +66,21 @@ class TemplateFoldersController < ApplicationController
|
|||||||
def template_folder_params
|
def template_folder_params
|
||||||
params.require(:template_folder).permit(:name)
|
params.require(:template_folder).permit(:name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def load_related_submissions
|
||||||
|
@related_submissions =
|
||||||
|
Submission.accessible_by(current_ability)
|
||||||
|
.where(archived_at: nil)
|
||||||
|
.where(template_id: current_account.templates.active
|
||||||
|
.where(folder: [@template_folder, *@template_folder.subfolders])
|
||||||
|
.select(:id))
|
||||||
|
.preload(:template_accesses, :created_by_user,
|
||||||
|
template: :author,
|
||||||
|
submitters: :start_form_submission_events)
|
||||||
|
|
||||||
|
@related_submissions = Submissions.search(current_user, @related_submissions, params[:q])
|
||||||
|
.order(id: :desc)
|
||||||
|
|
||||||
|
@related_submissions_pagy, @related_submissions = pagy_auto(@related_submissions, limit: 5)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,9 +4,27 @@ class TemplatesArchivedController < ApplicationController
|
|||||||
load_and_authorize_resource :template, parent: false
|
load_and_authorize_resource :template, parent: false
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@templates = @templates.where.not(archived_at: nil).preload(:author, :folder, :template_accesses).order(id: :desc)
|
@templates = @templates.where.not(archived_at: nil)
|
||||||
|
.preload(:author, :template_accesses, folder: :parent_folder)
|
||||||
|
.order(id: :desc)
|
||||||
|
|
||||||
@templates = Templates.search(current_user, @templates, params[:q])
|
@templates = Templates.search(current_user, @templates, params[:q])
|
||||||
|
|
||||||
@pagy, @templates = pagy_auto(@templates, limit: 12)
|
@pagy, @templates = pagy_auto(@templates, limit: 12)
|
||||||
|
|
||||||
|
return unless params[:q].present? && @templates.blank?
|
||||||
|
|
||||||
|
@related_submissions =
|
||||||
|
Submission.accessible_by(current_ability)
|
||||||
|
.joins(:template)
|
||||||
|
.where.not(templates: { archived_at: nil })
|
||||||
|
.preload(:template_accesses, :created_by_user,
|
||||||
|
template: :author,
|
||||||
|
submitters: :start_form_submission_events)
|
||||||
|
|
||||||
|
@related_submissions = Submissions.search(current_user, @related_submissions, params[:q])
|
||||||
|
.order(id: :desc)
|
||||||
|
|
||||||
|
@related_submissions_pagy, @related_submissions = pagy_auto(@related_submissions, limit: 5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -11,10 +11,11 @@ class TemplatesDashboardController < ApplicationController
|
|||||||
helper_method :selected_order
|
helper_method :selected_order
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@template_folders = @template_folders.where(id: @templates.active.select(:folder_id))
|
@template_folders =
|
||||||
|
TemplateFolders.filter_active_folders(@template_folders.where(parent_folder_id: nil), @templates)
|
||||||
|
|
||||||
@template_folders = TemplateFolders.search(@template_folders, params[:q])
|
@template_folders = TemplateFolders.search(@template_folders, params[:q])
|
||||||
@template_folders = sort_template_folders(@template_folders, current_user, selected_order)
|
@template_folders = TemplateFolders.sort(@template_folders, current_user, selected_order)
|
||||||
|
|
||||||
@pagy, @template_folders = pagy(
|
@pagy, @template_folders = pagy(
|
||||||
@template_folders,
|
@template_folders,
|
||||||
@@ -68,40 +69,6 @@ class TemplatesDashboardController < ApplicationController
|
|||||||
Templates.search(current_user, rel, params[:q])
|
Templates.search(current_user, rel, params[:q])
|
||||||
end
|
end
|
||||||
|
|
||||||
def sort_template_folders(template_folders, current_user, order)
|
|
||||||
case order
|
|
||||||
when 'used_at'
|
|
||||||
subquery =
|
|
||||||
Template.left_joins(:submissions)
|
|
||||||
.group(:folder_id)
|
|
||||||
.where(account_id: current_user.account_id)
|
|
||||||
.select(
|
|
||||||
:folder_id,
|
|
||||||
Template.arel_table[:updated_at].maximum.as('updated_at_max'),
|
|
||||||
Submission.arel_table[:created_at].maximum.as('submission_created_at_max')
|
|
||||||
)
|
|
||||||
|
|
||||||
template_folders = template_folders.joins(
|
|
||||||
Template.arel_table
|
|
||||||
.join(subquery.arel.as('templates'), Arel::Nodes::OuterJoin)
|
|
||||||
.on(TemplateFolder.arel_table[:id].eq(Template.arel_table[:folder_id]))
|
|
||||||
.join_sources
|
|
||||||
)
|
|
||||||
|
|
||||||
template_folders.order(
|
|
||||||
Arel::Nodes::Case.new
|
|
||||||
.when(Template.arel_table[:submission_created_at_max].gt(Template.arel_table[:updated_at_max]))
|
|
||||||
.then(Template.arel_table[:submission_created_at_max])
|
|
||||||
.else(Template.arel_table[:updated_at_max])
|
|
||||||
.desc
|
|
||||||
)
|
|
||||||
when 'name'
|
|
||||||
template_folders.order(name: :asc)
|
|
||||||
else
|
|
||||||
template_folders.order(id: :desc)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def selected_order
|
def selected_order
|
||||||
@selected_order ||=
|
@selected_order ||=
|
||||||
if cookies.permanent[:dashboard_templates_order].blank? ||
|
if cookies.permanent[:dashboard_templates_order].blank? ||
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ class TemplatesFoldersController < ApplicationController
|
|||||||
def edit; end
|
def edit; end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@template.folder = TemplateFolders.find_or_create_by_name(current_user, params[:name])
|
name = [params[:parent_name], params[:name]].compact_blank.join(' / ')
|
||||||
|
|
||||||
|
@template.folder = TemplateFolders.find_or_create_by_name(current_user, name)
|
||||||
|
|
||||||
if @template.save
|
if @template.save
|
||||||
redirect_back(fallback_location: template_path(@template), notice: I18n.t('document_template_has_been_moved'))
|
redirect_back(fallback_location: template_path(@template), notice: I18n.t('document_template_has_been_moved'))
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export default targetable(class extends HTMLElement {
|
|||||||
static [target.static] = [
|
static [target.static] = [
|
||||||
'form',
|
'form',
|
||||||
'fileDropzone',
|
'fileDropzone',
|
||||||
|
'folderDropzone',
|
||||||
'fileDropzoneLoading'
|
'fileDropzoneLoading'
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -25,12 +26,13 @@ export default targetable(class extends HTMLElement {
|
|||||||
window.addEventListener('dragleave', this.onWindowDragleave)
|
window.addEventListener('dragleave', this.onWindowDragleave)
|
||||||
|
|
||||||
this.fileDropzone?.addEventListener('drop', this.onDropFile)
|
this.fileDropzone?.addEventListener('drop', this.onDropFile)
|
||||||
|
this.folderDropzone?.addEventListener('drop', this.onDropNewFolder)
|
||||||
|
|
||||||
this.folderCards.forEach((el) => el.addEventListener('drop', (e) => this.onDropFolder(e, el)))
|
this.folderCards.forEach((el) => el.addEventListener('drop', (e) => this.onDropFolder(e, el)))
|
||||||
this.templateCards.forEach((el) => el.addEventListener('drop', this.onDropTemplate))
|
this.templateCards.forEach((el) => el.addEventListener('drop', this.onDropTemplate))
|
||||||
this.templateCards.forEach((el) => el.addEventListener('dragstart', this.onTemplateDragStart))
|
this.templateCards.forEach((el) => el.addEventListener('dragstart', this.onTemplateDragStart))
|
||||||
|
|
||||||
return [this.fileDropzone, ...this.folderCards, ...this.templateCards].forEach((el) => {
|
return [this.fileDropzone, this.folderDropzone, ...this.folderCards, ...this.templateCards].forEach((el) => {
|
||||||
el?.addEventListener('dragover', this.onDragover)
|
el?.addEventListener('dragover', this.onDragover)
|
||||||
el?.addEventListener('dragleave', this.onDragleave)
|
el?.addEventListener('dragleave', this.onDragleave)
|
||||||
})
|
})
|
||||||
@@ -46,6 +48,10 @@ export default targetable(class extends HTMLElement {
|
|||||||
onTemplateDragStart = (e) => {
|
onTemplateDragStart = (e) => {
|
||||||
const id = e.target.href.split('/').pop()
|
const id = e.target.href.split('/').pop()
|
||||||
|
|
||||||
|
this.folderCards.forEach((el) => el.classList.remove('bg-base-200', 'before:hidden'))
|
||||||
|
this.folderDropzone?.classList?.remove('hidden')
|
||||||
|
window.flash?.remove()
|
||||||
|
|
||||||
e.dataTransfer.effectAllowed = 'move'
|
e.dataTransfer.effectAllowed = 'move'
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
@@ -104,7 +110,7 @@ export default targetable(class extends HTMLElement {
|
|||||||
} else {
|
} else {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
|
|
||||||
formData.append('name', el.innerText.trim())
|
formData.append('name', el.dataset.fullName)
|
||||||
|
|
||||||
fetch(`/templates/${templateId}/folder`, {
|
fetch(`/templates/${templateId}/folder`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
@@ -176,6 +182,24 @@ export default targetable(class extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDropNewFolder (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const templateId = e.dataTransfer.getData('template_id')
|
||||||
|
|
||||||
|
const a = document.createElement('a')
|
||||||
|
|
||||||
|
a.href = `/templates/${templateId}/folder/edit?autocomplete=false`
|
||||||
|
a.dataset.turboFrame = 'modal'
|
||||||
|
a.classList.add('hidden')
|
||||||
|
|
||||||
|
document.body.append(a)
|
||||||
|
|
||||||
|
a.click()
|
||||||
|
|
||||||
|
a.remove()
|
||||||
|
}
|
||||||
|
|
||||||
onDragleave () {
|
onDragleave () {
|
||||||
this.style.backgroundColor = null
|
this.style.backgroundColor = null
|
||||||
|
|
||||||
@@ -199,6 +223,7 @@ export default targetable(class extends HTMLElement {
|
|||||||
|
|
||||||
this.isDrag = true
|
this.isDrag = true
|
||||||
|
|
||||||
|
window.flash?.remove()
|
||||||
this.fileDropzone?.classList?.remove('hidden')
|
this.fileDropzone?.classList?.remove('hidden')
|
||||||
|
|
||||||
this.hiddenOnDrag.forEach((el) => { el.style.display = 'none' })
|
this.hiddenOnDrag.forEach((el) => { el.style.display = 'none' })
|
||||||
@@ -212,6 +237,7 @@ export default targetable(class extends HTMLElement {
|
|||||||
this.isDrag = false
|
this.isDrag = false
|
||||||
|
|
||||||
this.fileDropzone?.classList?.add('hidden')
|
this.fileDropzone?.classList?.add('hidden')
|
||||||
|
this.folderDropzone?.classList?.add('hidden')
|
||||||
|
|
||||||
this.hiddenOnDrag.forEach((el) => { el.style.display = null })
|
this.hiddenOnDrag.forEach((el) => { el.style.display = null })
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import autocomplete from 'autocompleter'
|
|||||||
|
|
||||||
export default class extends HTMLElement {
|
export default class extends HTMLElement {
|
||||||
connectedCallback () {
|
connectedCallback () {
|
||||||
|
if (this.dataset.enabled === 'false') return
|
||||||
|
|
||||||
autocomplete({
|
autocomplete({
|
||||||
input: this.input,
|
input: this.input,
|
||||||
preventSubmit: this.dataset.submitOnSelect === 'true' ? 0 : 1,
|
preventSubmit: this.dataset.submitOnSelect === 'true' ? 0 : 1,
|
||||||
@@ -14,12 +16,16 @@ export default class extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSelect = (item) => {
|
onSelect = (item) => {
|
||||||
this.input.value = item.name
|
this.input.value = this.dataset.parentName ? item.name : item.full_name
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch = (text, resolve) => {
|
fetch = (text, resolve) => {
|
||||||
const queryParams = new URLSearchParams({ q: text })
|
const queryParams = new URLSearchParams({ q: text })
|
||||||
|
|
||||||
|
if (this.dataset.parentName) {
|
||||||
|
queryParams.append('parent_name', this.dataset.parentName)
|
||||||
|
}
|
||||||
|
|
||||||
fetch('/template_folders_autocomplete?' + queryParams).then(async (resp) => {
|
fetch('/template_folders_autocomplete?' + queryParams).then(async (resp) => {
|
||||||
const items = await resp.json()
|
const items = await resp.json()
|
||||||
|
|
||||||
@@ -34,7 +40,7 @@ export default class extends HTMLElement {
|
|||||||
|
|
||||||
div.setAttribute('dir', 'auto')
|
div.setAttribute('dir', 'auto')
|
||||||
|
|
||||||
div.textContent = item.name
|
div.textContent = this.dataset.parentName ? item.name : item.full_name
|
||||||
|
|
||||||
return div
|
return div
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
export default class extends HTMLElement {
|
export default class extends HTMLElement {
|
||||||
connectedCallback () {
|
connectedCallback () {
|
||||||
this.image.addEventListener('load', (e) => {
|
const image = this.querySelector('img')
|
||||||
this.image.setAttribute('width', e.target.naturalWidth)
|
|
||||||
this.image.setAttribute('height', e.target.naturalHeight)
|
image.addEventListener('load', (e) => {
|
||||||
|
image.setAttribute('width', e.target.naturalWidth)
|
||||||
|
image.setAttribute('height', e.target.naturalHeight)
|
||||||
|
|
||||||
this.style.aspectRatio = `${e.target.naturalWidth} / ${e.target.naturalHeight}`
|
this.style.aspectRatio = `${e.target.naturalWidth} / ${e.target.naturalHeight}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
get image () {
|
|
||||||
return this.querySelector('img')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,11 +56,11 @@
|
|||||||
<div
|
<div
|
||||||
v-else-if="field.type === 'signature' && signature"
|
v-else-if="field.type === 'signature' && signature"
|
||||||
class="flex justify-between h-full gap-1 overflow-hidden w-full"
|
class="flex justify-between h-full gap-1 overflow-hidden w-full"
|
||||||
:class="isNarrow && (withSignatureId || field.preferences?.reason_field_uuid) ? 'flex-row' : 'flex-col'"
|
:class="isNarrow && (isShowSignatureId || field.preferences?.reason_field_uuid) ? 'flex-row' : 'flex-col'"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex overflow-hidden"
|
class="flex overflow-hidden"
|
||||||
:class="isNarrow && (withSignatureId || field.preferences?.reason_field_uuid) ? 'w-1/2' : 'flex-grow'"
|
:class="isNarrow && (isShowSignatureId || field.preferences?.reason_field_uuid) ? 'w-1/2' : 'flex-grow'"
|
||||||
style="min-height: 50%"
|
style="min-height: 50%"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="withSignatureId || field.preferences?.reason_field_uuid"
|
v-if="isShowSignatureId || field.preferences?.reason_field_uuid"
|
||||||
class="text-[1vw] lg:text-[0.55rem] lg:leading-[0.65rem]"
|
class="text-[1vw] lg:text-[0.55rem] lg:leading-[0.65rem]"
|
||||||
:class="isNarrow ? 'w-1/2' : 'w-full'"
|
:class="isNarrow ? 'w-1/2' : 'w-full'"
|
||||||
>
|
>
|
||||||
@@ -333,6 +333,13 @@ export default {
|
|||||||
verification: this.t('verify_id')
|
verification: this.t('verify_id')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isShowSignatureId () {
|
||||||
|
if ([true, false].includes(this.field.preferences?.with_signature_id)) {
|
||||||
|
return this.field.preferences.with_signature_id
|
||||||
|
} else {
|
||||||
|
return this.withSignatureId
|
||||||
|
}
|
||||||
|
},
|
||||||
alignClasses () {
|
alignClasses () {
|
||||||
if (!this.field.preferences) {
|
if (!this.field.preferences) {
|
||||||
return { 'items-center': true }
|
return { 'items-center': true }
|
||||||
@@ -460,9 +467,6 @@ export default {
|
|||||||
fontScale () {
|
fontScale () {
|
||||||
return 1000 / 612.0
|
return 1000 / 612.0
|
||||||
},
|
},
|
||||||
ladscapeScale () {
|
|
||||||
return 8.5 / 11.0
|
|
||||||
},
|
|
||||||
computedStyle () {
|
computedStyle () {
|
||||||
const { x, y, w, h } = this.area
|
const { x, y, w, h } = this.area
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,8 @@ export default {
|
|||||||
return fetch(this.baseUrl + `/api/identity_verification/${this.field.uuid}`, {
|
return fetch(this.baseUrl + `/api/identity_verification/${this.field.uuid}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
submitter_slug: this.submitterSlug
|
submitter_slug: this.submitterSlug,
|
||||||
|
redirect_url: document.location.href
|
||||||
}),
|
}),
|
||||||
headers: { 'Content-Type': 'application/json' }
|
headers: { 'Content-Type': 'application/json' }
|
||||||
}).then(async (resp) => {
|
}).then(async (resp) => {
|
||||||
|
|||||||
@@ -140,6 +140,7 @@
|
|||||||
:background-color="'white'"
|
:background-color="'white'"
|
||||||
:with-required="false"
|
:with-required="false"
|
||||||
:with-areas="false"
|
:with-areas="false"
|
||||||
|
:with-signature-id="withSignatureId"
|
||||||
@click-formula="isShowFormulaModal = true"
|
@click-formula="isShowFormulaModal = true"
|
||||||
@click-font="isShowFontModal = true"
|
@click-font="isShowFontModal = true"
|
||||||
@click-description="isShowDescriptionModal = true"
|
@click-description="isShowDescriptionModal = true"
|
||||||
@@ -347,6 +348,11 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
withSignatureId: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
defaultSubmitters: {
|
defaultSubmitters: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: false,
|
required: false,
|
||||||
@@ -414,9 +420,6 @@ export default {
|
|||||||
fontScale () {
|
fontScale () {
|
||||||
return 1040 / 612.0
|
return 1040 / 612.0
|
||||||
},
|
},
|
||||||
ladscapeScale () {
|
|
||||||
return 8.5 / 11.0
|
|
||||||
},
|
|
||||||
isDefaultValuePresent () {
|
isDefaultValuePresent () {
|
||||||
if (this.field?.type === 'radio' && this.field?.areas?.length > 1) {
|
if (this.field?.type === 'radio' && this.field?.areas?.length > 1) {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -330,6 +330,7 @@
|
|||||||
:input-mode="inputMode"
|
:input-mode="inputMode"
|
||||||
:default-fields="[...defaultRequiredFields, ...defaultFields]"
|
:default-fields="[...defaultRequiredFields, ...defaultFields]"
|
||||||
:allow-draw="!onlyDefinedFields || drawField"
|
:allow-draw="!onlyDefinedFields || drawField"
|
||||||
|
:with-signature-id="withSignatureId"
|
||||||
:data-document-uuid="document.uuid"
|
:data-document-uuid="document.uuid"
|
||||||
:default-submitters="defaultSubmitters"
|
:default-submitters="defaultSubmitters"
|
||||||
:drag-field-placeholder="fieldsDragFieldRef.value || dragField"
|
:drag-field-placeholder="fieldsDragFieldRef.value || dragField"
|
||||||
@@ -436,6 +437,7 @@
|
|||||||
:default-required-fields="defaultRequiredFields"
|
:default-required-fields="defaultRequiredFields"
|
||||||
:field-types="fieldTypes"
|
:field-types="fieldTypes"
|
||||||
:with-sticky-submitters="withStickySubmitters"
|
:with-sticky-submitters="withStickySubmitters"
|
||||||
|
:with-signature-id="withSignatureId"
|
||||||
:only-defined-fields="onlyDefinedFields"
|
:only-defined-fields="onlyDefinedFields"
|
||||||
:editable="editable"
|
:editable="editable"
|
||||||
:show-tour-start-form="showTourStartForm"
|
:show-tour-start-form="showTourStartForm"
|
||||||
@@ -541,6 +543,7 @@ export default {
|
|||||||
withPayment: this.withPayment,
|
withPayment: this.withPayment,
|
||||||
isPaymentConnected: this.isPaymentConnected,
|
isPaymentConnected: this.isPaymentConnected,
|
||||||
withFormula: this.withFormula,
|
withFormula: this.withFormula,
|
||||||
|
withSignatureId: this.withSignatureId,
|
||||||
withConditions: this.withConditions,
|
withConditions: this.withConditions,
|
||||||
isInlineSize: this.isInlineSize,
|
isInlineSize: this.isInlineSize,
|
||||||
defaultDrawFieldType: this.defaultDrawFieldType,
|
defaultDrawFieldType: this.defaultDrawFieldType,
|
||||||
@@ -563,6 +566,11 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
withSignatureId: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
backgroundColor: {
|
backgroundColor: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
@@ -1068,6 +1076,11 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'signature' && [true, false].includes(this.withSignatureId)) {
|
||||||
|
field.preferences ||= {}
|
||||||
|
field.preferences.with_signature_id = this.withSignatureId
|
||||||
|
}
|
||||||
|
|
||||||
this.template.fields.push(field)
|
this.template.fields.push(field)
|
||||||
|
|
||||||
this.save()
|
this.save()
|
||||||
@@ -1474,6 +1487,11 @@ export default {
|
|||||||
field.preferences ||= {}
|
field.preferences ||= {}
|
||||||
field.preferences.format ||= this.defaultDateFormat
|
field.preferences.format ||= this.defaultDateFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (field.type === 'signature' && [true, false].includes(this.withSignatureId)) {
|
||||||
|
field.preferences ||= {}
|
||||||
|
field.preferences.with_signature_id = this.withSignatureId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldArea = {
|
const fieldArea = {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
:data-page="index"
|
:data-page="index"
|
||||||
:areas="areasIndex[index]"
|
:areas="areasIndex[index]"
|
||||||
:allow-draw="allowDraw"
|
:allow-draw="allowDraw"
|
||||||
|
:with-signature-id="withSignatureId"
|
||||||
:is-drag="isDrag"
|
:is-drag="isDrag"
|
||||||
:with-field-placeholder="withFieldPlaceholder"
|
:with-field-placeholder="withFieldPlaceholder"
|
||||||
:default-fields="defaultFields"
|
:default-fields="defaultFields"
|
||||||
@@ -66,6 +67,11 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
withSignatureId: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
drawFieldType: {
|
drawFieldType: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
|
|||||||
@@ -125,6 +125,7 @@
|
|||||||
:field="field"
|
:field="field"
|
||||||
:default-field="defaultField"
|
:default-field="defaultField"
|
||||||
:editable="editable"
|
:editable="editable"
|
||||||
|
:with-signature-id="withSignatureId"
|
||||||
:background-color="dropdownBgColor"
|
:background-color="dropdownBgColor"
|
||||||
@click-formula="isShowFormulaModal = true"
|
@click-formula="isShowFormulaModal = true"
|
||||||
@click-font="isShowFontModal = true"
|
@click-font="isShowFontModal = true"
|
||||||
@@ -302,6 +303,11 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
withSignatureId: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
withOptions: {
|
withOptions: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
|
|||||||
@@ -320,6 +320,21 @@
|
|||||||
{{ t('format') }}
|
{{ t('format') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<li
|
||||||
|
v-if="[true, false].includes(withSignatureId) && field.type === 'signature'"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<label class="cursor-pointer py-1.5">
|
||||||
|
<input
|
||||||
|
:checked="field.preferences?.with_signature_id"
|
||||||
|
type="checkbox"
|
||||||
|
:disabled="!editable || (defaultField && [true, false].includes(defaultField.required))"
|
||||||
|
class="toggle toggle-xs"
|
||||||
|
@change="[field.preferences ||= {}, field.preferences.with_signature_id = $event.target.checked, save()]"
|
||||||
|
>
|
||||||
|
<span class="label-text">{{ t('signature_id') }}</span>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="withRequired && field.type !== 'phone' && field.type !== 'stamp' && field.type !== 'verification'"
|
v-if="withRequired && field.type !== 'phone' && field.type !== 'stamp' && field.type !== 'verification'"
|
||||||
@click.stop
|
@click.stop
|
||||||
@@ -525,6 +540,11 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
withSignatureId: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
backgroundColor: {
|
backgroundColor: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
:field="field"
|
:field="field"
|
||||||
:type-index="fields.filter((f) => f.type === field.type).indexOf(field)"
|
:type-index="fields.filter((f) => f.type === field.type).indexOf(field)"
|
||||||
:editable="editable"
|
:editable="editable"
|
||||||
|
:with-signature-id="withSignatureId"
|
||||||
:default-field="defaultFieldsIndex[field.name]"
|
:default-field="defaultFieldsIndex[field.name]"
|
||||||
:draggable="editable"
|
:draggable="editable"
|
||||||
@dragstart="[fieldsDragFieldRef.value = field, removeDragOverlay($event), setDragPlaceholder($event)]"
|
@dragstart="[fieldsDragFieldRef.value = field, removeDragOverlay($event), setDragPlaceholder($event)]"
|
||||||
@@ -253,6 +254,11 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
withSignatureId: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
template: {
|
template: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
@@ -163,7 +163,7 @@
|
|||||||
class="flex border border-base-content/20 rounded-xl bg-white px-4 h-16 modal-field-font-preview"
|
class="flex border border-base-content/20 rounded-xl bg-white px-4 h-16 modal-field-font-preview"
|
||||||
:style="{
|
:style="{
|
||||||
color: preferences.color || 'black',
|
color: preferences.color || 'black',
|
||||||
fontSize: (preferences.font_size || 12) + 'pt',
|
fontSize: (preferences.font_size || 11) + 'pt',
|
||||||
}"
|
}"
|
||||||
:class="textClasses"
|
:class="textClasses"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const en = {
|
const en = {
|
||||||
|
signature_id: 'Signature ID',
|
||||||
error_message: 'Error message',
|
error_message: 'Error message',
|
||||||
length: 'Length',
|
length: 'Length',
|
||||||
min: 'Min',
|
min: 'Min',
|
||||||
@@ -172,6 +173,7 @@ const en = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const es = {
|
const es = {
|
||||||
|
signature_id: 'ID de Firma',
|
||||||
error_message: 'Mensaje de error',
|
error_message: 'Mensaje de error',
|
||||||
length: 'Longitud',
|
length: 'Longitud',
|
||||||
min: 'Mín',
|
min: 'Mín',
|
||||||
@@ -345,6 +347,7 @@ const es = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const it = {
|
const it = {
|
||||||
|
signature_id: 'ID firma',
|
||||||
error_message: 'Messaggio di errore',
|
error_message: 'Messaggio di errore',
|
||||||
length: 'Lunghezza',
|
length: 'Lunghezza',
|
||||||
min: 'Min',
|
min: 'Min',
|
||||||
@@ -518,6 +521,7 @@ const it = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pt = {
|
const pt = {
|
||||||
|
signature_id: 'ID da Assinatura',
|
||||||
error_message: 'Mensagem de erro',
|
error_message: 'Mensagem de erro',
|
||||||
length: 'Comprimento',
|
length: 'Comprimento',
|
||||||
min: 'Mín',
|
min: 'Mín',
|
||||||
@@ -691,6 +695,7 @@ const pt = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fr = {
|
const fr = {
|
||||||
|
signature_id: 'ID de signature',
|
||||||
error_message: 'Message d\'erreur',
|
error_message: 'Message d\'erreur',
|
||||||
length: 'Longueur',
|
length: 'Longueur',
|
||||||
min: 'Min',
|
min: 'Min',
|
||||||
@@ -864,6 +869,7 @@ const fr = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const de = {
|
const de = {
|
||||||
|
signature_id: 'Signatur-ID',
|
||||||
error_message: 'Fehlermeldung',
|
error_message: 'Fehlermeldung',
|
||||||
length: 'Länge',
|
length: 'Länge',
|
||||||
min: 'Min',
|
min: 'Min',
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
:field="item.field"
|
:field="item.field"
|
||||||
:editable="editable"
|
:editable="editable"
|
||||||
:with-field-placeholder="withFieldPlaceholder"
|
:with-field-placeholder="withFieldPlaceholder"
|
||||||
|
:with-signature-id="withSignatureId"
|
||||||
:default-field="defaultFieldsIndex[item.field.name]"
|
:default-field="defaultFieldsIndex[item.field.name]"
|
||||||
:default-submitters="defaultSubmitters"
|
:default-submitters="defaultSubmitters"
|
||||||
:max-page="totalPages - 1"
|
:max-page="totalPages - 1"
|
||||||
@@ -78,6 +79,11 @@ export default {
|
|||||||
required: false,
|
required: false,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
|
withSignatureId: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
areas: {
|
areas: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: false,
|
required: false,
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class AccountConfig < ApplicationRecord
|
|||||||
FORCE_SSO_AUTH_KEY = 'force_sso_auth'
|
FORCE_SSO_AUTH_KEY = 'force_sso_auth'
|
||||||
FLATTEN_RESULT_PDF_KEY = 'flatten_result_pdf'
|
FLATTEN_RESULT_PDF_KEY = 'flatten_result_pdf'
|
||||||
WITH_SIGNATURE_ID = 'with_signature_id'
|
WITH_SIGNATURE_ID = 'with_signature_id'
|
||||||
|
WITH_SIGNATURE_ID_REASON_KEY = 'with_signature_id_reason'
|
||||||
WITH_AUDIT_VALUES_KEY = 'with_audit_values'
|
WITH_AUDIT_VALUES_KEY = 'with_audit_values'
|
||||||
WITH_SUBMITTER_TIMEZONE_KEY = 'with_submitter_timezone'
|
WITH_SUBMITTER_TIMEZONE_KEY = 'with_submitter_timezone'
|
||||||
REQUIRE_SIGNING_REASON_KEY = 'require_signing_reason'
|
REQUIRE_SIGNING_REASON_KEY = 'require_signing_reason'
|
||||||
|
|||||||
@@ -72,12 +72,14 @@ class Template < ApplicationRecord
|
|||||||
scope :active, -> { where(archived_at: nil) }
|
scope :active, -> { where(archived_at: nil) }
|
||||||
scope :archived, -> { where.not(archived_at: nil) }
|
scope :archived, -> { where.not(archived_at: nil) }
|
||||||
|
|
||||||
delegate :name, to: :folder, prefix: true
|
|
||||||
|
|
||||||
def application_key
|
def application_key
|
||||||
external_id
|
external_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def folder_name
|
||||||
|
folder.full_name
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def maybe_set_default_folder
|
def maybe_set_default_folder
|
||||||
|
|||||||
@@ -4,36 +4,50 @@
|
|||||||
#
|
#
|
||||||
# Table name: template_folders
|
# Table name: template_folders
|
||||||
#
|
#
|
||||||
# id :bigint not null, primary key
|
# id :bigint not null, primary key
|
||||||
# archived_at :datetime
|
# archived_at :datetime
|
||||||
# name :string not null
|
# name :string not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
# account_id :bigint not null
|
# account_id :bigint not null
|
||||||
# author_id :bigint not null
|
# author_id :bigint not null
|
||||||
|
# parent_folder_id :bigint
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
# index_template_folders_on_account_id (account_id)
|
# index_template_folders_on_account_id (account_id)
|
||||||
# index_template_folders_on_author_id (author_id)
|
# index_template_folders_on_author_id (author_id)
|
||||||
|
# index_template_folders_on_parent_folder_id (parent_folder_id)
|
||||||
#
|
#
|
||||||
# Foreign Keys
|
# Foreign Keys
|
||||||
#
|
#
|
||||||
# fk_rails_... (account_id => accounts.id)
|
# fk_rails_... (account_id => accounts.id)
|
||||||
# fk_rails_... (author_id => users.id)
|
# fk_rails_... (author_id => users.id)
|
||||||
|
# fk_rails_... (parent_folder_id => template_folders.id)
|
||||||
#
|
#
|
||||||
class TemplateFolder < ApplicationRecord
|
class TemplateFolder < ApplicationRecord
|
||||||
DEFAULT_NAME = 'Default'
|
DEFAULT_NAME = 'Default'
|
||||||
|
|
||||||
belongs_to :author, class_name: 'User'
|
belongs_to :author, class_name: 'User'
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
belongs_to :parent_folder, class_name: 'TemplateFolder', optional: true
|
||||||
|
|
||||||
has_many :templates, dependent: :destroy, foreign_key: :folder_id, inverse_of: :folder
|
has_many :templates, dependent: :destroy, foreign_key: :folder_id, inverse_of: :folder
|
||||||
|
has_many :subfolders, class_name: 'TemplateFolder', foreign_key: :parent_folder_id, inverse_of: :parent_folder,
|
||||||
|
dependent: :destroy
|
||||||
has_many :active_templates, -> { where(archived_at: nil) },
|
has_many :active_templates, -> { where(archived_at: nil) },
|
||||||
class_name: 'Template', dependent: :destroy, foreign_key: :folder_id, inverse_of: :folder
|
class_name: 'Template', dependent: :destroy, foreign_key: :folder_id, inverse_of: :folder
|
||||||
|
|
||||||
scope :active, -> { where(archived_at: nil) }
|
scope :active, -> { where(archived_at: nil) }
|
||||||
|
|
||||||
|
def full_name
|
||||||
|
if parent_folder_id?
|
||||||
|
[parent_folder.name, name].join(' / ')
|
||||||
|
else
|
||||||
|
name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def default?
|
def default?
|
||||||
name == DEFAULT_NAME
|
name == DEFAULT_NAME
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
<span class="top-0 right-0 absolute">
|
<span class="top-0 right-0 absolute">
|
||||||
<%= render 'shared/clipboard_copy', icon: 'copy', text:, class: 'btn btn-ghost text-white', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
|
<%= render 'shared/clipboard_copy', icon: 'copy', text:, class: 'btn btn-ghost text-white', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
|
||||||
</span>
|
</span>
|
||||||
<pre data-prefix="$"><code class="overflow-hidden w-full"><%= text %></code></pre>
|
<pre data-prefix="$"><code class="overflow-hidden w-full"><%== HighlightCode.call(text, 'Shell', theme: 'base16.dark') %></code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
<span class="top-0 right-0 absolute">
|
<span class="top-0 right-0 absolute">
|
||||||
<%= render 'shared/clipboard_copy', icon: 'copy', text:, class: 'btn btn-ghost text-white', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
|
<%= render 'shared/clipboard_copy', icon: 'copy', text:, class: 'btn btn-ghost text-white', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
|
||||||
</span>
|
</span>
|
||||||
<pre data-prefix="$"><code class="overflow-hidden w-full"><%= text %></code></pre>
|
<pre data-prefix="$"><code class="overflow-hidden w-full"><%== HighlightCode.call(text, 'Shell', theme: 'base16.dark') %></code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
<span class="top-0 right-0 absolute">
|
<span class="top-0 right-0 absolute">
|
||||||
<%= render 'shared/clipboard_copy', icon: 'copy', text:, class: 'btn btn-ghost text-white', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
|
<%= render 'shared/clipboard_copy', icon: 'copy', text:, class: 'btn btn-ghost text-white', icon_class: 'w-6 h-6 text-white', copy_title: t('copy'), copied_title: t('copied') %>
|
||||||
</span>
|
</span>
|
||||||
<pre data-prefix="$"><code class="overflow-hidden w-full"><%= text %></code></pre>
|
<pre data-prefix="$"><code class="overflow-hidden w-full"><%== HighlightCode.call(text, 'Shell', theme: 'base16.dark') %></code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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="M5 12l14 0" /><path d="M5 12l6 6" /><path d="M5 12l6 -6" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 351 B |
@@ -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="M12 21a9 9 0 1 0 0 -18a9 9 0 0 0 0 18" /><path d="M8 12l4 4" /><path d="M8 12h8" /><path d="M12 8l-4 4" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 398 B |
@@ -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="M12 21a9 9 0 1 0 0 -18a9 9 0 0 0 0 18" /><path d="M9 12l4 4" /><path d="M13 8l-4 4" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 378 B |
@@ -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="M12 19h-7a2 2 0 0 1 -2 -2v-11a2 2 0 0 1 2 -2h4l3 3h7a2 2 0 0 1 2 2v3.5" /><path d="M16 19h6" /><path d="M19 16v6" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 408 B |
@@ -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="M17 5l-10 14" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 308 B |
@@ -1,5 +1,5 @@
|
|||||||
<a target="_blank" href="<%= Docuseal::GITHUB_URL %>" rel="noopener noreferrer nofollow" class="relative flex items-center rounded-full px-2 py-0.5 text-xs leading-4 mt-1 text-base-content border border-base-300 tooltip tooltip-bottom" data-tip="Give a star on GitHub">
|
<a target="_blank" href="<%= Docuseal::GITHUB_URL %>" rel="noopener noreferrer nofollow" class="relative flex items-center rounded-full px-2 py-0.5 text-xs leading-4 mt-1 text-base-content border border-base-300 tooltip tooltip-bottom" data-tip="Give a star on GitHub">
|
||||||
<span class="flex items-center justify-between space-x-0.5 font-extrabold">
|
<span class="flex items-center justify-between space-x-0.5 font-medium">
|
||||||
<%= svg_icon('start', class: 'h-3 w-3') %>
|
<%= svg_icon('start', class: 'h-3 w-3') %>
|
||||||
<span>9k</span>
|
<span>9k</span>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="block w-full md:w-52 flex-none">
|
<div class="block w-full md:w-52 flex-none">
|
||||||
<menu-active>
|
<menu-active>
|
||||||
<ul id="account_settings_menu" class="menu px-0">
|
<ul id="account_settings_menu" class="menu px-0">
|
||||||
<li class="menu-title py-0 !bg-transparent mb-3 -mt-5"><a href="<%= '/' %>" class="!bg-transparent !text-neutral font-medium">← <%= t('back') %></a></li>
|
<li class="menu-title py-0 !bg-transparent mb-3 -mt-5"><a href="<%= '/' %>" class="!bg-transparent !text-neutral font-medium flex items-center space-x-0.5"><%= svg_icon('arrow_left', class: 'w-4 h-4 stroke-2') %><span><%= t('back') %></span></a></li>
|
||||||
<li class="menu-title py-0 !bg-transparent">
|
<li class="menu-title py-0 !bg-transparent">
|
||||||
<span class="!bg-transparent"><%= t('settings') %></span>
|
<span class="!bg-transparent"><%= t('settings') %></span>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -16,15 +16,18 @@
|
|||||||
<div class="truncate uppercase">
|
<div class="truncate uppercase">
|
||||||
ID: <%= attachment.uuid %>
|
ID: <%= attachment.uuid %>
|
||||||
</div>
|
</div>
|
||||||
|
<% if local_assigns[:with_signature_id_reason] != false %>
|
||||||
|
<div>
|
||||||
|
<% reason_value = submitter.values[field.dig('preferences', 'reason_field_uuid')].presence %>
|
||||||
|
<% if reason_value %><%= t('reason') %>: <% end %><%= reason_value || t('digitally_signed_by') %> <%= submitter.name %>
|
||||||
|
<% if submitter.email %>
|
||||||
|
<<%= submitter.email %>>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
<div>
|
<div>
|
||||||
<% reason_value = submitter.values[field.dig('preferences', 'reason_field_uuid')].presence %>
|
<% timezone = local_assigns[:with_submitter_timezone] ? (submitter.timezone || local_assigns[:timezone]) : local_assigns[:timezone] %>
|
||||||
<% if reason_value %><%= t('reason') %>: <% end %><%= reason_value || t('digitally_signed_by') %> <%= submitter.name %>
|
<%= l(attachment.created_at.in_time_zone(timezone), format: :long, locale: local_assigns[:locale]) %> <%= TimeUtils.timezone_abbr(timezone, attachment.created_at) %>
|
||||||
<% if submitter.email %>
|
|
||||||
<<%= submitter.email %>>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<%= l(attachment.created_at.in_time_zone(local_assigns[:timezone]), format: :long, locale: local_assigns[:locale]) %> <%= TimeUtils.timezone_abbr(local_assigns[:timezone], attachment.created_at) %>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -2,7 +2,11 @@
|
|||||||
<%= render 'submissions/preview_tags' %>
|
<%= render 'submissions/preview_tags' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% font_scale = 1040.0 / PdfUtils::US_LETTER_W %>
|
<% font_scale = 1040.0 / PdfUtils::US_LETTER_W %>
|
||||||
<% with_signature_id, is_combined_enabled = AccountConfig.where(account_id: @submission.account_id, key: [AccountConfig::COMBINE_PDF_RESULT_KEY, AccountConfig::WITH_SIGNATURE_ID], value: true).then { |configs| [configs.any? { |e| e.key == AccountConfig::WITH_SIGNATURE_ID }, configs.any? { |e| e.key == AccountConfig::COMBINE_PDF_RESULT_KEY }] } %>
|
<% configs = AccountConfig.where(account_id: @submission.account_id, key: [AccountConfig::COMBINE_PDF_RESULT_KEY, AccountConfig::WITH_SIGNATURE_ID, AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY, AccountConfig::WITH_SIGNATURE_ID_REASON_KEY]) %>
|
||||||
|
<% with_signature_id = configs.find { |e| e.key == AccountConfig::WITH_SIGNATURE_ID }&.value == true %>
|
||||||
|
<% is_combined_enabled = configs.find { |e| e.key == AccountConfig::COMBINE_PDF_RESULT_KEY }&.value == true %>
|
||||||
|
<% with_submitter_timezone = configs.find { |e| e.key == AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY }&.value == true %>
|
||||||
|
<% with_signature_id_reason = configs.find { |e| e.key == AccountConfig::WITH_SIGNATURE_ID_REASON_KEY }&.value != false %>
|
||||||
<div style="max-width: 1600px" class="mx-auto pl-4">
|
<div style="max-width: 1600px" class="mx-auto pl-4">
|
||||||
<div class="flex justify-between py-1.5 items-center pr-4 sticky top-0 md:relative z-10 bg-base-100">
|
<div class="flex justify-between py-1.5 items-center pr-4 sticky top-0 md:relative z-10 bg-base-100">
|
||||||
<a href="<%= signed_in? && @submission.account_id == current_account&.id && @submission.template ? template_path(@submission.template) : '/' %>" class="flex items-center space-x-3 py-1">
|
<a href="<%= signed_in? && @submission.account_id == current_account&.id && @submission.template ? template_path(@submission.template) : '/' %>" class="flex items-center space-x-3 py-1">
|
||||||
@@ -108,17 +112,18 @@
|
|||||||
<% value = values[field['uuid']] %>
|
<% value = values[field['uuid']] %>
|
||||||
<% value ||= field['default_value'] if field['type'] == 'heading' %>
|
<% value ||= field['default_value'] if field['type'] == 'heading' %>
|
||||||
<% next if value.blank? %>
|
<% next if value.blank? %>
|
||||||
|
<% submitter = submitters_index[field['submitter_uuid']] %>
|
||||||
<% if (mask = field.dig('preferences', 'mask').presence) && signed_in? && can?(:read, @submission) %>
|
<% if (mask = field.dig('preferences', 'mask').presence) && signed_in? && can?(:read, @submission) %>
|
||||||
<span class="group">
|
<span class="group">
|
||||||
<span class="hidden group-hover:inline">
|
<span class="hidden group-hover:inline">
|
||||||
<%= render 'submissions/value', font_scale:, area:, field:, attachments_index:, value:, locale: @submission.account.locale, timezone: @submission.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: %>
|
<%= render 'submissions/value', font_scale:, area:, field:, attachments_index:, value:, locale: @submission.account.locale, timezone: @submission.account.timezone, submitter:, with_signature_id: %>
|
||||||
</span>
|
</span>
|
||||||
<span class="group-hover:hidden">
|
<span class="group-hover:hidden">
|
||||||
<%= render 'submissions/value', font_scale:, area:, field:, attachments_index:, value: Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', '), locale: @submission.account.locale, timezone: @submission.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: %>
|
<%= render 'submissions/value', font_scale:, area:, field:, attachments_index:, value: Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', '), locale: @submission.account.locale, timezone: @submission.account.timezone, submitter:, with_signature_id: %>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= render 'submissions/value', font_scale:, area:, field:, attachments_index:, value: mask.present? ? Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', ') : value, locale: @submission.account.locale, timezone: @submission.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: %>
|
<%= render 'submissions/value', font_scale:, area:, field:, attachments_index:, value: mask.present? ? Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', ') : value, locale: @submission.account.locale, timezone: @submission.account.timezone, submitter:, with_signature_id:, with_submitter_timezone:, with_signature_id_reason: %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<% filter_params = params.permit(Submissions::Filter::ALLOWED_PARAMS).compact_blank %>
|
<% filter_params = params.permit(Submissions::Filter::ALLOWED_PARAMS).compact_blank %>
|
||||||
<div>
|
<div>
|
||||||
<%= link_to root_path do %>
|
<%= link_to root_path, class: 'flex items-center' do %>
|
||||||
←
|
<%= svg_icon('chevron_left', class: 'w-5 h-5') %>
|
||||||
<span><%= t('back_to_active') %></span>
|
<span style="margin-left: 3px"><%= t('back_to_active') %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col md:flex-row md:items-center mb-4 gap-3">
|
<div class="flex flex-col md:flex-row md:items-center mb-4 gap-3">
|
||||||
<div class="flex w-full justify-between">
|
<div class="flex w-full justify-between items-center">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-4xl font-bold md:block <%= 'hidden' if params[:q].present? %>"><%= t('submissions') %> <span class="badge badge-outline badge-lg align-middle"><%= t('archived') %></span></h1>
|
<h1 class="text-2xl md:text-3xl font-bold md:block <%= 'hidden' if params[:q].present? %>"><%= t('submissions') %> <span class="badge badge-outline badge-lg align-middle"><%= t('archived') %></span></h1>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<% if params[:q].present? || @pagy.pages > 1 || filter_params.present? %>
|
<% if params[:q].present? || @pagy.pages > 1 || filter_params.present? %>
|
||||||
|
|||||||
@@ -10,6 +10,17 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<% if params[:folder].present? %>
|
||||||
|
<div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700" data-tip="<%= t('folder') %>">
|
||||||
|
<%= link_to submissions_filter_path('folder', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
|
||||||
|
<%= svg_icon('folder', class: 'w-5 h-5 shrink-0') %>
|
||||||
|
<span class="font-normal truncate"><%= params[:folder] %></span>
|
||||||
|
<% end %>
|
||||||
|
<%= link_to url_for(params.to_unsafe_h.except(:folder)), class: 'rounded-lg ml-1 hover:bg-base-content hover:text-white' do %>
|
||||||
|
<%= svg_icon('x', class: 'w-5 h-5') %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
<% if params[:author].present? %>
|
<% if params[:author].present? %>
|
||||||
<div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700" data-tip="<%= t('author') %>">
|
<div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700" data-tip="<%= t('author') %>">
|
||||||
<%= link_to submissions_filter_path('author', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
|
<%= link_to submissions_filter_path('author', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
<% next if field['conditions'].present? && values[field['uuid']].blank? && field['submitter_uuid'] != @submitter.uuid %>
|
<% next if field['conditions'].present? && values[field['uuid']].blank? && field['submitter_uuid'] != @submitter.uuid %>
|
||||||
<% next if field['conditions'].present? && field['submitter_uuid'] == @submitter.uuid %>
|
<% next if field['conditions'].present? && field['submitter_uuid'] == @submitter.uuid %>
|
||||||
<% next if field.dig('preferences', 'formula').present? && field['submitter_uuid'] == @submitter.uuid %>
|
<% next if field.dig('preferences', 'formula').present? && field['submitter_uuid'] == @submitter.uuid %>
|
||||||
<%= render 'submissions/value', font_scale:, area:, field:, attachments_index: @attachments_index, value: field.dig('preferences', 'mask').present? ? TextUtils.mask_value(value, field.dig('preferences', 'mask')) : value, locale: @submitter.account.locale, timezone: @submitter.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: @form_configs[:with_signature_id] %>
|
<%= render 'submissions/value', font_scale:, area:, field:, attachments_index: @attachments_index, value: field.dig('preferences', 'mask').present? ? TextUtils.mask_value(value, field.dig('preferences', 'mask')) : value, locale: @submitter.account.locale, timezone: @submitter.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: @form_configs[:with_signature_id], with_submitter_timezone: @form_configs[:with_submitter_timezone], with_signature_id_reason: @form_configs[:with_signature_id_reason] %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</page-container>
|
</page-container>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<% is_long = folder.name.size > 32 %>
|
<% is_long = folder.name.size > 32 %>
|
||||||
<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 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" data-full-name="<%= folder.full_name %>">
|
||||||
<% if !is_long %>
|
<% if !is_long %>
|
||||||
<%= svg_icon('folder', class: 'w-6 h-6') %>
|
<%= svg_icon('folder', class: 'w-6 h-6') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -1,24 +1,27 @@
|
|||||||
<div>
|
<div>
|
||||||
<%= link_to root_path do %>
|
<%= link_to @template_folder.parent_folder ? folder_path(@template_folder.parent_folder) : root_path, class: 'flex items-center' do %>
|
||||||
←
|
<%= svg_icon('chevron_left', class: 'w-5 h-5') %>
|
||||||
<span><%= t('home') %></span>
|
<span style="margin-left: 3px"><%= @template_folder.parent_folder&.name || t('home') %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<dashboard-dropzone>
|
<dashboard-dropzone>
|
||||||
<div class="relative flex justify-between items-center w-full mb-4">
|
<div class="relative flex justify-between items-center w-full mb-4">
|
||||||
<%= form_for '', url: '', id: form_id = SecureRandom.uuid, method: :post, class: 'hidden', data: { target: 'dashboard-dropzone.form' }, html: { enctype: 'multipart/form-data' } do %>
|
<%= form_for '', url: '', id: form_id = SecureRandom.uuid, method: :post, class: 'hidden', data: { target: 'dashboard-dropzone.form' }, html: { enctype: 'multipart/form-data' } do %>
|
||||||
<input name="form_id" value="<%= form_id %>">
|
<input name="form_id" value="<%= form_id %>">
|
||||||
<input name="folder_name" value="<%= @template_folder.name %>">
|
<input name="folder_name" value="<%= @template_folder.full_name %>">
|
||||||
<button type="submit"></button>
|
<button type="submit"></button>
|
||||||
<input id="dashboard_dropzone_input" name="files[]" type="file" multiple>
|
<input id="dashboard_dropzone_input" name="files[]" type="file" multiple>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= render 'templates/dashboard_dropzone', style: 'height: 137px' %>
|
<%= render 'templates/dashboard_dropzone', style: 'height: 137px' %>
|
||||||
|
<% unless @template_folder.parent_folder %>
|
||||||
|
<%= render 'templates/dashboard_folder_dropzone', style: 'height: 137px' %>
|
||||||
|
<% end %>
|
||||||
<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? %>">
|
<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? %>">
|
||||||
<%= svg_icon('folder', class: 'w-9 h-9 flex-shrink-0') %>
|
<%= svg_icon('folder', class: 'w-9 h-9 flex-shrink-0') %>
|
||||||
<span class="peer truncate">
|
<span class="peer truncate">
|
||||||
<%= @template_folder.name %>
|
<%= @template_folder.name %>
|
||||||
</span>
|
</span>
|
||||||
<% if can?(:update, @template_folder) && @template_folder.name != TemplateFolder::DEFAULT_NAME %>
|
<% if can?(:update, @template_folder) && @template_folder.full_name != TemplateFolder::DEFAULT_NAME %>
|
||||||
<span class="opacity-0 hover:opacity-100 peer-hover:opacity-100">
|
<span class="opacity-0 hover:opacity-100 peer-hover:opacity-100">
|
||||||
<a href="<%= edit_folder_path(@template_folder) %>" data-turbo-frame="modal">
|
<a href="<%= edit_folder_path(@template_folder) %>" data-turbo-frame="modal">
|
||||||
<%= svg_icon('pencil', class: 'w-7 h-7') %>
|
<%= svg_icon('pencil', class: 'w-7 h-7') %>
|
||||||
@@ -27,18 +30,23 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</h1>
|
</h1>
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<% if params[:q].present? || @pagy.pages > 1 %>
|
<% if params[:q].present? || @pagy.pages > 1 || @template_folders.present? %>
|
||||||
<%= render 'shared/search_input' %>
|
<%= render 'shared/search_input' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if can?(:create, ::Template) %>
|
<% if can?(:create, ::Template) %>
|
||||||
<%= render 'templates/upload_button', folder_name: @template_folder.name %>
|
<%= render 'templates/upload_button', folder_name: @template_folder.full_name %>
|
||||||
<%= link_to new_template_path(folder_name: @template_folder.name), class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %>
|
<%= link_to new_template_path(folder_name: @template_folder.full_name), class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %>
|
||||||
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
||||||
<span class="hidden md:block"><%= t('create') %></span>
|
<span class="hidden md:block"><%= t('create') %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<% 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 @pagy.count.nil? || @pagy.count > 0 %>
|
<% if @pagy.count.nil? || @pagy.count > 0 %>
|
||||||
<div class="grid gap-4 md:grid-cols-3">
|
<div class="grid gap-4 md:grid-cols-3">
|
||||||
<%= render partial: 'templates/template', collection: @templates %>
|
<%= render partial: 'templates/template', collection: @templates %>
|
||||||
@@ -48,12 +56,21 @@
|
|||||||
<%= render 'shared/templates_order_select', with_recently_used: @pagy.count.present? && @pagy.count < 10_000 && !can?(:manage, :countless), selected_order: %>
|
<%= render 'shared/templates_order_select', with_recently_used: @pagy.count.present? && @pagy.count < 10_000 && !can?(:manage, :countless), selected_order: %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= render 'shared/pagination', pagy: @pagy, items_name: 'templates', right_additional_html: templates_order_select_html %>
|
<%= render 'shared/pagination', pagy: @pagy, items_name: @templates.present? ? 'templates' : 'template_folders', right_additional_html: templates_order_select_html %>
|
||||||
<% elsif params[:q].present? %>
|
<% elsif params[:q].present? %>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="mt-16 text-3xl font-semibold">
|
<div class="mt-16 text-3xl font-semibold">
|
||||||
<%= t('templates_not_found') %>
|
<%= t('templates_not_found') %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<% if @related_submissions.present? %>
|
||||||
|
<h1 class="text-2xl md:text-3xl sm:text-4xl font-bold mt-8 md:mt-4">
|
||||||
|
<%= t('submissions') %>
|
||||||
|
</h1>
|
||||||
|
<div class="space-y-4 mt-4">
|
||||||
|
<%= render partial: 'templates/submission', collection: @related_submissions, locals: { with_template: true } %>
|
||||||
|
</div>
|
||||||
|
<%= render 'shared/pagination', pagy: @related_submissions_pagy, items_name: 'submissions', next_page_path: submissions_path(q: params[:q], folder: @template_folder.full_name) %>
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</dashboard-dropzone>
|
</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.folderDropzone" 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('folder_plus', class: 'w-9 h-9') %>
|
||||||
|
</span>
|
||||||
|
<div class="font-medium mb-1">
|
||||||
|
<%= t('create_a_new_folder') %>
|
||||||
|
</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('loading') %>...
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="h-36 relative group">
|
<div class="h-36 relative group">
|
||||||
<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">
|
<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="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="line-height: 1.6rem; 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 %>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<% if template.archived_at? %>
|
<% if template.archived_at? %>
|
||||||
<span class="flex items-center space-x-1 w-1/2">
|
<span class="flex items-center space-x-1 w-1/2">
|
||||||
<%= svg_icon('folder', class: 'w-4 h-4 flex-shrink-0') %>
|
<%= svg_icon('folder', class: 'w-4 h-4 flex-shrink-0') %>
|
||||||
<span class="truncate"><%= template.folder.name %></span>
|
<span class="truncate"><%= template.folder.full_name %></span>
|
||||||
</span>
|
</span>
|
||||||
<% end %>
|
<% end %>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -16,12 +16,12 @@
|
|||||||
<a href="<%= folder_path(@template.folder) %>" class="flex items-center space-x-1 mt-1 peer">
|
<a href="<%= folder_path(@template.folder) %>" class="flex items-center space-x-1 mt-1 peer">
|
||||||
<%= svg_icon('folder', class: 'w-5 h-5 flex-shrink-0') %>
|
<%= svg_icon('folder', class: 'w-5 h-5 flex-shrink-0') %>
|
||||||
<span class="text-sm">
|
<span class="text-sm">
|
||||||
<%= @template.folder.name %>
|
<%= @template.folder.full_name %>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<% if can?(:update, template) %>
|
<% if can?(:update, template) %>
|
||||||
<span class="pl-1 tooltip tooltip-right md:opacity-0 hover:opacity-100 peer-hover:opacity-100" data-tip="<%= t('move') %>">
|
<span class="pl-1 tooltip tooltip-right md:opacity-0 hover:opacity-100 peer-hover:opacity-100" data-tip="<%= t('move') %>">
|
||||||
<a href="<%= edit_template_folder_path(template.id) %>" data-turbo-frame="modal">
|
<a href="<%= edit_template_folder_path(template.id, subfolder: @template.folder.parent_folder.present?) %>" data-turbo-frame="modal">
|
||||||
<%= svg_icon('pencil_share', class: 'w-5 h-5') %>
|
<%= svg_icon('pencil_share', class: 'w-5 h-5') %>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<%= svg_icon('folder', class: 'w-6 h-6') %>
|
<%= svg_icon('folder', class: 'w-6 h-6') %>
|
||||||
</a>
|
</a>
|
||||||
<folder-autocomplete class="flex justify-between w-full">
|
<folder-autocomplete class="flex justify-between w-full">
|
||||||
<input id="folder_name" placeholder="<%= t('folder_name') %>" type="text" class="w-full outline-none border-transparent focus:border-transparent focus:ring-0 bg-base-100 px-1 peer" name="folder_name" value="<%= params[:folder_name].presence || @base_template&.folder&.name || TemplateFolder::DEFAULT_NAME %>" onblur="window.folder_name.value = window.folder_name.value || 'Default'" autocomplete="off">
|
<input id="folder_name" placeholder="<%= t('folder_name') %>" type="text" class="w-full outline-none border-transparent focus:border-transparent focus:ring-0 bg-base-100 px-1 peer" name="folder_name" value="<%= params[:folder_name].presence || @base_template&.folder&.full_name || TemplateFolder::DEFAULT_NAME %>" onblur="window.folder_name.value = window.folder_name.value || 'Default'" autocomplete="off">
|
||||||
<a href="#" onclick="[event.preventDefault(), window.folder_name.value = '', window.folder_name.focus()]" class="shrink-0 link peer-focus:hidden mr-1.5">
|
<a href="#" onclick="[event.preventDefault(), window.folder_name.value = '', window.folder_name.focus()]" class="shrink-0 link peer-focus:hidden mr-1.5">
|
||||||
<%= t('change_folder') %>
|
<%= t('change_folder') %>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<div>
|
<div>
|
||||||
<%= link_to root_path do %>
|
<%= link_to root_path, class: 'flex items-center' do %>
|
||||||
←
|
<%= svg_icon('chevron_left', class: 'w-5 h-5') %>
|
||||||
<span><%= t('back_to_active') %></span>
|
<span style="margin-left: 3px"><%= t('back_to_active') %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between mb-4 items-center">
|
<div class="flex justify-between mb-4 items-center">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-4xl font-bold md:block <%= 'hidden' if params[:q].present? %>"><%= t('document_templates_html') %> <span class="badge badge-outline badge-lg align-middle"><%= t('archived') %></span></h1>
|
<h1 class="text-2xl md:text-3xl font-bold md:block <%= 'hidden' if params[:q].present? %>"><%= t('document_templates_html') %> <span class="badge badge-outline badge-lg align-middle"><%= t('archived') %></span></h1>
|
||||||
</div>
|
</div>
|
||||||
<% if params[:q].present? || @pagy.pages > 1 %>
|
<% if params[:q].present? || @pagy.pages > 1 %>
|
||||||
<%= render 'shared/search_input', placeholder: "#{t('search')}..." %>
|
<%= render 'shared/search_input', placeholder: "#{t('search')}..." %>
|
||||||
@@ -22,5 +22,14 @@
|
|||||||
<%= t('templates_not_found') %>
|
<%= t('templates_not_found') %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<% if @related_submissions.present? %>
|
||||||
|
<h1 class="text-2xl md:text-3xl sm:text-4xl font-bold mt-8 md:mt-4">
|
||||||
|
<%= t('submissions') %>
|
||||||
|
</h1>
|
||||||
|
<div class="space-y-4 mt-4">
|
||||||
|
<%= render partial: 'templates/submission', collection: @related_submissions, locals: { with_template: true } %>
|
||||||
|
</div>
|
||||||
|
<%= render 'shared/pagination', pagy: @related_submissions_pagy, items_name: 'submissions', next_page_path: submissions_archived_index_path(q: params[:q]) %>
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= render 'shared/pagination', pagy: @pagy, items_name: 'templates' %>
|
<%= render 'shared/pagination', pagy: @pagy, items_name: 'templates' %>
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<% filter_params = params.permit(Submissions::Filter::ALLOWED_PARAMS).compact_blank %>
|
<% filter_params = params.permit(Submissions::Filter::ALLOWED_PARAMS).compact_blank %>
|
||||||
<% with_filters = @pagy.pages > 1 || params[:q].present? || filter_params.present? %>
|
<% with_filters = @pagy.pages > 1 || params[:q].present? || filter_params.present? %>
|
||||||
<%= render 'templates/title', template: @template %>
|
<%= render 'templates/title', template: @template %>
|
||||||
<div>
|
|
||||||
<%= link_to template_path(@template) do %>
|
|
||||||
←
|
|
||||||
<span><%= t('back_to_active') %></span>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col md:flex-row md:items-center mb-6 gap-3">
|
<div class="flex flex-col md:flex-row md:items-center mb-6 gap-3">
|
||||||
<div class="flex w-full justify-between md:items-end items-center">
|
<div class="flex w-full justify-between md:items-end items-end">
|
||||||
<div>
|
<div>
|
||||||
|
<div>
|
||||||
|
<%= link_to template_path(@template), class: 'flex items-center' do %>
|
||||||
|
<%= svg_icon('chevron_left', class: 'w-5 h-5') %>
|
||||||
|
<span style="margin-left: 3px"><%= t('back_to_active') %></span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
<h1 class="text-3xl font-bold md:block"><%= t('submissions') %> <span class="badge badge-outline badge-lg align-middle"><%= t('archived') %></span></h1>
|
<h1 class="text-3xl font-bold md:block"><%= t('submissions') %> <span class="badge badge-outline badge-lg align-middle"><%= t('archived') %></span></h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-2 justify-end">
|
<div class="flex space-x-2 justify-end">
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
<% unless show_dropzone %>
|
<% unless show_dropzone %>
|
||||||
<%= render 'templates/dashboard_dropzone', style: 'height: 114px' %>
|
<%= render 'templates/dashboard_dropzone', style: 'height: 114px' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<%= render 'templates/dashboard_folder_dropzone', style: 'height: 114px' %>
|
||||||
<div class="flex items-center flex-grow min-w-0">
|
<div class="flex items-center flex-grow min-w-0">
|
||||||
<% if has_archived || @pagy.count.nil? || @pagy.count > 0 || @template_folders.present? %>
|
<% if has_archived || @pagy.count.nil? || @pagy.count > 0 || @template_folders.present? %>
|
||||||
<div class="mr-2">
|
<div class="mr-2">
|
||||||
|
|||||||
@@ -1,7 +1,39 @@
|
|||||||
|
<% with_subfolder = @template.folder.name != TemplateFolder::DEFAULT_NAME && params[:subfolder] != 'false' %>
|
||||||
<%= render 'shared/turbo_modal', title: t('move_into_folder') do %>
|
<%= render 'shared/turbo_modal', title: t('move_into_folder') do %>
|
||||||
<%= form_for '', url: template_folder_path(@template), method: :put, data: { turbo_frame: :_top }, html: { autocomplete: :off } do |f| %>
|
<% if with_subfolder %>
|
||||||
<div class="form-control my-6">
|
<%= form_for '', url: template_folder_path(@template), method: :put, data: { turbo_frame: :_top }, html: { id: 'subfolder_form', autocomplete: :off } do |f| %>
|
||||||
<folder-autocomplete class="block" data-submit-on-select="true">
|
<%= f.hidden_field :parent_name, value: @template.folder.parent_folder&.name || @template.folder.name %>
|
||||||
|
<toggle-visible data-element-ids="<%= %w[folder_form subfolder_form].to_json %>" class="block relative">
|
||||||
|
<div class="flex items-center justify-between mb-2.5">
|
||||||
|
<label for="is_root_folder" class="flex items-center">
|
||||||
|
<%= check_box_tag :is_root_folder, 'folder_form', data: { action: 'change:toggle-visible#trigger' }, class: 'hidden' %>
|
||||||
|
<span class="flex items-center space-x-0.5 mt-1 peer">
|
||||||
|
<%= svg_icon('folder', class: 'w-6 h-5 flex-shrink-0') %>
|
||||||
|
<span class="text-md">
|
||||||
|
<%= @template.folder.parent_folder&.name || @template.folder.name %>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="pl-1 tooltip tooltip-right md:opacity-0 hover:opacity-100 peer-hover:opacity-100" data-tip="<%= t('change_parent_folder') %>">
|
||||||
|
<span href="<%= edit_template_folder_path(@template.id) %>" data-turbo-frame="modal">
|
||||||
|
<%= svg_icon('pencil', class: 'w-5 h-5') %>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</toggle-visible>
|
||||||
|
<div class="form-control mb-6">
|
||||||
|
<folder-autocomplete class="block" data-submit-on-select="true" data-parent-name="<%= @template.folder.parent_folder&.name || @template.folder.name %>" data-enabled="<%= params[:autocomplete] != 'false' %>">
|
||||||
|
<%= f.text_field :name, required: true, placeholder: "#{t('new_subfolder_name')}...", class: 'base-input w-full', autofocus: true %>
|
||||||
|
</folder-autocomplete>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<%= f.button button_title(title: t('move'), disabled_with: t('moving')), class: 'base-button' %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<%= form_for '', url: template_folder_path(@template), method: :put, data: { turbo_frame: :_top }, html: { id: 'folder_form', autocomplete: :off, class: "mt-6 #{'hidden' if with_subfolder}" } do |f| %>
|
||||||
|
<div class="form-control mb-6">
|
||||||
|
<folder-autocomplete class="block" data-submit-on-select="true" data-enabled="<%= params[:autocomplete] != 'false' %>">
|
||||||
<%= f.text_field :name, required: true, placeholder: "#{t('new_folder_name')}...", class: 'base-input w-full', autofocus: true %>
|
<%= f.text_field :name, required: true, placeholder: "#{t('new_folder_name')}...", class: 'base-input w-full', autofocus: true %>
|
||||||
</folder-autocomplete>
|
</folder-autocomplete>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ module Rouge
|
|||||||
|
|
||||||
module Lexers
|
module Lexers
|
||||||
autoload :JSON, 'rouge/lexers/json'
|
autoload :JSON, 'rouge/lexers/json'
|
||||||
|
autoload :Shell, 'rouge/lexers/shell'
|
||||||
end
|
end
|
||||||
|
|
||||||
autoload :Formatter, 'rouge/formatter'
|
autoload :Formatter, 'rouge/formatter'
|
||||||
|
|||||||
@@ -460,6 +460,10 @@ en: &en
|
|||||||
users_count_total_users_count_pro_users_limit_was_reached_to_invite_additional_users_please_purchase_more_pro_user_seats_via_the_manage_plan_button: '%{users_count}/%{total_users_count} Pro users limit was reached. To invite additional users, please purchase more Pro user seats via the "Manage plan" button.'
|
users_count_total_users_count_pro_users_limit_was_reached_to_invite_additional_users_please_purchase_more_pro_user_seats_via_the_manage_plan_button: '%{users_count}/%{total_users_count} Pro users limit was reached. To invite additional users, please purchase more Pro user seats via the "Manage plan" button.'
|
||||||
move_into_folder: Move Into Folder
|
move_into_folder: Move Into Folder
|
||||||
new_folder_name: New Folder Name
|
new_folder_name: New Folder Name
|
||||||
|
new_subfolder_name: New Subfolder Name
|
||||||
|
change_parent_folder: Change Parent Folder
|
||||||
|
folder: Folder
|
||||||
|
create_a_new_folder: Create a New Folder
|
||||||
exit_preview: Exit Preview
|
exit_preview: Exit Preview
|
||||||
general: General
|
general: General
|
||||||
recipients: Recipients
|
recipients: Recipients
|
||||||
@@ -1326,6 +1330,10 @@ es: &es
|
|||||||
users_count_total_users_count_pro_users_limit_was_reached_to_invite_additional_users_please_purchase_more_pro_user_seats_via_the_manage_plan_button: 'Se alcanzó el límite de %{users_count}/%{total_users_count} usuarios Pro. Para invitar a más usuarios, compra más plazas Pro usando el botón "Gestionar plan".'
|
users_count_total_users_count_pro_users_limit_was_reached_to_invite_additional_users_please_purchase_more_pro_user_seats_via_the_manage_plan_button: 'Se alcanzó el límite de %{users_count}/%{total_users_count} usuarios Pro. Para invitar a más usuarios, compra más plazas Pro usando el botón "Gestionar plan".'
|
||||||
move_into_folder: Mover a la carpeta
|
move_into_folder: Mover a la carpeta
|
||||||
new_folder_name: Nuevo nombre de la carpeta
|
new_folder_name: Nuevo nombre de la carpeta
|
||||||
|
new_subfolder_name: Nuevo nombre de subcarpeta
|
||||||
|
change_parent_folder: Cambiar carpeta principal
|
||||||
|
folder: Carpeta
|
||||||
|
create_a_new_folder: Crear una nueva carpeta
|
||||||
exit_preview: Salir de la vista previa
|
exit_preview: Salir de la vista previa
|
||||||
general: General
|
general: General
|
||||||
recipients: Destinatarios
|
recipients: Destinatarios
|
||||||
@@ -2191,6 +2199,10 @@ it: &it
|
|||||||
users_count_total_users_count_pro_users_limit_was_reached_to_invite_additional_users_please_purchase_more_pro_user_seats_via_the_manage_plan_button: 'È stato raggiunto il limite di %{users_count}/%{total_users_count} utenti Pro. Per invitare altri utenti, acquista più posti Pro tramite il pulsante "Gestisci piano".'
|
users_count_total_users_count_pro_users_limit_was_reached_to_invite_additional_users_please_purchase_more_pro_user_seats_via_the_manage_plan_button: 'È stato raggiunto il limite di %{users_count}/%{total_users_count} utenti Pro. Per invitare altri utenti, acquista più posti Pro tramite il pulsante "Gestisci piano".'
|
||||||
move_into_folder: Sposta nella cartella
|
move_into_folder: Sposta nella cartella
|
||||||
new_folder_name: Nuovo nome della cartella
|
new_folder_name: Nuovo nome della cartella
|
||||||
|
new_subfolder_name: Nuovo nome della sottocartella
|
||||||
|
change_parent_folder: Cambia cartella principale
|
||||||
|
folder: Cartella
|
||||||
|
create_a_new_folder: Crea una nuova cartella
|
||||||
exit_preview: "Esci dall'anteprima"
|
exit_preview: "Esci dall'anteprima"
|
||||||
general: Generale
|
general: Generale
|
||||||
recipients: Destinatari
|
recipients: Destinatari
|
||||||
@@ -3058,6 +3070,10 @@ fr: &fr
|
|||||||
users_count_total_users_count_pro_users_limit_was_reached_to_invite_additional_users_please_purchase_more_pro_user_seats_via_the_manage_plan_button: 'La limite de %{users_count}/%{total_users_count} utilisateurs Pro a été atteinte. Pour inviter d''autres utilisateurs, veuillez acheter plus de places Pro via le bouton "Gérer le plan".'
|
users_count_total_users_count_pro_users_limit_was_reached_to_invite_additional_users_please_purchase_more_pro_user_seats_via_the_manage_plan_button: 'La limite de %{users_count}/%{total_users_count} utilisateurs Pro a été atteinte. Pour inviter d''autres utilisateurs, veuillez acheter plus de places Pro via le bouton "Gérer le plan".'
|
||||||
move_into_folder: Déplacer dans le dossier
|
move_into_folder: Déplacer dans le dossier
|
||||||
new_folder_name: Nouveau nom du dossier
|
new_folder_name: Nouveau nom du dossier
|
||||||
|
new_subfolder_name: Nouveau nom du sous-dossier
|
||||||
|
change_parent_folder: Changer le dossier parent
|
||||||
|
folder: Dossier
|
||||||
|
create_a_new_folder: Créer un nouveau dossier
|
||||||
exit_preview: "Quitter l'aperçu"
|
exit_preview: "Quitter l'aperçu"
|
||||||
general: Général
|
general: Général
|
||||||
recipients: Destinataires
|
recipients: Destinataires
|
||||||
@@ -3925,6 +3941,10 @@ pt: &pt
|
|||||||
users_count_total_users_count_pro_users_limit_was_reached_to_invite_additional_users_please_purchase_more_pro_user_seats_via_the_manage_plan_button: 'O limite de %{users_count}/%{total_users_count} usuários Pro foi atingido. Para convidar mais usuários, adquira mais licenças Pro através do botão "Gerenciar plano".'
|
users_count_total_users_count_pro_users_limit_was_reached_to_invite_additional_users_please_purchase_more_pro_user_seats_via_the_manage_plan_button: 'O limite de %{users_count}/%{total_users_count} usuários Pro foi atingido. Para convidar mais usuários, adquira mais licenças Pro através do botão "Gerenciar plano".'
|
||||||
move_into_folder: Mover para pasta
|
move_into_folder: Mover para pasta
|
||||||
new_folder_name: Novo nome da pasta
|
new_folder_name: Novo nome da pasta
|
||||||
|
new_subfolder_name: Novo nome da subpasta
|
||||||
|
change_parent_folder: Alterar pasta pai
|
||||||
|
folder: Pasta
|
||||||
|
create_a_new_folder: Criar uma nova pasta
|
||||||
exit_preview: Sair da pré-visualização
|
exit_preview: Sair da pré-visualização
|
||||||
general: Geral
|
general: Geral
|
||||||
recipients: Destinatários
|
recipients: Destinatários
|
||||||
@@ -4792,6 +4812,10 @@ de: &de
|
|||||||
users_count_total_users_count_pro_users_limit_was_reached_to_invite_additional_users_please_purchase_more_pro_user_seats_via_the_manage_plan_button: 'Das Limit von %{users_count}/%{total_users_count} Pro-Benutzern wurde erreicht. Um weitere Benutzer einzuladen, kaufen Sie bitte zusätzliche Pro-Benutzerplätze über die Schaltfläche "Plan verwalten".'
|
users_count_total_users_count_pro_users_limit_was_reached_to_invite_additional_users_please_purchase_more_pro_user_seats_via_the_manage_plan_button: 'Das Limit von %{users_count}/%{total_users_count} Pro-Benutzern wurde erreicht. Um weitere Benutzer einzuladen, kaufen Sie bitte zusätzliche Pro-Benutzerplätze über die Schaltfläche "Plan verwalten".'
|
||||||
move_into_folder: In Ordner verschieben
|
move_into_folder: In Ordner verschieben
|
||||||
new_folder_name: Neuer Ordnername
|
new_folder_name: Neuer Ordnername
|
||||||
|
new_subfolder_name: Neuer Unterordnername
|
||||||
|
change_parent_folder: Übergeordneten Ordner ändern
|
||||||
|
folder: Ordner
|
||||||
|
create_a_new_folder: Neuen Ordner erstellen
|
||||||
exit_preview: Vorschau beenden
|
exit_preview: Vorschau beenden
|
||||||
general: Allgemein
|
general: Allgemein
|
||||||
recipients: Empfänger
|
recipients: Empfänger
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddParentFolderIdToTemplateFolders < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_reference :template_folders, :parent_folder, foreign_key: { to_table: :template_folders }, index: true
|
||||||
|
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[8.0].define(version: 2025_06_27_130628) do
|
ActiveRecord::Schema[8.0].define(version: 2025_07_18_121133) 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 "btree_gin"
|
enable_extension "btree_gin"
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
@@ -356,8 +356,10 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_27_130628) do
|
|||||||
t.datetime "archived_at"
|
t.datetime "archived_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.bigint "parent_folder_id"
|
||||||
t.index ["account_id"], name: "index_template_folders_on_account_id"
|
t.index ["account_id"], name: "index_template_folders_on_account_id"
|
||||||
t.index ["author_id"], name: "index_template_folders_on_author_id"
|
t.index ["author_id"], name: "index_template_folders_on_author_id"
|
||||||
|
t.index ["parent_folder_id"], name: "index_template_folders_on_parent_folder_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "template_sharings", force: :cascade do |t|
|
create_table "template_sharings", force: :cascade do |t|
|
||||||
@@ -498,6 +500,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_27_130628) do
|
|||||||
add_foreign_key "submitters", "submissions"
|
add_foreign_key "submitters", "submissions"
|
||||||
add_foreign_key "template_accesses", "templates"
|
add_foreign_key "template_accesses", "templates"
|
||||||
add_foreign_key "template_folders", "accounts"
|
add_foreign_key "template_folders", "accounts"
|
||||||
|
add_foreign_key "template_folders", "template_folders", column: "parent_folder_id"
|
||||||
add_foreign_key "template_folders", "users", column: "author_id"
|
add_foreign_key "template_folders", "users", column: "author_id"
|
||||||
add_foreign_key "template_sharings", "templates"
|
add_foreign_key "template_sharings", "templates"
|
||||||
add_foreign_key "templates", "accounts"
|
add_foreign_key "templates", "accounts"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ module Submissions
|
|||||||
ALLOWED_PARAMS = %w[
|
ALLOWED_PARAMS = %w[
|
||||||
author
|
author
|
||||||
status
|
status
|
||||||
|
folder
|
||||||
completed_at_from
|
completed_at_from
|
||||||
completed_at_to
|
completed_at_to
|
||||||
created_at_from
|
created_at_from
|
||||||
@@ -24,6 +25,7 @@ module Submissions
|
|||||||
filters = normalize_filter_params(params, current_user)
|
filters = normalize_filter_params(params, current_user)
|
||||||
|
|
||||||
submissions = filter_by_author(submissions, filters, current_user)
|
submissions = filter_by_author(submissions, filters, current_user)
|
||||||
|
submissions = filter_by_folder(submissions, filters, current_user)
|
||||||
submissions = filter_by_status(submissions, filters)
|
submissions = filter_by_status(submissions, filters)
|
||||||
submissions = filter_by_created_at(submissions, filters)
|
submissions = filter_by_created_at(submissions, filters)
|
||||||
|
|
||||||
@@ -34,6 +36,7 @@ module Submissions
|
|||||||
return submissions if filters[:author].blank?
|
return submissions if filters[:author].blank?
|
||||||
|
|
||||||
user = current_user.account.users.find_by(email: filters[:author])
|
user = current_user.account.users.find_by(email: filters[:author])
|
||||||
|
|
||||||
submissions.where(created_by_user_id: user&.id || -1)
|
submissions.where(created_by_user_id: user&.id || -1)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -87,6 +90,17 @@ module Submissions
|
|||||||
submissions
|
submissions
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filter_by_folder(submissions, filters, current_user)
|
||||||
|
return submissions if filters[:folder].blank?
|
||||||
|
|
||||||
|
folders =
|
||||||
|
TemplateFolders.filter_by_full_name(current_user.account.template_folders, filters[:folder])
|
||||||
|
|
||||||
|
folders += folders.preload(:subfolders).flat_map(&:subfolders)
|
||||||
|
|
||||||
|
submissions.joins(:template).where(templates: { folder_id: folders.map(&:id) })
|
||||||
|
end
|
||||||
|
|
||||||
def filter_by_completed_at(submissions, filters)
|
def filter_by_completed_at(submissions, filters)
|
||||||
return submissions unless filters[:completed_at_from].present? || filters[:completed_at_to].present?
|
return submissions unless filters[:completed_at_from].present? || filters[:completed_at_to].present?
|
||||||
|
|
||||||
|
|||||||
@@ -261,8 +261,6 @@ module Submissions
|
|||||||
e['type'] == 'verification' && e['submitter_uuid'] == submitter.uuid && submitter.values[e['uuid']].present?
|
e['type'] == 'verification' && e['submitter_uuid'] == submitter.uuid && submitter.values[e['uuid']].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
submitter_field_counters = Hash.new { 0 }
|
|
||||||
|
|
||||||
info_rows = [
|
info_rows = [
|
||||||
[
|
[
|
||||||
composer.document.layout.formatted_text_box(
|
composer.document.layout.formatted_text_box(
|
||||||
@@ -298,25 +296,53 @@ module Submissions
|
|||||||
|
|
||||||
composer.table(info_rows, cell_style: { padding: [0, 0, 0, 0], border: { width: 0 } })
|
composer.table(info_rows, cell_style: { padding: [0, 0, 0, 0], border: { width: 0 } })
|
||||||
|
|
||||||
|
submitter_field_counters = Hash.new { 0 }
|
||||||
|
grouped_value_field_names = {}
|
||||||
|
skip_grouped_field_uuids = {}
|
||||||
|
|
||||||
|
submission.template_fields.each do |field|
|
||||||
|
next unless field['type'].in?(%w[signature initials])
|
||||||
|
|
||||||
|
submitter_field_counters[field['type']] += 1
|
||||||
|
|
||||||
|
next if field['submitter_uuid'] != submitter.uuid
|
||||||
|
|
||||||
|
value = submitter.values[field['uuid']]
|
||||||
|
|
||||||
|
field_name = field['title'].presence || field['name'].presence ||
|
||||||
|
"#{I18n.t("#{field['type']}_field")} #{submitter_field_counters[field['type']]}"
|
||||||
|
|
||||||
|
if grouped_value_field_names[value]
|
||||||
|
skip_grouped_field_uuids[field['uuid']] = true
|
||||||
|
|
||||||
|
grouped_value_field_names[value] += ", #{field_name}"
|
||||||
|
else
|
||||||
|
grouped_value_field_names[value] = field_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
submitter_field_counters = Hash.new { 0 }
|
||||||
|
|
||||||
submission.template_fields.filter_map do |field|
|
submission.template_fields.filter_map do |field|
|
||||||
|
submitter_field_counters[field['type']] += 1
|
||||||
|
|
||||||
next if field['submitter_uuid'] != submitter.uuid
|
next if field['submitter_uuid'] != submitter.uuid
|
||||||
next if field['type'] == 'heading'
|
next if field['type'] == 'heading'
|
||||||
next if !with_audit_values && !field['type'].in?(%w[signature initials])
|
next if !with_audit_values && !field['type'].in?(%w[signature initials])
|
||||||
|
next if skip_grouped_field_uuids[field['uuid']]
|
||||||
submitter_field_counters[field['type']] += 1
|
|
||||||
|
|
||||||
value = submitter.values[field['uuid']]
|
value = submitter.values[field['uuid']]
|
||||||
|
|
||||||
next if Array.wrap(value).compact_blank.blank?
|
next if Array.wrap(value).compact_blank.blank?
|
||||||
|
|
||||||
field_name = field['title'].presence || field['name'].to_s
|
field_name = grouped_value_field_names[value].presence || field['title'].presence || field['name'].to_s
|
||||||
|
|
||||||
[
|
[
|
||||||
composer.formatted_text_box(
|
composer.formatted_text_box(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
text: TextUtils.maybe_rtl_reverse(field_name).upcase.presence ||
|
text: TextUtils.maybe_rtl_reverse(field_name).upcase.presence ||
|
||||||
"#{I18n.t("#{field['type']}_field")} #{submitter_field_counters[field['type']]}\n".upcase,
|
"#{I18n.t("#{field['type']}_field")} #{submitter_field_counters[field['type']]}".upcase,
|
||||||
font_size: 6
|
font_size: 6
|
||||||
}
|
}
|
||||||
].compact_blank,
|
].compact_blank,
|
||||||
|
|||||||
@@ -14,11 +14,14 @@ module Submissions
|
|||||||
|
|
||||||
configs = submission.account.account_configs.where(key: [AccountConfig::FLATTEN_RESULT_PDF_KEY,
|
configs = submission.account.account_configs.where(key: [AccountConfig::FLATTEN_RESULT_PDF_KEY,
|
||||||
AccountConfig::WITH_SIGNATURE_ID,
|
AccountConfig::WITH_SIGNATURE_ID,
|
||||||
AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY])
|
AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY,
|
||||||
|
AccountConfig::WITH_SIGNATURE_ID_REASON_KEY])
|
||||||
|
|
||||||
with_signature_id = configs.find { |c| c.key == AccountConfig::WITH_SIGNATURE_ID }&.value == true
|
with_signature_id = configs.find { |c| c.key == AccountConfig::WITH_SIGNATURE_ID }&.value == true
|
||||||
is_flatten = configs.find { |c| c.key == AccountConfig::FLATTEN_RESULT_PDF_KEY }&.value != false
|
is_flatten = configs.find { |c| c.key == AccountConfig::FLATTEN_RESULT_PDF_KEY }&.value != false
|
||||||
with_submitter_timezone = configs.find { |c| c.key == AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY }&.value == true
|
with_submitter_timezone = configs.find { |c| c.key == AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY }&.value == true
|
||||||
|
with_signature_id_reason =
|
||||||
|
configs.find { |c| c.key == AccountConfig::WITH_SIGNATURE_ID_REASON_KEY }&.value != false
|
||||||
|
|
||||||
pdfs_index = GenerateResultAttachments.build_pdfs_index(submission, flatten: is_flatten)
|
pdfs_index = GenerateResultAttachments.build_pdfs_index(submission, flatten: is_flatten)
|
||||||
|
|
||||||
@@ -31,7 +34,8 @@ module Submissions
|
|||||||
submitters.preload(attachments_attachments: :blob).each_with_index do |s, index|
|
submitters.preload(attachments_attachments: :blob).each_with_index do |s, index|
|
||||||
GenerateResultAttachments.fill_submitter_fields(s, submission.account, pdfs_index,
|
GenerateResultAttachments.fill_submitter_fields(s, submission.account, pdfs_index,
|
||||||
with_signature_id:, is_flatten:, with_headings: index.zero?,
|
with_signature_id:, is_flatten:, with_headings: index.zero?,
|
||||||
with_submitter_timezone:)
|
with_submitter_timezone:,
|
||||||
|
with_signature_id_reason:)
|
||||||
end
|
end
|
||||||
|
|
||||||
template = submission.template
|
template = submission.template
|
||||||
|
|||||||
@@ -139,11 +139,14 @@ module Submissions
|
|||||||
def generate_pdfs(submitter)
|
def generate_pdfs(submitter)
|
||||||
configs = submitter.account.account_configs.where(key: [AccountConfig::FLATTEN_RESULT_PDF_KEY,
|
configs = submitter.account.account_configs.where(key: [AccountConfig::FLATTEN_RESULT_PDF_KEY,
|
||||||
AccountConfig::WITH_SIGNATURE_ID,
|
AccountConfig::WITH_SIGNATURE_ID,
|
||||||
AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY])
|
AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY,
|
||||||
|
AccountConfig::WITH_SIGNATURE_ID_REASON_KEY])
|
||||||
|
|
||||||
with_signature_id = configs.find { |c| c.key == AccountConfig::WITH_SIGNATURE_ID }&.value == true
|
with_signature_id = configs.find { |c| c.key == AccountConfig::WITH_SIGNATURE_ID }&.value == true
|
||||||
is_flatten = configs.find { |c| c.key == AccountConfig::FLATTEN_RESULT_PDF_KEY }&.value != false
|
is_flatten = configs.find { |c| c.key == AccountConfig::FLATTEN_RESULT_PDF_KEY }&.value != false
|
||||||
with_submitter_timezone = configs.find { |c| c.key == AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY }&.value == true
|
with_submitter_timezone = configs.find { |c| c.key == AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY }&.value == true
|
||||||
|
with_signature_id_reason =
|
||||||
|
configs.find { |c| c.key == AccountConfig::WITH_SIGNATURE_ID_REASON_KEY }&.value != false
|
||||||
|
|
||||||
pdfs_index = build_pdfs_index(submitter.submission, submitter:, flatten: is_flatten)
|
pdfs_index = build_pdfs_index(submitter.submission, submitter:, flatten: is_flatten)
|
||||||
|
|
||||||
@@ -188,11 +191,12 @@ module Submissions
|
|||||||
end
|
end
|
||||||
|
|
||||||
fill_submitter_fields(submitter, submitter.account, pdfs_index, with_signature_id:, is_flatten:,
|
fill_submitter_fields(submitter, submitter.account, pdfs_index, with_signature_id:, is_flatten:,
|
||||||
with_submitter_timezone:)
|
with_submitter_timezone:,
|
||||||
|
with_signature_id_reason:)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fill_submitter_fields(submitter, account, pdfs_index, with_signature_id:, is_flatten:, with_headings: nil,
|
def fill_submitter_fields(submitter, account, pdfs_index, with_signature_id:, is_flatten:, with_headings: nil,
|
||||||
with_submitter_timezone: false)
|
with_submitter_timezone: false, with_signature_id_reason: true)
|
||||||
cell_layouter = HexaPDF::Layout::TextLayouter.new(text_valign: :center, text_align: :center)
|
cell_layouter = HexaPDF::Layout::TextLayouter.new(text_valign: :center, text_align: :center)
|
||||||
|
|
||||||
attachments_data_cache = {}
|
attachments_data_cache = {}
|
||||||
@@ -274,6 +278,10 @@ module Submissions
|
|||||||
field_type = 'file' if field_type == 'image' &&
|
field_type = 'file' if field_type == 'image' &&
|
||||||
!submitter.attachments.find { |a| a.uuid == value }.image?
|
!submitter.attachments.find { |a| a.uuid == value }.image?
|
||||||
|
|
||||||
|
if field_type == 'signature' && field.dig('preferences', 'with_signature_id').in?([true, false])
|
||||||
|
with_signature_id = field['preferences']['with_signature_id']
|
||||||
|
end
|
||||||
|
|
||||||
case field_type
|
case field_type
|
||||||
when ->(type) { type == 'signature' && (with_signature_id || field.dig('preferences', 'reason_field_uuid')) }
|
when ->(type) { type == 'signature' && (with_signature_id || field.dig('preferences', 'reason_field_uuid')) }
|
||||||
attachment = submitter.attachments.find { |a| a.uuid == value }
|
attachment = submitter.attachments.find { |a| a.uuid == value }
|
||||||
@@ -295,10 +303,15 @@ module Submissions
|
|||||||
timezone = submitter.account.timezone
|
timezone = submitter.account.timezone
|
||||||
timezone = submitter.timezone || submitter.account.timezone if with_submitter_timezone
|
timezone = submitter.timezone || submitter.account.timezone if with_submitter_timezone
|
||||||
|
|
||||||
"#{reason_value ? "#{I18n.t('reason')}: " : ''}#{reason_value || I18n.t('digitally_signed_by')} " \
|
if with_signature_id_reason
|
||||||
"#{submitter.name}#{submitter.email.present? ? " <#{submitter.email}>" : ''}\n" \
|
"#{reason_value ? "#{I18n.t('reason')}: " : ''}#{reason_value || I18n.t('digitally_signed_by')} " \
|
||||||
|
"#{submitter.name}#{submitter.email.present? ? " <#{submitter.email}>" : ''}\n" \
|
||||||
|
"#{I18n.l(attachment.created_at.in_time_zone(timezone), format: :long)} " \
|
||||||
|
"#{TimeUtils.timezone_abbr(timezone, attachment.created_at)}"
|
||||||
|
else
|
||||||
"#{I18n.l(attachment.created_at.in_time_zone(timezone), format: :long)} " \
|
"#{I18n.l(attachment.created_at.in_time_zone(timezone), format: :long)} " \
|
||||||
"#{TimeUtils.timezone_abbr(timezone, attachment.created_at)}"
|
"#{TimeUtils.timezone_abbr(timezone, attachment.created_at)}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
reason_text = HexaPDF::Layout::TextFragment.create(reason_string,
|
reason_text = HexaPDF::Layout::TextFragment.create(reason_string,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ module Submitters
|
|||||||
AccountConfig::REUSE_SIGNATURE_KEY,
|
AccountConfig::REUSE_SIGNATURE_KEY,
|
||||||
AccountConfig::ALLOW_TO_PARTIAL_DOWNLOAD_KEY,
|
AccountConfig::ALLOW_TO_PARTIAL_DOWNLOAD_KEY,
|
||||||
AccountConfig::ALLOW_TYPED_SIGNATURE,
|
AccountConfig::ALLOW_TYPED_SIGNATURE,
|
||||||
|
AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY,
|
||||||
|
AccountConfig::WITH_SIGNATURE_ID_REASON_KEY,
|
||||||
*(Docuseal.multitenant? ? [] : [AccountConfig::POLICY_LINKS_KEY])].freeze
|
*(Docuseal.multitenant? ? [] : [AccountConfig::POLICY_LINKS_KEY])].freeze
|
||||||
|
|
||||||
module_function
|
module_function
|
||||||
@@ -31,20 +33,15 @@ module Submitters
|
|||||||
with_signature_id = find_safe_value(configs, AccountConfig::WITH_SIGNATURE_ID) == true
|
with_signature_id = find_safe_value(configs, AccountConfig::WITH_SIGNATURE_ID) == true
|
||||||
require_signing_reason = find_safe_value(configs, AccountConfig::REQUIRE_SIGNING_REASON_KEY) == true
|
require_signing_reason = find_safe_value(configs, AccountConfig::REQUIRE_SIGNING_REASON_KEY) == true
|
||||||
enforce_signing_order = find_safe_value(configs, AccountConfig::ENFORCE_SIGNING_ORDER_KEY) == true
|
enforce_signing_order = find_safe_value(configs, AccountConfig::ENFORCE_SIGNING_ORDER_KEY) == true
|
||||||
|
with_submitter_timezone = find_safe_value(configs, AccountConfig::WITH_SUBMITTER_TIMEZONE_KEY) == true
|
||||||
|
with_signature_id_reason = find_safe_value(configs, AccountConfig::WITH_SIGNATURE_ID_REASON_KEY) != false
|
||||||
policy_links = find_safe_value(configs, AccountConfig::POLICY_LINKS_KEY)
|
policy_links = find_safe_value(configs, AccountConfig::POLICY_LINKS_KEY)
|
||||||
|
|
||||||
attrs = { completed_button:,
|
attrs = { completed_button:, with_typed_signature:, with_confetti:,
|
||||||
with_typed_signature:,
|
reuse_signature:, with_decline:, with_partial_download:,
|
||||||
with_confetti:,
|
policy_links:, enforce_signing_order:, completed_message:,
|
||||||
reuse_signature:,
|
require_signing_reason:, prefill_signature:, with_submitter_timezone:,
|
||||||
with_decline:,
|
with_signature_id_reason:, with_signature_id: }
|
||||||
with_partial_download:,
|
|
||||||
policy_links:,
|
|
||||||
enforce_signing_order:,
|
|
||||||
completed_message:,
|
|
||||||
require_signing_reason:,
|
|
||||||
prefill_signature:,
|
|
||||||
with_signature_id: }
|
|
||||||
|
|
||||||
keys.each do |key|
|
keys.each do |key|
|
||||||
attrs[key.to_sym] = configs.find { |e| e.key == key.to_s }&.value
|
attrs[key.to_sym] = configs.find { |e| e.key == key.to_s }&.value
|
||||||
|
|||||||
+73
-1
@@ -3,15 +3,87 @@
|
|||||||
module TemplateFolders
|
module TemplateFolders
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
|
def filter_by_full_name(template_folders, name)
|
||||||
|
parent_name, name = name.split(' / ', 2).map(&:squish)
|
||||||
|
|
||||||
|
if name.present?
|
||||||
|
parent_folder = template_folders.where(parent_folder_id: nil).find_by(name: parent_name)
|
||||||
|
else
|
||||||
|
name = parent_name
|
||||||
|
end
|
||||||
|
|
||||||
|
template_folders.where(name:, parent_folder:)
|
||||||
|
end
|
||||||
|
|
||||||
def search(folders, keyword)
|
def search(folders, keyword)
|
||||||
return folders if keyword.blank?
|
return folders if keyword.blank?
|
||||||
|
|
||||||
folders.where(TemplateFolder.arel_table[:name].lower.matches("%#{keyword.downcase}%"))
|
folders.where(TemplateFolder.arel_table[:name].lower.matches("%#{keyword.downcase}%"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filter_active_folders(template_folders, templates)
|
||||||
|
folder_exists =
|
||||||
|
templates.active.where(TemplateFolder.arel_table[:id].eq(Template.arel_table[:folder_id]))
|
||||||
|
.select(1).limit(1).arel.exists
|
||||||
|
|
||||||
|
subfolders_arel = TemplateFolder.arel_table.alias('subfolders')
|
||||||
|
|
||||||
|
subfolder_exists =
|
||||||
|
TemplateFolder.from(subfolders_arel)
|
||||||
|
.where(subfolders_arel[:parent_folder_id].eq(TemplateFolder.arel_table[:id]))
|
||||||
|
.where(
|
||||||
|
templates.active.where(Template.arel_table[:folder_id].eq(subfolders_arel[:id])).arel.exists
|
||||||
|
).select(1).limit(1).arel.exists
|
||||||
|
|
||||||
|
template_folders.where(folder_exists).or(template_folders.where(subfolder_exists))
|
||||||
|
end
|
||||||
|
|
||||||
|
def sort(template_folders, current_user, order)
|
||||||
|
case order
|
||||||
|
when 'used_at'
|
||||||
|
subquery =
|
||||||
|
Template.left_joins(:submissions)
|
||||||
|
.group(:folder_id)
|
||||||
|
.where(account_id: current_user.account_id)
|
||||||
|
.select(
|
||||||
|
:folder_id,
|
||||||
|
Template.arel_table[:updated_at].maximum.as('updated_at_max'),
|
||||||
|
Submission.arel_table[:created_at].maximum.as('submission_created_at_max')
|
||||||
|
)
|
||||||
|
|
||||||
|
template_folders = template_folders.joins(
|
||||||
|
Template.arel_table
|
||||||
|
.join(subquery.arel.as('templates'), Arel::Nodes::OuterJoin)
|
||||||
|
.on(TemplateFolder.arel_table[:id].eq(Template.arel_table[:folder_id]))
|
||||||
|
.join_sources
|
||||||
|
)
|
||||||
|
|
||||||
|
template_folders.order(
|
||||||
|
Arel::Nodes::Case.new
|
||||||
|
.when(Template.arel_table[:submission_created_at_max].gt(Template.arel_table[:updated_at_max]))
|
||||||
|
.then(Template.arel_table[:submission_created_at_max])
|
||||||
|
.else(Template.arel_table[:updated_at_max])
|
||||||
|
.desc
|
||||||
|
)
|
||||||
|
when 'name'
|
||||||
|
template_folders.order(name: :asc)
|
||||||
|
else
|
||||||
|
template_folders.order(id: :desc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def find_or_create_by_name(author, name)
|
def find_or_create_by_name(author, name)
|
||||||
return author.account.default_template_folder if name.blank? || name == TemplateFolder::DEFAULT_NAME
|
return author.account.default_template_folder if name.blank? || name == TemplateFolder::DEFAULT_NAME
|
||||||
|
|
||||||
author.account.template_folders.create_with(author:, account: author.account).find_or_create_by(name:)
|
parent_name, name = name.split(' / ', 2).map(&:squish)
|
||||||
|
|
||||||
|
if name.present?
|
||||||
|
parent_folder = author.account.template_folders.create_with(author:)
|
||||||
|
.find_or_create_by(name: parent_name, parent_folder_id: nil)
|
||||||
|
else
|
||||||
|
name = parent_name
|
||||||
|
end
|
||||||
|
|
||||||
|
author.account.template_folders.create_with(author:).find_or_create_by(name:, parent_folder:)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user