update submission templates

pull/105/head
Alex Turchyn 2 years ago
parent b0b4386927
commit 5d6b5ab6d7

@ -7,7 +7,7 @@ import DisableHidden from './elements/disable_hidden'
import TurboModal from './elements/turbo_modal'
import FileDropzone from './elements/file_dropzone'
import MenuActive from './elements/menu_active'
import ClipboardCopy from './elements/clipboard_copy'
import TemplateBuilder from './template_builder/builder'
document.addEventListener('turbo:before-cache', () => {
@ -19,6 +19,7 @@ window.customElements.define('disable-hidden', DisableHidden)
window.customElements.define('turbo-modal', TurboModal)
window.customElements.define('file-dropzone', FileDropzone)
window.customElements.define('menu-active', MenuActive)
window.customElements.define('clipboard-copy', ClipboardCopy)
window.customElements.define('template-builder', class extends HTMLElement {
connectedCallback () {
@ -38,3 +39,7 @@ window.customElements.define('template-builder', class extends HTMLElement {
this.appElem?.remove()
}
})
document.addEventListener('clipboard-copy', function(event) {
console.log('Copied to clipboard') // TODO: Add a toast message
})

@ -39,6 +39,10 @@ button[disabled] .enabled {
@apply input input-bordered bg-white;
}
.base-textarea {
@apply textarea textarea-bordered bg-white;
}
.base-button {
@apply btn text-white text-base;
}

@ -0,0 +1,144 @@
// 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())
}
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 }))
}
if (text) {
await copyText(text)
trigger()
} 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()
}
}
}
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)
}

@ -16,6 +16,7 @@
ref="input"
type="file"
class="hidden"
accept=".pdf"
multiple
@change="upload"
>

@ -42,4 +42,16 @@ class Submission < ApplicationRecord
has_many_attached :attachments
scope :active, -> { where(deleted_at: nil) }
def status
if completed_at?
'completed'
elsif opened_at?
'opened'
elsif sent_at?
'sent'
else
'awaiting'
end
end
end

@ -0,0 +1,8 @@
<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-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>
</div>

@ -1,7 +1,7 @@
<%= f.fields_for :value do |ff| %>
<%= ff.hidden_field :service, value: 'google' %>
<%= ff.fields_for :configs, configs do |fff| %>
<div class="grid md:grid-cols-2 gap-4">
<div class="grid md:grid-cols-2 gap-4">
<div class="form-control">
<%= fff.label :project, 'Project', class: 'label' %>
<%= fff.text_field :project, value: configs['project'], required: true, class: 'base-input' %>
@ -13,7 +13,7 @@
</div>
<div class="form-control">
<%= fff.label :credentials, 'Credentials (JSON key content)', class: 'label' %>
<%= fff.text_area :credentials, value: configs['credentials'], required: true, class: 'base-input' %>
<%= fff.text_area :credentials, value: configs['credentials'], required: true, class: 'base-textarea' %>
</div>
<% end %>
<% end %>

