style template show page

pull/105/head
Alex Turchyn 2 years ago
parent ed157dc952
commit d0c7d449d5

@ -11,6 +11,10 @@ class ApplicationController < ActionController::Base
:current_account,
:svg_icon
rescue_from Pagy::OverflowError do
redirect_to request.path
end
def default_url_options
Docuseal.default_url_options
end

@ -47,7 +47,7 @@ class SubmissionsController < ApplicationController
emails = params[:emails].to_s.scan(User::EMAIL_REGEXP)
emails.map do |email|
submission = @template.submissions.new
submission = @template.submissions.new(created_by_user: current_user)
submission.submitters.new(email:, uuid: @template.submitters.first['uuid'],
sent_at: params[:send_email] == '1' ? Time.current : nil)
@ -57,7 +57,7 @@ class SubmissionsController < ApplicationController
def create_submissions_from_submitters
submissions_params[:submission].to_h.map do |_, attrs|
submission = @template.submissions.new
submission = @template.submissions.new(created_by_user: current_user)
attrs[:submitters].each do |submitter_attrs|
submission.submitters.new(**submitter_attrs, sent_at: params[:send_email] == '1' ? Time.current : nil)

@ -4,9 +4,11 @@ class TemplatesController < ApplicationController
before_action :load_base_template, only: %i[new create]
def show
@template = current_account.templates.find(params[:id])
@template = current_account.templates.active.find(params[:id])
@pagy, @submissions = pagy(@template.submissions.active)
@pagy, @submissions = pagy(@template.submissions.active.preload(:submitters).order(id: :desc))
rescue ActiveRecord::RecordNotFound
redirect_to root_path
end
def new

