Remove vform - working on form public page
This commit is contained in:
parent
8db2b09767
commit
e2dd0295ff
|
@ -156,7 +156,7 @@ class Form extends Model implements CachableAttributes
|
||||||
if ($this->custom_domain) {
|
if ($this->custom_domain) {
|
||||||
return 'https://' . $this->custom_domain . '/forms/' . $this->slug;
|
return 'https://' . $this->custom_domain . '/forms/' . $this->slug;
|
||||||
}
|
}
|
||||||
return url('/forms/' . $this->slug);
|
return '/forms/' . $this->slug;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getEditUrlAttribute()
|
public function getEditUrlAttribute()
|
||||||
|
|
|
@ -108,20 +108,17 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
|
||||||
import OpenForm from './OpenForm.vue'
|
import OpenForm from './OpenForm.vue'
|
||||||
import OpenFormButton from './OpenFormButton.vue'
|
import OpenFormButton from './OpenFormButton.vue'
|
||||||
import { themes } from '~/lib/forms/form-themes.js'
|
import { themes } from '~/lib/forms/form-themes.js'
|
||||||
import VButton from '~/components/global/VButton.vue'
|
import VButton from '~/components/global/VButton.vue'
|
||||||
import VTransition from '~/components/global/transitions/VTransition.vue'
|
|
||||||
import FormPendingSubmissionKey from '../../../mixins/forms/form-pending-submission-key.js'
|
|
||||||
import FormCleanings from '../../pages/forms/show/FormCleanings.vue'
|
import FormCleanings from '../../pages/forms/show/FormCleanings.vue'
|
||||||
|
import VTransition from '~/components/global/transitions/VTransition.vue'
|
||||||
|
import {pendingSubmission} from "~/composables/forms/pendingSubmission.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { VTransition, VButton, OpenFormButton, OpenForm, FormCleanings },
|
components: { VTransition, VButton, OpenFormButton, OpenForm, FormCleanings },
|
||||||
|
|
||||||
mixins: [FormPendingSubmissionKey],
|
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
form: { type: Object, required: true },
|
form: { type: Object, required: true },
|
||||||
creating: { type: Boolean, default: false }, // If true, fake form submit
|
creating: { type: Boolean, default: false }, // If true, fake form submit
|
||||||
|
@ -129,9 +126,10 @@ export default {
|
||||||
submitButtonClass: { type: String, default: '' }
|
submitButtonClass: { type: String, default: '' }
|
||||||
},
|
},
|
||||||
|
|
||||||
setup() {
|
setup(props) {
|
||||||
return {
|
return {
|
||||||
isIframe: useIsIframe()
|
isIframe: useIsIframe(),
|
||||||
|
pendingSubmission: pendingSubmission(props.form)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -140,7 +138,7 @@ export default {
|
||||||
loading: false,
|
loading: false,
|
||||||
submitted: false,
|
submitted: false,
|
||||||
themes: themes,
|
themes: themes,
|
||||||
passwordForm: new Form({
|
passwordForm: useForm({
|
||||||
password: null
|
password: null
|
||||||
}),
|
}),
|
||||||
hidePasswordDisabledMsg: false,
|
hidePasswordDisabledMsg: false,
|
||||||
|
@ -163,9 +161,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted () {
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
submitForm (form, onFailure) {
|
submitForm (form, onFailure) {
|
||||||
if (this.creating) {
|
if (this.creating) {
|
||||||
|
@ -175,8 +170,8 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.closeAlert()
|
// this.closeAlert()
|
||||||
form.post('/api/forms/' + this.form.slug + '/answer').then((response) => {
|
form.post('/forms/' + this.form.slug + '/answer').then((data) => {
|
||||||
this.$logEvent('form_submission', {
|
this.$logEvent('form_submission', {
|
||||||
workspace_id: this.form.workspace_id,
|
workspace_id: this.form.workspace_id,
|
||||||
form_id: this.form.id
|
form_id: this.form.id
|
||||||
|
@ -202,15 +197,15 @@ export default {
|
||||||
}, '*')
|
}, '*')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
window.localStorage.removeItem(this.formPendingSubmissionKey)
|
this.pendingSubmission.remove()
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
if (response.data.redirect && response.data.redirect_url) {
|
if (data.redirect && data.redirect_url) {
|
||||||
window.location.href = response.data.redirect_url
|
window.location.href = data.redirect_url
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.data.submission_id) {
|
if (data.submission_id) {
|
||||||
this.submissionId = response.data.submission_id
|
this.submissionId = data.submission_id
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
@ -223,7 +218,8 @@ export default {
|
||||||
}
|
}
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
if (error.response && error.response.data && error.response.data.message) {
|
if (error.response && error.response.data && error.response.data.message) {
|
||||||
this.alertError(error.response.data.message)
|
console.error(error)
|
||||||
|
// this.alertError(error.response.data.message)
|
||||||
}
|
}
|
||||||
this.loading = false
|
this.loading = false
|
||||||
onFailure()
|
onFailure()
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<form v-else-if="dataForm" @submit.prevent="">
|
<form v-else-if="dataForm" @submit.prevent="">
|
||||||
<transition name="fade" mode="out-in" appear>
|
<transition name="fade" mode="out-in">
|
||||||
<div :key="currentFieldGroupIndex" class="form-group flex flex-wrap w-full">
|
<div :key="currentFieldGroupIndex" class="form-group flex flex-wrap w-full">
|
||||||
<draggable v-model="currentFields"
|
<draggable v-model="currentFields"
|
||||||
item-key="id"
|
item-key="id"
|
||||||
|
@ -61,22 +61,17 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import Form from 'vform'
|
|
||||||
import OpenFormButton from './OpenFormButton.vue'
|
|
||||||
import clonedeep from 'clone-deep'
|
import clonedeep from 'clone-deep'
|
||||||
import FormLogicPropertyResolver from "~/lib/forms/FormLogicPropertyResolver.js";
|
|
||||||
import OpenFormField from './OpenFormField.vue'
|
|
||||||
import draggable from 'vuedraggable'
|
import draggable from 'vuedraggable'
|
||||||
import FormPendingSubmissionKey from '../../../mixins/forms/form-pending-submission-key.js'
|
import OpenFormButton from './OpenFormButton.vue'
|
||||||
|
import VueHcaptcha from "@hcaptcha/vue3-hcaptcha"
|
||||||
draggable.compatConfig = { MODE: 3 }
|
import OpenFormField from './OpenFormField.vue'
|
||||||
|
import {pendingSubmission} from "~/composables/forms/pendingSubmission.js";
|
||||||
const VueHcaptcha = () => import('@hcaptcha/vue3-hcaptcha')
|
import FormLogicPropertyResolver from "~/lib/forms/FormLogicPropertyResolver.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'OpenForm',
|
name: 'OpenForm',
|
||||||
components: { draggable, OpenFormField, OpenFormButton, VueHcaptcha },
|
components: { draggable, OpenFormField, OpenFormButton, VueHcaptcha },
|
||||||
mixins: [FormPendingSubmissionKey],
|
|
||||||
props: {
|
props: {
|
||||||
form: {
|
form: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -101,24 +96,26 @@ export default {
|
||||||
adminPreview: { type: Boolean, default: false } // If used in FormEditorPreview
|
adminPreview: { type: Boolean, default: false } // If used in FormEditorPreview
|
||||||
},
|
},
|
||||||
|
|
||||||
setup () {
|
setup (props) {
|
||||||
const recordsStore = useRecordsStore()
|
const recordsStore = useRecordsStore()
|
||||||
const workingFormStore = useWorkingFormStore()
|
const workingFormStore = useWorkingFormStore()
|
||||||
|
const dataForm = ref(useForm())
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
dataForm,
|
||||||
recordsStore,
|
recordsStore,
|
||||||
workingFormStore,
|
workingFormStore,
|
||||||
darkModeEnabled: useDark()
|
darkModeEnabled: useDark(),
|
||||||
|
pendingSubmission: pendingSubmission(props.form)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
dataForm: null,
|
|
||||||
currentFieldGroupIndex: 0,
|
currentFieldGroupIndex: 0,
|
||||||
/**
|
/**
|
||||||
* Used to force refresh components by changing their keys
|
* Used to force refresh components by changing their keys
|
||||||
*/
|
*/
|
||||||
formVersionId: 1,
|
|
||||||
isAutoSubmit: false,
|
isAutoSubmit: false,
|
||||||
/**
|
/**
|
||||||
* If currently dragging a field
|
* If currently dragging a field
|
||||||
|
@ -128,7 +125,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
hCaptchaSiteKey: () => this.$config.hCaptchaSiteKey,
|
hCaptchaSiteKey: () => useAppConfig().hCaptchaSiteKey,
|
||||||
/**
|
/**
|
||||||
* Create field groups (or Page) using page breaks if any
|
* Create field groups (or Page) using page breaks if any
|
||||||
*/
|
*/
|
||||||
|
@ -191,7 +188,7 @@ export default {
|
||||||
return this.currentFieldGroupIndex === (this.fieldGroups.length - 1)
|
return this.currentFieldGroupIndex === (this.fieldGroups.length - 1)
|
||||||
},
|
},
|
||||||
isPublicFormPage () {
|
isPublicFormPage () {
|
||||||
return this.$route.name === 'forms.show_public'
|
return this.$route.name === 'forms-slug'
|
||||||
},
|
},
|
||||||
dataFormValue () {
|
dataFormValue () {
|
||||||
// For get values instead of Id for select/multi select options
|
// For get values instead of Id for select/multi select options
|
||||||
|
@ -226,19 +223,11 @@ export default {
|
||||||
this.initForm()
|
this.initForm()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
theme: {
|
dataFormValue: {
|
||||||
handler () {
|
|
||||||
this.formVersionId++
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dataForm: {
|
|
||||||
deep: true,
|
deep: true,
|
||||||
handler () {
|
handler () {
|
||||||
if (this.isPublicFormPage && this.form && this.form.auto_save && this.dataFormValue) {
|
if (this.isPublicFormPage && this.form && this.form.auto_save) {
|
||||||
try {
|
this.pendingSubmission.set(this.dataFormValue)
|
||||||
window.localStorage.setItem(this.formPendingSubmissionKey, JSON.stringify(this.dataFormValue))
|
|
||||||
} catch (e) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,8 +235,7 @@ export default {
|
||||||
|
|
||||||
mounted () {
|
mounted () {
|
||||||
this.initForm()
|
this.initForm()
|
||||||
|
if (window.client && window.location.href.includes('auto_submit=true')) {
|
||||||
if (window.location.href.includes('auto_submit=true')) {
|
|
||||||
this.isAutoSubmit = true
|
this.isAutoSubmit = true
|
||||||
this.submitForm()
|
this.submitForm()
|
||||||
}
|
}
|
||||||
|
@ -318,20 +306,14 @@ export default {
|
||||||
this.form.submission_id = urlParam.get('submission_id')
|
this.form.submission_id = urlParam.get('submission_id')
|
||||||
const data = await this.getSubmissionData()
|
const data = await this.getSubmissionData()
|
||||||
if (data !== null && data) {
|
if (data !== null && data) {
|
||||||
this.dataForm = new Form(data)
|
this.dataForm = useForm(data)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.isPublicFormPage && this.form.auto_save) {
|
if (this.isPublicFormPage && this.form.auto_save) {
|
||||||
let pendingData
|
let pendingData = this.pendingSubmission.get()
|
||||||
try {
|
|
||||||
pendingData = window.localStorage.getItem(this.formPendingSubmissionKey)
|
|
||||||
} catch (e) {
|
|
||||||
pendingData = null
|
|
||||||
}
|
|
||||||
if (pendingData !== null && pendingData) {
|
if (pendingData !== null && pendingData) {
|
||||||
pendingData = JSON.parse(pendingData)
|
|
||||||
this.fields.forEach((field) => {
|
this.fields.forEach((field) => {
|
||||||
if (field.type === 'date' && field.prefill_today === true) { // For Prefill with 'today'
|
if (field.type === 'date' && field.prefill_today === true) { // For Prefill with 'today'
|
||||||
const dateObj = new Date()
|
const dateObj = new Date()
|
||||||
|
@ -345,7 +327,7 @@ export default {
|
||||||
pendingData[field.id] = currentDate
|
pendingData[field.id] = currentDate
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.dataForm = new Form(pendingData)
|
this.dataForm = useForm(pendingData)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -389,7 +371,7 @@ export default {
|
||||||
formData[field.id] = field.prefill
|
formData[field.id] = field.prefill
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.dataForm = new Form(formData)
|
this.dataForm = useForm(formData)
|
||||||
},
|
},
|
||||||
previousPage () {
|
previousPage () {
|
||||||
this.currentFieldGroupIndex -= 1
|
this.currentFieldGroupIndex -= 1
|
||||||
|
|
|
@ -81,12 +81,10 @@
|
||||||
<script>
|
<script>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import FormLogicPropertyResolver from "~/lib/forms/FormLogicPropertyResolver.js"
|
import FormLogicPropertyResolver from "~/lib/forms/FormLogicPropertyResolver.js"
|
||||||
import FormPendingSubmissionKey from '~/mixins/forms/form-pending-submission-key.js'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'OpenFormField',
|
name: 'OpenFormField',
|
||||||
components: {},
|
components: {},
|
||||||
mixins: [FormPendingSubmissionKey],
|
|
||||||
props: {
|
props: {
|
||||||
form: {
|
form: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -115,7 +113,7 @@ export default {
|
||||||
adminPreview: { type: Boolean, default: false } // If used in FormEditorPreview
|
adminPreview: { type: Boolean, default: false } // If used in FormEditorPreview
|
||||||
},
|
},
|
||||||
|
|
||||||
setup () {
|
setup (props) {
|
||||||
const workingFormStore = useWorkingFormStore()
|
const workingFormStore = useWorkingFormStore()
|
||||||
return {
|
return {
|
||||||
workingFormStore,
|
workingFormStore,
|
||||||
|
@ -125,20 +123,6 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
fieldComponents () {
|
|
||||||
return {
|
|
||||||
text: 'TextInput',
|
|
||||||
number: 'TextInput',
|
|
||||||
select: 'SelectInput',
|
|
||||||
multi_select: 'SelectInput',
|
|
||||||
date: 'DateInput',
|
|
||||||
files: 'FileInput',
|
|
||||||
checkbox: 'CheckboxInput',
|
|
||||||
url: 'TextInput',
|
|
||||||
email: 'TextInput',
|
|
||||||
phone_number: 'TextInput'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* Get the right input component for the field/options combination
|
* Get the right input component for the field/options combination
|
||||||
*/
|
*/
|
||||||
|
@ -168,7 +152,18 @@ export default {
|
||||||
if (field.type === 'phone_number' && !field.use_simple_text_input) {
|
if (field.type === 'phone_number' && !field.use_simple_text_input) {
|
||||||
return 'PhoneInput'
|
return 'PhoneInput'
|
||||||
}
|
}
|
||||||
return this.fieldComponents[field.type]
|
return {
|
||||||
|
text: 'TextInput',
|
||||||
|
number: 'TextInput',
|
||||||
|
select: 'SelectInput',
|
||||||
|
multi_select: 'SelectInput',
|
||||||
|
date: 'DateInput',
|
||||||
|
files: 'FileInput',
|
||||||
|
checkbox: 'CheckboxInput',
|
||||||
|
url: 'TextInput',
|
||||||
|
email: 'TextInput',
|
||||||
|
phone_number: 'TextInput'
|
||||||
|
}[field.type]
|
||||||
},
|
},
|
||||||
isPublicFormPage () {
|
isPublicFormPage () {
|
||||||
return this.$route.name === 'forms.show_public'
|
return this.$route.name === 'forms.show_public'
|
||||||
|
|
|
@ -89,7 +89,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Fuse from 'fuse.js'
|
import Fuse from 'fuse.js'
|
||||||
import Form from 'vform'
|
|
||||||
import clonedeep from 'clone-deep'
|
import clonedeep from 'clone-deep'
|
||||||
import VSwitch from '../../../forms/components/VSwitch.vue'
|
import VSwitch from '../../../forms/components/VSwitch.vue'
|
||||||
import OpenTable from '../../tables/OpenTable.vue'
|
import OpenTable from '../../tables/OpenTable.vue'
|
||||||
|
@ -117,7 +116,7 @@ export default {
|
||||||
properties: [],
|
properties: [],
|
||||||
removed_properties: [],
|
removed_properties: [],
|
||||||
displayColumns: {},
|
displayColumns: {},
|
||||||
searchForm: new Form({
|
searchForm: useForm({
|
||||||
search: ''
|
search: ''
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,10 +61,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
|
||||||
import clonedeep from 'clone-deep'
|
import clonedeep from 'clone-deep'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useWorkingFormStore } from '../../../../../stores/working_form'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AddFormBlock',
|
name: 'AddFormBlock',
|
||||||
|
@ -206,7 +204,7 @@ export default {
|
||||||
this.workingFormStore.closeAddFieldSidebar()
|
this.workingFormStore.closeAddFieldSidebar()
|
||||||
},
|
},
|
||||||
reset () {
|
reset () {
|
||||||
this.blockForm = new Form({
|
this.blockForm = useForm({
|
||||||
type: null,
|
type: null,
|
||||||
name: null
|
name: null
|
||||||
})
|
})
|
||||||
|
@ -222,7 +220,6 @@ export default {
|
||||||
}
|
}
|
||||||
newBlock.help_position = 'below_input'
|
newBlock.help_position = 'below_input'
|
||||||
if (this.selectedFieldIndex === null || this.selectedFieldIndex === undefined) {
|
if (this.selectedFieldIndex === null || this.selectedFieldIndex === undefined) {
|
||||||
console.log('------',this.form)
|
|
||||||
const newFields = clonedeep(this.form.properties)
|
const newFields = clonedeep(this.form.properties)
|
||||||
newFields.push(newBlock)
|
newFields.push(newBlock)
|
||||||
this.form.properties = newFields
|
this.form.properties = newFields
|
||||||
|
|
|
@ -73,10 +73,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import Form from 'vform'
|
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useAuthStore } from '../../../../../stores/auth'
|
|
||||||
import { useTemplatesStore } from '../../../../../stores/templates'
|
|
||||||
import QuestionsEditor from './QuestionsEditor.vue'
|
import QuestionsEditor from './QuestionsEditor.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -105,7 +102,7 @@ export default {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
mounted () {
|
mounted () {
|
||||||
this.templateForm = new Form(this.template ?? {
|
this.templateForm = useForm(this.template ?? {
|
||||||
publicly_listed: false,
|
publicly_listed: false,
|
||||||
name: '',
|
name: '',
|
||||||
slug: '',
|
slug: '',
|
||||||
|
|
|
@ -78,7 +78,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { useWorkingFormStore } from '../../../stores/working_form'
|
|
||||||
import OpenText from './components/OpenText.vue'
|
import OpenText from './components/OpenText.vue'
|
||||||
import OpenUrl from './components/OpenUrl.vue'
|
import OpenUrl from './components/OpenUrl.vue'
|
||||||
import OpenSelect from './components/OpenSelect.vue'
|
import OpenSelect from './components/OpenSelect.vue'
|
||||||
|
@ -89,19 +88,6 @@ import ResizableTh from './components/ResizableTh.vue'
|
||||||
import RecordOperations from '../components/RecordOperations.vue'
|
import RecordOperations from '../components/RecordOperations.vue'
|
||||||
import clonedeep from 'clone-deep'
|
import clonedeep from 'clone-deep'
|
||||||
|
|
||||||
const cyrb53 = function (str, seed = 0) {
|
|
||||||
let h1 = 0xdeadbeef ^ seed
|
|
||||||
let h2 = 0x41c6ce57 ^ seed
|
|
||||||
for (let i = 0, ch; i < str.length; i++) {
|
|
||||||
ch = str.charCodeAt(i)
|
|
||||||
h1 = Math.imul(h1 ^ ch, 2654435761)
|
|
||||||
h2 = Math.imul(h2 ^ ch, 1597334677)
|
|
||||||
}
|
|
||||||
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909)
|
|
||||||
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909)
|
|
||||||
return 4294967296 * (2097151 & h2) + (h1 >>> 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { ResizableTh, RecordOperations },
|
components: { ResizableTh, RecordOperations },
|
||||||
props: {
|
props: {
|
||||||
|
@ -181,7 +167,7 @@ export default {
|
||||||
|
|
||||||
mounted () {
|
mounted () {
|
||||||
const parent = document.getElementById('table-page')
|
const parent = document.getElementById('table-page')
|
||||||
this.tableHash = cyrb53(JSON.stringify(this.form.properties))
|
this.tableHash = hash(JSON.stringify(this.form.properties))
|
||||||
if (parent) {
|
if (parent) {
|
||||||
parent.addEventListener('scroll', this.handleScroll, { passive: true })
|
parent.addEventListener('scroll', this.handleScroll, { passive: true })
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ForgotPasswordModal',
|
name: 'ForgotPasswordModal',
|
||||||
components: { },
|
components: { },
|
||||||
|
@ -60,7 +58,7 @@
|
||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
isMailSent: false,
|
isMailSent: false,
|
||||||
form: new Form({
|
form: useForm({
|
||||||
email: ''
|
email: ''
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -48,8 +48,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'RegisterForm',
|
name: 'RegisterForm',
|
||||||
components: {},
|
components: {},
|
||||||
|
@ -71,7 +69,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
form: new Form({
|
form: useForm({
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
password: '',
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import Form from 'vform'
|
|
||||||
import { useAuthStore } from '../../../stores/auth'
|
|
||||||
import TextInput from '../../forms/TextInput.vue'
|
import TextInput from '../../forms/TextInput.vue'
|
||||||
import VButton from '~/components/global/VButton.vue'
|
import VButton from '~/components/global/VButton.vue'
|
||||||
|
|
||||||
|
@ -43,7 +41,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
form: new Form({
|
form: useForm({
|
||||||
name: '',
|
name: '',
|
||||||
email: ''
|
email: ''
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import {hash} from "~/lib/utils.js"
|
||||||
|
import {useStorage} from "@vueuse/core"
|
||||||
|
|
||||||
|
export const pendingSubmission = (form) => {
|
||||||
|
|
||||||
|
const formPendingSubmissionKey = computed(() => {
|
||||||
|
return (form) ? form.form_pending_submission_key + '-' + hash(window.location.href) : ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const enabled = computed(() => {
|
||||||
|
return form?.auto_save ?? false
|
||||||
|
})
|
||||||
|
|
||||||
|
const set = (value) => {
|
||||||
|
if (process.server || !enabled.value) return
|
||||||
|
useStorage(formPendingSubmissionKey.value).value = JSON.stringify(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const remove = () => {
|
||||||
|
return set(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const get = (defaultValue = {}) => {
|
||||||
|
if (process.server || !enabled.value) return
|
||||||
|
const pendingSubmission = useStorage(formPendingSubmissionKey.value).value
|
||||||
|
return pendingSubmission ? JSON.parse(pendingSubmission) : defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
enabled,
|
||||||
|
set,
|
||||||
|
get
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import Form from "~/composables/lib/vForm/Form.js"
|
import Form from "~/composables/lib/vForm/Form.js"
|
||||||
|
|
||||||
export const useForm = (formData) => {
|
export const useForm = (formData = {}) => {
|
||||||
return new Form(formData)
|
return new Form(formData)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
* Handle form public pages dark mode and transparent mode
|
||||||
|
*/
|
||||||
|
export function handleDarkMode (darkMode, elem = null) {
|
||||||
|
if (process.server) return
|
||||||
|
const darkModeNodeParent = elem ?? document.body
|
||||||
|
|
||||||
|
// Dark mode
|
||||||
|
if (['dark', 'light'].includes(darkMode)) {
|
||||||
|
return handleDarkModeToggle(darkMode === 'dark')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case auto
|
||||||
|
handleDarkModeToggle(window.matchMedia('(prefers-color-scheme: dark)').matches, darkModeNodeParent)
|
||||||
|
|
||||||
|
// Create listener
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)')
|
||||||
|
.addEventListener('change', handleDarkModeToggle)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDarkModeToggle (enabled, darkModeNodeParent) {
|
||||||
|
if (enabled !== false && enabled !== true) {
|
||||||
|
// if we received an event
|
||||||
|
enabled = enabled.matches
|
||||||
|
}
|
||||||
|
enabled ? darkModeNodeParent.classList.add('dark') : darkModeNodeParent.classList.remove('dark')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function disableDarkMode () {
|
||||||
|
if (process.server) return
|
||||||
|
const body = document.body
|
||||||
|
body.classList.remove('dark')
|
||||||
|
// Remove event listener
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', handleDarkModeToggle)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleTransparentMode (transparentModeEnabled) {
|
||||||
|
if (process.server) return
|
||||||
|
if (!useIsIframe() || !transparentModeEnabled) return
|
||||||
|
|
||||||
|
const app = document.getElementById('app')
|
||||||
|
app.classList.remove('bg-white')
|
||||||
|
app.classList.remove('dark:bg-notion-dark')
|
||||||
|
app.classList.add('bg-transparent')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function focusOnFirstFormElement() {
|
||||||
|
if (process.server) return
|
||||||
|
for (const ele of document.querySelectorAll('input,button,textarea,[role="button"]')) {
|
||||||
|
if (ele.offsetWidth !== 0 || ele.offsetHeight !== 0) {
|
||||||
|
ele.focus()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
const cyrb53 = (str, seed = 0) => {
|
|
||||||
|
export const hash = (str, seed = 0) => {
|
||||||
let h1 = 0xdeadbeef ^ seed,
|
let h1 = 0xdeadbeef ^ seed,
|
||||||
h2 = 0x41c6ce57 ^ seed;
|
h2 = 0x41c6ce57 ^ seed;
|
||||||
for (let i = 0, ch; i < str.length; i++) {
|
for (let i = 0, ch; i < str.length; i++) {
|
||||||
|
@ -11,12 +12,4 @@ const cyrb53 = (str, seed = 0) => {
|
||||||
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
||||||
|
|
||||||
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
computed: {
|
|
||||||
formPendingSubmissionKey() {
|
|
||||||
return (this.form) ? this.form.form_pending_submission_key + '-' + cyrb53(window.location.href) : ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -23,7 +23,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
|
||||||
import OpenFormFooter from '../../../components/pages/OpenFormFooter.vue'
|
import OpenFormFooter from '../../../components/pages/OpenFormFooter.vue'
|
||||||
import SeoMeta from '../../../mixins/seo-meta.js'
|
import SeoMeta from '../../../mixins/seo-meta.js'
|
||||||
|
|
||||||
|
@ -38,7 +37,7 @@ export default {
|
||||||
data: () => ({
|
data: () => ({
|
||||||
metaTitle: 'Reset Password',
|
metaTitle: 'Reset Password',
|
||||||
status: '',
|
status: '',
|
||||||
form: new Form({
|
form: useForm({
|
||||||
email: ''
|
email: ''
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
|
||||||
import OpenFormFooter from '../../../components/pages/OpenFormFooter.vue'
|
import OpenFormFooter from '../../../components/pages/OpenFormFooter.vue'
|
||||||
import SeoMeta from '../../../mixins/seo-meta.js'
|
import SeoMeta from '../../../mixins/seo-meta.js'
|
||||||
|
|
||||||
|
@ -48,7 +47,7 @@ export default {
|
||||||
data: () => ({
|
data: () => ({
|
||||||
metaTitle: 'Reset Password',
|
metaTitle: 'Reset Password',
|
||||||
status: '',
|
status: '',
|
||||||
form: new Form({
|
form: useForm({
|
||||||
token: '',
|
token: '',
|
||||||
email: '',
|
email: '',
|
||||||
password: '',
|
password: '',
|
||||||
|
|
|
@ -24,17 +24,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
|
||||||
import SeoMeta from '../../../mixins/seo-meta.js'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [SeoMeta],
|
|
||||||
middleware: 'guest',
|
middleware: 'guest',
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
metaTitle: 'Verify Email',
|
metaTitle: 'Verify Email',
|
||||||
status: '',
|
status: '',
|
||||||
form: new Form({
|
form: useForm({
|
||||||
email: ''
|
email: ''
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div v-if="form && !isIframe && (form.logo_picture || form.cover_picture)">
|
||||||
|
<div v-if="form.cover_picture">
|
||||||
|
<div id="cover-picture" class="max-h-56 w-full overflow-hidden flex items-center justify-center">
|
||||||
|
<img alt="Form Cover Picture" :src="form.cover_picture" class="w-full">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="form.logo_picture" class="w-full p-5 relative mx-auto"
|
||||||
|
:class="{'pt-20':!form.cover_picture, 'md:w-3/5 lg:w-1/2 md:max-w-2xl': form.width === 'centered', 'max-w-7xl': (form.width === 'full' && !isIframe) }"
|
||||||
|
>
|
||||||
|
<img alt="Logo Picture" :src="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>
|
||||||
|
<div class="w-full mx-auto px-4"
|
||||||
|
:class="{'mt-6':!isIframe, 'md:w-3/5 lg:w-1/2 md:max-w-2xl': form && (form.width === 'centered'), 'max-w-7xl': (form && form.width === 'full' && !isIframe)}"
|
||||||
|
>
|
||||||
|
<div v-if="!formLoading && !form">
|
||||||
|
<h1 class="mt-6" v-text="'Whoops'"/>
|
||||||
|
<p class="mt-6">
|
||||||
|
Unfortunately we could not find this form. It may have been deleted by it's author.
|
||||||
|
</p>
|
||||||
|
<p class="mb-10 mt-4">
|
||||||
|
<router-link :to="{name:'index'}">
|
||||||
|
Create your form for free with OpnForm
|
||||||
|
</router-link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="formLoading">
|
||||||
|
<p class="text-center mt-6 p-4">
|
||||||
|
<loader class="h-6 w-6 text-nt-blue mx-auto"/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<template v-else>
|
||||||
|
<div v-if="recordLoading">
|
||||||
|
<p class="text-center mt-6 p-4">
|
||||||
|
<loader class="h-6 w-6 text-nt-blue mx-auto"/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<open-complete-form v-show="!recordLoading" ref="open-complete-form" :form="form" class="mb-10"
|
||||||
|
@password-entered="passwordEntered"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {computed} from 'vue'
|
||||||
|
import OpenCompleteForm from '../../components/open/forms/OpenCompleteForm.vue'
|
||||||
|
import sha256 from 'js-sha256'
|
||||||
|
import {onBeforeRouteLeave} from 'vue-router'
|
||||||
|
import {disableDarkMode, handleDarkMode, handleTransparentMode, focusOnFirstFormElement} from '~/lib/forms/public-page'
|
||||||
|
|
||||||
|
const crisp = useCrisp()
|
||||||
|
const formsStore = useFormsStore()
|
||||||
|
const recordsStore = useRecordsStore()
|
||||||
|
|
||||||
|
const isIframe = useIsIframe()
|
||||||
|
const formLoading = computed(() => formsStore.loading)
|
||||||
|
const recordLoading = computed(() => recordsStore.loading)
|
||||||
|
const slug = useRoute().params.slug
|
||||||
|
const form = computed(() => formsStore.getByKey(slug))
|
||||||
|
const submitted = ref(false)
|
||||||
|
|
||||||
|
crisp.hideChat()
|
||||||
|
onBeforeRouteLeave((to, from) => {
|
||||||
|
crisp.showChat()
|
||||||
|
disableDarkMode()
|
||||||
|
})
|
||||||
|
|
||||||
|
const passwordEntered = function (password) {
|
||||||
|
useCookie('password-' + slug, {
|
||||||
|
maxAge: {expires: 60 * 60 * 7},
|
||||||
|
sameSite: false,
|
||||||
|
secure: true
|
||||||
|
}).value = sha256(password)
|
||||||
|
loadForm(slug).then(() => {
|
||||||
|
if (form.value?.is_password_protected) {
|
||||||
|
this.$refs['open-complete-form'].addPasswordError('Invalid password.')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadForm = async () => {
|
||||||
|
if (formsStore.loading || form.value) return Promise.resolve()
|
||||||
|
console.info('Loading form', slug)
|
||||||
|
const {data, error} = await formsStore.publicLoad(slug)
|
||||||
|
if (error.value) {
|
||||||
|
formsStore.stopLoading()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
formsStore.save(data.value)
|
||||||
|
formsStore.stopLoading()
|
||||||
|
|
||||||
|
// Adapt page to form: colors, custom code etc
|
||||||
|
handleDarkMode(form.value)
|
||||||
|
handleTransparentMode(form.value)
|
||||||
|
|
||||||
|
if (process.server) return
|
||||||
|
if (form.value.custom_code) {
|
||||||
|
const scriptEl = document.createRange().createContextualFragment(form.value.custom_code)
|
||||||
|
document.head.append(scriptEl)
|
||||||
|
}
|
||||||
|
if (!isIframe) focusOnFirstFormElement()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadForm(slug)
|
||||||
|
})
|
||||||
|
|
||||||
|
await loadForm(slug)
|
||||||
|
|
||||||
|
// metaTitle () {
|
||||||
|
// if (this.form && this.form.is_pro && this.form.seo_meta.page_title) {
|
||||||
|
// return this.form.seo_meta.page_title
|
||||||
|
// }
|
||||||
|
// return this.form ? this.form.title : 'Create beautiful forms'
|
||||||
|
// },
|
||||||
|
// metaTemplate () {
|
||||||
|
// if (this.form && this.form.is_pro && this.form.seo_meta.page_title) {
|
||||||
|
// // Disable template if custom SEO title
|
||||||
|
// return '%s'
|
||||||
|
// }
|
||||||
|
// return null
|
||||||
|
// },
|
||||||
|
// metaDescription () {
|
||||||
|
// if (this.form && this.form.is_pro && this.form.seo_meta.page_description) {
|
||||||
|
// return this.form.seo_meta.page_description
|
||||||
|
// }
|
||||||
|
// return (this.form && this.form.description) ? this.form.description.substring(0, 160) : null
|
||||||
|
// },
|
||||||
|
// metaImage () {
|
||||||
|
// if (this.form && this.form.is_pro && this.form.seo_meta.page_thumbnail) {
|
||||||
|
// return this.form.seo_meta.page_thumbnail
|
||||||
|
// }
|
||||||
|
// return (this.form && this.form.cover_picture) ? this.form.cover_picture : null
|
||||||
|
// },
|
||||||
|
// metaTags () {
|
||||||
|
// return (this.form && this.form.can_be_indexed) ? [] : [{ name: 'robots', content: 'noindex' }]
|
||||||
|
// }
|
||||||
|
</script>
|
|
@ -128,7 +128,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import Form from 'vform'
|
|
||||||
import ProTag from '~/components/global/ProTag.vue'
|
import ProTag from '~/components/global/ProTag.vue'
|
||||||
import VButton from '~/components/global/VButton.vue'
|
import VButton from '~/components/global/VButton.vue'
|
||||||
import ExtraMenu from '../../../components/pages/forms/show/ExtraMenu.vue'
|
import ExtraMenu from '../../../components/pages/forms/show/ExtraMenu.vue'
|
||||||
|
@ -222,13 +221,13 @@ export default {
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
form () {
|
form () {
|
||||||
this.workingForm = new Form(this.form)
|
this.workingForm = useForm(this.form)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted () {
|
mounted () {
|
||||||
if (this.form) {
|
if (this.form) {
|
||||||
this.workingForm = new Form(this.form)
|
this.workingForm = useForm(this.form)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -25,13 +25,8 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import Form from 'vform'
|
|
||||||
import { useTemplatesStore } from '../../../stores/templates.js'
|
|
||||||
import { useWorkingFormStore } from '../../../stores/working_form.js'
|
|
||||||
import { useWorkspacesStore } from '../../../stores/workspaces.js'
|
|
||||||
import QuickRegister from '~/components/pages/auth/components/QuickRegister.vue'
|
import QuickRegister from '~/components/pages/auth/components/QuickRegister.vue'
|
||||||
import initForm from '../../../mixins/form_editor/initForm.js'
|
import initForm from '../../../mixins/form_editor/initForm.js'
|
||||||
import SeoMeta from '../../../mixins/seo-meta.js'
|
|
||||||
import CreateFormBaseModal from '../../../components/pages/forms/create/CreateFormBaseModal.vue'
|
import CreateFormBaseModal from '../../../components/pages/forms/create/CreateFormBaseModal.vue'
|
||||||
|
|
||||||
const loadTemplates = function () {
|
const loadTemplates = function () {
|
||||||
|
@ -47,7 +42,7 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
QuickRegister, CreateFormBaseModal
|
QuickRegister, CreateFormBaseModal
|
||||||
},
|
},
|
||||||
mixins: [initForm, SeoMeta],
|
mixins: [initForm],
|
||||||
middleware: 'guest',
|
middleware: 'guest',
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
beforeRouteEnter (to, from, next) {
|
||||||
|
@ -118,7 +113,7 @@ export default {
|
||||||
if (this.$route.query.template !== undefined && this.$route.query.template) {
|
if (this.$route.query.template !== undefined && this.$route.query.template) {
|
||||||
const template = this.templatesStore.getByKey(this.$route.query.template)
|
const template = this.templatesStore.getByKey(this.$route.query.template)
|
||||||
if (template && template.structure) {
|
if (template && template.structure) {
|
||||||
this.form = new Form({ ...this.form.data(), ...template.structure })
|
this.form = useForm({ ...this.form.data(), ...template.structure })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No template loaded, ask how to start
|
// No template loaded, ask how to start
|
||||||
|
@ -146,7 +141,7 @@ export default {
|
||||||
}, 500)
|
}, 500)
|
||||||
},
|
},
|
||||||
formGenerated (form) {
|
formGenerated (form) {
|
||||||
this.form = new Form({ ...this.form.data(), ...form })
|
this.form = useForm({ ...this.form.data(), ...form })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,222 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<div v-if="form && !isIframe && (form.logo_picture || form.cover_picture)">
|
|
||||||
<div v-if="form.cover_picture">
|
|
||||||
<div id="cover-picture" class="max-h-56 w-full overflow-hidden flex items-center justify-center">
|
|
||||||
<img alt="Form Cover Picture" :src="form.cover_picture" class="w-full">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="form.logo_picture" class="w-full p-5 relative mx-auto"
|
|
||||||
:class="{'pt-20':!form.cover_picture, 'md:w-3/5 lg:w-1/2 md:max-w-2xl': form.width === 'centered', 'max-w-7xl': (form.width === 'full' && !isIframe) }"
|
|
||||||
>
|
|
||||||
<img alt="Logo Picture" :src="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>
|
|
||||||
<div class="w-full mx-auto px-4"
|
|
||||||
:class="{'mt-6':!isIframe, 'md:w-3/5 lg:w-1/2 md:max-w-2xl': form && (form.width === 'centered'), 'max-w-7xl': (form && form.width === 'full' && !isIframe)}"
|
|
||||||
>
|
|
||||||
<div v-if="!formLoading && !form">
|
|
||||||
<h1 class="mt-6" v-text="'Whoops'" />
|
|
||||||
<p class="mt-6">
|
|
||||||
Unfortunately we could not find this form. It may have been deleted by it's author.
|
|
||||||
</p>
|
|
||||||
<p class="mb-10 mt-4">
|
|
||||||
<NuxtLink :to="{name:'index'}">
|
|
||||||
Create your form for free with OpnForm
|
|
||||||
</NuxtLink>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="formLoading">
|
|
||||||
<p class="text-center mt-6 p-4">
|
|
||||||
<Loader class="h-6 w-6 text-nt-blue mx-auto" />
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<template v-else>
|
|
||||||
<div v-if="recordLoading">
|
|
||||||
<p class="text-center mt-6 p-4">
|
|
||||||
<Loader class="h-6 w-6 text-nt-blue mx-auto" />
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<open-complete-form v-show="!recordLoading" ref="open-complete-form" :form="form" class="mb-10"
|
|
||||||
@password-entered="passwordEntered"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import axios from 'axios'
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { useFormsStore } from '../../stores/forms'
|
|
||||||
import { useRecordsStore } from '../../stores/records'
|
|
||||||
import OpenCompleteForm from '../../components/open/forms/OpenCompleteForm.vue'
|
|
||||||
import sha256 from 'js-sha256'
|
|
||||||
import SeoMeta from '../../mixins/seo-meta.js'
|
|
||||||
|
|
||||||
const isFrame = window.location !== window.parent.location || window.frameElement
|
|
||||||
|
|
||||||
function handleDarkMode (form) {
|
|
||||||
// Dark mode
|
|
||||||
const body = document.body
|
|
||||||
if (form.dark_mode === 'dark') {
|
|
||||||
body.classList.add('dark')
|
|
||||||
} else if (form.dark_mode === 'light') {
|
|
||||||
body.classList.remove('dark')
|
|
||||||
} else if (form.dark_mode === 'auto' && isFrame) {
|
|
||||||
// Remove dark mode if embed in a notion basic site
|
|
||||||
let parentUrl
|
|
||||||
try {
|
|
||||||
parentUrl = window.location.ancestorOrigins[0]
|
|
||||||
} catch (e) {
|
|
||||||
parentUrl = (window.location !== window.parent.location)
|
|
||||||
? document.referrer
|
|
||||||
: document.location.href
|
|
||||||
}
|
|
||||||
if (parentUrl.includes('.notion.site')) {
|
|
||||||
body.classList.remove('dark')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTransparentMode (form) {
|
|
||||||
const isFrame = window.location !== window.parent.location || window.frameElement
|
|
||||||
if (!isFrame || !form.transparent_background) return
|
|
||||||
|
|
||||||
const app = document.getElementById('app')
|
|
||||||
app.classList.remove('bg-white')
|
|
||||||
app.classList.remove('dark:bg-notion-dark')
|
|
||||||
app.classList.add('bg-transparent')
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadForm (slug) {
|
|
||||||
const formsStore = useFormsStore()
|
|
||||||
if (formsStore.loading) return
|
|
||||||
formsStore.startLoading()
|
|
||||||
return axios.get('/api/forms/' + slug).then((response) => {
|
|
||||||
const form = response.data
|
|
||||||
formsStore.set([response.data])
|
|
||||||
|
|
||||||
// Custom code injection
|
|
||||||
if (form.custom_code) {
|
|
||||||
const scriptEl = document.createRange().createContextualFragment(form.custom_code)
|
|
||||||
document.head.append(scriptEl)
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDarkMode(form)
|
|
||||||
handleTransparentMode(form)
|
|
||||||
|
|
||||||
formsStore.stopLoading()
|
|
||||||
}).catch(() => {
|
|
||||||
formsStore.stopLoading()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: { OpenCompleteForm },
|
|
||||||
mixins: [SeoMeta],
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
if (window.$crisp) {
|
|
||||||
window.$crisp.push(['do', 'chat:hide'])
|
|
||||||
}
|
|
||||||
next()
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteLeave (to, from, next) {
|
|
||||||
if (window.$crisp) {
|
|
||||||
window.$crisp.push(['do', 'chat:show'])
|
|
||||||
}
|
|
||||||
next()
|
|
||||||
},
|
|
||||||
|
|
||||||
setup () {
|
|
||||||
const formsStore = useFormsStore()
|
|
||||||
const recordsStore = useRecordsStore()
|
|
||||||
return {
|
|
||||||
formsStore,
|
|
||||||
forms : computed(() => formsStore.content),
|
|
||||||
formLoading : computed(() => formsStore.loading),
|
|
||||||
recordLoading : computed(() => recordsStore.loading)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
submitted: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
|
||||||
loadForm(this.formSlug).then(() => {
|
|
||||||
if (this.isIframe) return
|
|
||||||
// Auto focus on first input
|
|
||||||
const visibleElements = []
|
|
||||||
document.querySelectorAll('input,button,textarea,[role="button"]').forEach(ele => {
|
|
||||||
if (ele.offsetWidth !== 0 || ele.offsetHeight !== 0) {
|
|
||||||
visibleElements.push(ele)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (visibleElements.length > 0) {
|
|
||||||
visibleElements[0].focus()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
passwordEntered (password) {
|
|
||||||
if (process.client) {
|
|
||||||
useCookie('password-' + this.form.slug, {maxAge: { expires: 60*60*7}}).value = sha256(password)
|
|
||||||
}
|
|
||||||
loadForm(this.formSlug).then(() => {
|
|
||||||
if (this.form.is_password_protected) {
|
|
||||||
this.$refs['open-complete-form'].addPasswordError('Invalid password.')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
formSlug () {
|
|
||||||
return this.$route.params.slug
|
|
||||||
},
|
|
||||||
form () {
|
|
||||||
return this.formsStore.getByKey(this.formSlug)
|
|
||||||
},
|
|
||||||
isIframe () {
|
|
||||||
return window.location !== window.parent.location || window.frameElement
|
|
||||||
},
|
|
||||||
metaTitle () {
|
|
||||||
if(this.form && this.form.is_pro && this.form.seo_meta.page_title) {
|
|
||||||
return this.form.seo_meta.page_title
|
|
||||||
}
|
|
||||||
return this.form ? this.form.title : 'Create beautiful forms'
|
|
||||||
},
|
|
||||||
metaTemplate () {
|
|
||||||
if (this.form && this.form.is_pro && this.form.seo_meta.page_title) {
|
|
||||||
// Disable template if custom SEO title
|
|
||||||
return '%s'
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
},
|
|
||||||
metaDescription () {
|
|
||||||
if (this.form && this.form.is_pro && this.form.seo_meta.page_description) {
|
|
||||||
return this.form.seo_meta.page_description
|
|
||||||
}
|
|
||||||
return (this.form && this.form.description) ? this.form.description.substring(0, 160) : null
|
|
||||||
},
|
|
||||||
metaImage () {
|
|
||||||
if (this.form && this.form.is_pro && this.form.seo_meta.page_thumbnail) {
|
|
||||||
return this.form.seo_meta.page_thumbnail
|
|
||||||
}
|
|
||||||
return (this.form && this.form.cover_picture) ? this.form.cover_picture : null
|
|
||||||
},
|
|
||||||
metaTags () {
|
|
||||||
return (this.form && this.form.can_be_indexed) ? [] : [{ name: 'robots', content: 'noindex' }]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -16,14 +16,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { useAuthStore } from '../../stores/auth'
|
|
||||||
import SeoMeta from '../../mixins/seo-meta.js'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
scrollToTop: false,
|
scrollToTop: false,
|
||||||
mixins: [SeoMeta],
|
|
||||||
|
|
||||||
setup () {
|
setup () {
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
@ -34,7 +30,7 @@ export default {
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
metaTitle: 'Account',
|
metaTitle: 'Account',
|
||||||
form: new Form({
|
form: useForm({
|
||||||
identifier: ''
|
identifier: ''
|
||||||
}),
|
}),
|
||||||
loading: false
|
loading: false
|
||||||
|
|
|
@ -35,17 +35,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { useAuthStore } from '../../stores/auth'
|
|
||||||
import { useWorkspacesStore } from '../../stores/workspaces'
|
|
||||||
import SeoMeta from '../../mixins/seo-meta.js'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { },
|
components: { },
|
||||||
middleware: 'admin',
|
middleware: 'admin',
|
||||||
scrollToTop: false,
|
scrollToTop: false,
|
||||||
mixins: [SeoMeta],
|
|
||||||
|
|
||||||
setup () {
|
setup () {
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
@ -58,7 +53,7 @@ export default {
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
metaTitle: 'Admin',
|
metaTitle: 'Admin',
|
||||||
form: new Form({
|
form: useForm({
|
||||||
identifier: ''
|
identifier: ''
|
||||||
}),
|
}),
|
||||||
loading: false
|
loading: false
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
|
||||||
import SeoMeta from '../../mixins/seo-meta.js'
|
import SeoMeta from '../../mixins/seo-meta.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -36,7 +35,7 @@ export default {
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
metaTitle: 'Password',
|
metaTitle: 'Password',
|
||||||
form: new Form({
|
form: useForm({
|
||||||
password: '',
|
password: '',
|
||||||
password_confirmation: ''
|
password_confirmation: ''
|
||||||
})
|
})
|
||||||
|
|
|
@ -23,13 +23,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { useAuthStore } from '../../stores/auth'
|
|
||||||
import SeoMeta from '../../mixins/seo-meta.js'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [SeoMeta],
|
|
||||||
scrollToTop: false,
|
scrollToTop: false,
|
||||||
|
|
||||||
setup () {
|
setup () {
|
||||||
|
@ -42,7 +36,7 @@ export default {
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
metaTitle: 'Profile',
|
metaTitle: 'Profile',
|
||||||
form: new Form({
|
form: useForm({
|
||||||
name: '',
|
name: '',
|
||||||
email: ''
|
email: ''
|
||||||
})
|
})
|
||||||
|
|
|
@ -115,20 +115,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { computed } from 'vue'
|
|
||||||
import Form from 'vform'
|
|
||||||
import { useFormsStore } from '../../stores/forms'
|
|
||||||
import { useWorkspacesStore } from '../../stores/workspaces'
|
|
||||||
import SeoMeta from '../../mixins/seo-meta.js'
|
|
||||||
import TextAreaInput from '../../components/forms/TextAreaInput.vue'
|
import TextAreaInput from '../../components/forms/TextAreaInput.vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import * as domain from 'domain'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { TextAreaInput },
|
components: { TextAreaInput },
|
||||||
mixins: [SeoMeta],
|
|
||||||
scrollToTop: false,
|
scrollToTop: false,
|
||||||
mixins: [SeoMeta],
|
|
||||||
|
|
||||||
setup () {
|
setup () {
|
||||||
const formsStore = useFormsStore()
|
const formsStore = useFormsStore()
|
||||||
|
@ -143,7 +135,7 @@ export default {
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
metaTitle: 'Workspaces',
|
metaTitle: 'Workspaces',
|
||||||
form: new Form({
|
form: useForm({
|
||||||
name: '',
|
name: '',
|
||||||
emoji: ''
|
emoji: ''
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -42,6 +42,14 @@ export const useFormsStore = defineStore('forms', () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a form from the public API
|
||||||
|
*/
|
||||||
|
const publicLoad = (slug) => {
|
||||||
|
contentStore.startLoading()
|
||||||
|
return useOpnApi('/forms/' + slug)
|
||||||
|
}
|
||||||
|
|
||||||
const allTags = computed(() => {
|
const allTags = computed(() => {
|
||||||
let tags = []
|
let tags = []
|
||||||
contentStore.getAll.value.forEach((form) => {
|
contentStore.getAll.value.forEach((form) => {
|
||||||
|
@ -56,7 +64,8 @@ export const useFormsStore = defineStore('forms', () => {
|
||||||
...contentStore,
|
...contentStore,
|
||||||
allLoaded,
|
allLoaded,
|
||||||
allTags,
|
allTags,
|
||||||
|
publicLoad,
|
||||||
loadAll,
|
loadAll,
|
||||||
load
|
load,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -27,7 +27,6 @@
|
||||||
"qrcode": "^1.5.1",
|
"qrcode": "^1.5.1",
|
||||||
"query-builder-vue-3": "^1.0.1",
|
"query-builder-vue-3": "^1.0.1",
|
||||||
"tinymotion": "^0.2.0",
|
"tinymotion": "^0.2.0",
|
||||||
"vform": "^2.1.1",
|
|
||||||
"vue": "^3.2.13",
|
"vue": "^3.2.13",
|
||||||
"vue-chartjs": "^5.2.0",
|
"vue-chartjs": "^5.2.0",
|
||||||
"vue-codemirror": "^4.0.6",
|
"vue-codemirror": "^4.0.6",
|
||||||
|
|
|
@ -109,10 +109,10 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
import Form from 'vform'
|
||||||
|
import { themes } from '~/lib/forms/form-themes'
|
||||||
import OpenForm from './OpenForm.vue'
|
import OpenForm from './OpenForm.vue'
|
||||||
import OpenFormButton from './OpenFormButton.vue'
|
|
||||||
import { themes } from '~/config/form-themes.js'
|
|
||||||
import VButton from '../../common/Button.vue'
|
import VButton from '../../common/Button.vue'
|
||||||
|
import OpenFormButton from './OpenFormButton.vue'
|
||||||
import VTransition from '../../common/transitions/VTransition.vue'
|
import VTransition from '../../common/transitions/VTransition.vue'
|
||||||
import FormPendingSubmissionKey from '../../../mixins/forms/form-pending-submission-key.js'
|
import FormPendingSubmissionKey from '../../../mixins/forms/form-pending-submission-key.js'
|
||||||
import FormCleanings from '../../pages/forms/show/FormCleanings.vue'
|
import FormCleanings from '../../pages/forms/show/FormCleanings.vue'
|
||||||
|
@ -165,6 +165,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted () {
|
mounted () {
|
||||||
|
console.log('inside', this.form)
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -54,44 +54,10 @@ import { computed } from 'vue'
|
||||||
import { useFormsStore } from '../../stores/forms'
|
import { useFormsStore } from '../../stores/forms'
|
||||||
import { useRecordsStore } from '../../stores/records'
|
import { useRecordsStore } from '../../stores/records'
|
||||||
import OpenCompleteForm from '../../components/open/forms/OpenCompleteForm.vue'
|
import OpenCompleteForm from '../../components/open/forms/OpenCompleteForm.vue'
|
||||||
import Cookies from 'js-cookie'
|
|
||||||
import sha256 from 'js-sha256'
|
import sha256 from 'js-sha256'
|
||||||
import SeoMeta from '../../mixins/seo-meta.js'
|
import { onBeforeRouteLeave } from 'vue-router'
|
||||||
|
import { disableDarkMode, handleDarkMode, handleTransparentMode } from '~lib/forms/public-page'
|
||||||
const isFrame = window.location !== window.parent.location || window.frameElement
|
import { focusOnFirstFormElement } from '../../../../client/lib/forms/public-page'
|
||||||
|
|
||||||
function handleDarkMode (form) {
|
|
||||||
// Dark mode
|
|
||||||
const body = document.body
|
|
||||||
if (form.dark_mode === 'dark') {
|
|
||||||
body.classList.add('dark')
|
|
||||||
} else if (form.dark_mode === 'light') {
|
|
||||||
body.classList.remove('dark')
|
|
||||||
} else if (form.dark_mode === 'auto' && isFrame) {
|
|
||||||
// Remove dark mode if embed in a notion basic site
|
|
||||||
let parentUrl
|
|
||||||
try {
|
|
||||||
parentUrl = window.location.ancestorOrigins[0]
|
|
||||||
} catch (e) {
|
|
||||||
parentUrl = (window.location !== window.parent.location)
|
|
||||||
? document.referrer
|
|
||||||
: document.location.href
|
|
||||||
}
|
|
||||||
if (parentUrl.includes('.notion.site')) {
|
|
||||||
body.classList.remove('dark')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTransparentMode (form) {
|
|
||||||
const isFrame = window.location !== window.parent.location || window.frameElement
|
|
||||||
if (!isFrame || !form.transparent_background) return
|
|
||||||
|
|
||||||
const app = document.getElementById('app')
|
|
||||||
app.classList.remove('bg-white')
|
|
||||||
app.classList.remove('dark:bg-notion-dark')
|
|
||||||
app.classList.add('bg-transparent')
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadForm (slug) {
|
function loadForm (slug) {
|
||||||
const formsStore = useFormsStore()
|
const formsStore = useFormsStore()
|
||||||
|
@ -118,64 +84,39 @@ function loadForm (slug) {
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { OpenCompleteForm },
|
components: { OpenCompleteForm },
|
||||||
mixins: [SeoMeta],
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
if (window.$crisp) {
|
|
||||||
window.$crisp.push(['do', 'chat:hide'])
|
|
||||||
}
|
|
||||||
next()
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeRouteLeave (to, from, next) {
|
|
||||||
if (window.$crisp) {
|
|
||||||
window.$crisp.push(['do', 'chat:show'])
|
|
||||||
}
|
|
||||||
next()
|
|
||||||
},
|
|
||||||
|
|
||||||
setup () {
|
setup () {
|
||||||
|
const crisp = useCrisp()
|
||||||
|
crisp.hideChat()
|
||||||
|
|
||||||
|
onBeforeRouteLeave((to, from) => {
|
||||||
|
crisp.showChat()
|
||||||
|
disableDarkMode()
|
||||||
|
})
|
||||||
|
|
||||||
const formsStore = useFormsStore()
|
const formsStore = useFormsStore()
|
||||||
const recordsStore = useRecordsStore()
|
const recordsStore = useRecordsStore()
|
||||||
return {
|
|
||||||
formsStore,
|
|
||||||
forms : computed(() => formsStore.content),
|
|
||||||
formLoading : computed(() => formsStore.loading),
|
|
||||||
recordLoading : computed(() => recordsStore.loading)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
const passwordEntered = function (password) {
|
||||||
return {
|
useCookie('password-' + this.form.slug, { maxAge: { expires: 60 * 60 * 7 }, sameSite: false, secure: true }).value = sha256(password)
|
||||||
submitted: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
|
||||||
loadForm(this.formSlug).then(() => {
|
|
||||||
if (this.isIframe) return
|
|
||||||
// Auto focus on first input
|
|
||||||
const visibleElements = []
|
|
||||||
document.querySelectorAll('input,button,textarea,[role="button"]').forEach(ele => {
|
|
||||||
if (ele.offsetWidth !== 0 || ele.offsetHeight !== 0) {
|
|
||||||
visibleElements.push(ele)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (visibleElements.length > 0) {
|
|
||||||
visibleElements[0].focus()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
passwordEntered (password) {
|
|
||||||
Cookies.set('password-' + this.form.slug, sha256(password), { expires: 7, sameSite: 'None', secure: true })
|
|
||||||
loadForm(this.formSlug).then(() => {
|
loadForm(this.formSlug).then(() => {
|
||||||
if (this.form.is_password_protected) {
|
if (this.form.is_password_protected) {
|
||||||
this.$refs['open-complete-form'].addPasswordError('Invalid password.')
|
this.$refs['open-complete-form'].addPasswordError('Invalid password.')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const slug = useRoute()
|
||||||
|
|
||||||
|
return {
|
||||||
|
formsStore,
|
||||||
|
passwordEntered,
|
||||||
|
submitted: ref(false),
|
||||||
|
isIframe: useIsIframe(),
|
||||||
|
form: computed(() => formsStore.getByKey()),
|
||||||
|
formLoading: computed(() => formsStore.loading),
|
||||||
|
recordLoading: computed(() => recordsStore.loading)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -184,38 +125,42 @@ export default {
|
||||||
},
|
},
|
||||||
form () {
|
form () {
|
||||||
return this.formsStore.getBySlug(this.formSlug)
|
return this.formsStore.getBySlug(this.formSlug)
|
||||||
},
|
|
||||||
isIframe () {
|
|
||||||
return window.location !== window.parent.location || window.frameElement
|
|
||||||
},
|
|
||||||
metaTitle () {
|
|
||||||
if(this.form && this.form.is_pro && this.form.seo_meta.page_title) {
|
|
||||||
return this.form.seo_meta.page_title
|
|
||||||
}
|
}
|
||||||
return this.form ? this.form.title : 'Create beautiful forms'
|
// metaTitle () {
|
||||||
|
// if (this.form && this.form.is_pro && this.form.seo_meta.page_title) {
|
||||||
|
// return this.form.seo_meta.page_title
|
||||||
|
// }
|
||||||
|
// return this.form ? this.form.title : 'Create beautiful forms'
|
||||||
|
// },
|
||||||
|
// metaTemplate () {
|
||||||
|
// if (this.form && this.form.is_pro && this.form.seo_meta.page_title) {
|
||||||
|
// // Disable template if custom SEO title
|
||||||
|
// return '%s'
|
||||||
|
// }
|
||||||
|
// return null
|
||||||
|
// },
|
||||||
|
// metaDescription () {
|
||||||
|
// if (this.form && this.form.is_pro && this.form.seo_meta.page_description) {
|
||||||
|
// return this.form.seo_meta.page_description
|
||||||
|
// }
|
||||||
|
// return (this.form && this.form.description) ? this.form.description.substring(0, 160) : null
|
||||||
|
// },
|
||||||
|
// metaImage () {
|
||||||
|
// if (this.form && this.form.is_pro && this.form.seo_meta.page_thumbnail) {
|
||||||
|
// return this.form.seo_meta.page_thumbnail
|
||||||
|
// }
|
||||||
|
// return (this.form && this.form.cover_picture) ? this.form.cover_picture : null
|
||||||
|
// },
|
||||||
|
// metaTags () {
|
||||||
|
// return (this.form && this.form.can_be_indexed) ? [] : [{ name: 'robots', content: 'noindex' }]
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
metaTemplate () {
|
|
||||||
if (this.form && this.form.is_pro && this.form.seo_meta.page_title) {
|
mounted () {
|
||||||
// Disable template if custom SEO title
|
loadForm(this.formSlug).then(() => {
|
||||||
return '%s'
|
if (this.isIframe) return
|
||||||
}
|
focusOnFirstFormElement()
|
||||||
return null
|
})
|
||||||
},
|
|
||||||
metaDescription () {
|
|
||||||
if (this.form && this.form.is_pro && this.form.seo_meta.page_description) {
|
|
||||||
return this.form.seo_meta.page_description
|
|
||||||
}
|
|
||||||
return (this.form && this.form.description) ? this.form.description.substring(0, 160) : null
|
|
||||||
},
|
|
||||||
metaImage () {
|
|
||||||
if (this.form && this.form.is_pro && this.form.seo_meta.page_thumbnail) {
|
|
||||||
return this.form.seo_meta.page_thumbnail
|
|
||||||
}
|
|
||||||
return (this.form && this.form.cover_picture) ? this.form.cover_picture : null
|
|
||||||
},
|
|
||||||
metaTags () {
|
|
||||||
return (this.form && this.form.can_be_indexed) ? [] : [{ name: 'robots', content: 'noindex' }]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue