Work in progress

This commit is contained in:
Julien Nahum
2023-12-09 15:47:03 +01:00
parent f970557b76
commit 1f853e8178
315 changed files with 34058 additions and 25 deletions

View File

@@ -0,0 +1,275 @@
<template>
<div>
<div class="p-4 border-b sticky top-0 z-10 bg-white">
<div class="flex">
<button class="text-gray-500 hover:text-gray-900 cursor-pointer" @click.prevent="closeSidebar">
<svg class="h-6 w-6" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 6L6 18M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
<div class="font-semibold inline ml-2 truncate flex-grow truncate">
Add Block
</div>
</div>
</div>
<div class="py-2 px-4">
<div>
<p class="text-gray-500 uppercase text-xs font-semibold mb-2">
Input Blocks
</p>
<div class="grid grid-cols-2 gap-2">
<div v-for="(block, i) in inputBlocks" :key="block.name"
class="bg-gray-50 border hover:bg-gray-100 dark:bg-gray-900 rounded-md dark:hover:bg-gray-800 py-2 flex flex-col"
role="button" @click.prevent="addBlock(block.name)"
>
<div class="mx-auto">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="2" v-html="block.icon"
></svg>
</div>
<p class="w-full text-xs text-gray-500 uppercase text-center font-semibold mt-1">
{{ block.title }}
</p>
</div>
</div>
</div>
<div class="border-t mt-6">
<p class="text-gray-500 uppercase text-xs font-semibold mb-2 mt-6">
Layout Blocks
</p>
<div class="grid grid-cols-2 gap-2">
<div v-for="(block, i) in layoutBlocks" :key="block.name"
class="bg-gray-50 border hover:bg-gray-100 dark:bg-gray-900 rounded-md dark:hover:bg-gray-800 py-2 flex flex-col"
role="button" @click.prevent="addBlock(block.name)"
>
<div class="mx-auto">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="2" v-html="block.icon"
></svg>
</div>
<p class="w-full text-xs text-gray-500 uppercase text-center font-semibold mt-1">
{{ block.title }}
</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Form from 'vform'
import clonedeep from 'clone-deep'
import { computed } from 'vue'
import { useWorkingFormStore } from '../../../../../stores/working_form'
export default {
name: 'AddFormBlock',
components: {},
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore,
selectedFieldIndex : computed(() => workingFormStore.selectedFieldIndex)
}
},
data () {
return {
blockForm: null,
inputBlocks: [
{
name: 'text',
title: 'Text Input',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h7"/>'
},
{
name: 'date',
title: 'Date Input',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>'
},
{
name: 'url',
title: 'URL Input',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>'
},
{
name: 'phone_number',
title: 'Phone Input',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"/>'
},
{
name: 'email',
title: 'Email Input',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207"/>'
},
{
name: 'checkbox',
title: 'Checkbox Input',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>'
},
{
name: 'select',
title: 'Select Input',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M8 9l4-4 4 4m0 6l-4 4-4-4"/>'
},
{
name: 'multi_select',
title: 'Multi-select Input',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M8 9l4-4 4 4m0 6l-4 4-4-4"/>'
},
{
name: 'number',
title: 'Number Input',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M7 20l4-16m2 16l4-16M6 9h14M4 15h14"/>'
},
{
name: 'files',
title: 'File Input',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />'
},
{
name: 'signature',
title: 'Signature Input',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125" />'
}
],
layoutBlocks: [
{
name: 'nf-text',
title: 'Text Block',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h8m-8 6h16" />'
},
{
name: 'nf-page-break',
title: 'Page-break Block',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M9 13h6m-3-3v6m5 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />'
},
{
name: 'nf-divider',
title: 'Divider Block',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M20 12H4" />'
},
{
name: 'nf-image',
title: 'Image Block',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />'
},
{
name: 'nf-code',
title: 'Code Block',
icon: '<path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5" />'
}
]
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
},
defaultBlockNames () {
return {
text: 'Your name',
date: 'Date',
url: 'Link',
phone_number: 'Phone Number',
number: 'Number',
email: 'Email',
checkbox: 'Checkbox',
select: 'Select',
multi_select: 'Multi Select',
files: 'Files',
signature: 'Signature',
'nf-text': 'Text Block',
'nf-page-break': 'Page Break',
'nf-divider': 'Divider',
'nf-image': 'Image',
'nf-code': 'Code Block'
}
}
},
watch: {},
mounted () {
this.reset()
},
methods: {
closeSidebar () {
this.workingFormStore.closeAddFieldSidebar()
},
reset () {
this.blockForm = new Form({
type: null,
name: null
})
},
addBlock (type) {
this.blockForm.type = type
this.blockForm.name = this.defaultBlockNames[type]
const newBlock = this.prefillDefault(this.blockForm.data())
newBlock.id = this.generateUUID()
newBlock.hidden = false
if (['select', 'multi_select'].includes(this.blockForm.type)) {
newBlock[this.blockForm.type] = { options: [] }
}
newBlock.help_position = 'below_input'
if (this.selectedFieldIndex === null || this.selectedFieldIndex === undefined) {
const newFields = clonedeep(this.form.properties)
newFields.push(newBlock)
this.form.properties = newFields
this.workingFormStore.openSettingsForField(this.form.properties.length - 1)
} else {
const newFields = clonedeep(this.form.properties)
newFields.splice(this.selectedFieldIndex + 1, 0, newBlock)
this.form.properties = newFields
this.workingFormStore.openSettingsForField(this.selectedFieldIndex + 1)
}
this.reset()
},
generateUUID () {
let d = new Date().getTime()// Timestamp
let d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now() * 1000)) || 0// Time in microseconds since page-load or 0 if unsupported
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
let r = Math.random() * 16// random number between 0 and 16
if (d > 0) { // Use timestamp until depleted
r = (d + r) % 16 | 0
d = Math.floor(d / 16)
} else { // Use microseconds since page-load if supported
r = (d2 + r) % 16 | 0
d2 = Math.floor(d2 / 16)
}
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16)
})
},
prefillDefault (data) {
if (data.type === 'nf-text') {
data.content = '<p>This is a text block.</p>'
} else if (data.type === 'nf-page-break') {
data.next_btn_text = 'Next'
data.previous_btn_text = 'Previous'
} else if (data.type === 'nf-code') {
data.content = '<div class="text-blue-500 italic">This is a code block.</div>'
} else if (data.type === 'signature') {
data.help = 'Draw your signature above'
}
return data
}
}
}
</script>

View File