@ -1,152 +1,15 @@
// Source: https://github.com/github/clipboard-copy-element
// License: MIT
export default class extends HTMLElement {
constructor () {
super()
this.addEventListener('click', clicked)
this.addEventListener('focus', focused)
this.addEventListener('blur', blurred)
}
connectedCallback () {
if (!this.hasAttribute('tabindex')) {
this.setAttribute('tabindex', '0')
}
if (!this.hasAttribute('role')) {
this.setAttribute('role', 'button')
}
}
get value () {
return this.getAttribute('value') || ''
}
set value (text) {
this.setAttribute('value', text)
}
}
function createNode (text) {
const node = document.createElement('pre')
node.style.width = '1px'
node.style.height = '1px'
node.style.position = 'fixed'
node.style.top = '5px'
node.textContent = text
return node
}
function copyNode (node) {
if ('clipboard' in navigator) {
return navigator.clipboard.writeText(node.textContent || '')
}
const selection = getSelection()
if (selection == null) {
return Promise.reject(new Error())
}
selection.removeAllRanges()
const range = document.createRange()
range.selectNodeContents(node)
selection.addRange(range)
document.execCommand('copy')
selection.removeAllRanges()
return Promise.resolve()
}
function copyText (text) {
if ('clipboard' in navigator) {
return navigator.clipboard.writeText(text)
}
const body = document.body
if (!body) {
return Promise.reject(new Error())
}
this.addEventListener('click', (e) => {
e.stopPropagation()
const node = createNode(text)
body.appendChild(node)
copyNode(node)
body.removeChild(node)
return Promise.resolve()
}
function copyTarget (content) {
if (
content instanceof HTMLInputElement ||
content instanceof HTMLTextAreaElement
) {
return copyText(content.value)
} else if (
content instanceof HTMLAnchorElement &&
content.hasAttribute('href')
) {
return copyText(content.href)
} else {
return copyNode(content)
}
}
async function copy (button) {
const id = button.getAttribute('for')
const text = button.getAttribute('value')
function trigger () {
button.dispatchEvent(new CustomEvent('clipboard-copy', { bubbles: true }))
navigator.clipboard.writeText(this.dataset.text || this.innerText.trim())
})
}
function toggleActiveIcon () {
if (button.classList.contains('swap')) {
button.classList.toggle('swap-active')
}
disconnectedCallback () {
this.querySelectorAll('input').forEach((e) => {
e.checked = false
})
}
if (text) {
await copyText(text)
trigger()
toggleActiveIcon()
} else if (id) {
const root = 'getRootNode' in Element.prototype ? button.getRootNode() : button.ownerDocument
if (!(root instanceof Document || ('ShadowRoot' in window && root instanceof ShadowRoot))) return
const node = root.getElementById(id)
if (node) {
await copyTarget(node)
trigger()
toggleActiveIcon()
}
}
}
function clicked (event) {
const button = event.currentTarget
if (button instanceof HTMLElement) {
copy(button)
}
}
function keydown (event) {
if (event.key === ' ' || event.key === 'Enter') {
const button = event.currentTarget
if (button instanceof HTMLElement) {
event.preventDefault()
copy(button)
}
}
}
function focused (event) {
event.currentTarget.addEventListener('keydown', keydown)
}
function blurred (event) {
event.currentTarget.removeEventListener('keydown', keydown)
}

@ -4,22 +4,26 @@
#
# Table name: submissions
#
# id :bigint not null, primary key
# deleted_at :datetime
# created_at :datetime not null
# updated_at :datetime not null
# template_id :bigint not null
# id :bigint not null, primary key
# deleted_at :datetime
# created_at :datetime not null
# updated_at :datetime not null
# created_by_user_id :bigint
# template_id :bigint not null
#
# Indexes
#
# index_submissions_on_template_id (template_id)
# index_submissions_on_created_by_user_id (created_by_user_id)
# index_submissions_on_template_id (template_id)
#
# Foreign Keys
#
# fk_rails_... (created_by_user_id => users.id)
# fk_rails_... (template_id => templates.id)
#
class Submission < ApplicationRecord
belongs_to :template
belongs_to :created_by_user, class_name: 'User', optional: true
has_many :submitters, dependent: :destroy

@ -1,8 +1,8 @@
<% if @templates.any? %>
<div class="flex justify-between mb-4">
<div class="flex justify-between mb-4 items-center">
<h1 class="text-4xl font-bold">Templates</h1>
<%= link_to new_template_path, class: 'btn btn-primary btn-md gap-2', data: { turbo_frame: :modal } do %>
<%= svg_icon('plus', class: 'w-6 h-6') %>
<%= link_to new_template_path, class: 'btn btn-primary text-base btn-md gap-2', data: { turbo_frame: :modal } do %>
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
<span class="hidden md:block">Create</span>
<% end %>
</div>
@ -25,13 +25,13 @@
</div>
</a>
<div class="absolute top-0 bottom-0 w-0 pt-7 space-y-1.5 hidden group-hover:block" style="right: 40px">
<a href="<%= edit_template_path(template) %>" class="btn btn-xs btn-outline bg-base-200 btn-circle">
<a href="<%= edit_template_path(template) %>" class="btn btn-xs hover:btn-outline bg-base-200 btn-circle">
<%= svg_icon('pencil', class: 'w-4 h-4') %>
</a>
<a href="<%= new_template_path(base_template_id: template.id) %>" data-turbo-frame="modal" class="btn btn-xs btn-outline bg-base-200 btn-circle">
<a href="<%= new_template_path(base_template_id: template.id) %>" data-turbo-frame="modal" class="btn btn-xs hover:btn-outline bg-base-200 btn-circle">
<%= svg_icon('copy', class: 'w-4 h-4') %>
</a>
<%= button_to template_path(template), data: { turbo_confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-xs btn-outline bg-base-200 btn-circle' do %>
<%= button_to template_path(template), data: { turbo_confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-xs hover:btn-outline bg-base-200 btn-circle' do %>
<%= svg_icon('trash', class: 'w-4 h-4 enabled') %>
<%= svg_icon('loader', class: 'w-4 h-4 animate-spin disabled') %>
<% end %>

@ -3,10 +3,10 @@
<div class="alert">
<%= svg_icon('x_circle', class: 'stroke-current shrink-0 h-6 w-6') %>
<div>
<h3 class="font-bold">
<p class="font-bold">
<%= I18n.t('errors.messages.not_saved',
count: resource.errors.count,
resource: resource.class.model_name.human.downcase) %></h3>
resource: resource.class.model_name.human.downcase) %></p>
<% resource.errors.full_messages.each do |message| %>
<p class="text-sm"><%= message %></p>
<% end %>

@ -15,7 +15,7 @@
</div>
<% end %>
<file-dropzone data-name="verify_attachments" data-submit-on-upload="true" class="w-full">
<label for="file" class="w-full block h-32 relative bg-base-300 hover:bg-base-200 rounded-md border border-base-content border-dashed">
<label for="file" class="w-full block h-32 relative bg-base-200 hover:bg-base-200/70 rounded-md border border-base-content border-dashed">
<div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center">
<div class="flex flex-col items-center">
<span data-target="file-dropzone.icon">

@ -0,0 +1,6 @@
<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>
<path d="M9 15l6 -6"></path>
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464"></path>
<path d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463"></path>
</svg>

After

Width:  |  Height:  |  Size: 496 B

@ -0,0 +1,6 @@
<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>
<path d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0"></path>
<path d="M6 21v-2a4 4 0 0 1 4 -4h4c.348 0 .686 .045 1.008 .128"></path>
<text x="90%" y="95%" stroke-width="1" text-anchor="end" font-size="9" fill="black"><%= number %></text>
</svg>

After

Width:  |  Height:  |  Size: 523 B

@ -18,7 +18,7 @@
<div class="card-body">
<div class="text-center transition-all">
<div class="inline-block p-4 mb-6 -mt-16 bg-base-content rounded-full">
<%= svg_icon('brand_docker', class: 'w-10 h-10 text-base-100') %>
<%= svg_icon('brand_docker', class: 'w-10 h-10 text-base-100 stroke-1') %>
</div>
<h3 class="mb-4 text-2xl font-semibold">Easy to Install</h3>
<p class="text-base text-gray-500">

@ -30,7 +30,7 @@
<%= f.button button_title(title: 'Update', disabled_with: 'Updating'), class: 'base-button' %>
</div>
<% end %>
<h2 class="text-2xl font-bold mt-8 mb-4">Change Password</h2>
<p class="text-2xl font-bold mt-8 mb-4">Change Password</p>
<%= form_for current_user, url: update_password_settings_profile_index_path, method: :patch, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<div class="form-control">
<%= f.label :password, 'New password', class: 'label' %>
@ -44,7 +44,7 @@
<%= f.button button_title(title: 'Update', disabled_with: 'Updating'), class: 'base-button' %>
</div>
<% end %>
<h2 class="text-2xl font-bold mt-8 mb-4">App URL</h2>
<p class="text-2xl font-bold mt-8 mb-4">App URL</p>
<%= form_for @encrypted_config, url: update_app_url_settings_profile_index_path, method: :patch, html: { autocomplete: 'off', class: 'space-y-4' } do |f| %>
<div class="form-control">
<%= f.text_field :value, required: true, class: 'base-input' %>

@ -0,0 +1,17 @@
<clipboard-copy data-text="<%= text %>">
<label class="<%= local_assigns[:class] %>">
<input type="radio" class="peer hidden">
<span class="peer-checked:hidden flex items-center space-x-2">
<%= svg_icon('link', class: local_assigns[:icon_class] || 'w-6 h-6 text-white') %>
<span>
<%= local_assigns[:copy_title] || 'Copy' %>
</span>
</span>
<span class="hidden peer-checked:flex items-center space-x-2">
<%= svg_icon('clipboard_copy', class: local_assigns[:icon_class] || 'w-6 h-6 text-white') %>
<span>
<%= local_assigns[:copied_title] || 'Copied' %>
</span>
</span>
</label>
</clipboard-copy>

@ -7,7 +7,7 @@
<div class="space-x-6">
<%= link_to 'Settings', settings_profile_index_path, class: 'font-medium text-lg' %>
<div class="dropdown dropdown-end z-50">
<label tabindex="0" class="cursor-pointer bg-base-content text-purple-300 rounded-full w-8 p-2">
<label tabindex="0" class="cursor-pointer bg-base-content text-purple-300 rounded-full p-2 w-9 justify-center flex">
<span class="text-sm align-text-top"><%= current_user.initials %></span>
</label>
<ul tabindex="0" class="dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box whitespace-nowrap">

@ -1,7 +1,7 @@
<div class="card bg-base-200">
<div class="card-body text-center">
<div class="max-w-md mx-auto">
<h1 class="text-4xl font-semibold text-gray-700 mb-4">Nothing to display</h1>
<p class="text-4xl font-semibold text-gray-700 mb-4">Nothing to display</p>
<p class="text-gray-500">We apologize, but currently there is no data available to be displayed. You can try adding new entries or refreshing the page to see if any data becomes available.</p>
</div>
</div>

@ -3,15 +3,13 @@
<div class="card-body">
<div class="space-y-4 w-full md:px-10 mx-auto">
<div class="space-y-4">
<div class="flex items-center justify-center">
<div class="flex items-center">
<div class="mr-3">
<%= render 'shared/logo', width: '50px', height: '50px' %>
</div>
<h1 class="text-5xl font-bold text-center">DocuSeal</h1>
</div>
</div>
<h2 class="text-xl font-semibold text-center">You have been invited to submit the form</h2>
<a href="/" class="flex justify-center">
<span class="mr-3">
<%= render 'shared/logo', width: '50px', height: '50px' %>
</span>
<h1 class="text-5xl font-bold text-center">DocuSeal</h1>
</a>
<p class="text-xl font-semibold text-center">You have been invited to submit the form</p>
<div class="flex items-center bg-neutral rounded-xl p-4">
<div class="flex items-center">
<div class="mr-3">
@ -23,7 +21,7 @@
</svg>
</div>
<div>
<h3 class="text-gray-100 font-bold mb-1"><%= @template.name %></h3>
<p class="text-gray-100 font-bold mb-1"><%= @template.name %></p>
<p class="text-sm text-gray-300">Invited by <%= @template.account.name %></p>
</div>
</div>

@ -4,7 +4,7 @@
<div class="alert my-4">
<%= svg_icon('info_circle', class: 'stroke-current flex-shrink-0 w-6 h-6') %>
<div>
<h3 class="font-bold">Store all files on disk</h3>
<p class="font-bold">Store all files on disk</p>
<p class="text-gray-700">
No configs are needed but make sure your disk is persistent
<br>

@ -30,7 +30,7 @@
</div>
</div>
<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') %>
<%= svg_icon('user_plus', class: 'w-4 h-4 stroke-2') %>
<span>Add Recipient</span>
</a>
</dynamic-list>
@ -45,7 +45,7 @@
<div class="alert my-4">
<%= svg_icon('info_circle', class: 'w-6 h-6') %>
<div>
<h3 class="font-bold">SMTP not Configured</h3>
<p class="font-bold">SMTP not Configured</p>
<p class="text-gray-700">
Configure SMTP settings in order to send emails:
<br>

@ -60,7 +60,7 @@
<% submitter = @submission.submitters.find { |e| e.uuid == item['uuid'] } %>
<div class="sticky -top-1 bg-base-100 pt-1 -mt-1 z-10">
<div class="border border-base-300 rounded-md px-2 py-1 mb-1">
<div class="flex items-center space-x-1 ">
<div class="flex items-center space-x-1">
<span class="mx-1 w-3 h-3 rounded-full <%= colors[index] %>"></span>
<span class="text-lg">
<%= @submission.template.submitters.find { |e| e['uuid'] == submitter&.uuid }&.dig('name') || "#{(index + 1).ordinalize} Submitter" %>

@ -20,7 +20,7 @@
</svg>
</div>
<div>
<h3 class="text-lg text-gray-700 font-bold mb-1"><%= @submitter.submission.template.name %></h3>
<p class="text-lg text-gray-700 font-bold mb-1"><%= @submitter.submission.template.name %></p>
<p class="text-sm text-gray-500">Signed on <%= l(@submitter.completed_at.to_date, format: :long) %></p>
</div>
</div>

@ -1,103 +1,123 @@
<div class="card card-compact bg-primary mb-12 md:card-normal">
<div class="card-body">
<div class="grid md:grid-cols-2 gap-4 md:flex md:justify-between">
<h2 class="card-title text-4xl ">
<%= @template.name %>
</h2>
<div class="flex md:justify-between space-x-2">
<%= link_to new_template_path(base_template_id: @template.id), class: 'btn btn-outline btn-sm', data: { turbo_frame: :modal } do %>
<%= svg_icon('copy', class: 'w-6 h-6') %>
<span>Clone</span>
<% end %>
<%= link_to edit_template_path(@template), class: 'btn btn-outline btn-sm' do %>
<span class="flex items-center justify-center space-x-2">
<%= svg_icon('pencil', class: 'w-6 h-6') %>
<span>Edit</span>
</span>
<% end %>
</div>
</div>
<% if @template.submitters.size == 1 %>
<div class="join w-full">
<buttun class="btn bg-neutral btn-disabled text-white join-item">
Share link
</buttun>
<input id="share-link-input" autocomplete="off" type="text" class="input input-bordered w-full join-item" value="<%= start_form_url(slug: @template.slug) %>" disabled>
<clipboard-copy class="btn btn-neutral btn-square join-item text-white font-bold swap swap-active" for="share-link-input">
<%= svg_icon('clipboard', class: 'w-6 h-6 swap-on text-white') %>
<%= svg_icon('clipboard_copy', class: 'w-6 h-6 swap-off text-white') %>
</clipboard-copy>
</div>
<div class="flex md:justify-between items-start mb-6">
<h1 class="text-4xl font-semibold mr-4" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;">
<%= @template.name %>
</h1>
<div class="flex md:justify-between space-x-2 flex-none">
<%= button_to button_title(title: 'Remove', icon: svg_icon('trash', class: 'w-6 h-6')), template_path(@template), class: 'btn btn-outline', method: :delete, data: { turbo_confirm: 'Are you sure?' } %>
<%= link_to new_template_path(base_template_id: @template.id), class: 'btn btn-outline', data: { turbo_frame: :modal } do %>
<%= svg_icon('copy', class: 'w-6 h-6') %>
<span>Clone</span>
<% end %>
<%= link_to edit_template_path(@template), class: 'btn btn-outline' do %>
<span class="flex items-center justify-center space-x-2">
<%= svg_icon('pencil', class: 'w-6 h-6') %>
<span>Edit</span>
</span>
<% end %>
</div>
</div>
<div class="flex justify-between mb-4">
<h1 class="text-3xl font-bold">Recipients</h1>
<%= link_to new_template_submission_path(@template), class: 'btn btn-primary btn-sm gap-2', data: { turbo_frame: 'modal' } do %>
<%= svg_icon('plus', class: 'w-6 h-6') %>
<span class="hidden md:block">Add Recipients</span>
<% end %>
<div class="flex justify-between mb-4 items-end">
<p class="text-3xl font-bold">Submissions</p>
<div class="flex space-x-2">
<% if @template.submitters.to_a.size == 1 %>
<%= render 'shared/clipboard_copy', text: start_form_url(slug: @template.slug), class: 'base-button', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy Share Link', copied_title: 'Copied to Clipboard' %>
<% end %>
<%= link_to new_template_submission_path(@template), class: 'btn btn-primary text-base', data: { turbo_frame: 'modal' } do %>
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
<span class="hidden md:block">Add Recipients</span>
<% end %>
</div>
</div>
<div class="overflow-x-auto">
<% if @submissions.any? %>
<table class="table w-full table-lg rounded-t-2xl overflow-hidden">
<thead class="bg-base-200">
<tr class="text-neutral uppercase">
<th>
Email
</th>
<th>
Status
</th>
<th>
Share Link
</th>
<th class="text-right" width="1px">
</th>
</tr>
</thead>
<tbody>
<% @submissions.each do |submission| %>
<tr>
<td>
<% submission.submitters.each do |submitter| %>
<%= submitter.email %>
<br>
<% end %>
</td>
<td>
<% submission.submitters.each do |submitter| %>
<div>
<span class="badge badge-info badge-outline">
<%= submitter.status %>
<% status_badges = { 'awaiting' => 'badge-info', 'sent' => 'badge-info', 'completed' => 'badge-success', 'opened' => 'badge-warning' } %>
<% if @submissions.present? %>
<div class="space-y-4">
<% @submissions.each do |submission| %>
<a href="<%= submission_path(submission) %>" class="bg-base-200 w-full flex justify-between rounded-2xl px-6 py-5 items-center">
<% if submission.template.submitters.size == 1 %>
<div>
<% submitter = submission.submitters.first %>
<div class="flex items-center space-x-4">
<span class="flex items-center space-x-2">
<%= svg_icon('user', class: 'w-6 h-6 stroke-2') %>
<span class="text-lg">
<%= submitter.email %>
</span>
</span>
</div>
</div>
<div class="flex space-x-2 items-center">
<span class="badge <%= status_badges[submitter.status] %> w-32 badge-lg btn-sm uppercase text-sm font-medium border-1">
<%= submitter.status %>
</span>
<% if submitter.completed_at? %>
<form onsubmit="event.preventDefault()">
<button onclick="event.stopPropagation()">
<download-button data-src="<%= submitter_download_index_path(submitter.slug) %>" class="btn btn-sm btn-neutral text-white w-36">
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
<%= svg_icon('download', class: 'w-5 h-5 stroke-2') %>
<span>Download</span>
</span>
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
<%= svg_icon('loader', class: 'w-5 h-5 animate-spin') %>
<span>Downloa...</span>
</span>
</download-button>
</button>
</form>
<% else %>
<%= render 'shared/clipboard_copy', text: submit_form_url(slug: submission.submitters.first.slug), class: 'btn btn-sm btn-neutral text-white w-36', icon_class: 'w-6 h-6 text-white', copy_title: 'Copy Link' %>
<% end %>
<%= button_to submitter.completed_at? ? 'Archive' : 'Remove', submission_path(submission), class: 'btn btn-outline btn-sm w-28', title: 'Delete', method: :delete, data: { turbo_confirm: 'Are you sure?' }, onclick: 'event.stopPropagation()' %>
</div>
<% else %>
<div class="space-y-1 w-full mr-4">
<% submission.template.submitters.each_with_index do |item, index| %>
<% submitter = submission.submitters.find { |e| e.uuid == item['uuid'] } %>
<div class="flex justify-between items-center">
<span class="flex items-center space-x-2 text-lg">
<%= render 'icons/user_number', class: 'w-6 h-6 stroke-2', number: index + 1 %>
<span>
<%= submitter.email %>
</span>
</div>
<% end %>
</td>
<td>
<% submission.submitters.each do |submitter| %>
<% share_link_input_id = "share-link-input_#{submitter.id}" %>
<div class="join ">
<input id="<%= share_link_input_id %>" autocomplete="off" type="text" class="input input-xs input-bordered join-item" value="<%= submit_form_url(slug: submitter.slug) %>" disabled>
<clipboard-copy class="btn btn-xs btn-neutral btn-square join-item text-white font-bold swap swap-active" for="<%= share_link_input_id %>">
<%= svg_icon('clipboard', class: 'w-3 h-3 swap-on text-white') %>
<%= svg_icon('clipboard_copy', class: 'w-3 h-3 swap-off text-white') %>
</clipboard-copy>
</div>
<br>
<% end %>
</td>
<td class="flex items-center space-x-2 justify-end">
<%= link_to 'View', submission_path(submission), title: 'View', class: 'btn btn-outline btn-xs' %>
<%= button_to 'Remove', submission_path(submission), class: 'btn btn-outline btn-error btn-xs', title: 'Delete', method: :delete, data: { turbo_confirm: 'Are you sure?' } %>
</td>
</tr>
</span>
<% unless submission.submitters.all?(&:completed_at?) %>
<div class="flex items-center space-x-3">
<span class="badge w-24 <%= status_badges[submitter.status] %> btn-xs uppercase text-xs font-medium border-1">
<%= submitter.status %>
</span>
<%= render 'shared/clipboard_copy', text: submit_form_url(slug: submitter.slug), class: 'btn btn-xs text-xs btn-neutral text-white w-32', icon_class: 'w-4 h-4 text-white', copy_title: 'Copy Link' %>
</div>
<% end %>
</div>
<% end %>
</div>
<div class="flex space-x-2 items-center">
<% if submission.submitters.all?(&:completed_at?) %>
<span class="badge <%= status_badges[submitter.status] %> w-32 badge-lg btn-sm uppercase text-sm font-medium border-1">
<%= submitter.status %>
</span>
<form onsubmit="event.preventDefault()">
<button onclick="event.stopPropagation()">
<download-button data-src="<%= submitter_download_index_path(submission.submitters.select(&:completed_at?).max_by(&:completed_at).slug) %>" class="btn btn-sm btn-neutral text-white w-36">
<span class="flex items-center justify-center space-x-2" data-target="download-button.defaultButton">
<%= svg_icon('download', class: 'w-5 h-5 stroke-2') %>
<span>Download</span>
</span>
<span class="flex items-center justify-center space-x-2 hidden" data-target="download-button.loadingButton">
<%= svg_icon('loader', class: 'w-5 h-5 animate-spin') %>
<span>Downloa...</span>
</span>
</download-button>
</button>
</form>
<% end %>
<%= button_to submitter.completed_at? ? 'Archive' : 'Remove', submission_path(submission), class: 'btn btn-outline btn-sm w-28', title: 'Delete', method: :delete, data: { turbo_confirm: 'Are you sure?' }, onclick: 'event.stopPropagation()' %>
</div>
<% end %>
</tbody>
</table>
</a>
<% end %>
</div>
<%= render 'shared/pagination', pagy: @pagy, items_name: 'Submissions' %>
<% else %>
<%= render 'shared/no_data_banner' %>
<% end %>
</div>

@ -38,7 +38,7 @@
<%= user.email %>
</td>
<td>
<span class="badge badge-success badge-outline">
<span class="badge badge-info badge-outline">
<%= user.role %>
</span>
</td>

@ -4,6 +4,7 @@ class CreateSubmissions < ActiveRecord::Migration[7.0]
def change
create_table :submissions do |t|
t.references :template, null: false, foreign_key: true, index: true
t.references :created_by_user, null: true, foreign_key: { to_table: :users }, index: true
t.datetime :deleted_at

@ -62,9 +62,11 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_12_182744) do
create_table "submissions", force: :cascade do |t|
t.bigint "template_id", null: false
t.bigint "created_by_user_id"
t.datetime "deleted_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["created_by_user_id"], name: "index_submissions_on_created_by_user_id"
t.index ["template_id"], name: "index_submissions_on_template_id"
end
@ -133,6 +135,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_12_182744) do
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "encrypted_configs", "accounts"
add_foreign_key "submissions", "templates"
add_foreign_key "submissions", "users", column: "created_by_user_id"
add_foreign_key "submitters", "submissions"
add_foreign_key "templates", "accounts"
add_foreign_key "templates", "users", column: "author_id"

