add dynamic documents

This commit is contained in:
Pete Matsyburka
2026-02-12 09:22:08 +02:00
parent b50d982497
commit 37196ff89f
65 changed files with 4372 additions and 485 deletions
@@ -3,8 +3,8 @@
<% submitters = template.submitters.reject { |e| e['invite_by_uuid'].present? || e['optional_invite_by_uuid'].present? || e['invite_via_field_uuid'].present? } %>
<dynamic-list class="space-y-4">
<div class="space-y-4">
<div class="card card-compact bg-base-300/40" data-targets="dynamic-list.items">
<div class="card-body">
<div class="<%= 'p-4 rounded-xl bg-base-300/40' unless local_assigns[:variables_form] %>" data-targets="dynamic-list.items">
<div>
<div class="absolute right-4 top-5">
<a href="#" data-action="click:dynamic-list#removeItem" class="<%= submitters.size == 1 ? 'right-2 top-1' : '-top-3' %> relative block w-6 h-6 rounded-lg text-neutral-700 text-center bg-base-300 p-1 hidden hover:bg-neutral hover:text-white">
<%= svg_icon('trash', class: 'w-4 h-4') %>
@@ -86,13 +86,14 @@
</div>
</div>
</div>
<% if params[:selfsign].blank? && local_assigns[:prefillable_fields].blank? && local_assigns[:recipient_form_fields].blank? %>
<% if params[:selfsign].blank? && local_assigns[:prefillable_fields].blank? && local_assigns[:recipient_form_fields].blank? && local_assigns[:variables_form].blank? %>
<a href="#" class="btn btn-primary btn-sm w-full flex items-center justify-center" data-action="click:dynamic-list#addItem">
<%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %>
<span><%= t('add_new') %></span>
</a>
<% end %>
</dynamic-list>
<%= local_assigns[:variables_form] %>
<div>
<%= render('submitters_order', f:, template:) if Accounts.can_send_emails?(current_account) %>
<%= render 'send_email', f:, template: %>
+26 -4
View File
@@ -1,6 +1,27 @@
<%= form_for '', url: template_submissions_path(template), html: { class: 'space-y-4', autocomplete: 'off' }, data: { turbo_frame: :_top } do |f| %>
<% submitters = template.submitters.reject { |e| e['invite_by_uuid'].present? || e['optional_invite_by_uuid'].present? || e['invite_via_field_uuid'].present? } %>
<% if submitters.size == 1 %>
<% if submitters.size == 1 && local_assigns[:variables_form] %>
<% item = submitters.first %>
<div class="grid gap-1">
<submitter-item class="grid md:grid-cols-2 gap-1">
<div class="form-control">
<input type="hidden" name="submission[1][submitters][][uuid]" value="<%= item['uuid'] %>">
<submitters-autocomplete data-field="email">
<linked-input data-target-id="<%= "email_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>">
<%= tag.input type: 'email', multiple: true, name: 'submission[1][submitters][][email]', autocomplete: 'off', class: 'base-input !h-10 w-full', placeholder: t('email'), required: true, value: item['email'].presence || (params[:selfsign] || item['is_requester'] ? current_user.email : ''), id: "email_#{item['uuid']}" %>
</linked-input>
</submitters-autocomplete>
</div>
<div class="form-control flex">
<submitters-autocomplete data-field="name">
<linked-input data-target-id="<%= "email_name_#{item['linked_to_uuid']}" if item['linked_to_uuid'].present? %>">
<input type="text" name="submission[1][submitters][][name]" autocomplete="off" class="base-input !h-10 w-full" placeholder="<%= "#{t('name')} (#{t('optional')})" %>" value="<%= params[:selfsign] || item['is_requester'] ? current_user.full_name : '' %>" dir="auto" id="email_name_<%= item['uuid'] %>">
</linked-input>
</submitters-autocomplete>
</div>
</submitter-item>
</div>
<% elsif submitters.size == 1 %>
<submitter-item class="form-control">
<emails-textarea data-bulk-enabled="<%= Docuseal.demo? || !Docuseal.multitenant? || can?(:manage, :bulk_send) %>" data-limit="<%= Docuseal.multitenant? ? (can?(:manage, :bulk_send) ? 40 : 1) : nil %>">
<submitters-autocomplete data-field="email" class="block relative">
@@ -13,8 +34,8 @@
<% else %>
<dynamic-list class="space-y-4">
<div class="space-y-4">
<div class="card card-compact bg-base-300/40" data-targets="dynamic-list.items">
<div class="card-body">
<div class="<%= 'p-4 rounded-xl bg-base-300/40' unless local_assigns[:variables_form] %>" data-targets="dynamic-list.items">
<div>
<div class="absolute right-4 top-5">
<a href="#" data-action="click:dynamic-list#removeItem" class="-top-3 relative block w-6 h-6 rounded-lg text-neutral-700 text-center bg-base-300 p-1 hidden hover:bg-neutral hover:text-white">
<%= svg_icon('trash', class: 'w-4 h-4') %>
@@ -38,7 +59,7 @@
</div>
</div>
</div>
<% if params[:selfsign].blank? %>
<% if params[:selfsign].blank? && local_assigns[:variables_form].blank? %>
<a href="#" class="btn btn-primary btn-sm w-full flex items-center justify-center" data-action="click:dynamic-list#addItem">
<%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %>
<span><%= t('add_new') %></span>
@@ -46,6 +67,7 @@
<% end %>
</dynamic-list>
<% end %>
<%= local_assigns[:variables_form] %>
<div>
<%= render('submitters_order', f:, template:) if Accounts.can_send_emails?(current_account) %>
<%= render 'send_email', f:, template: %>
+4 -3
View File
@@ -2,8 +2,8 @@
<% submitters = template.submitters.reject { |e| e['invite_by_uuid'].present? || e['optional_invite_by_uuid'].present? || e['invite_via_field_uuid'].present? } %>
<dynamic-list class="space-y-4">
<div class="space-y-4">
<div class="card card-compact bg-base-300/40" data-targets="dynamic-list.items">
<div class="card-body">
<div class="<%= 'p-4 rounded-xl bg-base-300/40' unless local_assigns[:variables_form] %>" data-targets="dynamic-list.items">
<div>
<div class="absolute right-4 top-5">
<a href="#" data-action="click:dynamic-list#removeItem" class="<%= submitters.size == 1 ? 'right-2 top-1' : '-top-3' %> relative block w-6 h-6 rounded-lg text-neutral-700 text-center bg-base-300 p-1 hidden hover:bg-neutral hover:text-white">
<%= svg_icon('trash', class: 'w-4 h-4') %>
@@ -49,13 +49,14 @@
</div>
</div>
</div>
<% if params[:selfsign].blank? %>
<% if params[:selfsign].blank? && local_assigns[:variables_form].blank? %>
<a href="#" class="btn btn-primary btn-sm w-full flex items-center justify-center" data-action="click:dynamic-list#addItem">
<%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %>
<span><%= t('add_new') %></span>
</a>
<% end %>
</dynamic-list>
<%= local_assigns[:variables_form] %>
<div>
<%= render('submitters_order', f:, template:) if Accounts.can_send_emails?(current_account) %>
<%= render 'send_sms', f:, checked: true %>
+11 -7
View File
@@ -4,8 +4,10 @@
<% default_tab = cookies.permanent[:add_recipients_tab].presence || 'email' %>
<% recipient_form_fields = Accounts.load_recipient_form_fields(current_account) if prefillable_fields.blank? %>
<% only_detailed = require_phone_2fa || require_email_2fa || prefillable_fields.present? || recipient_form_fields.present? %>
<% with_list = @template.variables_schema.blank? %>
<% variables_form = render 'variables_form', schema: @template.variables_schema if @template.variables_schema.present? && @template.variables_schema.any? { |_, v| !v['disabled'] } %>
<%= render 'shared/turbo_modal_large', title: params[:selfsign] ? t('add_recipients') : t('add_new_recipients') do %>
<% options = [only_detailed ? nil : [t('via_email'), 'email'], only_detailed ? nil : [t('via_phone'), 'phone'], [t('detailed'), 'detailed'], [t('upload_list'), 'list']].compact %>
<% options = [only_detailed ? nil : [t('via_email'), 'email'], only_detailed ? nil : [t('via_phone'), 'phone'], [t('detailed'), 'detailed'], with_list ? [t('upload_list'), 'list'] : nil].compact %>
<toggle-visible data-element-ids="<%= options.map(&:last).to_json %>" class="relative text-center px-2 mt-4 block">
<div class="flex justify-center">
<% options.each_with_index do |(label, value), index| %>
@@ -21,18 +23,20 @@
<div class="px-5 mb-5 mt-4">
<% unless only_detailed %>
<div id="email" class="<%= 'hidden' if default_tab != 'email' %>">
<%= render 'email_form', template: @template %>
<%= render 'email_form', template: @template, variables_form: %>
</div>
<div id="phone" class="<%= 'hidden' if default_tab != 'phone' %>">
<%= render 'phone_form', template: @template %>
<%= render 'phone_form', template: @template, variables_form: %>
</div>
<% end %>
<div id="detailed" class="<%= 'hidden' if !only_detailed && default_tab != 'detailed' %>">
<%= render 'detailed_form', template: @template, require_phone_2fa:, require_email_2fa:, prefillable_fields:, recipient_form_fields: %>
</div>
<div id="list" class="hidden">
<%= render 'list_form', template: @template %>
<%= render 'detailed_form', template: @template, require_phone_2fa:, require_email_2fa:, prefillable_fields:, recipient_form_fields:, variables_form: %>
</div>
<% if with_list %>
<div id="list" class="hidden">
<%= render 'list_form', template: @template %>
</div>
<% end %>
<%= render 'submissions/error' %>
</div>
<%= content_for(:modal_extra) %>
+1 -1
View File
@@ -104,7 +104,7 @@
<% preview_images_index = document.preview_images.loaded? ? document.preview_images.index_by { |e| e.filename.base.to_i } : {} %>
<% lazyload_metadata = document.preview_images.first&.metadata || Templates::ProcessDocument::US_LETTER_SIZE %>
<% (document.metadata.dig('pdf', 'number_of_pages') || (document.preview_images.loaded? ? preview_images_index.size : document.preview_images.size)).times do |index| %>
<% page = preview_images_index[index] || page_blob_struct.new(metadata: lazyload_metadata, url: preview_document_page_path(document.signed_uuid, "#{index}.jpg")) %>
<% page = preview_images_index[index] || page_blob_struct.new(metadata: lazyload_metadata, url: preview_document_page_path(document.signed_key, "#{index}.jpg")) %>
<page-container id="<%= "page-#{document.uuid}-#{index}" %>" class="block before:border before:absolute before:top-0 before:bottom-0 before:left-0 before:right-0 before:rounded relative mb-4" style="container-type: size; aspect-ratio: <%= width = page.metadata['width'] %> / <%= height = page.metadata['height'] %>">
<img loading="lazy" src="<%= page.url %>" width="<%= width %>" class="rounded" height="<%= height %>">
<div class="top-0 bottom-0 left-0 right-0 absolute">
+1 -1
View File
@@ -65,7 +65,7 @@
<% preview_images_index = document.preview_images.loaded? ? document.preview_images.index_by { |e| e.filename.base.to_i } : {} %>
<% lazyload_metadata = document.preview_images.last&.metadata || Templates::ProcessDocument::US_LETTER_SIZE %>
<% (document.metadata.dig('pdf', 'number_of_pages') || (document.preview_images.loaded? ? preview_images_index.size : document.preview_images.size)).times do |index| %>
<% page = preview_images_index[index] || page_blob_struct.new(metadata: lazyload_metadata, url: preview_document_page_path(document.signed_uuid, "#{index}.jpg")) %>
<% page = preview_images_index[index] || page_blob_struct.new(metadata: lazyload_metadata, url: preview_document_page_path(document.signed_key, "#{index}.jpg")) %>
<page-container class="block relative my-4 shadow-md" style="container-type: size; aspect-ratio: <%= width = page.metadata['width'] %> / <%= height = page.metadata['height'] %>">
<img loading="lazy" src="<%= page.url %>" width="<%= width %>" height="<%= height %>">
<div id="page-<%= [document.uuid, index].join('-') %>" class="top-0 bottom-0 left-0 right-0 absolute">
+2 -2
View File
@@ -49,9 +49,9 @@
<% end %>
</div>
<% end %>
<%= link_to template_share_link_path(template), class: 'absolute md:relative bottom-0 right-0 btn btn-xs md:btn-sm whitespace-nowrap btn-neutral text-white mt-1 px-2', data: { turbo_frame: :modal } do %>
<%= link_to template_share_link_path(template), class: "absolute md:relative bottom-0 right-0 btn btn-xs md:btn-sm whitespace-nowrap btn-neutral text-white mt-1 px-2 #{'btn-disabled text-base-100' if template.variables_schema.present?}", data: { turbo_frame: :modal } do %>
<span class="flex items-center justify-center space-x-2">
<%= svg_icon('link', class: 'w-4 h-4 md:w-6 md:h-6 text-white') %>
<%= svg_icon('link', class: 'w-4 h-4 md:w-6 md:h-6') %>
<span><%= t('link') %></span>
</span>
<% end %>
+1 -1
View File
@@ -94,7 +94,7 @@
<span class="mr-1"><%= t('send_to_recipients') %></span>
<% end %>
<% end %>
<% if Templates.filter_undefined_submitters(@template.submitters).size == 1 %>
<% if Templates.filter_undefined_submitters(@template.submitters).size == 1 && @template.variables_schema.blank? %>
<%= button_to start_form_path(@template.slug), params: { selfsign: true }, method: :put, class: 'white-button w-full', form: { style: 'display: inline', target: '_blank', data: { turbo: false } } do %>
<%= svg_icon('writing', class: 'w-6 h-6') %>
<span class="mr-1"><%= t('sign_it_yourself') %></span>