@@ -0,0 +1,205 @@
<template>
<editor-options-panel name="About Submissions" :already-opened="true">
<template #icon>
<svg class="h-5 w-5" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M4.83333 6.08333H9M4.83333 9H11.5M4.83333 14V15.9463C4.83333 16.3903 4.83333 16.6123 4.92436 16.7263C5.00352 16.8255 5.12356 16.8832 5.25045 16.8831C5.39636 16.8829 5.56973 16.7442 5.91646 16.4668L7.90434 14.8765C8.31043 14.5517 8.51347 14.3892 8.73957 14.2737C8.94017 14.1712 9.15369 14.0963 9.37435 14.051C9.62306 14 9.88308 14 10.4031 14H12.5C13.9001 14 14.6002 14 15.135 13.7275C15.6054 13.4878 15.9878 13.1054 16.2275 12.635C16.5 12.1002 16.5 11.4001 16.5 10V5.5C16.5 4.09987 16.5 3.3998 16.2275 2.86502C15.9878 2.39462 15.6054 2.01217 15.135 1.77248C14.6002 1.5 13.9001 1.5 12.5 1.5H5.5C4.09987 1.5 3.3998 1.5 2.86502 1.77248C2.39462 2.01217 2.01217 2.39462 1.77248 2.86502C1.5 3.3998 1.5 4.09987 1.5 5.5V10.6667C1.5 11.4416 1.5 11.8291 1.58519 12.147C1.81635 13.0098 2.49022 13.6836 3.35295 13.9148C3.67087 14 4.05836 14 4.83333 14Z"
stroke="currentColor" stroke-width="1.67" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</template>
<text-input name="submit_button_text" class="mt-4"
:form="form"
label="Text of Submit Button"
:required="true"
/>
<toggle-switch-input name="editable_submissions" :form="form" class="mt-4"
help="Gives user a unique url to update their submission"
>
<template #label>
Editable submissions
<pro-tag class="ml-1" />
</template>
</toggle-switch-input>
<text-input v-if="form.editable_submissions" name="editable_submissions_button_text"
:form="form"
label="Text of editable submissions button"
:required="true"
/>
<flat-select-input :form="submissionOptions" name="databaseAction" label="Database Submission Action"
:options="[
{name:'Create new record (default)', value:'create'},
{name:'Update Record (or create if no match)', value:'update'}
]" :required="true" help="Create a new record or update an existing one"
>
<template #selected="{option,optionName}">
<div class="flex items-center truncate mr-6">
{{ optionName }}
<pro-tag v-if="option === 'update'" class="ml-2" />
</div>
</template>
<template #option="{option, selected}">
<span class="flex hover:text-white">
<p class="flex-grow hover:text-white">
{{ option.name }} <template v-if="option.value === 'update'"><pro-tag /></template>
</p>
<span v-if="selected" class="absolute inset-y-0 right-0 flex items-center pr-4 dark:text-white">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
</span>
</template>
</flat-select-input>
<v-transition>
<div v-if="submissionOptions.databaseAction == 'update' && filterableFields.length">
<select-input v-if="filterableFields.length" :form="form" name="database_fields_update"
label="Properties to check on update" :options="filterableFields" :required="true"
:multiple="true"
/>
<div class="-mt-3 mb-3 text-gray-400 dark:text-gray-500">
<small>If the submission has the same value(s) as a previous one for the selected
column(s), we will update it, instead of creating a new one.
<a href="#"
@click.prevent="$crisp.push(['do', 'helpdesk:article:open', ['en', 'how-to-update-a-page-on-form-submission-1t1jwmn']])"
>More
info here.</a>
</small>
</div>
</div>
</v-transition>
<select-input :form="submissionOptions" name="submissionMode" label="Post Submission Action"
:options="[
{name:'Show Success page', value:'default'},
{name:'Redirect', value:'redirect'}
]" :required="true" help="Show a message, or redirect to a URL"
>
<template #selected="{option,optionName}">
<div class="flex items-center truncate mr-6">
{{ optionName }}
<pro-tag v-if="option === 'redirect'" class="ml-2" />
</div>
</template>
<template #option="{option, selected}">
<span class="flex hover:text-white">
<p class="flex-grow hover:text-white">
{{ option.name }} <template v-if="option.value === 'redirect'"><pro-tag /></template>
</p>
<span v-if="selected" class="absolute inset-y-0 right-0 flex items-center pr-4">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
</span>
</template>
</select-input>
<template v-if="submissionOptions.submissionMode === 'redirect'">
<text-input name="redirect_url"
:form="form"
label="Redirect URL"
:required="true" help="On submit, redirects to that URL"
/>
</template>
<template v-else>
<toggle-switch-input name="re_fillable" :form="form" class="mt-4"
label="Allow users to fill the form again"
/>
<text-input v-if="form.re_fillable" name="re_fill_button_text"
:form="form"
label="Text of re-start button"
:required="true"
/>
<rich-text-area-input name="submitted_text"
:form="form"
label="Text After Submission"
:required="false"
/>
</template>
</editor-options-panel>
</template>
<script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
import ProTag from '~/components/global/ProTag.vue'
import VTransition from '~/components/global/transitions/VTransition.vue'
export default {
components: {EditorOptionsPanel, ProTag, VTransition},
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
submissionOptions: {}
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
},
/**
* Used for the update record on submission. Lists all visible fields on which you can filter records to update
* on submission instead of creating
*/
filterableFields () {
if (this.submissionOptions.databaseAction !== 'update') return []
return this.form.properties.filter((field) => {
return !field.hidden && !['files', 'signature', 'multi_select'].includes(field.type)
}).map((field) => {
return {
name: field.name,
value: field.id
}
})
}
},
watch: {
form: {
handler () {
if (this.form) {
this.submissionOptions = {
submissionMode: this.form.redirect_url ? 'redirect' : 'default',
databaseAction: this.form.database_fields_update ? 'update' : 'create'
}
}
},
deep: true
},
submissionOptions: {
deep: true,
handler: function (val) {
if (val.submissionMode === 'default') {
this.form.redirect_url = null
}
if (val.databaseAction === 'create') {
this.form.database_fields_update = null
}
}
}
}
}
</script>

View File

@@ -0,0 +1,73 @@
<template>
<editor-options-panel name="Form Access" :already-opened="false">
<template #icon>
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z"
/>
</svg>
</template>
<text-input name="password" :form="form" class="mt-4"
label="Form Password" help="Leave empty to disable password"
/>
<date-input :with-time="true" name="closes_at" class="mt-4"
:form="form"
label="Close form on a scheduled date"
help="Leave empty to keep the form open"
:required="false"
/>
<rich-text-area-input v-if="form.closes_at || form.visibility=='closed'" name="closed_text"
:form="form" class="mt-4"
label="Closed form text"
help="This message will be shown when the form will be closed"
:required="false"
/>
<text-input name="max_submissions_count" native-type="number" :min="1" :form="form"
label="Limit number of submissions" placeholder="Max submissions" class="mt-4"
help="Leave empty for unlimited submissions"
:required="false"
/>
<rich-text-area-input v-if="form.max_submissions_count && form.max_submissions_count > 0"
name="max_submissions_reached_text" class="mt-4"
:form="form"
label="Max Submissions reached text"
help="This message will be shown when the form will have the maximum number of submissions"
:required="false"
/>
</editor-options-panel>
</template>
<script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
export default {
components: { EditorOptionsPanel },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
}
},
watch: {},
mounted () {
},
methods: {}
}
</script>