@ -47,15 +47,7 @@ module Submissions
when 'image', 'signature'
attachment = submitter.attachments.find { |a| a.uuid == value }
image_data =
if SUPPORTED_IMAGE_TYPES.include?(attachment.content_type)
attachment.download
else
Vips::Image.new_from_buffer(attachment.download, '')
.write_to_buffer('.png')
end
io = StringIO.new(image_data)
io = StringIO.new(download_supported_image_data(attachment))
scale = [(area['w'] * width) / attachment.metadata['width'],
(area['h'] * height) / attachment.metadata['height']].min
@ -249,7 +241,7 @@ module Submissions
page.box.height = attachment.metadata['height'] * scale
page.canvas.image(
StringIO.new(attachment.download),
StringIO.new(download_supported_image_data(attachment)),
at: [0, 0],
width: page.box.width,
height: page.box.height
@ -257,5 +249,14 @@ module Submissions
pdf
end
def download_supported_image_data(attachment)
if SUPPORTED_IMAGE_TYPES.include?(attachment.content_type)
attachment.download
else
Vips::Image.new_from_buffer(attachment.download, '')
.write_to_buffer('.png')
end
end
end
end

@ -7,6 +7,7 @@ module.exports = {
'./app/views/submit_form/**/*.erb',
'./app/views/start_form/**/*.erb',
'./app/views/shared/_button_title.html.erb',
'./app/views/shared/_attribution.html.erb',
'./app/views/send_submission_copy/**/*.erb'
]
}

Loading…
Cancel
Save