mirror of
https://github.com/docusealco/docuseal.git
synced 2026-08-07 07:14:43 +00:00
rename to templates
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
class FlowsController < ApiBaseController
|
||||
def update
|
||||
@flow = current_account.flows.find(params[:id])
|
||||
|
||||
@flow.update!(flow_params)
|
||||
|
||||
render :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def flow_params
|
||||
params.require(:flow).permit(:name,
|
||||
schema: [%i[attachment_uuid name]],
|
||||
fields: [[:uuid, :name, :type, :required,
|
||||
{ options: [], areas: [%i[x y w h attachment_uuid page]] }]])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
class TemplatesController < ApiBaseController
|
||||
def update
|
||||
@template = current_account.templates.find(params[:id])
|
||||
|
||||
@template.update!(template_params)
|
||||
|
||||
render :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def template_params
|
||||
params.require(:template).permit(:name,
|
||||
schema: [%i[attachment_uuid name]],
|
||||
fields: [[:uuid, :name, :type, :required,
|
||||
{ options: [], areas: [%i[x y w h attachment_uuid page]] }]])
|
||||
end
|
||||
end
|
||||
end
|
||||
+4
-4
@@ -1,17 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
class FlowsDocumentsController < ApiBaseController
|
||||
class TemplatesDocumentsController < ApiBaseController
|
||||
def create
|
||||
@flow = current_account.flows.find(params[:flow_id])
|
||||
@template = current_account.templates.find(params[:template_id])
|
||||
|
||||
documents =
|
||||
params[:blobs].map do |blob|
|
||||
blob = ActiveStorage::Blob.find_signed(blob[:signed_id])
|
||||
|
||||
document = @flow.documents.create!(blob:)
|
||||
document = @template.documents.create!(blob:)
|
||||
|
||||
Flows::ProcessDocument.call(document)
|
||||
Templates::ProcessDocument.call(document)
|
||||
end
|
||||
|
||||
schema = documents.map do |doc|
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
class DashboardController < ApplicationController
|
||||
def index
|
||||
@flows = current_account.flows.active
|
||||
@templates = current_account.templates.active
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class FlowsController < ApplicationController
|
||||
layout false
|
||||
|
||||
def show
|
||||
@flow = current_account.flows.preload(documents_attachments: { preview_images_attachments: :blob })
|
||||
.find(params[:id])
|
||||
end
|
||||
|
||||
def new
|
||||
@flow = current_account.flows.new
|
||||
end
|
||||
|
||||
def create
|
||||
@flow = current_account.flows.new(flow_params)
|
||||
@flow.author = current_user
|
||||
|
||||
if @flow.save
|
||||
redirect_to flow_path(@flow)
|
||||
else
|
||||
render turbo_stream: turbo_stream.replace(:modal, template: 'flows/new'), status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@flow = current_account.flows.find(params[:id])
|
||||
@flow.update!(deleted_at: Time.current)
|
||||
|
||||
redirect_to settings_users_path, notice: 'Flow has been archived.'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def flow_params
|
||||
params.require(:flow).permit(:name, :schema)
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SendSubmissionEmailController < ApplicationController
|
||||
layout 'flow'
|
||||
layout 'form'
|
||||
|
||||
skip_before_action :authenticate_user!
|
||||
skip_before_action :verify_authenticity_token
|
||||
@@ -10,8 +10,8 @@ class SendSubmissionEmailController < ApplicationController
|
||||
|
||||
def create
|
||||
@submission =
|
||||
if params[:flow_slug]
|
||||
Submission.joins(:flow).find_by!(email: params[:email], flow: { slug: params[:flow_slug] })
|
||||
if params[:template_slug]
|
||||
Submission.joins(:template).find_by!(email: params[:email], template: { slug: params[:template_slug] })
|
||||
else
|
||||
Submission.find_by!(slug: params[:submission_slug])
|
||||
end
|
||||
|
||||
+11
-11
@@ -1,23 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class StartFlowController < ApplicationController
|
||||
layout 'flow'
|
||||
class StartFormController < ApplicationController
|
||||
layout 'form'
|
||||
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
before_action :load_flow
|
||||
before_action :load_template
|
||||
|
||||
def show
|
||||
@submission = @flow.submissions.new
|
||||
@submission = @template.submissions.new
|
||||
end
|
||||
|
||||
def update
|
||||
@submission = @flow.submissions.find_or_initialize_by(
|
||||
@submission = @template.submissions.find_or_initialize_by(
|
||||
deleted_at: nil, **submission_params
|
||||
)
|
||||
|
||||
if @submission.completed_at?
|
||||
redirect_to start_flow_completed_path(@flow.slug, email: submission_params[:email])
|
||||
redirect_to start_form_completed_path(@template.slug, email: submission_params[:email])
|
||||
else
|
||||
@submission.assign_attributes(
|
||||
opened_at: Time.current,
|
||||
@@ -26,7 +26,7 @@ class StartFlowController < ApplicationController
|
||||
)
|
||||
|
||||
if @submission.save
|
||||
redirect_to submit_flow_path(@submission.slug)
|
||||
redirect_to submit_form_path(@submission.slug)
|
||||
else
|
||||
render :show
|
||||
end
|
||||
@@ -34,7 +34,7 @@ class StartFlowController < ApplicationController
|
||||
end
|
||||
|
||||
def completed
|
||||
@submission = @flow.submissions.find_by(email: params[:email])
|
||||
@submission = @template.submissions.find_by(email: params[:email])
|
||||
end
|
||||
|
||||
private
|
||||
@@ -43,9 +43,9 @@ class StartFlowController < ApplicationController
|
||||
params.require(:submission).permit(:email)
|
||||
end
|
||||
|
||||
def load_flow
|
||||
slug = params[:slug] || params[:start_flow_slug]
|
||||
def load_template
|
||||
slug = params[:slug] || params[:start_template_slug]
|
||||
|
||||
@flow = Flow.find_by!(slug:)
|
||||
@template = Template.find_by!(slug:)
|
||||
end
|
||||
end
|
||||
@@ -1,16 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubmissionsController < ApplicationController
|
||||
before_action :load_flow, only: %i[index new create]
|
||||
before_action :load_template, only: %i[index new create]
|
||||
|
||||
def index
|
||||
@submissions = @flow.submissions.active
|
||||
@submissions = @template.submissions.active
|
||||
end
|
||||
|
||||
def show
|
||||
@submission =
|
||||
Submission.joins(:flow).where(flow: { account_id: current_account.id })
|
||||
.preload(flow: { documents_attachments: { preview_images_attachments: :blob } })
|
||||
Submission.joins(:template).where(template: { account_id: current_account.id })
|
||||
.preload(template: { documents_attachments: { preview_images_attachments: :blob } })
|
||||
.find(params[:id])
|
||||
end
|
||||
|
||||
@@ -21,7 +21,7 @@ class SubmissionsController < ApplicationController
|
||||
|
||||
submissions =
|
||||
emails.map do |email|
|
||||
submission = @flow.submissions.create!(email:, sent_at: params[:send_email] == '1' ? Time.current : nil)
|
||||
submission = @template.submissions.create!(email:, sent_at: params[:send_email] == '1' ? Time.current : nil)
|
||||
|
||||
if params[:send_email] == '1'
|
||||
SubmissionMailer.invitation_email(submission, message: params[:message]).deliver_later!
|
||||
@@ -30,21 +30,21 @@ class SubmissionsController < ApplicationController
|
||||
submission
|
||||
end
|
||||
|
||||
redirect_to flow_submissions_path(@flow), notice: "#{submissions.size} recepients added"
|
||||
redirect_to template_submissions_path(@template), notice: "#{submissions.size} recepients added"
|
||||
end
|
||||
|
||||
def destroy
|
||||
submission = Submission.joins(:flow).where(flow: { account_id: current_account.id })
|
||||
submission = Submission.joins(:template).where(template: { account_id: current_account.id })
|
||||
.find(params[:id])
|
||||
|
||||
submission.update!(deleted_at: Time.current)
|
||||
|
||||
redirect_to flow_submissions_path(submission.flow), notice: 'Submission has been archieved'
|
||||
redirect_to template_submissions_path(submission.template), notice: 'Submission has been archieved'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_flow
|
||||
@flow = current_account.flows.find(params[:flow_id])
|
||||
def load_template
|
||||
@template = current_account.templates.find(params[:template_id])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubmissionsDebugController < ApplicationController
|
||||
layout 'flow'
|
||||
layout 'form'
|
||||
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
@submission = Submission.preload({ attachments_attachments: :blob },
|
||||
flow: { documents_attachments: :blob })
|
||||
template: { documents_attachments: :blob })
|
||||
.find_by(slug: params[:submission_slug])
|
||||
|
||||
respond_to do |f|
|
||||
f.html do
|
||||
render 'submit_flow/show'
|
||||
render 'submit_template/show'
|
||||
end
|
||||
f.pdf do
|
||||
Submissions::GenerateResultAttachments.call(@submission)
|
||||
|
||||
+5
-5
@@ -1,15 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubmitFlowController < ApplicationController
|
||||
layout 'flow'
|
||||
class SubmitFormController < ApplicationController
|
||||
layout 'form'
|
||||
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def show
|
||||
@submission = Submission.preload(flow: { documents_attachments: { preview_images_attachments: :blob } })
|
||||
@submission = Submission.preload(template: { documents_attachments: { preview_images_attachments: :blob } })
|
||||
.find_by!(slug: params[:slug])
|
||||
|
||||
return redirect_to submit_flow_completed_path(@submission.slug) if @submission.completed_at?
|
||||
return redirect_to submit_form_completed_path(@submission.slug) if @submission.completed_at?
|
||||
end
|
||||
|
||||
def update
|
||||
@@ -23,7 +23,7 @@ class SubmitFlowController < ApplicationController
|
||||
end
|
||||
|
||||
def completed
|
||||
@submission = Submission.find_by!(slug: params[:submit_flow_slug])
|
||||
@submission = Submission.find_by!(slug: params[:submit_form_slug])
|
||||
end
|
||||
|
||||
private
|
||||
@@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TemplatesController < ApplicationController
|
||||
layout false
|
||||
|
||||
def show
|
||||
@template = current_account.templates.preload(documents_attachments: { preview_images_attachments: :blob })
|
||||
.find(params[:id])
|
||||
end
|
||||
|
||||
def new
|
||||
@template = current_account.templates.new
|
||||
end
|
||||
|
||||
def create
|
||||
@template = current_account.templates.new(template_params)
|
||||
@template.author = current_user
|
||||
|
||||
if @template.save
|
||||
redirect_to template_path(@template)
|
||||
else
|
||||
render turbo_stream: turbo_stream.replace(:modal, template: 'templates/new'), status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@template = current_account.templates.find(params[:id])
|
||||
@template.update!(deleted_at: Time.current)
|
||||
|
||||
redirect_to settings_users_path, notice: 'template has been archived.'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def template_params
|
||||
params.require(:template).permit(:name, :schema)
|
||||
end
|
||||
end
|
||||
@@ -8,7 +8,7 @@ import TurboModal from './elements/turbo_modal'
|
||||
import FileDropzone from './elements/file_dropzone'
|
||||
import MenuActive from './elements/menu_active'
|
||||
|
||||
import FlowBuilder from './flow_builder/builder'
|
||||
import TemplateBuilder from './template_builder/builder'
|
||||
|
||||
document.addEventListener('turbo:before-cache', () => {
|
||||
window.flash?.remove()
|
||||
@@ -20,12 +20,12 @@ window.customElements.define('turbo-modal', TurboModal)
|
||||
window.customElements.define('file-dropzone', FileDropzone)
|
||||
window.customElements.define('menu-active', MenuActive)
|
||||
|
||||
window.customElements.define('flow-builder', class extends HTMLElement {
|
||||
window.customElements.define('template-builder', class extends HTMLElement {
|
||||
connectedCallback () {
|
||||
this.appElem = document.createElement('div')
|
||||
|
||||
this.app = createApp(FlowBuilder, {
|
||||
flow: reactive(JSON.parse(this.dataset.flow))
|
||||
this.app = createApp(TemplateBuilder, {
|
||||
template: reactive(JSON.parse(this.dataset.template))
|
||||
})
|
||||
|
||||
this.app.mount(this.appElem)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { createApp, reactive } from 'vue'
|
||||
|
||||
import Flow from './flow_form/form'
|
||||
import Form from './submission_form/form'
|
||||
|
||||
window.customElements.define('flow-form', class extends HTMLElement {
|
||||
window.customElements.define('submission-form', class extends HTMLElement {
|
||||
connectedCallback () {
|
||||
this.appElem = document.createElement('div')
|
||||
|
||||
this.app = createApp(Flow, {
|
||||
this.app = createApp(Form, {
|
||||
submissionSlug: this.dataset.submissionSlug,
|
||||
authenticityToken: this.dataset.authenticityToken,
|
||||
values: reactive(JSON.parse(this.dataset.values)),
|
||||
@@ -1,4 +1,4 @@
|
||||
@config "../../tailwind.flow.config.js";
|
||||
@config "../../tailwind.form.config.js";
|
||||
|
||||
@import "tailwindcss/base";
|
||||
@import "tailwindcss/components";
|
||||
@@ -164,7 +164,7 @@ import CheckboxStep from './checkbox_step'
|
||||
import FormCompleted from './completed'
|
||||
|
||||
export default {
|
||||
name: 'FlowForm',
|
||||
name: 'SubmissionForm',
|
||||
components: {
|
||||
FieldAreas,
|
||||
ImageStep,
|
||||
+23
-23
@@ -5,13 +5,13 @@
|
||||
>
|
||||
<div class="flex justify-between py-1.5 items-center">
|
||||
<Contenteditable
|
||||
:model-value="flow.name"
|
||||
:model-value="template.name"
|
||||
class="text-3xl focus:text-clip"
|
||||
@update:model-value="updateName"
|
||||
/>
|
||||
<div class="space-x-3 flex items-center">
|
||||
<a
|
||||
:href="`/flows/${flow.id}/submissions`"
|
||||
:href="`/templates/${template.id}/submissions`"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
<IconUsersPlus
|
||||
@@ -40,9 +40,9 @@
|
||||
class="overflow-auto w-52 flex-none pr-4 mt-0.5 pt-0.5"
|
||||
>
|
||||
<DocumentPreview
|
||||
v-for="(item, index) in flow.schema"
|
||||
v-for="(item, index) in template.schema"
|
||||
:key="index"
|
||||
:with-arrows="flow.schema.length > 1"
|
||||
:with-arrows="template.schema.length > 1"
|
||||
:item="item"
|
||||
:document="sortedDocuments[index]"
|
||||
@scroll-to="scrollIntoDocument(item)"
|
||||
@@ -53,7 +53,7 @@
|
||||
/>
|
||||
<div class="sticky bottom-0 bg-base-100 py-2">
|
||||
<Upload
|
||||
:flow-id="flow.id"
|
||||
:template-id="template.id"
|
||||
@success="updateFromUpload"
|
||||
/>
|
||||
</div>
|
||||
@@ -90,7 +90,7 @@
|
||||
FIelds
|
||||
<Fields
|
||||
ref="fields"
|
||||
v-model:fields="flow.fields"
|
||||
v-model:fields="template.fields"
|
||||
@set-draw="drawField = $event"
|
||||
@set-drag="dragFieldType = $event"
|
||||
@drag-end="dragFieldType = null"
|
||||
@@ -110,7 +110,7 @@ import DocumentPreview from './preview'
|
||||
import { IconUsersPlus, IconDeviceFloppy } from '@tabler/icons-vue'
|
||||
|
||||
export default {
|
||||
name: 'FlowBuilder',
|
||||
name: 'TemplateBuilder',
|
||||
components: {
|
||||
Upload,
|
||||
Document,
|
||||
@@ -121,7 +121,7 @@ export default {
|
||||
IconDeviceFloppy
|
||||
},
|
||||
props: {
|
||||
flow: {
|
||||
template: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
@@ -138,7 +138,7 @@ export default {
|
||||
fieldAreasIndex () {
|
||||
const areas = {}
|
||||
|
||||
this.flow.fields.forEach((f) => {
|
||||
this.template.fields.forEach((f) => {
|
||||
(f.areas || []).forEach((a) => {
|
||||
areas[a.attachment_uuid] ||= {}
|
||||
|
||||
@@ -151,8 +151,8 @@ export default {
|
||||
return areas
|
||||
},
|
||||
sortedDocuments () {
|
||||
return this.flow.schema.map((item) => {
|
||||
return this.flow.documents.find(doc => doc.uuid === item.attachment_uuid)
|
||||
return this.template.schema.map((item) => {
|
||||
return this.template.documents.find(doc => doc.uuid === item.attachment_uuid)
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -191,8 +191,8 @@ export default {
|
||||
this.$refs.fields.addField(this.dragFieldType, area)
|
||||
},
|
||||
updateFromUpload ({ schema, documents }) {
|
||||
this.flow.schema.push(...schema)
|
||||
this.flow.documents.push(...documents)
|
||||
this.template.schema.push(...schema)
|
||||
this.template.documents.push(...documents)
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$refs.previews.scrollTop = this.$refs.previews.scrollHeight
|
||||
@@ -203,28 +203,28 @@ export default {
|
||||
this.save()
|
||||
},
|
||||
updateName (value) {
|
||||
this.flow.name = value
|
||||
this.template.name = value
|
||||
|
||||
this.save()
|
||||
},
|
||||
onDocumentRemove (item) {
|
||||
if (window.confirm('Are you sure?')) {
|
||||
this.flow.schema.splice(this.flow.schema.indexOf(item), 1)
|
||||
this.template.schema.splice(this.template.schema.indexOf(item), 1)
|
||||
}
|
||||
|
||||
this.save()
|
||||
},
|
||||
moveDocument (item, direction) {
|
||||
const currentIndex = this.flow.schema.indexOf(item)
|
||||
const currentIndex = this.template.schema.indexOf(item)
|
||||
|
||||
this.flow.schema.splice(currentIndex, 1)
|
||||
this.template.schema.splice(currentIndex, 1)
|
||||
|
||||
if (currentIndex + direction > this.flow.schema.length) {
|
||||
this.flow.schema.unshift(item)
|
||||
if (currentIndex + direction > this.template.schema.length) {
|
||||
this.template.schema.unshift(item)
|
||||
} else if (currentIndex + direction < 0) {
|
||||
this.flow.schema.push(item)
|
||||
this.template.schema.push(item)
|
||||
} else {
|
||||
this.flow.schema.splice(currentIndex + direction, 0, item)
|
||||
this.template.schema.splice(currentIndex + direction, 0, item)
|
||||
}
|
||||
|
||||
this.save()
|
||||
@@ -239,9 +239,9 @@ export default {
|
||||
})
|
||||
},
|
||||
save () {
|
||||
return fetch(`/api/flows/${this.flow.id}`, {
|
||||
return fetch(`/api/templates/${this.template.id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ flow: this.flow }),
|
||||
body: JSON.stringify({ template: this.template }),
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}).then((resp) => {
|
||||
console.log(resp)
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
import Page from './page'
|
||||
|
||||
export default {
|
||||
name: 'FlowDocument',
|
||||
name: 'TemplateDocument',
|
||||
components: {
|
||||
Page
|
||||
},
|
||||
@@ -67,7 +67,7 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FlowField',
|
||||
name: 'TemplateField',
|
||||
props: {
|
||||
field: {
|
||||
type: Object,
|
||||
@@ -49,7 +49,7 @@ import Field from './field'
|
||||
import { v4 } from 'uuid'
|
||||
|
||||
export default {
|
||||
name: 'FlowFields',
|
||||
name: 'TemplateFields',
|
||||
components: {
|
||||
Field
|
||||
},
|
||||
@@ -44,7 +44,7 @@
|
||||
import FieldArea from './area'
|
||||
|
||||
export default {
|
||||
name: 'FlowPage',
|
||||
name: 'TemplatePage',
|
||||
components: {
|
||||
FieldArea
|
||||
},
|
||||
@@ -32,7 +32,7 @@ export default {
|
||||
IconUpload
|
||||
},
|
||||
props: {
|
||||
flowId: {
|
||||
templateId: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
@@ -76,7 +76,7 @@ export default {
|
||||
})
|
||||
)
|
||||
|
||||
fetch(`/api/flows/${this.flowId}/documents`, {
|
||||
fetch(`/api/templates/${this.templateId}/documents`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ blobs }),
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
@@ -12,7 +12,7 @@
|
||||
class Account < ApplicationRecord
|
||||
has_many :users, dependent: :destroy
|
||||
has_many :encrypted_configs, dependent: :destroy
|
||||
has_many :flows, dependent: :destroy
|
||||
has_many :templates, dependent: :destroy
|
||||
has_many :active_users, -> { active }, dependent: :destroy,
|
||||
inverse_of: :account, class_name: 'User'
|
||||
end
|
||||
|
||||
@@ -16,20 +16,20 @@
|
||||
# values :string not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# flow_id :bigint not null
|
||||
# template_id :bigint not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_submissions_on_email (email)
|
||||
# index_submissions_on_flow_id (flow_id)
|
||||
# index_submissions_on_slug (slug) UNIQUE
|
||||
# index_submissions_on_email (email)
|
||||
# index_submissions_on_slug (slug) UNIQUE
|
||||
# index_submissions_on_template_id (template_id)
|
||||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (flow_id => flows.id)
|
||||
# fk_rails_... (template_id => templates.id)
|
||||
#
|
||||
class Submission < ApplicationRecord
|
||||
belongs_to :flow
|
||||
belongs_to :template
|
||||
|
||||
attribute :values, :string, default: -> { {} }
|
||||
attribute :slug, :string, default: -> { SecureRandom.base58(8) }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: flows
|
||||
# Table name: templates
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# deleted_at :datetime
|
||||
@@ -17,16 +17,16 @@
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_flows_on_account_id (account_id)
|
||||
# index_flows_on_author_id (author_id)
|
||||
# index_flows_on_slug (slug) UNIQUE
|
||||
# index_templates_on_account_id (account_id)
|
||||
# index_templates_on_author_id (author_id)
|
||||
# index_templates_on_slug (slug) UNIQUE
|
||||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (account_id => accounts.id)
|
||||
# fk_rails_... (author_id => users.id)
|
||||
#
|
||||
class Flow < ApplicationRecord
|
||||
class Template < ApplicationRecord
|
||||
belongs_to :author, class_name: 'User'
|
||||
belongs_to :account
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<div class="max-w-6xl mx-auto px-2 py-3">
|
||||
<%= link_to 'Create Flow', new_flow_path, data: { turbo_frame: :modal } %>
|
||||
<% @flows.each do |flow| %>
|
||||
<%= link_to 'Create Template', new_template_path, data: { turbo_frame: :modal } %>
|
||||
<% @templates.each do |template| %>
|
||||
<div>
|
||||
<%= flow.name %> |
|
||||
<a href="<%= flow_path(flow) %>">edit</a> |
|
||||
<a href="<%= flow_submissions_path(flow) %>">submissions</a> |
|
||||
<%= template.name %> |
|
||||
<a href="<%= template_path(template) %>">edit</a> |
|
||||
<a href="<%= template_submissions_path(template) %>">submissions</a> |
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<%= render 'shared/turbo_modal', title: 'New Flow' do %>
|
||||
<%= form_for @flow, data: { turbo_frame: :_top } do |f| %>
|
||||
<div class="form-control my-6">
|
||||
<%= f.text_field :name, required: true, placeholder: 'Flow Name', class: 'base-input' %>
|
||||
</div>
|
||||
<div class="form-control mt-4">
|
||||
<%= f.button button_title('Create'), class: 'base-button' %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -7,8 +7,8 @@
|
||||
<%= csrf_meta_tags %>
|
||||
<%= csp_meta_tag %>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<%= javascript_pack_tag 'flow', defer: true %>
|
||||
<%= stylesheet_pack_tag 'flow', media: 'all' %>
|
||||
<%= javascript_pack_tag 'form', defer: true %>
|
||||
<%= stylesheet_pack_tag 'form', media: 'all' %>
|
||||
<%= yield :head %>
|
||||
</head>
|
||||
<body class="font-sans antialiased font-normal leading-normal bg-white text-gray-700">
|
||||
@@ -1,9 +0,0 @@
|
||||
You have been invited to submit flow <%= @flow.name %>
|
||||
<%= form_for @submission, url: start_flow_path(@flow.slug), data: { turbo_frame: :_top }, method: :put do |f| %>
|
||||
Provide youe email to start
|
||||
<div>
|
||||
<%= f.label :email %>
|
||||
<%= f.email_field :email, required: true %>
|
||||
</div>
|
||||
<%= f.button button_title %>
|
||||
<% end %>
|
||||
@@ -1,5 +1,5 @@
|
||||
<p>
|
||||
Form has been submitted alredy by ypu - thanks!
|
||||
</p>
|
||||
<%= button_to button_title('Send copy to Email'), send_submission_email_index_path, params: { flow_slug: @flow.slug, email: params[:email] }, form: { onsubmit: 'event.submitter.disabled = true' } %>
|
||||
<%= button_to button_title('Send copy to Email'), send_submission_email_index_path, params: { template_slug: @template.slug, email: params[:email] }, form: { onsubmit: 'event.submitter.disabled = true' } %>
|
||||
<%# do not allow donwload for securetiy reaosn<a href="">Download documets</a> %>
|
||||
@@ -0,0 +1,9 @@
|
||||
You have been invited to submit template <%= @template.name %>
|
||||
<%= form_for @submission, url: start_form_path(@template.slug), data: { turbo_frame: :_top }, method: :put do |f| %>
|
||||
Provide youe email to start
|
||||
<div>
|
||||
<%= f.label :email %>
|
||||
<%= f.email_field :email, required: true %>
|
||||
</div>
|
||||
<%= f.button button_title %>
|
||||
<% end %>
|
||||
@@ -1,4 +1,4 @@
|
||||
<p>Hi there</p>
|
||||
<p>You have been invited to submit a form:</p>
|
||||
<p><%= link_to 'Submit', submit_flow_index_url(slug: @submission.slug) %></p>
|
||||
<p><%= link_to 'Submit', submit_form_index_url(slug: @submission.slug) %></p>
|
||||
<p>If you didn't request this, please ignore this email.</p>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
Submissions
|
||||
Flow <%= @flow.name %>
|
||||
template <%= @template.name %>
|
||||
Copy share link:
|
||||
<input autocomplete="off" type="text" class="w-full" value="<%= start_flow_url(slug: @flow.slug) %>" disabled>
|
||||
<a href="<%= new_flow_submission_path(@flow) %>" class="bg-green-600" data-turbo-frame="modal">Add Recepients</a>
|
||||
<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>
|
||||
@@ -32,7 +32,7 @@ Copy share link:
|
||||
</td>
|
||||
<td>
|
||||
copy link<br>
|
||||
<%= link_to 'View', submission_path(@flow) %>
|
||||
<%= link_to 'View', submission_path(@template) %>
|
||||
<%= button_to 'Remove', submission_path(submission), method: :delete, data: { turbo_confirm: 'Are you sure?' } %>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<%= render 'shared/turbo_modal' do %>
|
||||
<%= form_for '', url: flow_submissions_path(@flow), data: { turbo_frame: :_top } do |f| %>
|
||||
<%= form_for '', url: template_submissions_path(@template), data: { turbo_frame: :_top } do |f| %>
|
||||
<div>
|
||||
<%= f.label :emails %>
|
||||
<%= f.text_area :emails, required: true %>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
Flow: <%= @submission.flow.name %>
|
||||
template: <%= @submission.template.name %>
|
||||
Sub: <%= @submission.slug %>
|
||||
Copy share link:
|
||||
<input autocomplete="off" type="text" class="w-full" value="<%= submit_flow_url(slug: @submission.slug) %>" disabled>
|
||||
<% @submission.flow.fields.each do |field| %>
|
||||
<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']) %>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<% attachment_field_uuids = @submission.flow.fields.select { |f| f['type'].in?(%w[image signature attachment]) }.pluck('uuid') %>
|
||||
<% attachments = ActiveStorage::Attachment.where(uuid: @submission.values.values_at(*attachment_field_uuids).flatten).preload(:blob) %>
|
||||
<flow-view class="mx-auto block" style="max-width: 1000px">
|
||||
<% @submission.flow.schema.each do |item| %>
|
||||
<% document = @submission.flow.documents.find { |a| a.uuid == item['attachment_uuid'] } %>
|
||||
<% document.preview_images.sort_by { |a| a.filename.base.to_i }.each_with_index do |page, index| %>
|
||||
<div class="relative">
|
||||
<img src="<%= page.url %>" width="<%= page.metadata['width'] %>" height="<%= page.metadata['height'] %>" loading="lazy">
|
||||
<div id="page-<%= [document.uuid, index].join('-') %>" class="top-0 bottom-0 left-0 right-0 absolute"></div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<div class="sticky bottom-8 w-full">
|
||||
<div class="bg-white mx-8 md:mx-32 border p-4 rounded">
|
||||
<flow-form data-submission-slug="<%= @submission.slug %>" data-attachments="<%= attachments.to_json(only: %i[uuid], methods: %i[url filename content_type]) %>" data-fields="<%= @submission.flow.fields.to_json %>" data-values="<%= @submission.values.to_json %>" data-authenticity-token="<%= form_authenticity_token %>"></flow-form>
|
||||
</div>
|
||||
</div>
|
||||
</flow-view>
|
||||
@@ -0,0 +1,18 @@
|
||||
<% attachment_field_uuids = @submission.template.fields.select { |f| f['type'].in?(%w[image signature attachment]) }.pluck('uuid') %>
|
||||
<% attachments = ActiveStorage::Attachment.where(uuid: @submission.values.values_at(*attachment_field_uuids).flatten).preload(:blob) %>
|
||||
<div class="mx-auto block" style="max-width: 1000px">
|
||||
<% @submission.template.schema.each do |item| %>
|
||||
<% document = @submission.template.documents.find { |a| a.uuid == item['attachment_uuid'] } %>
|
||||
<% document.preview_images.sort_by { |a| a.filename.base.to_i }.each_with_index do |page, index| %>
|
||||
<div class="relative">
|
||||
<img src="<%= page.url %>" width="<%= page.metadata['width'] %>" height="<%= page.metadata['height'] %>" loading="lazy">
|
||||
<div id="page-<%= [document.uuid, index].join('-') %>" class="top-0 bottom-0 left-0 right-0 absolute"></div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<div class="sticky bottom-8 w-full">
|
||||
<div class="bg-white mx-8 md:mx-32 border p-4 rounded">
|
||||
<submission-form data-submission-slug="<%= @submission.slug %>" data-attachments="<%= attachments.to_json(only: %i[uuid], methods: %i[url filename content_type]) %>" data-fields="<%= @submission.template.fields.to_json %>" data-values="<%= @submission.values.to_json %>" data-authenticity-token="<%= form_authenticity_token %>"></submission-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
<%= render 'shared/turbo_modal', title: 'New Template' do %>
|
||||
<%= form_for @template, data: { turbo_frame: :_top } do |f| %>
|
||||
<div class="form-control my-6">
|
||||
<%= f.text_field :name, required: true, placeholder: 'Template Name', class: 'base-input' %>
|
||||
</div>
|
||||
<div class="form-control mt-4">
|
||||
<%= f.button button_title('Create'), class: 'base-button' %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -11,6 +11,6 @@
|
||||
<%= stylesheet_pack_tag 'application', media: 'all' %>
|
||||
</head>
|
||||
<body class="h-full" data>
|
||||
<flow-builder data-flow="<%= @flow.to_json(include: { documents: { include: { preview_images: { methods: %i[url metadata filename] } } } }) %>"></flow-builder>
|
||||
<template-builder data-template="<%= @template.to_json(include: { documents: { include: { preview_images: { methods: %i[url metadata filename] } } } }) %>"></template-builder>
|
||||
</body>
|
||||
</html>
|
||||
+5
-5
@@ -21,8 +21,8 @@ Rails.application.routes.draw do
|
||||
|
||||
namespace :api do
|
||||
resources :attachments, only: %i[create]
|
||||
resources :flows, only: %i[update] do
|
||||
resources :documents, only: %i[create destroy], controller: 'flows_documents'
|
||||
resources :templates, only: %i[update] do
|
||||
resources :documents, only: %i[create destroy], controller: 'templates_documents'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,15 +30,15 @@ Rails.application.routes.draw do
|
||||
resources :setup, only: %i[index create]
|
||||
resources :users, only: %i[new create edit update destroy]
|
||||
resources :submissions, only: %i[show destroy]
|
||||
resources :flows, only: %i[new create show destroy] do
|
||||
resources :templates, only: %i[new create show destroy] do
|
||||
resources :submissions, only: %i[index new create]
|
||||
end
|
||||
|
||||
resources :start_flow, only: %i[show update], path: 'f', param: 'slug' do
|
||||
resources :start_form, only: %i[show update], path: 'f', param: 'slug' do
|
||||
get :completed
|
||||
end
|
||||
|
||||
resources :submit_flow, only: %i[show update], path: 'l', param: 'slug' do
|
||||
resources :submit_form, only: %i[show update], path: 'l', param: 'slug' do
|
||||
get :completed
|
||||
end
|
||||
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CreateFlows < ActiveRecord::Migration[7.0]
|
||||
class CreateTemplates < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :flows do |t|
|
||||
create_table :templates do |t|
|
||||
t.string :slug, null: false, index: { unique: true }
|
||||
t.string :name, null: false
|
||||
t.string :schema, null: false
|
||||
@@ -5,7 +5,7 @@ class CreateSubmissions < ActiveRecord::Migration[7.0]
|
||||
create_table :submissions do |t|
|
||||
t.string :email, null: false, index: true
|
||||
t.string :slug, null: false, index: { unique: true }
|
||||
t.references :flow, null: false, foreign_key: true, index: true
|
||||
t.references :template, null: false, foreign_key: true, index: true
|
||||
t.string :values, null: false
|
||||
t.string :ua
|
||||
t.string :ip
|
||||
|
||||
+20
-20
@@ -60,25 +60,10 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_19_144036) do
|
||||
t.index ["account_id"], name: "index_encrypted_configs_on_account_id"
|
||||
end
|
||||
|
||||
create_table "flows", force: :cascade do |t|
|
||||
t.string "slug", null: false
|
||||
t.string "name", null: false
|
||||
t.string "schema", null: false
|
||||
t.string "fields", null: false
|
||||
t.bigint "author_id", null: false
|
||||
t.bigint "account_id", null: false
|
||||
t.datetime "deleted_at"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["account_id"], name: "index_flows_on_account_id"
|
||||
t.index ["author_id"], name: "index_flows_on_author_id"
|
||||
t.index ["slug"], name: "index_flows_on_slug", unique: true
|
||||
end
|
||||
|
||||
create_table "submissions", force: :cascade do |t|
|
||||
t.string "email", null: false
|
||||
t.string "slug", null: false
|
||||
t.bigint "flow_id", null: false
|
||||
t.bigint "template_id", null: false
|
||||
t.string "values", null: false
|
||||
t.string "ua"
|
||||
t.string "ip"
|
||||
@@ -89,8 +74,23 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_19_144036) do
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["email"], name: "index_submissions_on_email"
|
||||
t.index ["flow_id"], name: "index_submissions_on_flow_id"
|
||||
t.index ["slug"], name: "index_submissions_on_slug", unique: true
|
||||
t.index ["template_id"], name: "index_submissions_on_template_id"
|
||||
end
|
||||
|
||||
create_table "templates", force: :cascade do |t|
|
||||
t.string "slug", null: false
|
||||
t.string "name", null: false
|
||||
t.string "schema", null: false
|
||||
t.string "fields", null: false
|
||||
t.bigint "author_id", null: false
|
||||
t.bigint "account_id", null: false
|
||||
t.datetime "deleted_at"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["account_id"], name: "index_templates_on_account_id"
|
||||
t.index ["author_id"], name: "index_templates_on_author_id"
|
||||
t.index ["slug"], name: "index_templates_on_slug", unique: true
|
||||
end
|
||||
|
||||
create_table "users", force: :cascade do |t|
|
||||
@@ -123,8 +123,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_19_144036) do
|
||||
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
||||
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
||||
add_foreign_key "encrypted_configs", "accounts"
|
||||
add_foreign_key "flows", "accounts"
|
||||
add_foreign_key "flows", "users", column: "author_id"
|
||||
add_foreign_key "submissions", "flows"
|
||||
add_foreign_key "submissions", "templates"
|
||||
add_foreign_key "templates", "accounts"
|
||||
add_foreign_key "templates", "users", column: "author_id"
|
||||
add_foreign_key "users", "accounts"
|
||||
end
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Flows
|
||||
module_function
|
||||
|
||||
def build_field_areas_index(flow)
|
||||
hash = {}
|
||||
|
||||
flow.fields.each do |field|
|
||||
(field['areas'] || []).each do |area|
|
||||
hash[area['attachment_uuid']] ||= {}
|
||||
acc = (hash[area['attachment_uuid']][area['page']] ||= [])
|
||||
|
||||
acc << { area:, field: }
|
||||
end
|
||||
end
|
||||
|
||||
hash
|
||||
end
|
||||
end
|
||||
@@ -9,18 +9,18 @@ module Submissions
|
||||
|
||||
# rubocop:disable Metrics
|
||||
def call(submission)
|
||||
cert = submission.flow.account.encrypted_configs
|
||||
cert = submission.template.account.encrypted_configs
|
||||
.find_by(key: EncryptedConfig::ESIGN_CERTS_KEY).value
|
||||
|
||||
zip_file = Tempfile.new
|
||||
zip_stream = Zip::ZipOutputStream.open(zip_file)
|
||||
|
||||
pdfs_index =
|
||||
submission.flow.documents.to_h do |attachment|
|
||||
submission.template.documents.to_h do |attachment|
|
||||
[attachment.uuid, HexaPDF::Document.new(io: StringIO.new(attachment.download))]
|
||||
end
|
||||
|
||||
submission.flow.fields.each do |field|
|
||||
submission.template.fields.each do |field|
|
||||
field.fetch('areas', []).each do |area|
|
||||
pdf = pdfs_index[area['attachment_uuid']]
|
||||
|
||||
@@ -96,29 +96,29 @@ module Submissions
|
||||
end
|
||||
end
|
||||
|
||||
submission.flow.schema.map do |item|
|
||||
document = submission.flow.documents.find { |a| a.uuid == item['attachment_uuid'] }
|
||||
submission.template.schema.map do |item|
|
||||
document = submission.template.documents.find { |a| a.uuid == item['attachment_uuid'] }
|
||||
|
||||
io = StringIO.new
|
||||
|
||||
zip_stream.put_next_entry("#{item['name']}.pdf")
|
||||
zip_stream.write(io.string)
|
||||
|
||||
pdf = pdfs_index[item['attachment_uuid']]
|
||||
|
||||
pdf.sign(io, reason: "Signed by #{submission.email}",
|
||||
doc_mdp_permissions: :no_changes,
|
||||
# doc_mdp_permissions: :no_changes,
|
||||
certificate: OpenSSL::X509::Certificate.new(cert['cert']),
|
||||
key: OpenSSL::PKey::RSA.new(cert['key']),
|
||||
certificate_chain: [OpenSSL::X509::Certificate.new(cert['sub_ca']),
|
||||
OpenSSL::X509::Certificate.new(cert['root_ca'])])
|
||||
|
||||
zip_stream.put_next_entry("#{item['name']}.pdf")
|
||||
zip_stream.write(io.string)
|
||||
|
||||
submission.documents.attach(io: StringIO.new(io.string), filename: document.filename)
|
||||
end
|
||||
|
||||
zip_stream.close
|
||||
|
||||
submission.archive.attach(io: zip_file, filename: "#{submission.flow.name}.zip")
|
||||
submission.archive.attach(io: zip_file, filename: "#{submission.template.name}.zip")
|
||||
end
|
||||
# rubocop:enable Metrics
|
||||
end
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Templates
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Flows
|
||||
module Templates
|
||||
module ProcessDocument
|
||||
DPI = 200
|
||||
FORMAT = '.jpg'
|
||||
@@ -3,9 +3,9 @@ const baseConfigs = require('./tailwind.config.js')
|
||||
module.exports = {
|
||||
...baseConfigs,
|
||||
content: [
|
||||
'./app/javascript/flow_form/**/*.vue',
|
||||
'./app/views/submit_flow/**/*.erb',
|
||||
'./app/views/start_flow/**/*.erb',
|
||||
'./app/javascript/submission_form/**/*.vue',
|
||||
'./app/views/submit_form/**/*.erb',
|
||||
'./app/views/start_form/**/*.erb',
|
||||
'./app/views/send_submission_copy/**/*.erb'
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user