View File

@@ -0,0 +1,50 @@
<template>
<editor-options-panel name="Custom Code" :already-opened="false" :has-pro-tag="true">
<template #icon>
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 2.26953V6.40007C14 6.96012 14 7.24015 14.109 7.45406C14.2049 7.64222 14.3578 7.7952 14.546 7.89108C14.7599 8.00007 15.0399 8.00007 15.6 8.00007H19.7305M14 17.5L16.5 15L14 12.5M10 12.5L7.5 15L10 17.5M20 9.98822V17.2C20 18.8802 20 19.7202 19.673 20.362C19.3854 20.9265 18.9265 21.3854 18.362 21.673C17.7202 22 16.8802 22 15.2 22H8.8C7.11984 22 6.27976 22 5.63803 21.673C5.07354 21.3854 4.6146 20.9265 4.32698 20.362C4 19.7202 4 18.8802 4 17.2V6.8C4 5.11984 4 4.27976 4.32698 3.63803C4.6146 3.07354 5.07354 2.6146 5.63803 2.32698C6.27976 2 7.11984 2 8.8 2H12.0118C12.7455 2 13.1124 2 13.4577 2.08289C13.7638 2.15638 14.0564 2.27759 14.3249 2.44208C14.6276 2.6276 14.887 2.88703 15.4059 3.40589L18.5941 6.59411C19.113 7.11297 19.3724 7.3724 19.5579 7.67515C19.7224 7.94356 19.8436 8.2362 19.9171 8.5423C20 8.88757 20 9.25445 20 9.98822Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</template>
<p class="mt-4">
The code will be injected in the <span class="font-semibold">head</span> section of your form page.
</p>
<code-input name="custom_code" class="mt-4"
:form="form" help="Custom code cannot be previewed in our editor. Please test your code using
your actual form page (save changes beforehand)."
label="Custom Code"
/>
</editor-options-panel>
</template>
<script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
import CodeInput from '../../../../forms/CodeInput.vue'
export default {
components: { EditorOptionsPanel, CodeInput },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
}
},
}
</script>

View File

@@ -0,0 +1,80 @@
<template>
<editor-options-panel name="Link Settings - SEO" :already-opened="false" :has-pro-tag="true">
<template #icon>
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"
/>
</svg>
</template>
<p class="mt-4 text-gray-500 text-sm">
Customize the link, images and text that appear when you share your form on other sites (Open Graph).
</p>
<select-input v-if="customDomainAllowed" v-model="form.custom_domain" :disabled="customDomainOptions.length <= 0" :options="customDomainOptions" name="type"
class="mt-4" label="Form Domain" placeholder="yourdomain.com"
/>
<text-input v-model="form.seo_meta.page_title" name="page_title" class="mt-4"
label="Page Title" help="Under or approximately 60 characters"
/>
<text-area-input v-model="form.seo_meta.page_description" name="page_description" class="mt-4"
label="Page Description" help="Between 150 and 160 characters"
/>
<image-input v-model="form.seo_meta.page_thumbnail" name="page_thumbnail" class="mt-4"
label="Page Thumbnail Image" help="Also know as og:image - 1200 X 630"
/>
</editor-options-panel>
</template>
<script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
export default {
components: { EditorOptionsPanel },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
},
workspace () {
return this.$store.getters['open/workspaces/getCurrent']()
},
customDomainOptions () {
return this.workspace.custom_domains.map((domain) => {
return {
name: domain,
value: domain
}
})
},
customDomainAllowed () {
return this.$config.custom_domains_enabled
}
},
watch: {},
mounted () {
['page_title', 'page_description', 'page_thumbnail'].forEach((keyname) => {
if (this.form.seo_meta[keyname] === undefined) {
this.form.seo_meta[keyname] = null
}
})
},
methods: {}
}
</script>

View File

@@ -0,0 +1,135 @@
<template>
<editor-options-panel name="Customization" :already-opened="true">
<template #icon>
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.66667 9.99984C1.66667 14.6022 5.39763 18.3332 10 18.3332C11.3807 18.3332 12.5 17.2139 12.5 15.8332V15.4165C12.5 15.0295 12.5 14.836 12.5214 14.6735C12.6691 13.5517 13.5519 12.6689 14.6737 12.5212C14.8361 12.4998 15.0297 12.4998 15.4167 12.4998H15.8333C17.214 12.4998 18.3333 11.3805 18.3333 9.99984C18.3333 5.39746 14.6024 1.6665 10 1.6665C5.39763 1.6665 1.66667 5.39746 1.66667 9.99984Z" stroke="currentColor" stroke-width="1.67" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.83333 10.8332C6.29357 10.8332 6.66667 10.4601 6.66667 9.99984C6.66667 9.5396 6.29357 9.1665 5.83333 9.1665C5.3731 9.1665 5 9.5396 5 9.99984C5 10.4601 5.3731 10.8332 5.83333 10.8332Z" stroke="currentColor" stroke-width="1.67" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.3333 7.49984C13.7936 7.49984 14.1667 7.12674 14.1667 6.6665C14.1667 6.20627 13.7936 5.83317 13.3333 5.83317C12.8731 5.83317 12.5 6.20627 12.5 6.6665C12.5 7.12674 12.8731 7.49984 13.3333 7.49984Z" stroke="currentColor" stroke-width="1.67" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.33333 6.6665C8.79357 6.6665 9.16667 6.29341 9.16667 5.83317C9.16667 5.37293 8.79357 4.99984 8.33333 4.99984C7.8731 4.99984 7.5 5.37293 7.5 5.83317C7.5 6.29341 7.8731 6.6665 8.33333 6.6665Z" stroke="currentColor" stroke-width="1.67" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</template>
<select-input name="theme" class="mt-4"
:options="[
{name:'Default',value:'default'},
{name:'Simple',value:'simple'},
{name:'Notion',value:'notion'},
]"
:form="form" label="Form Theme"
/>
<div class="-mt-3 mb-3 text-gray-400 dark:text-gray-500">
<small>
Need another theme? <a href="#" @click.prevent="openChat">Send us some suggestions!</a>
</small>
</div>
<select-input name="width" class="mt-4"
:options="[
{name:'Centered',value:'centered'},
{name:'Full Width',value:'full'},
]"
:form="form" label="Form Width" help="Useful when embedding your form"
/>
<image-input name="cover_picture" class="mt-4"
:form="form" label="Cover Picture" help="Not visible when form is embedded"
:required="false"
/>
<image-input name="logo_picture" class="mt-4"
:form="form" label="Logo" help="Not visible when form is embedded"
:required="false"
/>
<select-input name="dark_mode" class="mt-4"
help="To see changes, save your form and open it"
:options="[
{name:'Auto - use Device System Preferences',value:'auto'},
{name:'Light Mode',value:'light'},
{name:'Dark Mode',value:'dark'}
]"
:form="form" label="Dark Mode"
/>
<color-input name="color" class="mt-4"
:form="form"
label="Color (for buttons & inputs border)"
/>
<toggle-switch-input name="hide_title" :form="form" class="mt-4"
label="Hide Title"
/>
<toggle-switch-input name="no_branding" :form="form" class="mt-4">
<template #label>
Remove OpnForm Branding
<pro-tag class="ml-1" />
</template>
</toggle-switch-input>
<toggle-switch-input name="uppercase_labels" :form="form" class="mt-4"
label="Uppercase Input Labels"
/>
<toggle-switch-input name="transparent_background" :form="form" class="mt-4"
label="Transparent Background" help="Only applies when form is embedded"
/>
<toggle-switch-input name="confetti_on_submission" :form="form" class="mt-4"
label="Confetti on successful submisison"
@update:model-value="onChangeConfettiOnSubmission"
/>
<toggle-switch-input name="auto_save" :form="form"
label="Auto save form response"
help="Will save data in browser, if user not submit the form then next time will auto prefill last entered data"
/>
</editor-options-panel>
</template>
<script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
import ProTag from '~/components/global/ProTag.vue'
export default {
components: { EditorOptionsPanel, ProTag },
props: {
},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
isMounted: false
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
}
},
watch: {},
mounted () {
this.isMounted = true
},
methods: {
onChangeConfettiOnSubmission (val) {
this.form.confetti_on_submission = val
if (this.isMounted && val) {
this.playConfetti()
}
},
openChat () {
window.$crisp.push(['do', 'chat:show'])
window.$crisp.push(['do', 'chat:open'])
}
}
}
</script>

View File

@@ -0,0 +1,114 @@
<template>
<!-- Form Preview (desktop only) -->
<div
class="bg-gray-50 dark:bg-notion-dark-light hidden md:flex flex-grow p-5 flex-col items-center overflow-y-scroll"
>
<div class="border rounded-lg bg-white dark:bg-notion-dark w-full block transition-all max-w-5xl">
<transition enter-active-class="linear duration-100 overflow-hidden"
enter-from-class="max-h-0"
enter-to-class="max-h-56"
leave-active-class="linear duration-100 overflow-hidden"
leave-from-class="max-h-56"
leave-to-class="max-h-0"
>
<div v-if="(form.logo_picture || form.cover_picture)">
<div v-if="form.cover_picture">
<div id="cover-picture"
class="max-h-56 rounded-t-lg w-full overflow-hidden flex items-center justify-center"
>
<img alt="Cover Picture" :src="coverPictureSrc(form.cover_picture)" class="w-full">
</div>
</div>
<div v-if="form.logo_picture" class="w-full mx-auto p-5 relative"
:class="{'pt-20':!form.cover_picture, 'max-w-lg': form && (form.width === 'centered')}"
>
<img alt="Logo Picture" :src="coverPictureSrc(form.logo_picture)"
:class="{'top-5':!form.cover_picture, '-top-10':form.cover_picture}"
class="w-20 h-20 object-contain absolute left-5 transition-all"
>
</div>
</div>
</transition>
<open-complete-form ref="form-preview" class="w-full mx-auto py-5 px-3" :class="{'max-w-lg': form && (form.width === 'centered')}"
:creating="creating"
:form="form"
:admin-preview="true"
@restarted="previewFormSubmitted=false"
@submitted="previewFormSubmitted=true"
/>
</div>
<p class="text-center text-xs text-gray-400 dark:text-gray-600 mt-1">
Form Preview <span v-if="creating"
class="font-normal text-gray-400 dark:text-gray-600 text-xs"
>- Answers won't be saved</span>
<br>
<span v-if="previewFormSubmitted && !form.re_fillable">
<a href="#" @click.prevent="$refs['form-preview'].restart()">Restart Form
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-nt-blue inline" viewBox="0 0 20 20"
fill="currentColor"
>
<path fill-rule="evenodd"
d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z"
clip-rule="evenodd"
/>
</svg>
</a>
</span>
</p>
</div>
</template>
<script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import VSwitch from '../../../../forms/components/VSwitch.vue'
import OpenCompleteForm from '../../OpenCompleteForm.vue'
export default {
components: { OpenCompleteForm, VSwitch },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
previewFormSubmitted: false
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
},
creating () { // returns true if we are creating a form
return !this.form.hasOwnProperty('id')
}
},
watch: {},
mounted () {
},
methods: {
coverPictureSrc (val) {
try {
// Is valid url
new URL(val)
} catch (_) {
// Is file
return URL.createObjectURL(val)
}
return val
}
}
}
</script>

View File

@@ -0,0 +1,49 @@
<template>
<editor-right-sidebar :show="form && (showEditFieldSidebar || showAddFieldSidebar)">
<transition mode="out-in">
<form-field-edit v-if="showEditFieldSidebar" :key="editFieldIndex" v-motion-fade="'fade'" />
<add-form-block v-else-if="showAddFieldSidebar" v-motion-fade="'fade'" />
</transition>
</editor-right-sidebar>
</template>
<script>
import { computed } from 'vue'
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorRightSidebar from '../../../editors/EditorRightSidebar.vue'
import FormFieldEdit from '../../fields/FormFieldEdit.vue'
import AddFormBlock from './AddFormBlock.vue'
export default {
name: 'FormEditorSidebar',
components: { EditorRightSidebar, AddFormBlock, FormFieldEdit },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore,
editFieldIndex: computed(() => workingFormStore.selectedFieldIndex),
showEditFieldSidebar: computed(() => workingFormStore.showEditFieldSidebar),
showAddFieldSidebar: computed(() => workingFormStore.showAddFieldSidebar)
}
},
data () {
return {}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
}
},
watch: {},
mounted () {
},
methods: {}
}
</script>

View File

@@ -0,0 +1,40 @@
<template>
<modal :show="show" @close="$emit('close')">
<div class="-mx-5">
<h2 class="text-red-400 text-2xl font-bold mb-4 px-4">
Error saving your form
</h2>
<div v-if="validationErrorResponse" class="p-4 border-b border-t">
<p v-if="validationErrorResponse.message" v-text="validationErrorResponse.message" />
<ul class="list-disc list-inside">
<li v-for="err, key in validationErrorResponse.errors" :key="key">
{{ Array.isArray(err)?err[0]:err }}
</li>
</ul>
</div>
<div class="px-4 pt-4 text-right">
<v-button color="gray" shade="light" @click="$emit('close')">
Close
</v-button>
</div>
</div>
</modal>
</template>
<script>
export default {
name: 'FormErrorModal',
components: {},
props: {
show: { type: Boolean, required: true },
validationErrorResponse: { type: Object, required: false }
},
data: () => ({}),
computed: {},
methods: {}
}
</script>

View File

@@ -0,0 +1,169 @@
<template>
<editor-options-panel name="Information" :already-opened="true">
<template #icon>
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 13.3332V9.99984M10 6.6665H10.0083M18.3333 9.99984C18.3333 14.6022 14.6024 18.3332 10 18.3332C5.39763 18.3332 1.66667 14.6022 1.66667 9.99984C1.66667 5.39746 5.39763 1.6665 10 1.6665C14.6024 1.6665 18.3333 5.39746 18.3333 9.99984Z" stroke="currentColor" stroke-width="1.67" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</template>
<text-input name="title" class="mt-4"
:form="form"
label="Form Title"
:required="true"
/>
<rich-text-area-input name="description"
:form="form"
label="Description"
:required="false"
/>
<select-input name="tags" label="Tags" :form="form" class="mt-4"
help="To organize your forms (hidden to respondents)"
placeholder="Select Tag(s)" :multiple="true" :allow-creation="true"
:options="allTagsOptions"
/>
<select-input name="visibility" label="Visibility" :form="form" class="mt-4"
help="Only public form will be accessible"
placeholder="Select Visibility" :required="true"
:options="visibilityOptions"
/>
<v-button v-if="copyFormOptions.length > 0" color="light-gray" class="w-full mt-4" @click="showCopyFormSettingsModal=true">
<svg class="h-5 w-5 -mt-1 text-nt-blue inline mr-2" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.16667 12.4998C3.3901 12.4998 3.00182 12.4998 2.69553 12.373C2.28715 12.2038 1.9627 11.8794 1.79354 11.471C1.66667 11.1647 1.66667 10.7764 1.66667 9.99984V4.33317C1.66667 3.39975 1.66667 2.93304 1.84833 2.57652C2.00812 2.26292 2.26308 2.00795 2.57669 1.84816C2.93321 1.6665 3.39992 1.6665 4.33334 1.6665H10C10.7766 1.6665 11.1649 1.6665 11.4711 1.79337C11.8795 1.96253 12.204 2.28698 12.3731 2.69536C12.5 3.00165 12.5 3.38993 12.5 4.1665M10.1667 18.3332H15.6667C16.6001 18.3332 17.0668 18.3332 17.4233 18.1515C17.7369 17.9917 17.9919 17.7368 18.1517 17.4232C18.3333 17.0666 18.3333 16.5999 18.3333 15.6665V10.1665C18.3333 9.23308 18.3333 8.76637 18.1517 8.40985C17.9919 8.09625 17.7369 7.84128 17.4233 7.68149C17.0668 7.49984 16.6001 7.49984 15.6667 7.49984H10.1667C9.23325 7.49984 8.76654 7.49984 8.41002 7.68149C8.09642 7.84128 7.84145 8.09625 7.68166 8.40985C7.50001 8.76637 7.50001 9.23308 7.50001 10.1665V15.6665C7.50001 16.5999 7.50001 17.0666 7.68166 17.4232C7.84145 17.7368 8.09642 17.9917 8.41002 18.1515C8.76654 18.3332 9.23325 18.3332 10.1667 18.3332Z" stroke="currentColor" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" />
</svg>
Copy another form's settings
</v-button>
<modal :show="showCopyFormSettingsModal" max-width="md" @close="showCopyFormSettingsModal=false">
<template #icon>
<svg class="w-10 h-10 text-blue" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17 27C16.0681 27 15.6022 27 15.2346 26.8478C14.7446 26.6448 14.3552 26.2554 14.1522 25.7654C14 25.3978 14 24.9319 14 24V17.2C14 16.0799 14 15.5198 14.218 15.092C14.4097 14.7157 14.7157 14.4097 15.092 14.218C15.5198 14 16.0799 14 17.2 14H24C24.9319 14 25.3978 14 25.7654 14.1522C26.2554 14.3552 26.6448 14.7446 26.8478 15.2346C27 15.6022 27 16.0681 27 17M24.2 34H30.8C31.9201 34 32.4802 34 32.908 33.782C33.2843 33.5903 33.5903 33.2843 33.782 32.908C34 32.4802 34 31.9201 34 30.8V24.2C34 23.0799 34 22.5198 33.782 22.092C33.5903 21.7157 33.2843 21.4097 32.908 21.218C32.4802 21 31.9201 21 30.8 21H24.2C23.0799 21 22.5198 21 22.092 21.218C21.7157 21.4097 21.4097 21.7157 21.218 22.092C21 22.5198 21 23.0799 21 24.2V30.8C21 31.9201 21 32.4802 21.218 32.908C21.4097 33.2843 21.7157 33.5903 22.092 33.782C22.5198 34 23.0799 34 24.2 34Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</template>
<template #title>
Copy Settings from another form
</template>
<div class="p-4 min-h-[450px]">
<p class="text-gray-600">
If you already have another form that you like to use as a base for this form, you can do that here.
Select another form, confirm, and we will copy all of the other form settings (except the form structure)
to this form.
</p>
<select-input v-model="copyFormId" name="copy_form_id"
label="Copy Settings From" class="mt-3 mb-6"
placeholder="Choose a form" :searchable="copyFormOptions.length > 5"
:options="copyFormOptions"
/>
<div class="flex">
<v-button color="white" class="w-full mr-2" @click="showCopyFormSettingsModal=false">
Cancel
</v-button>
<v-button color="blue" class="w-full" @click="copySettings">
Confirm & Copy
</v-button>
</div>
</div>
</modal>
</editor-options-panel>
</template>
<script>
import { computed } from 'vue'
import clonedeep from 'clone-deep'
import { useFormsStore } from '../../../../../stores/forms'
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
import SelectInput from '../../../../forms/SelectInput.vue'
export default {
components: { SelectInput, EditorOptionsPanel },
props: {},
setup () {
const formsStore = useFormsStore()
const workingFormStore = useWorkingFormStore()
return {
formsStore,
workingFormStore,
forms : computed(() => formsStore.content)
}
},
data () {
return {
showCopyFormSettingsModal: false,
copyFormId: null,
visibilityOptions: [
{
name: "Published",
value: "public"
},
{
name: "Draft - not publicly accessible",
value: "draft"
},
{
name: "Closed - won\'t accept new submissions",
value: "closed"
}
]
}
},
computed: {
copyFormOptions () {
return this.forms.filter((form) => {
return this.form.id !== form.id
}).map((form) => {
return {
name: form.title,
value: form.id
}
})
},
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
},
allTagsOptions () {
return this.formsStore.getAllTags.map((tagname) => {
return {
name: tagname,
value: tagname
}
})
}
},
watch: {},
mounted () {
},
methods: {
copySettings () {
if (this.copyFormId == null) return
const copyForm = clonedeep(this.forms.find((form) => form.id === this.copyFormId))
if (!copyForm) return
// Clean copy from form
['title', 'description', 'properties', 'cleanings', 'views_count', 'submissions_count', 'workspace', 'workspace_id', 'updated_at',
'share_url', 'slug', 'notion_database_url', 'id', 'database_id', 'database_fields_update', 'creator',
'created_at', 'deleted_at', 'last_edited_human'].forEach((property) => {
if (copyForm.hasOwnProperty(property)) {
delete copyForm[property]
}
})
// Apply changes
Object.keys(copyForm).forEach((property) => {
this.form[property] = copyForm[property]
})
this.showCopyFormSettingsModal = false
}
}
}
</script>

View File

@@ -0,0 +1,75 @@
<template>
<editor-options-panel name="Notifications & Integrations" :already-opened="true" :has-pro-tag="true">
<template #icon>
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22 6C22 4.9 21.1 4 20 4H4C2.9 4 2 4.9 2 6M22 6V18C22 19.1 21.1 20 20 20H4C2.9 20 2 19.1 2 18V6M22 6L12 13L2 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</template>
<form-notifications-option class="mt-2" />
<form-notifications-submission-confirmation />
<form-notifications-slack />
<form-notifications-discord />
<form-notifications-webhook />
<v-button color="white"
class="flex items-center mt-3 cursor-pointer relative w-full rounded-lg flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full py-2 px-4 bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100"
:href="zapierUrl" target="_blank"
>
<div class="flex-grow flex items-center">
<svg class="w-5 h-5 inline text-yellow-500" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
d="M318 256c0 19-4 36-10 52-16 7-34 10-52 10-19 0-36-3-52-9-7-17-10-34-10-53 0-18 3-36 10-52 16-6 33-10 52-10 18 0 36 4 52 10 6 16 10 34 10 52zm182-41H355l102-102c-8-11-17-22-26-32-10-9-21-18-32-26L297 157V12c-13-2-27-3-41-3s-28 1-41 3v145L113 55c-12 8-22 17-32 26-10 10-19 21-27 32l102 102H12s-3 27-3 41 1 28 3 41h144L54 399c16 23 36 43 59 59l102-102v144c13 2 27 3 41 3s28-1 41-3V356l102 102c11-8 22-17 32-27 9-10 18-20 26-32L355 297h145c2-13 3-27 3-41s-1-28-3-41z"
/>
</svg>
<p class="flex-grow text-center font-normal">
Zapier Integration
</p>
</div>
</v-button>
</editor-options-panel>
</template>
<script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
import FormNotificationsOption from './components/FormNotificationsOption.vue'
import FormNotificationsSlack from './components/FormNotificationsSlack.vue'
import FormNotificationsDiscord from './components/FormNotificationsDiscord.vue'
import FormNotificationsSubmissionConfirmation from './components/FormNotificationsSubmissionConfirmation.vue'
import FormNotificationsWebhook from './components/FormNotificationsWebhook.vue'
export default {
components: { FormNotificationsSubmissionConfirmation, FormNotificationsSlack, FormNotificationsDiscord, FormNotificationsOption, EditorOptionsPanel, FormNotificationsWebhook },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
},
zapierUrl: () => this.$config.links.zapier_integration
},
watch: {
},
mounted () {
},
}
</script>

View File

@@ -0,0 +1,48 @@
<template>
<editor-options-panel name="Security & Privacy" :already-opened="false">
<template #icon>
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
</template>
<toggle-switch-input name="can_be_indexed" :form="form" class="mt-4"
label="Indexable by Google"
help="If enabled, your form can appear in the search results of Google"
/>
<toggle-switch-input name="use_captcha" :form="form" class="mt-4"
label="Protect your form with a Captcha"
help="If enabled we will make sure respondant is a human"
/>
</editor-options-panel>
</template>
<script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
export default {
components: { EditorOptionsPanel },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
}
},
}
</script>

View File

@@ -0,0 +1,44 @@
<template>
<editor-options-panel name="Form Structure" :already-opened="true">
<template #icon>
<svg class="h-5 w-5" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.8333 7.33333C14.7668 7.33333 15.2335 7.33333 15.59 7.15168C15.9036 6.99189 16.1586 6.73692 16.3183 6.42332C16.5 6.0668 16.5 5.60009 16.5 4.66667V4.16667C16.5 3.23325 16.5 2.76654 16.3183 2.41002C16.1586 2.09641 15.9036 1.84145 15.59 1.68166C15.2335 1.5 14.7668 1.5 13.8333 1.5L4.16667 1.5C3.23325 1.5 2.76654 1.5 2.41002 1.68166C2.09641 1.84144 1.84144 2.09641 1.68166 2.41002C1.5 2.76654 1.5 3.23325 1.5 4.16667L1.5 4.66667C1.5 5.60009 1.5 6.0668 1.68166 6.42332C1.84144 6.73692 2.09641 6.99189 2.41002 7.15168C2.76654 7.33333 3.23325 7.33333 4.16667 7.33333L13.8333 7.33333Z" stroke="currentColor" stroke-width="1.67" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.8333 16.5C14.7668 16.5 15.2335 16.5 15.59 16.3183C15.9036 16.1586 16.1586 15.9036 16.3183 15.59C16.5 15.2335 16.5 14.7668 16.5 13.8333V13.3333C16.5 12.3999 16.5 11.9332 16.3183 11.5767C16.1586 11.2631 15.9036 11.0081 15.59 10.8483C15.2335 10.6667 14.7668 10.6667 13.8333 10.6667L4.16667 10.6667C3.23325 10.6667 2.76654 10.6667 2.41002 10.8483C2.09641 11.0081 1.84144 11.2631 1.68166 11.5767C1.5 11.9332 1.5 12.3999 1.5 13.3333L1.5 13.8333C1.5 14.7668 1.5 15.2335 1.68166 15.59C1.84144 15.9036 2.09641 16.1586 2.41002 16.3183C2.76654 16.5 3.23325 16.5 4.16667 16.5H13.8333Z" stroke="currentColor" stroke-width="1.67" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</template>
<form-fields-editor class="mt-5" />
</editor-options-panel>
</template>
<script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
import FormFieldsEditor from '../FormFieldsEditor.vue'
export default {
components: { EditorOptionsPanel, FormFieldsEditor },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
}
}
}
</script>

View File