@ -1,40 +1,78 @@
Submissions
template <%= @template.name %>
Copy share link:
<input autocomplete="off" type="text" class="w-full" value="<%= start_form_url(slug: @template.slug) %>" disabled>
<a href="<%= new_template_submission_path(@template) %>" class="bg-green-600" data-turbo-frame="modal">Add Recepients</a>
<table>
<tr>
<th>
Email
</th>
<th>
Status
</th>
<th>
</th>
</tr>
<% @submissions.each do |submission| %>
<tr>
<td>
<%= submission.email %>
</td>
<td>
<% if submission.completed_at? %>
Completed
<% elsif submission.opened_at? %>
Opened
<% elsif submission.sent_at? %>
Sent
<% else %>
Awaiting
<div class="card card-compact bg-primary mb-12 md:card-normal">
<div class="card-body">
<h2 class="card-title text-4xl flex justify-between">
<%= @template.name %>
<%= link_to 'Edit', template_path(@template), class: 'btn btn-outline btn-sm' %>
</h2>
<div class="form-control">
<label class="label">
<span class="label-text text-opacity-70">Send this link your clients who must sign this document</span>
</label>
<label class="input-group input-group-lg">
<input id="share-link-input" autocomplete="off" type="text" class="input input-bordered w-full" value="<%= start_form_url(slug: @template.slug) %>" disabled>
<clipboard-copy class="btn btn-square text-white bg-neutral font-bold swap hover:bg-opacity-70 active:bg-opacity-90" for="share-link-input">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 swap-off" 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="M9 5h-2a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-12a2 2 0 0 0 -2 -2h-2" />
<path d="M9 3m0 2a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v0a2 2 0 0 1 -2 2h-2a2 2 0 0 1 -2 -2z" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 swap-on" 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="M9 5h-2a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-12a2 2 0 0 0 -2 -2h-2" />
<path d="M9 3m0 2a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v0a2 2 0 0 1 -2 2h-2a2 2 0 0 1 -2 -2z" />
<path d="M9 14l2 2l4 -4" />
</svg>
</clipboard-copy>
</label>
</div>
</div>
</div>
<div class="flex justify-between mb-4">
<h1 class="text-3xl font-bold">Recepients</h1>
<%= link_to new_template_submission_path(@template), class: 'btn btn-primary btn-sm gap-2', data: { turbo_frame: 'modal' } do %>
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<line x1="12" y1="5" x2="12" y2="19" />
<line x1="5" y1="12" x2="19" y2="12" />
</svg>
<span class="hidden md:block">Add Recepients</span>
<% end %>
</div>
<div class="overflow-x-auto">
<%- if @submissions.any? %>
<table class="table w-full">
<thead>
<tr>
<th>
Email
</th>
<th>
Status
</th>
<th class="text-right" width="1px">
</th>
</tr>
</thead>
<tbody>
<% @submissions.each do |submission| %>
<tr>
<td>
<%= submission.email %>
</td>
<td>
<span class="badge badge-accent">
<%= submission.status.humanize %>
</span>
</td>
<td class="flex items-center space-x-2 justify-end">
<%= link_to 'View', submission_path(@template), 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>
<% end %>
</td>
<td>
copy link<br>
<%= link_to 'View', submission_path(@template) %>
<%= button_to 'Remove', submission_path(submission), method: :delete, data: { turbo_confirm: 'Are you sure?' } %>
</td>
</tr>
</tbody>
</table>
<% else %>
<%= render 'shared/no_data_banner' %>
<% end %>
</table>
</div>

@ -1,19 +1,34 @@
<%= render 'shared/turbo_modal' do %>
<%= form_for '', url: template_submissions_path(@template), data: { turbo_frame: :_top } do |f| %>
<div>
<%= f.label :emails %>
<%= f.text_area :emails, required: true %>
<%= render 'shared/turbo_modal', title: 'New Recepients' do %>
<%= form_for '', url: template_submissions_path(@template), html: { class: 'space-y-4' }, data: { turbo_frame: :_top } do |f| %>
<div class="form-control">
<%= f.label :emails, class: 'label' %>
<%= f.text_area :emails, required: true, class: 'base-textarea' %>
</div>
<div>
<%= f.check_box :send_email, { onchange: "message_field.classList.toggle('hidden', !event.currentTarget.checked)" } %>
<%= f.label :send_email %>
<div class="form-control">
<%= f.label :send_email, class: 'flex items-center cursor-pointer' do %>
<%= f.check_box :send_email, class: 'base-checkbox', onchange: "message_field.classList.toggle('hidden', !event.currentTarget.checked)" %>
<span class="label">Send Email</span>
<% end %>
</div>
<div id="message_field" class="hidden">
Hi There,
<%= f.text_area :message, value: SubmissionMailer::DEFAULT_MESSAGE, required: true %>
Thanks,
<%= current_account.name %>
<div id="message_field" class="card card-compact bg-base-200 hidden">
<div class="card-body">
<div class="form-control">
<label class="label">
<span class="label-text">Hi There,</span>
</label>
<%= f.text_area :message, value: SubmissionMailer::DEFAULT_MESSAGE, required: true, class: 'base-textarea !rounded-lg' %>
<label class="label">
<span class="label-text">
Thanks,
</br>
<%= current_account.name %>
</span>
</label>
</div>
</div>
<%= f.button button_title %>
<% end %>
</div>
<div class="form-control">
<%= f.button button_title, class: 'base-button' %>
</div>
<% end %>
<% end %>

@ -1,21 +1,81 @@
template: <%= @submission.template.name %>
Sub: <%= @submission.slug %>
Copy share link:
<input autocomplete="off" type="text" class="w-full" value="<%= submit_form_url(slug: @submission.slug) %>" disabled>
<% @submission.template.fields.each do |field| %>
<div>
<%= field['name'] %>:
<% if ['image', 'signature'].include?(field['type']) %>
<% Array.wrap(@submission.values[field['uuid']]).each do |uuid| %>
<img src="<%= ActiveStorage::Attachment.find_by(uuid:).url %>">
<% end %>
<% elsif ['attachment'].include?(field['type']) %>
<% Array.wrap(@submission.values[field['uuid']]).each do |uuid| %>
<% attachment = ActiveStorage::Attachment.find_by(uuid:) %>
<a href="<%= attachment.url %>"><%= attachment.filename %></a>
<% end %>
<% else %>
<%= @submission.values[field['uuid']] %>
<% end %>
<div class="card card-compact bg-base-200 md:card-normal">
<div class="card-body">
<div>
<h3 class="text-4xl font-bold leading-7 text-gray-900">Submission Information</h3>
<p class="mt-2 max-w-2xl text-sm leading-6 text-gray-500">Personal details</p>
</div>
<div class="mt-2 border-t border-gray-300">
<dl class="divide-y divide-gray-300">
<div class="py-6 sm:grid sm:grid-cols-3 sm:gap-4">
<dt class="text-sm font-medium leading-6 text-gray-900">Template</dt>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
<%= link_to @submission.template.name, template_submissions_path(@submission.template), class: 'link link-hover' %>
</dd>
</div>
<div class="py-6 sm:grid sm:grid-cols-3 sm:gap-4">
<dt class="text-sm font-medium leading-6 text-gray-900">URL</dt>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
<%= link_to submit_form_url(slug: @submission.slug), submit_form_url(slug: @submission.slug), class: 'link link-hover' %>
</dd>
</div>
<div class="py-6 sm:grid sm:grid-cols-3 sm:gap-4">
<dt class="text-sm font-medium leading-6 text-gray-900">Email address</dt>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
<%= mail_to @submission.email %>
</dd>
</div>
<%- if @submission.completed_at.present? %>
<div class="py-6 sm:grid sm:grid-cols-3 sm:gap-4">
<dt class="text-sm font-medium leading-6 text-gray-900">Completed at</dt>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
<%= l(@submission.completed_at, format: :long) %>
</dd>
</div>
<% end %>
<% @submission.template.fields.each do |field| %>
<div class="py-6 sm:grid sm:grid-cols-3 sm:gap-4">
<dt class="text-sm font-medium leading-6 text-gray-900">
<%= field['name'] %>
</dt>
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
<% if ['image', 'signature'].include?(field['type']) %>
<ul role="list" class="divide-y divide-gray-100 rounded-md border border-gray-200">
<% Array.wrap(@submission.values[field['uuid']]).each do |uuid| %>
<li class="py-4 pl-4 pr-5 text-sm leading-6">
<div class="flex w-0 flex-1 items-center">
<%= image_tag ActiveStorage::Attachment.find_by(uuid:).url %>
</div>
</li>
<% end %>
</ul>
<% elsif ['attachment'].include?(field['type']) %>
<ul role="list" class="divide-y divide-gray-100 rounded-md border border-gray-200">
<% Array.wrap(@submission.values[field['uuid']]).each do |uuid| %>
<% attachment = ActiveStorage::Attachment.find_by(uuid:) %>
<li class="flex items-center justify-between py-4 pl-4 pr-5 text-sm leading-6">
<div class="flex w-0 flex-1 items-center">
<svg class="h-5 w-5 flex-shrink-0 text-gray-300" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M15.621 4.379a3 3 0 00-4.242 0l-7 7a3 3 0 004.241 4.243h.001l.497-.5a.75.75 0 011.064 1.057l-.498.501-.002.002a4.5 4.5 0 01-6.364-6.364l7-7a4.5 4.5 0 016.368 6.36l-3.455 3.553A2.625 2.625 0 119.52 9.52l3.45-3.451a.75.75 0 111.061 1.06l-3.45 3.451a1.125 1.125 0 001.587 1.595l3.454-3.553a3 3 0 000-4.242z" clip-rule="evenodd" />
</svg>
<div class="ml-4 flex min-w-0 flex-1 gap-2">
<span class="truncate font-medium">
<%= attachment.filename %>
</span>
</div>
</div>
<div class="ml-4 flex-shrink-0">
<%= link_to 'Download', attachment.url, class: "font-medium text-indigo-600 hover:text-indigo-500" %>
</div>
</li>
<% end %>
</ul>
<% else %>
<%= @submission.values[field['uuid']] %>
<% end %>
</dt>
</div>
<% end %>
</dl>
</div>
</div>
<% end %>
</div>

Loading…
Cancel
Save