Remove vform - working on form public page
This commit is contained in:
145
client/pages/forms/[slug]/index.vue
Normal file
145
client/pages/forms/[slug]/index.vue
Normal file
@@ -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>
|
||||
import { computed } from 'vue'
|
||||
import Form from 'vform'
|
||||
import ProTag from '~/components/global/ProTag.vue'
|
||||
import VButton from '~/components/global/VButton.vue'
|
||||
import ExtraMenu from '../../../components/pages/forms/show/ExtraMenu.vue'
|
||||
@@ -222,13 +221,13 @@ export default {
|
||||
|
||||
watch: {
|
||||
form () {
|
||||
this.workingForm = new Form(this.form)
|
||||
this.workingForm = useForm(this.form)
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
if (this.form) {
|
||||
this.workingForm = new Form(this.form)
|
||||
this.workingForm = useForm(this.form)
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -25,13 +25,8 @@
|
||||
|
||||
<script>
|
||||
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 initForm from '../../../mixins/form_editor/initForm.js'
|
||||
import SeoMeta from '../../../mixins/seo-meta.js'
|
||||
import CreateFormBaseModal from '../../../components/pages/forms/create/CreateFormBaseModal.vue'
|
||||
|
||||
const loadTemplates = function () {
|
||||
@@ -47,7 +42,7 @@ export default {
|
||||
components: {
|
||||
QuickRegister, CreateFormBaseModal
|
||||
},
|
||||
mixins: [initForm, SeoMeta],
|
||||
mixins: [initForm],
|
||||
middleware: 'guest',
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
@@ -118,7 +113,7 @@ export default {
|
||||
if (this.$route.query.template !== undefined && this.$route.query.template) {
|
||||
const template = this.templatesStore.getByKey(this.$route.query.template)
|
||||
if (template && template.structure) {
|
||||
this.form = new Form({ ...this.form.data(), ...template.structure })
|
||||
this.form = useForm({ ...this.form.data(), ...template.structure })
|
||||
}
|
||||
} else {
|
||||
// No template loaded, ask how to start
|
||||
@@ -146,7 +141,7 @@ export default {
|
||||
}, 500)
|
||||
},
|
||||
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>
|
||||
Reference in New Issue
Block a user