@@ -0,0 +1,90 @@
<template>
<div>
<button
class="flex items-center mt-3 cursor-pointer relative w-full rounded-lg flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full py-2 px-4 bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100"
@click.prevent="showModal=true"
>
<div class="flex-grow flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 inline" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M9 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path><path d="M15 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path><path d="M7.5 7.5c3.5 -1 5.5 -1 9 0"></path><path d="M7 16.5c3.5 1 6.5 1 10 0"></path><path d="M15.5 17c0 1 1.5 3 2 3c1.5 0 2.833 -1.667 3.5 -3c.667 -1.667 .5 -5.833 -1.5 -11.5c-1.457 -1.015 -3 -1.34 -4.5 -1.5l-1 2.5"></path><path d="M8.5 17c0 1 -1.356 3 -1.832 3c-1.429 0 -2.698 -1.667 -3.333 -3c-.635 -1.667 -.476 -5.833 1.428 -11.5c1.388 -1.015 2.782 -1.34 4.237 -1.5l1 2.5"></path></svg>
<p class="flex-grow text-center">
Discord Notifications
</p>
</div>
<div v-if="form.notifies_discord">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-5 h-5 text-nt-blue"
>
<path stroke-linecap="round" stroke-linejoin="round"
d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</div>
</button>
<modal :show="showModal" @close="showModal=false">
<h2 class="text-2xl font-bold z-10 truncate mb-5 text-nt-blue">
Discord Notifications
<pro-tag />
</h2>
<toggle-switch-input name="notifies_discord" :form="form" class="mt-4"
label="Receive a Discord notification on submission"
/>
<template v-if="form.notifies_discord">
<text-input name="discord_webhook_url" :form="form" class="mt-4"
label="Discord webhook url" help="help"
>
<template #help>
Receive a discord message on each form submission.
<a href="https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks" target="_blank">Click
here</a> to learn how to get a discord webhook url.
</template>
</text-input>
<h4 class="font-bold mt-4">Discord message actions</h4>
<form-notifications-message-actions v-model="form.notification_settings.discord" />
</template>
</modal>
</div>
</template>
<script>
import { useWorkingFormStore } from '../../../../../../stores/working_form'
import ProTag from '~/components/global/ProTag.vue'
import FormNotificationsMessageActions from './FormNotificationsMessageActions.vue'
export default {
components: { ProTag, FormNotificationsMessageActions },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
showModal: false
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
}
},
watch: {},
mounted () {
if(!this.form.notification_settings.discord || Array.isArray(this.form.notification_settings.discord)){
this.form.notification_settings.discord = {}
}
},
methods: {}
}
</script>

View File

@@ -0,0 +1,64 @@
<template>
<div>
<toggle-switch-input name="include_submission_data" v-model="compVal.include_submission_data" class="mt-4"
label="Include submission data"
help="With form submission answers"
/>
<toggle-switch-input name="link_open_form" v-model="compVal.link_open_form" class="mt-4"
label="Open Form"
help="Link to the form public page"
/>
<toggle-switch-input name="link_edit_form" v-model="compVal.link_edit_form" class="mt-4"
label="Edit Form"
help="Link to the form admin page"
/>
<toggle-switch-input name="views_submissions_count" v-model="compVal.views_submissions_count" class="mt-4"
label="Analytics (views & submissions)"
/>
<toggle-switch-input name="link_edit_submission" v-model="compVal.link_edit_submission" class="mt-4"
label="Link to the Edit Submission Record"
/>
</div>
</template>
<script>
export default {
name: 'FormNotificationsMessageActions',
components: { },
props: {
value: { required: false }
},
data () {
return {
content: this.value ?? {}
}
},
computed: {
compVal: {
set (val) {
this.content = val
this.$emit('input', this.compVal)
},
get () {
return this.content
}
}
},
watch: {},
created () {
if(this.compVal === undefined || this.compVal === null){
this.compVal = {}
}
['include_submission_data', 'link_open_form', 'link_edit_form', 'views_submissions_count', 'link_edit_submission'].forEach((keyname) => {
if (this.compVal[keyname] === undefined) {
this.compVal[keyname] = true
}
})
},
methods: { }
}
</script>

View File

@@ -0,0 +1,103 @@
<template>
<div>
<button
class="flex items-center mt-3 cursor-pointer relative w-full rounded-lg flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full py-2 px-4 bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100"
@click.prevent="showModal=true"
>
<div class="flex-grow flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-5 h-5 inline"
>
<path stroke-linecap="round" stroke-linejoin="round"
d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75"
/>
</svg>
<p class="flex-grow text-center">
Email Notifications
</p>
</div>
<div v-if="form.notifies">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-5 h-5 text-nt-blue"
>
<path stroke-linecap="round" stroke-linejoin="round"
d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</div>
</button>
<modal :show="showModal" @close="showModal=false">
<h2 class="text-2xl font-bold z-10 truncate mb-5 text-nt-blue">
Form Notifications
<pro-tag />
</h2>
<toggle-switch-input name="notifies" :form="form" class="mt-4"
label="Receive email notifications on submission"
/>
<template v-if="form.notifies">
<text-input name="notification_reply_to"
v-model="form.notification_settings.notification_reply_to" class="mt-4"
label="Notification Reply To"
:help="notifiesHelp"
/>
<text-area-input name="notification_emails" :form="form" class="mt-4"
label="Notification Emails" help="Add one email per line"
/>
</template>
</modal>
</div>
</template>
<script>
import { useWorkingFormStore } from '../../../../../../stores/working_form'
import ProTag from '~/components/global/ProTag.vue'
export default {
components: { ProTag },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
showModal: false
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
},
replayToEmailField () {
const emailFields = this.form.properties.filter((field) => {
return field.type === 'email' && !field.hidden
})
if (emailFields.length === 1) return emailFields[0]
return null
},
notifiesHelp () {
if (this.replayToEmailField) {
return 'If empty, Reply-to for this notification will be the email filled in the field "' + this.replayToEmailField.name + '".'
}
return 'If empty, Reply-to for this notification will be your own email. Add a single email field to your form, and it will automatically become the reply to value.'
}
},
watch: {},
mounted () {
},
methods: {}
}
</script>

View File

@@ -0,0 +1,91 @@
<template>
<div>
<button
class="flex items-center mt-3 cursor-pointer relative w-full rounded-lg flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full py-2 px-4 bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100"
@click.prevent="showModal=true"
>
<div class="flex-grow flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 inline" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 10c-.83 0-1.5-.67-1.5-1.5v-5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5v5c0 .83-.67 1.5-1.5 1.5z" /><path d="M20.5 10H19V8.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" /><path d="M9.5 14c.83 0 1.5.67 1.5 1.5v5c0 .83-.67 1.5-1.5 1.5S8 21.33 8 20.5v-5c0-.83.67-1.5 1.5-1.5z" /><path d="M3.5 14H5v1.5c0 .83-.67 1.5-1.5 1.5S2 16.33 2 15.5 2.67 14 3.5 14z" /><path d="M14 14.5c0-.83.67-1.5 1.5-1.5h5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-5c-.83 0-1.5-.67-1.5-1.5z" /><path d="M15.5 19H14v1.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z" /><path d="M10 9.5C10 8.67 9.33 8 8.5 8h-5C2.67 8 2 8.67 2 9.5S2.67 11 3.5 11h5c.83 0 1.5-.67 1.5-1.5z" /><path d="M8.5 5H10V3.5C10 2.67 9.33 2 8.5 2S7 2.67 7 3.5 7.67 5 8.5 5z" /></svg>
<p class="flex-grow text-center">
Slack Notifications
</p>
</div>
<div v-if="form.notifies_slack">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-5 h-5 text-nt-blue"
>
<path stroke-linecap="round" stroke-linejoin="round"
d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</div>
</button>
<modal :show="showModal" @close="showModal=false">
<h2 class="text-2xl font-bold z-10 truncate mb-5 text-nt-blue">
Slack Notifications
<pro-tag />
</h2>
<toggle-switch-input name="notifies_slack" :form="form" class="mt-4"
label="Receive a Slack notification on submission"
/>
<template v-if="form.notifies_slack">
<text-input name="slack_webhook_url" :form="form" class="mt-4"
label="Slack webhook url" help="help"
>
<template #help>
Receive slack message on each form submission. <a href="https://api.slack.com/messaging/webhooks"
target="_blank"
>Click here</a> to learn how to get a slack
webhook url
</template>
</text-input>
<h4 class="font-bold mt-4">Slack message actions</h4>
<form-notifications-message-actions v-model="form.notification_settings.slack" />
</template>
</modal>
</div>
</template>
<script>
import { useWorkingFormStore } from '../../../../../../stores/working_form'
import ProTag from '~/components/global/ProTag.vue'
import FormNotificationsMessageActions from './FormNotificationsMessageActions.vue'
export default {
components: { ProTag, FormNotificationsMessageActions },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
showModal: false
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
}
},
watch: {},
mounted () {
if(!this.form.notification_settings.slack || Array.isArray(this.form.notification_settings.slack)){
this.form.notification_settings.slack = {}
}
},
methods: {}
}
</script>

View File

@@ -0,0 +1,122 @@
<template>
<div>
<button
class="flex items-center mt-3 cursor-pointer relative w-full rounded-lg flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full py-2 px-4 bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100"
@click.prevent="showModal=true"
>
<div class="flex-grow flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5" />
</svg>
<p class="flex-grow text-center">
Send submission confirmation
</p>
</div>
<div v-if="form.send_submission_confirmation">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-5 h-5 text-nt-blue"
>
<path stroke-linecap="round" stroke-linejoin="round"
d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</div>
</button>
<modal :show="showModal" @close="showModal=false">
<h2 class="text-2xl font-bold z-10 truncate mb-5 text-nt-blue">
Submission confirmation
<pro-tag />
</h2>
<toggle-switch-input :disabled="(emailSubmissionConfirmationField===null)?true:null" name="send_submission_confirmation"
:form="form" class="mt-4"
label="Send submission confirmation" :help="emailSubmissionConfirmationHelp"
/>
<template v-if="form.send_submission_confirmation">
<text-input v-model="form.notification_settings.confirmation_reply_to"
name="confirmation_reply_to" class="mt-4"
label="Confirmation Reply To" help="help"
>
<template #help>
If empty, Reply-to will be your own email.
</template>
</text-input>
<text-input name="notification_sender"
:form="form" class="mt-4"
label="Confirmation Email Sender Name" help="Emails will be sent from our email address but you can customize the name of the Sender"
/>
<text-input name="notification_subject"
:form="form" class="mt-4"
label="Confirmation email subject" help="Subject of the confirmation email that will be sent"
/>
<rich-text-area-input name="notification_body"
:form="form" class="mt-4"
label="Confirmation email content" help="Content of the confirmation email that will be sent"
/>
<toggle-switch-input name="notifications_include_submission"
:form="form" class="mt-4"
label="Include submission data" help="If enabled the confirmation email will contain form submission answers"
/>
</template>
</modal>
</div>
</template>
<script>
import { useWorkingFormStore } from '../../../../../../stores/working_form'
import ProTag from '~/components/global/ProTag.vue'
export default {
components: { ProTag },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
showModal: false
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
},
emailSubmissionConfirmationField () {
if (!this.form.properties || !Array.isArray(this.form.properties)) return null
const emailFields = this.form.properties.filter((field) => {
return field.type === 'email' && !field.hidden
})
if (emailFields.length === 1) return emailFields[0]
return null
},
emailSubmissionConfirmationHelp () {
if (this.emailSubmissionConfirmationField) {
return 'Confirmation will be sent to the email in the "' + this.emailSubmissionConfirmationField.name + '" field.'
}
return 'Only available if your form contains 1 email field.'
}
},
watch: {
emailSubmissionConfirmationField (val) {
if (val === null) {
this.form.send_submission_confirmation = false
}
}
},
mounted () {
},
methods: {}
}
</script>

View File

@@ -0,0 +1,89 @@
<template>
<div>
<button
class="flex items-center mt-3 cursor-pointer relative w-full rounded-lg flex-1 appearance-none border border-gray-300 dark:border-gray-600 w-full py-2 px-4 bg-white text-gray-700 dark:bg-notion-dark-light dark:text-gray-300 dark:placeholder-gray-500 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:border-transparent focus:ring-opacity-100"
@click.prevent="showModal=true"
>
<div class="flex-grow flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-5 h-5 inline"
>
<path stroke-linecap="round" stroke-linejoin="round"
d="M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 01-.657.643 48.39 48.39 0 01-4.163-.3c.186 1.613.293 3.25.315 4.907a.656.656 0 01-.658.663v0c-.355 0-.676-.186-.959-.401a1.647 1.647 0 00-1.003-.349c-1.036 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401v0c.31 0 .555.26.532.57a48.039 48.039 0 01-.642 5.056c1.518.19 3.058.309 4.616.354a.64.64 0 00.657-.643v0c0-.355-.186-.676-.401-.959a1.647 1.647 0 01-.349-1.003c0-1.035 1.008-1.875 2.25-1.875 1.243 0 2.25.84 2.25 1.875 0 .369-.128.713-.349 1.003-.215.283-.4.604-.4.959v0c0 .333.277.599.61.58a48.1 48.1 0 005.427-.63 48.05 48.05 0 00.582-4.717.532.532 0 00-.533-.57v0c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.035 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.37 0 .713.128 1.003.349.283.215.604.401.96.401v0a.656.656 0 00.658-.663 48.422 48.422 0 00-.37-5.36c-1.886.342-3.81.574-5.766.689a.578.578 0 01-.61-.58v0z"
/>
</svg>
<p class="flex-grow text-center">
Webhook Notifications
</p>
</div>
<div v-if="form.notifies_webhook">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-5 h-5 text-nt-blue"
>
<path stroke-linecap="round" stroke-linejoin="round"
d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</div>
</button>
<modal :show="showModal" @close="showModal=false">
<h2 class="text-2xl font-bold z-10 truncate mb-5 text-nt-blue">
Webhook Notifications
<pro-tag />
</h2>
<toggle-switch-input name="notifies_webhook" :form="form" class="mt-4"
label="Trigger a webhook notification on form submission"
@change="onToggleChange"
/>
<text-input v-if="form.notifies_webhook" name="webhook_url" :form="form" class="mt-4"
label="Webhook url" help="We will post form submissions to this endpoint"
/>
</modal>
</div>
</template>
<script>
import { useWorkingFormStore } from '../../../../../../stores/working_form'
import ProTag from '~/components/global/ProTag.vue'
export default {
components: { ProTag },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
showModal: false
}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
}
},
watch: {},
mounted () {
},
methods: {
onToggleChange () {
if (!this.form.notifies_webhook) {
this.form.webhook_url = ''
}
}
}
}
</script>