create form as a guest (#29)
* create form as a guest * Remove vue-tour * Refactor initForm function and improve template merge * Add templates to navbar * Fix template preview submit issue Co-authored-by: Chirag Chhatrala <chirag@notionforms.io>
This commit is contained in:
parent
0ce0a1dcc1
commit
b9ba6e1e29
File diff suppressed because it is too large
Load Diff
|
@ -37,7 +37,6 @@
|
||||||
"vue-prism-editor": "^1.2.2",
|
"vue-prism-editor": "^1.2.2",
|
||||||
"vue-router": "^3.5.2",
|
"vue-router": "^3.5.2",
|
||||||
"vue-tailwind": "^2.5.0",
|
"vue-tailwind": "^2.5.0",
|
||||||
"vue-tour": "^2.0.0",
|
|
||||||
"vue2-editor": "^2.10.3",
|
"vue2-editor": "^2.10.3",
|
||||||
"vuedraggable": "^2.24.3",
|
"vuedraggable": "^2.24.3",
|
||||||
"vuex": "^3.6.2",
|
"vuex": "^3.6.2",
|
||||||
|
|
|
@ -6,8 +6,6 @@ import App from '~/components/App'
|
||||||
import LoadScript from 'vue-plugin-load-script'
|
import LoadScript from 'vue-plugin-load-script'
|
||||||
import Base from './base'
|
import Base from './base'
|
||||||
|
|
||||||
import VueTour from 'vue-tour'
|
|
||||||
|
|
||||||
import '~/plugins'
|
import '~/plugins'
|
||||||
import '~/components'
|
import '~/components'
|
||||||
|
|
||||||
|
@ -16,10 +14,6 @@ Vue.config.productionTip = false
|
||||||
Vue.mixin(Base)
|
Vue.mixin(Base)
|
||||||
Vue.use(LoadScript)
|
Vue.use(LoadScript)
|
||||||
|
|
||||||
/* Vue Tour */
|
|
||||||
require('vue-tour/dist/vue-tour.css')
|
|
||||||
Vue.use(VueTour)
|
|
||||||
|
|
||||||
/* eslint-disable no-new */
|
/* eslint-disable no-new */
|
||||||
new Vue({
|
new Vue({
|
||||||
i18n,
|
i18n,
|
||||||
|
|
|
@ -15,10 +15,10 @@
|
||||||
<workspace-dropdown class="ml-6"/>
|
<workspace-dropdown class="ml-6"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="hidden md:block ml-auto relative">
|
<div class="hidden md:block ml-auto relative">
|
||||||
<!-- <router-link :to="{name:'integrations'}"-->
|
<router-link :to="{name:'templates'}"
|
||||||
<!-- class="text-sm text-gray-600 dark:text-white hover:text-gray-800 cursor-pointer mt-1 mr-8">-->
|
class="text-sm text-gray-600 dark:text-white hover:text-gray-800 cursor-pointer mt-1 mr-8">
|
||||||
<!-- Integrations-->
|
Templates
|
||||||
<!-- </router-link>-->
|
</router-link>
|
||||||
<a href="#" class="text-sm text-gray-600 dark:text-white hover:text-gray-800 cursor-pointer mt-1"
|
<a href="#" class="text-sm text-gray-600 dark:text-white hover:text-gray-800 cursor-pointer mt-1"
|
||||||
@click.prevent="$getCrisp().push(['do', 'helpdesk:search'])" v-if="hasCrisp"
|
@click.prevent="$getCrisp().push(['do', 'helpdesk:search'])" v-if="hasCrisp"
|
||||||
>
|
>
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
{{ $t('login') }}
|
{{ $t('login') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<v-button size="small" :to="{ name: 'register' }" color="outline-blue" v-track.nav_create_form_click :arrow="true">
|
<v-button size="small" :to="{ name: 'forms.create.guest' }" color="outline-blue" v-track.nav_create_form_click :arrow="true">
|
||||||
Create a form
|
Create a form
|
||||||
</v-button>
|
</v-button>
|
||||||
|
|
||||||
|
|
|
@ -7,17 +7,17 @@
|
||||||
</div>
|
</div>
|
||||||
<modal :show="showPremiumModal" @close="showPremiumModal=false">
|
<modal :show="showPremiumModal" @close="showPremiumModal=false">
|
||||||
<h2 class="text-nt-blue">
|
<h2 class="text-nt-blue">
|
||||||
OpenForm PRO
|
OpnForm PRO
|
||||||
</h2>
|
</h2>
|
||||||
<h4 v-if="user.is_subscribed && !user.has_enterprise_subscription" class="text-center mt-5">
|
<h4 v-if="user.is_subscribed && !user.has_enterprise_subscription" class="text-center mt-5">
|
||||||
We're happy to have you as a Pro customer. If you're having any issue with OpenForm, or if you have a
|
We're happy to have you as a Pro customer. If you're having any issue with OpnForm, or if you have a
|
||||||
feature request, please <a href="mailto:contact@opnform.com">contact us</a>.
|
feature request, please <a href="mailto:contact@opnform.com">contact us</a>.
|
||||||
<br><br>
|
<br><br>
|
||||||
If you need to collaborate, or to work with multiple workspaces, or just larger file uploads, you can
|
If you need to collaborate, or to work with multiple workspaces, or just larger file uploads, you can
|
||||||
also upgrade our subscription to get an Enterprise subscription.
|
also upgrade our subscription to get an Enterprise subscription.
|
||||||
</h4>
|
</h4>
|
||||||
<h4 v-if="user.is_subscribed && user.has_enterprise_subscription" class="text-center mt-5">
|
<h4 v-if="user.is_subscribed && user.has_enterprise_subscription" class="text-center mt-5">
|
||||||
We're happy to have you as an Enterprise customer. If you're having any issue with OpenForm, or if you have a
|
We're happy to have you as an Enterprise customer. If you're having any issue with OpnForm, or if you have a
|
||||||
feature request, please <a href="mailto:contact@opnform.com">contact us</a>.
|
feature request, please <a href="mailto:contact@opnform.com">contact us</a>.
|
||||||
</h4>
|
</h4>
|
||||||
<p v-if="!user.is_subscribed" class="mt-4">
|
<p v-if="!user.is_subscribed" class="mt-4">
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
class="bg-nt-blue text-white px-2 text-xs uppercase inline rounded-full font-semibold mx-1"
|
class="bg-nt-blue text-white px-2 text-xs uppercase inline rounded-full font-semibold mx-1"
|
||||||
>
|
>
|
||||||
PRO
|
PRO
|
||||||
</span> tag are available in the Pro plan of OpenForm. <b>You can play around and try all Pro features
|
</span> tag are available in the Pro plan of OpnForm. <b>You can play around and try all Pro features
|
||||||
within
|
within
|
||||||
the form editor, but you can't use them in your real forms</b>. You can subscribe now to gain unlimited access
|
the form editor, but you can't use them in your real forms</b>. You can subscribe now to gain unlimited access
|
||||||
to
|
to
|
||||||
|
|
|
@ -91,7 +91,7 @@
|
||||||
>
|
>
|
||||||
<template #submit-btn="{submitForm}">
|
<template #submit-btn="{submitForm}">
|
||||||
<open-form-button :loading="loading" :theme="theme" :color="form.color" class="mt-2 px-8 mx-1"
|
<open-form-button :loading="loading" :theme="theme" :color="form.color" class="mt-2 px-8 mx-1"
|
||||||
@click="submitForm"
|
@click.prevent="submitForm"
|
||||||
>
|
>
|
||||||
{{ form.submit_button_text }}
|
{{ form.submit_button_text }}
|
||||||
</open-form-button>
|
</open-form-button>
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="form" id="form-editor" class="w-full flex flex-grow relative overflow-x-hidden">
|
<div v-if="form" id="form-editor" class="w-full flex flex-grow relative overflow-x-hidden">
|
||||||
<!-- Form fields selection -->
|
<!-- Form fields selection -->
|
||||||
<v-tour name="tutorial" :steps="steps"/>
|
|
||||||
<div class="w-full md:w-1/2 lg:w-2/5 border-r relative overflow-y-scroll md:max-w-sm flex-shrink-0">
|
<div class="w-full md:w-1/2 lg:w-2/5 border-r relative overflow-y-scroll md:max-w-sm flex-shrink-0">
|
||||||
<div class="p-4 bg-blue-50 border-b text-nt-blue-dark md:hidden">
|
<div class="p-4 bg-blue-50 border-b text-nt-blue-dark md:hidden">
|
||||||
We suggest you create this form on a device with a larger screen such as computed. That will allow you
|
We suggest you create this form on a device with a larger screen such as computed. That will allow you
|
||||||
to preview your form changes.
|
to preview your form changes.
|
||||||
</div>
|
</div>
|
||||||
<div class="p-4 pb-0">
|
<div class="p-4 pb-0">
|
||||||
<a href="#" @click.prevent="$router.back()" class="flex text-blue mb-2 font-semibold text-sm">
|
<a v-if="!isGuest" href="#" @click.prevent="$router.back()" class="flex text-blue mb-2 font-semibold text-sm">
|
||||||
<svg class="w-3 h-3 text-blue mt-1 mr-1" viewBox="0 0 6 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg class="w-3 h-3 text-blue mt-1 mr-1" viewBox="0 0 6 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M5 9L1 5L5 1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
|
<path d="M5 9L1 5L5 1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
|
||||||
stroke-linejoin="round"/>
|
stroke-linejoin="round"/>
|
||||||
|
@ -88,6 +87,11 @@ export default {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
isGuest: {
|
||||||
|
required: false,
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
@ -123,7 +127,7 @@ export default {
|
||||||
{
|
{
|
||||||
target: '#v-step-0',
|
target: '#v-step-0',
|
||||||
header: {
|
header: {
|
||||||
title: 'Welcome to the OpenForm Editor!'
|
title: 'Welcome to the OpnForm Editor!'
|
||||||
},
|
},
|
||||||
content: 'Discover <strong>your form Editor</strong>!'
|
content: 'Discover <strong>your form Editor</strong>!'
|
||||||
},
|
},
|
||||||
|
@ -160,20 +164,16 @@ export default {
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$emit('mounted')
|
this.$emit('mounted')
|
||||||
this.startTour()
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
startTour() {
|
|
||||||
if (!this.user.has_forms) {
|
|
||||||
this.$tours.tutorial.start()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
showValidationErrors() {
|
showValidationErrors() {
|
||||||
this.showFormErrorModal = true
|
this.showFormErrorModal = true
|
||||||
},
|
},
|
||||||
saveForm() {
|
saveForm() {
|
||||||
if (this.isEdit) {
|
if(this.isGuest) {
|
||||||
|
this.saveFormGuest()
|
||||||
|
} else if (this.isEdit) {
|
||||||
this.saveFormEdit()
|
this.saveFormEdit()
|
||||||
} else {
|
} else {
|
||||||
this.saveFormCreate()
|
this.saveFormCreate()
|
||||||
|
@ -230,6 +230,9 @@ export default {
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.updateFormLoading = false
|
this.updateFormLoading = false
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
saveFormGuest() {
|
||||||
|
this.$emit('openRegister')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,7 +266,7 @@ export default {
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
init() {
|
init() {
|
||||||
if (this.$route.name === 'forms.create') { // Set Default fields
|
if (this.$route.name === 'forms.create' || this.$route.name === 'forms.create.guest') { // Set Default fields
|
||||||
this.formFields = (this.form.properties.length > 0) ? clonedeep(this.form.properties) : this.getDefaultFields()
|
this.formFields = (this.form.properties.length > 0) ? clonedeep(this.form.properties) : this.getDefaultFields()
|
||||||
} else {
|
} else {
|
||||||
this.formFields = clonedeep(this.form.properties).map((field) => {
|
this.formFields = clonedeep(this.form.properties).map((field) => {
|
||||||
|
|
|
@ -47,6 +47,17 @@
|
||||||
</svg>
|
</svg>
|
||||||
Duplicate form
|
Duplicate form
|
||||||
</a>
|
</a>
|
||||||
|
<a href="#" v-if="user.admin"
|
||||||
|
class="block block px-4 py-2 text-md text-gray-700 dark:text-white hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:text-white dark:hover:bg-gray-600 flex items-center"
|
||||||
|
@click.prevent="showCreateTemplateModal=true"
|
||||||
|
>
|
||||||
|
<svg class="w-4 h-4 mr-2" 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="M17 14v6m-3-3h6M6 10h2a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v2a2 2 0 002 2zm10 0h2a2 2 0 002-2V6a2 2 0 00-2-2h-2a2 2 0 00-2 2v2a2 2 0 002 2zM6 20h2a2 2 0 002-2v-2a2 2 0 00-2-2H6a2 2 0 00-2 2v2a2 2 0 002 2z"/>
|
||||||
|
</svg>
|
||||||
|
Create Template
|
||||||
|
</a>
|
||||||
<a href="#"
|
<a href="#"
|
||||||
class="block block px-4 py-2 text-md text-red-600 hover:bg-red-50 flex items-center"
|
class="block block px-4 py-2 text-md text-red-600 hover:bg-red-50 flex items-center"
|
||||||
v-track.delete_form_click="{form_id:form.id, form_slug:form.slug}"
|
v-track.delete_form_click="{form_id:form.id, form_slug:form.slug}"
|
||||||
|
@ -60,17 +71,6 @@
|
||||||
</svg>
|
</svg>
|
||||||
Delete form
|
Delete form
|
||||||
</a>
|
</a>
|
||||||
<a href="#" v-if="user.admin"
|
|
||||||
class="block block px-4 py-2 text-md text-gray-700 dark:text-white hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:text-white dark:hover:bg-gray-600 flex items-center"
|
|
||||||
@click.prevent="showCreateTemplateModal=true"
|
|
||||||
>
|
|
||||||
<svg class="w-4 h-4 mr-2" 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="M17 14v6m-3-3h6M6 10h2a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v2a2 2 0 002 2zm10 0h2a2 2 0 002-2V6a2 2 0 00-2-2h-2a2 2 0 00-2 2v2a2 2 0 002 2zM6 20h2a2 2 0 002-2v-2a2 2 0 00-2-2H6a2 2 0 00-2 2v2a2 2 0 002 2z"/>
|
|
||||||
</svg>
|
|
||||||
Create Template
|
|
||||||
</a>
|
|
||||||
</dropdown>
|
</dropdown>
|
||||||
|
|
||||||
<create-template-modal :form="form" :show="showCreateTemplateModal" @close="showCreateTemplateModal=false"/>
|
<create-template-modal :form="form" :show="showCreateTemplateModal" @close="showCreateTemplateModal=false"/>
|
||||||
|
@ -127,4 +127,3 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import Form from "vform";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
initForm() {
|
||||||
|
this.form = new Form({
|
||||||
|
title: 'My Form',
|
||||||
|
description: null,
|
||||||
|
visibility: 'public',
|
||||||
|
workspace_id: this.workspace?.id,
|
||||||
|
properties: [],
|
||||||
|
|
||||||
|
notifies: false,
|
||||||
|
slack_notifies: false,
|
||||||
|
send_submission_confirmation: false,
|
||||||
|
webhook_url: null,
|
||||||
|
|
||||||
|
// Customization
|
||||||
|
theme: 'default',
|
||||||
|
width: 'centered',
|
||||||
|
dark_mode: 'auto',
|
||||||
|
color: '#3B82F6',
|
||||||
|
hide_title: false,
|
||||||
|
no_branding: false,
|
||||||
|
uppercase_labels: true,
|
||||||
|
transparent_background: false,
|
||||||
|
closes_at: null,
|
||||||
|
closed_text: 'This form has now been closed by its owner and does not accept submissions anymore.',
|
||||||
|
|
||||||
|
// Submission
|
||||||
|
submit_button_text: 'Submit',
|
||||||
|
re_fillable: false,
|
||||||
|
re_fill_button_text: 'Fill Again',
|
||||||
|
submitted_text: 'Amazing, we saved your answers. Thank you for your time and have a great day!',
|
||||||
|
notification_sender: 'OpnForm',
|
||||||
|
notification_subject: 'We saved your answers',
|
||||||
|
notification_body: 'Hello there 👋 <br>This is a confirmation that your submission was successfully saved.',
|
||||||
|
notifications_include_submission: true,
|
||||||
|
use_captcha: false,
|
||||||
|
is_rating: false,
|
||||||
|
rating_max_value: 5,
|
||||||
|
max_submissions_count: null,
|
||||||
|
max_submissions_reached_text: 'This form has now reached the maximum number of allowed submissions and is now closed.',
|
||||||
|
|
||||||
|
// Security & Privacy
|
||||||
|
can_be_indexed: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<forgot-password-modal :show="showForgotModal" @close="showForgotModal=false" />
|
||||||
|
|
||||||
|
<form @submit.prevent="login" @keydown="form.onKeydown($event)" class="mt-4">
|
||||||
|
<!-- Email -->
|
||||||
|
<text-input name="email" :form="form" :label="$t('email')" :required="true" placeholder="Your email address" />
|
||||||
|
|
||||||
|
<!-- Password -->
|
||||||
|
<text-input native-type="password" placeholder="Your password"
|
||||||
|
name="password" :form="form" :label="$t('password')" :required="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Remember Me -->
|
||||||
|
<div class="relative flex items-center my-5">
|
||||||
|
<v-checkbox v-model="remember" class="w-full md:w-1/2" name="remember" size="small">
|
||||||
|
{{ $t('remember_me') }}
|
||||||
|
</v-checkbox>
|
||||||
|
|
||||||
|
<div class="w-full md:w-1/2 text-right">
|
||||||
|
<a href="#" @click.prevent="showForgotModal=true" class="text-xs hover:underline text-gray-500 sm:text-sm hover:text-gray-700">
|
||||||
|
Forgot your password?
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Submit Button -->
|
||||||
|
<v-button dusk="btn_login" :loading="form.busy">Log in to continue</v-button>
|
||||||
|
|
||||||
|
<p class="text-gray-500 mt-4">
|
||||||
|
Don't have an account?
|
||||||
|
<a href="#" v-if="isQuick" @click.prevent="$emit('openRegister')" class="font-semibold ml-1">Sign Up</a>
|
||||||
|
<router-link v-else :to="{name:'register'}" class="font-semibold ml-1">Sign Up</router-link>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Form from 'vform'
|
||||||
|
import Cookies from 'js-cookie'
|
||||||
|
import OpenFormFooter from '../../../components/pages/OpenFormFooter'
|
||||||
|
import Testimonials from '../../../components/pages/welcome/Testimonials'
|
||||||
|
import ForgotPasswordModal from '../ForgotPasswordModal'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'LoginForm',
|
||||||
|
components: {
|
||||||
|
OpenFormFooter,
|
||||||
|
Testimonials,
|
||||||
|
ForgotPasswordModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
isQuick: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
form: new Form({
|
||||||
|
email: '',
|
||||||
|
password: ''
|
||||||
|
}),
|
||||||
|
remember: false,
|
||||||
|
showForgotModal: false
|
||||||
|
}),
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async login () {
|
||||||
|
// Submit the form.
|
||||||
|
const { data } = await this.form.post('/api/login')
|
||||||
|
|
||||||
|
// Save the token.
|
||||||
|
this.$store.dispatch('auth/saveToken', {
|
||||||
|
token: data.token,
|
||||||
|
remember: this.remember
|
||||||
|
})
|
||||||
|
|
||||||
|
// Fetch the user.
|
||||||
|
await this.$store.dispatch('auth/fetchUser')
|
||||||
|
|
||||||
|
// Redirect home.
|
||||||
|
this.redirect()
|
||||||
|
},
|
||||||
|
|
||||||
|
redirect () {
|
||||||
|
if(this.isQuick){
|
||||||
|
this.$emit('afterQuickLogin')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const intendedUrl = Cookies.get('intended_url')
|
||||||
|
|
||||||
|
if (intendedUrl) {
|
||||||
|
Cookies.remove('intended_url')
|
||||||
|
this.$router.push({ path: intendedUrl })
|
||||||
|
} else {
|
||||||
|
this.$router.push({ name: 'home' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,80 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- Login modal -->
|
||||||
|
<modal :show="showLoginModal" @close="showLoginModal=false" max-width="lg">
|
||||||
|
<template #icon>
|
||||||
|
<svg class="w-8 h-8" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M12 8V16M8 12H16M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
<template #title>
|
||||||
|
Login to OpnForm
|
||||||
|
</template>
|
||||||
|
<div class="px-4">
|
||||||
|
<login-form :isQuick="true" @openRegister="openRegister" @afterQuickLogin="afterQuickLogin" />
|
||||||
|
</div>
|
||||||
|
</modal>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Register modal -->
|
||||||
|
<modal :show="showRegisterModal" @close="$emit('close')" max-width="lg">
|
||||||
|
<template #icon>
|
||||||
|
<svg class="w-8 h-8" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M12 8V16M8 12H16M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
<template #title>
|
||||||
|
Create an account
|
||||||
|
</template>
|
||||||
|
<div class="px-4">
|
||||||
|
<register-form :isQuick="true" @openLogin="openLogin" @afterQuickLogin="afterQuickLogin" />
|
||||||
|
</div>
|
||||||
|
</modal>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import LoginForm from './LoginForm'
|
||||||
|
import RegisterForm from './RegisterForm'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'QuickRegister',
|
||||||
|
components: {
|
||||||
|
LoginForm,
|
||||||
|
RegisterForm
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
showRegisterModal: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
showLoginModal: false,
|
||||||
|
}),
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
openLogin(){
|
||||||
|
this.showLoginModal = true
|
||||||
|
this.$emit('close')
|
||||||
|
},
|
||||||
|
openRegister(){
|
||||||
|
this.showLoginModal = false
|
||||||
|
this.$emit('reopen')
|
||||||
|
},
|
||||||
|
afterQuickLogin(){
|
||||||
|
this.showLoginModal = false
|
||||||
|
this.$emit('afterLogin')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,128 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<form @submit.prevent="register" @keydown="form.onKeydown($event)" class="mt-4">
|
||||||
|
<!-- Name -->
|
||||||
|
<text-input name="name" :form="form" :label="$t('name')" placeholder="Your name" :required="true" />
|
||||||
|
|
||||||
|
<!-- Email -->
|
||||||
|
<text-input name="email" :form="form" :label="$t('email')" :required="true" placeholder="Your email address" />
|
||||||
|
|
||||||
|
<select-input name="hear_about_us" :options="hearAboutUsOptions" :form="form" placeholder="Select option"
|
||||||
|
label="How did you hear about us?" :required="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Password -->
|
||||||
|
<text-input native-type="password" placeholder="Enter password"
|
||||||
|
name="password" :form="form" :label="$t('password')" :required="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Password Confirmation-->
|
||||||
|
<text-input native-type="password" :form="form" :required="true" placeholder="Enter confirm password"
|
||||||
|
name="password_confirmation" :label="$t('confirm_password')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<checkbox-input :form="form" name="agree_terms" :required="true">
|
||||||
|
<template #label>
|
||||||
|
I agree with the <router-link :to="{name:'terms-conditions'}" target="_blank">Terms and conditions</router-link> and <router-link :to="{name:'privacy-policy'}" target="_blank">Privacy policy</router-link> of the website and I accept them.
|
||||||
|
</template>
|
||||||
|
</checkbox-input>
|
||||||
|
|
||||||
|
<!-- Submit Button -->
|
||||||
|
<v-button :loading="form.busy">Create an account</v-button>
|
||||||
|
|
||||||
|
<p class="text-gray-500 mt-4">
|
||||||
|
Already have an account?
|
||||||
|
<a href="#" v-if="isQuick" @click.prevent="$emit('openLogin')" class="font-semibold ml-1">Log In</a>
|
||||||
|
<router-link v-else :to="{name:'login'}" class="font-semibold ml-1">Log In</router-link>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- GitHub Register Button -->
|
||||||
|
<login-with-github />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Form from 'vform'
|
||||||
|
import LoginWithGithub from '~/components/LoginWithGithub'
|
||||||
|
import SelectInput from '../../../components/forms/SelectInput'
|
||||||
|
import { initCrisp } from '../../../middleware/check-auth'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RegisterForm',
|
||||||
|
components: {
|
||||||
|
SelectInput,
|
||||||
|
LoginWithGithub,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
isQuick: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
form: new Form({
|
||||||
|
name: '',
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
password_confirmation: '',
|
||||||
|
agree_terms: false
|
||||||
|
}),
|
||||||
|
mustVerifyEmail: false
|
||||||
|
}),
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
hearAboutUsOptions () {
|
||||||
|
const options = [
|
||||||
|
{ name: 'Facebook', value: 'facebook' },
|
||||||
|
{ name: 'Twitter', value: 'twitter' },
|
||||||
|
{ name: 'Reddit', value: 'reddit' },
|
||||||
|
{ name: 'Github', value: 'github' },
|
||||||
|
{ name: 'Search Engine (Google, DuckDuckGo...)', value: 'search_engine' },
|
||||||
|
{ name: 'Friend or Colleague', value: 'friend_colleague' },
|
||||||
|
{ name: 'Blog/Article', value: 'blog_article' }
|
||||||
|
].map((value) => ({ value, sort: Math.random() }))
|
||||||
|
.sort((a, b) => a.sort - b.sort)
|
||||||
|
.map(({ value }) => value)
|
||||||
|
options.push({ name: 'Other', value: 'other' })
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async register () {
|
||||||
|
// Register the user.
|
||||||
|
const { data } = await this.form.post('/api/register')
|
||||||
|
|
||||||
|
// Must verify email fist.
|
||||||
|
if (data.status) {
|
||||||
|
this.mustVerifyEmail = true
|
||||||
|
} else {
|
||||||
|
// Log in the user.
|
||||||
|
const { data: { token } } = await this.form.post('/api/login')
|
||||||
|
|
||||||
|
// Save the token.
|
||||||
|
this.$store.dispatch('auth/saveToken', { token })
|
||||||
|
|
||||||
|
// Update the user.
|
||||||
|
await this.$store.dispatch('auth/updateUser', { user: data })
|
||||||
|
|
||||||
|
// Track event
|
||||||
|
this.$logEvent('register', { source: this.form.hear_about_us })
|
||||||
|
initCrisp(data).then(() => {
|
||||||
|
this.$getCrisp().push(['set', 'session:event', [[['register', {}, 'blue']]]])
|
||||||
|
})
|
||||||
|
|
||||||
|
// Redirect
|
||||||
|
if(this.isQuick){
|
||||||
|
this.$emit('afterQuickLogin')
|
||||||
|
}else{
|
||||||
|
this.$router.push({ name: 'forms.create' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,7 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<forgot-password-modal :show="showForgotModal" @close="showForgotModal=false" />
|
|
||||||
|
|
||||||
<div class="flex mt-6 mb-10">
|
<div class="flex mt-6 mb-10">
|
||||||
<div class="w-full md:max-w-6xl mx-auto px-4 flex md:flex-row-reverse flex-wrap">
|
<div class="w-full md:max-w-6xl mx-auto px-4 flex md:flex-row-reverse flex-wrap">
|
||||||
<div class="w-full md:w-1/2 md:p-6">
|
<div class="w-full md:w-1/2 md:p-6">
|
||||||
|
@ -11,35 +9,7 @@
|
||||||
</h2>
|
</h2>
|
||||||
<small>Welcome back! Please enter your details.</small>
|
<small>Welcome back! Please enter your details.</small>
|
||||||
|
|
||||||
<form @submit.prevent="login" @keydown="form.onKeydown($event)" class="mt-4">
|
<login-form />
|
||||||
<!-- Email -->
|
|
||||||
<text-input name="email" :form="form" :label="$t('email')" :required="true" placeholder="Your email address" />
|
|
||||||
|
|
||||||
<!-- Password -->
|
|
||||||
<text-input native-type="password" placeholder="Your password"
|
|
||||||
name="password" :form="form" :label="$t('password')" :required="true"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Remember Me -->
|
|
||||||
<div class="relative flex items-center my-5">
|
|
||||||
<v-checkbox v-model="remember" class="w-full md:w-1/2" name="remember" size="small">
|
|
||||||
{{ $t('remember_me') }}
|
|
||||||
</v-checkbox>
|
|
||||||
|
|
||||||
<div class="w-full md:w-1/2 text-right">
|
|
||||||
<a href="#" @click.prevent="showForgotModal=true" class="text-xs hover:underline text-gray-500 sm:text-sm hover:text-gray-700">
|
|
||||||
Forgot your password?
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Submit Button -->
|
|
||||||
<v-button dusk="btn_login" :loading="form.busy">Log in to continue</v-button>
|
|
||||||
|
|
||||||
<p class="text-gray-500 mt-4">
|
|
||||||
Don't have an account? <router-link :to="{name:'register'}" class="font-semibold ml-1">Sign Up</router-link>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full md:w-1/2 md:p-6 mt-8 md:mt-0 ">
|
<div class="w-full md:w-1/2 md:p-6 mt-8 md:mt-0 ">
|
||||||
|
@ -86,17 +56,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
|
||||||
import Cookies from 'js-cookie'
|
|
||||||
import OpenFormFooter from '../../components/pages/OpenFormFooter'
|
import OpenFormFooter from '../../components/pages/OpenFormFooter'
|
||||||
import Testimonials from '../../components/pages/welcome/Testimonials'
|
import Testimonials from '../../components/pages/welcome/Testimonials'
|
||||||
import ForgotPasswordModal from './ForgotPasswordModal'
|
import LoginForm from './components/LoginForm'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
OpenFormFooter,
|
OpenFormFooter,
|
||||||
Testimonials,
|
Testimonials,
|
||||||
ForgotPasswordModal
|
LoginForm
|
||||||
},
|
},
|
||||||
|
|
||||||
middleware: 'guest',
|
middleware: 'guest',
|
||||||
|
@ -106,42 +74,11 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
form: new Form({
|
|
||||||
email: '',
|
|
||||||
password: ''
|
|
||||||
}),
|
|
||||||
remember: false,
|
|
||||||
showForgotModal: false
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
async login () {
|
|
||||||
// Submit the form.
|
|
||||||
const { data } = await this.form.post('/api/login')
|
|
||||||
|
|
||||||
// Save the token.
|
|
||||||
this.$store.dispatch('auth/saveToken', {
|
|
||||||
token: data.token,
|
|
||||||
remember: this.remember
|
|
||||||
})
|
|
||||||
|
|
||||||
// Fetch the user.
|
|
||||||
await this.$store.dispatch('auth/fetchUser')
|
|
||||||
|
|
||||||
// Redirect home.
|
|
||||||
this.redirect()
|
|
||||||
},
|
|
||||||
|
|
||||||
redirect () {
|
|
||||||
const intendedUrl = Cookies.get('intended_url')
|
|
||||||
|
|
||||||
if (intendedUrl) {
|
|
||||||
Cookies.remove('intended_url')
|
|
||||||
this.$router.push({ path: intendedUrl })
|
|
||||||
} else {
|
|
||||||
this.$router.push({ name: 'home' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -9,43 +9,7 @@
|
||||||
</h2>
|
</h2>
|
||||||
<small>Sign up in less than 2 minutes.</small>
|
<small>Sign up in less than 2 minutes.</small>
|
||||||
|
|
||||||
<form @submit.prevent="register" @keydown="form.onKeydown($event)" class="mt-4">
|
<register-form />
|
||||||
<!-- Name -->
|
|
||||||
<text-input name="name" :form="form" :label="$t('name')" placeholder="Your name" :required="true" />
|
|
||||||
|
|
||||||
<!-- Email -->
|
|
||||||
<text-input name="email" :form="form" :label="$t('email')" :required="true" placeholder="Your email address" />
|
|
||||||
|
|
||||||
<select-input name="hear_about_us" :options="hearAboutUsOptions" :form="form" placeholder="Select option"
|
|
||||||
label="How did you hear about us?" :required="true"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Password -->
|
|
||||||
<text-input native-type="password" placeholder="Enter password"
|
|
||||||
name="password" :form="form" :label="$t('password')" :required="true"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Password Confirmation-->
|
|
||||||
<text-input native-type="password" :form="form" :required="true" placeholder="Enter confirm password"
|
|
||||||
name="password_confirmation" :label="$t('confirm_password')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<checkbox-input :form="form" name="agree_terms" :required="true">
|
|
||||||
<template #label>
|
|
||||||
I agree with the <router-link :to="{name:'terms-conditions'}" target="_blank">Terms and conditions</router-link> and <router-link :to="{name:'privacy-policy'}" target="_blank">Privacy policy</router-link> of the website and I accept them.
|
|
||||||
</template>
|
|
||||||
</checkbox-input>
|
|
||||||
|
|
||||||
<!-- Submit Button -->
|
|
||||||
<v-button :loading="form.busy">Create an account</v-button>
|
|
||||||
|
|
||||||
<p class="text-gray-500 mt-4">
|
|
||||||
Already have an account? <router-link :to="{name:'login'}" class="font-semibold ml-1">Log In</router-link>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<!-- GitHub Register Button -->
|
|
||||||
<login-with-github />
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full hidden lg:block lg:w-1/2 md:p-6 mt-8 md:mt-0 ">
|
<div class="w-full hidden lg:block lg:w-1/2 md:p-6 mt-8 md:mt-0 ">
|
||||||
|
@ -92,19 +56,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Form from 'vform'
|
|
||||||
import LoginWithGithub from '~/components/LoginWithGithub'
|
|
||||||
import SelectInput from '../../components/forms/SelectInput'
|
|
||||||
import OpenFormFooter from '../../components/pages/OpenFormFooter'
|
import OpenFormFooter from '../../components/pages/OpenFormFooter'
|
||||||
import { initCrisp } from '../../middleware/check-auth'
|
|
||||||
import Testimonials from '../../components/pages/welcome/Testimonials'
|
import Testimonials from '../../components/pages/welcome/Testimonials'
|
||||||
|
import RegisterForm from './components/RegisterForm'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Testimonials,
|
Testimonials,
|
||||||
SelectInput,
|
OpenFormFooter,
|
||||||
LoginWithGithub,
|
RegisterForm
|
||||||
OpenFormFooter
|
|
||||||
},
|
},
|
||||||
|
|
||||||
middleware: 'guest',
|
middleware: 'guest',
|
||||||
|
@ -114,62 +74,13 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
form: new Form({
|
|
||||||
name: '',
|
|
||||||
email: '',
|
|
||||||
password: '',
|
|
||||||
password_confirmation: '',
|
|
||||||
agree_terms: false
|
|
||||||
}),
|
|
||||||
mustVerifyEmail: false
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
hearAboutUsOptions () {
|
|
||||||
const options = [
|
|
||||||
{ name: 'Facebook', value: 'facebook' },
|
|
||||||
{ name: 'Twitter', value: 'twitter' },
|
|
||||||
{ name: 'Reddit', value: 'reddit' },
|
|
||||||
{ name: 'Github', value: 'github' },
|
|
||||||
{ name: 'Search Engine (Google, DuckDuckGo...)', value: 'search_engine' },
|
|
||||||
{ name: 'Friend or Colleague', value: 'friend_colleague' },
|
|
||||||
{ name: 'Blog/Article', value: 'blog_article' }
|
|
||||||
].map((value) => ({ value, sort: Math.random() }))
|
|
||||||
.sort((a, b) => a.sort - b.sort)
|
|
||||||
.map(({ value }) => value)
|
|
||||||
options.push({ name: 'Other', value: 'other' })
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
async register () {
|
|
||||||
// Register the user.
|
|
||||||
const { data } = await this.form.post('/api/register')
|
|
||||||
|
|
||||||
// Must verify email fist.
|
|
||||||
if (data.status) {
|
|
||||||
this.mustVerifyEmail = true
|
|
||||||
} else {
|
|
||||||
// Log in the user.
|
|
||||||
const { data: { token } } = await this.form.post('/api/login')
|
|
||||||
|
|
||||||
// Save the token.
|
|
||||||
this.$store.dispatch('auth/saveToken', { token })
|
|
||||||
|
|
||||||
// Update the user.
|
|
||||||
await this.$store.dispatch('auth/updateUser', { user: data })
|
|
||||||
|
|
||||||
// Track event
|
|
||||||
this.$logEvent('register', { source: this.form.hear_about_us })
|
|
||||||
initCrisp(data).then(() => {
|
|
||||||
this.$getCrisp().push(['set', 'session:event', [[['register', {}, 'blue']]]])
|
|
||||||
})
|
|
||||||
|
|
||||||
// Redirect home.
|
|
||||||
this.$router.push({ name: 'forms.create' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-wrap flex-col">
|
||||||
|
<transition v-if="stateReady" name="fade" mode="out-in">
|
||||||
|
<div key="2">
|
||||||
|
<form-editor v-if="!workspacesLoading" ref="editor"
|
||||||
|
class="w-full flex flex-grow"
|
||||||
|
:style="{
|
||||||
|
'max-height': editorMaxHeight + 'px'
|
||||||
|
}" :error="error"
|
||||||
|
:isGuest="isGuest"
|
||||||
|
@mounted="onResize"
|
||||||
|
@openRegister="openRegister"
|
||||||
|
/>
|
||||||
|
<div v-else class="text-center mt-4 py-6">
|
||||||
|
<loader class="h-6 w-6 text-nt-blue mx-auto"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
|
||||||
|
<quick-register :showRegisterModal="registerModal" @close="registerModal=false" @reopen="registerModal=true"
|
||||||
|
@afterLogin="afterLogin"/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import store from '~/store'
|
||||||
|
import Form from 'vform'
|
||||||
|
import {mapState, mapActions} from 'vuex'
|
||||||
|
import QuickRegister from '../auth/components/QuickRegister'
|
||||||
|
import initForm from "../../mixins/form_editor/initForm"
|
||||||
|
|
||||||
|
const FormEditor = () => import('../../components/open/forms/components/FormEditor')
|
||||||
|
|
||||||
|
const loadTemplates = function () {
|
||||||
|
store.commit('open/templates/startLoading')
|
||||||
|
store.dispatch('open/templates/loadIfEmpty').then(() => {
|
||||||
|
store.commit('open/templates/stopLoading')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CreateFormGuest',
|
||||||
|
mixins: [initForm],
|
||||||
|
components: {
|
||||||
|
FormEditor,
|
||||||
|
QuickRegister
|
||||||
|
},
|
||||||
|
|
||||||
|
middleware: 'guest',
|
||||||
|
|
||||||
|
metaInfo() {
|
||||||
|
return {title: 'Create a new Form as Guest'}
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeRouteEnter(to, from, next) {
|
||||||
|
loadTemplates()
|
||||||
|
next()
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
stateReady: false,
|
||||||
|
loading: false,
|
||||||
|
error: '',
|
||||||
|
editorMaxHeight: 500,
|
||||||
|
registerModal: false,
|
||||||
|
isGuest: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
workspaces: state => state['open/workspaces'].content,
|
||||||
|
workspacesLoading: state => state['open/workspaces'].loading,
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state['open/working_form'].content
|
||||||
|
},
|
||||||
|
/* We add a setter */
|
||||||
|
set(value) {
|
||||||
|
this.$store.commit('open/working_form/set', value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
workspace() {
|
||||||
|
return this.$store.getters['open/workspaces/getCurrent']()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
workspace() {
|
||||||
|
if (this.workspace) {
|
||||||
|
this.form.workspace_id = this.workspace.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
// Set as guest user
|
||||||
|
const guestWorkspace = {
|
||||||
|
id: null,
|
||||||
|
name: "Guest Workspace",
|
||||||
|
is_enterprise: false,
|
||||||
|
is_pro: false
|
||||||
|
}
|
||||||
|
this.$store.commit('open/workspaces/set', [guestWorkspace])
|
||||||
|
this.$store.commit('open/workspaces/setCurrentId', guestWorkspace.id)
|
||||||
|
|
||||||
|
this.initForm()
|
||||||
|
if (this.$route.query.template !== undefined && this.$route.query.template) {
|
||||||
|
const template = this.$store.getters['open/templates/getBySlug'](this.$route.query.template)
|
||||||
|
if (template && template.structure) {
|
||||||
|
this.form = new Form({...this.form.data(), ...template.structure})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.closeAlert()
|
||||||
|
this.stateReady = true
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
window.addEventListener('resize', this.onResize)
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
window.removeEventListener('resize', this.onResize)
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
loadWorkspaces: 'open/workspaces/load'
|
||||||
|
}),
|
||||||
|
/**
|
||||||
|
* Compute max height of editor
|
||||||
|
*/
|
||||||
|
onResize() {
|
||||||
|
if (this.$refs.editor) {
|
||||||
|
this.editorMaxHeight = window.innerHeight - this.$refs.editor.$el.offsetTop
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openRegister() {
|
||||||
|
this.registerModal = true
|
||||||
|
},
|
||||||
|
afterLogin() {
|
||||||
|
this.registerModal = false
|
||||||
|
this.isGuest = false
|
||||||
|
this.loadWorkspaces()
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.editor.saveFormCreate()
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -21,18 +21,21 @@
|
||||||
import store from '~/store'
|
import store from '~/store'
|
||||||
import Form from 'vform'
|
import Form from 'vform'
|
||||||
import {mapState, mapActions} from 'vuex'
|
import {mapState, mapActions} from 'vuex'
|
||||||
|
import initForm from "../../mixins/form_editor/initForm";
|
||||||
|
|
||||||
const FormEditor = () => import('../../components/open/forms/components/FormEditor')
|
const FormEditor = () => import('../../components/open/forms/components/FormEditor')
|
||||||
|
|
||||||
const loadTemplates = function () {
|
const loadTemplates = function () {
|
||||||
store.commit('open/templates/startLoading')
|
store.commit('open/templates/startLoading')
|
||||||
store.dispatch('open/templates/loadIfEmpty').then(() => {
|
store.dispatch('open/templates/loadIfEmpty').then(() => {
|
||||||
store.commit('open/templates/stopLoading')
|
store.commit('open/templates/stopLoading')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CreateForm',
|
name: 'CreateForm',
|
||||||
|
|
||||||
|
mixins: [initForm],
|
||||||
components: {
|
components: {
|
||||||
FormEditor,
|
FormEditor,
|
||||||
},
|
},
|
||||||
|
@ -41,7 +44,7 @@ export default {
|
||||||
return {title: 'Create a new Form'}
|
return {title: 'Create a new Form'}
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
beforeRouteEnter(to, from, next) {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
next()
|
next()
|
||||||
},
|
},
|
||||||
|
@ -75,12 +78,6 @@ export default {
|
||||||
workspace() {
|
workspace() {
|
||||||
return this.$store.getters['open/workspaces/getCurrent']()
|
return this.$store.getters['open/workspaces/getCurrent']()
|
||||||
},
|
},
|
||||||
fromOnboarding() {
|
|
||||||
return this.$route.params.from_onboarding
|
|
||||||
},
|
|
||||||
fbGroupLink() {
|
|
||||||
return window.config.links.facebook_group
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -95,15 +92,12 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
if(this.$route.query.template !== undefined && this.$route.query.template){
|
this.initForm()
|
||||||
let template = this.$store.getters['open/templates/getBySlug'](this.$route.query.template)
|
if (this.$route.query.template !== undefined && this.$route.query.template) {
|
||||||
if(template && template.structure){
|
const template = this.$store.getters['open/templates/getBySlug'](this.$route.query.template)
|
||||||
this.form = new Form(template.structure)
|
if (template && template.structure) {
|
||||||
}else{
|
this.form = new Form({...this.form.data(), ...template.structure})
|
||||||
this.initForm()
|
|
||||||
}
|
}
|
||||||
}else{
|
|
||||||
this.initForm()
|
|
||||||
}
|
}
|
||||||
this.closeAlert()
|
this.closeAlert()
|
||||||
this.loadWorkspaces()
|
this.loadWorkspaces()
|
||||||
|
@ -122,50 +116,6 @@ export default {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
loadWorkspaces: 'open/workspaces/loadIfEmpty'
|
loadWorkspaces: 'open/workspaces/loadIfEmpty'
|
||||||
}),
|
}),
|
||||||
initForm() {
|
|
||||||
this.form = new Form({
|
|
||||||
title: 'My Form',
|
|
||||||
description: null,
|
|
||||||
visibility: 'public',
|
|
||||||
workspace_id: this.workspace?.id,
|
|
||||||
properties: [],
|
|
||||||
|
|
||||||
notifies: false,
|
|
||||||
slack_notifies: false,
|
|
||||||
send_submission_confirmation: false,
|
|
||||||
webhook_url: null,
|
|
||||||
|
|
||||||
// Customization
|
|
||||||
theme: 'default',
|
|
||||||
width: 'centered',
|
|
||||||
dark_mode: 'auto',
|
|
||||||
color: '#3B82F6',
|
|
||||||
hide_title: false,
|
|
||||||
no_branding: false,
|
|
||||||
uppercase_labels: true,
|
|
||||||
transparent_background: false,
|
|
||||||
closes_at: null,
|
|
||||||
closed_text: 'This form has now been closed by its owner and does not accept submissions anymore.',
|
|
||||||
|
|
||||||
// Submission
|
|
||||||
submit_button_text: 'Submit',
|
|
||||||
re_fillable: false,
|
|
||||||
re_fill_button_text: 'Fill Again',
|
|
||||||
submitted_text: 'Amazing, we saved your answers. Thank you for your time and have a great day!',
|
|
||||||
notification_sender: 'OpnForm',
|
|
||||||
notification_subject: 'We saved your answers',
|
|
||||||
notification_body: 'Hello there 👋 <br>This is a confirmation that your submission was successfully saved.',
|
|
||||||
notifications_include_submission: true,
|
|
||||||
use_captcha: false,
|
|
||||||
is_rating: false,
|
|
||||||
rating_max_value: 5,
|
|
||||||
max_submissions_count: null,
|
|
||||||
max_submissions_reached_text: 'This form has now reached the maximum number of allowed submissions and is now closed.',
|
|
||||||
|
|
||||||
// Security & Privacy
|
|
||||||
can_be_indexed: true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* Compute max height of editor
|
* Compute max height of editor
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,34 +1,38 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col min-h-full mt-6">
|
<div class="flex flex-col min-h-full mt-6">
|
||||||
<div class="w-full flex-grow md:w-4/5 lg:w-2/3 md:mx-auto md:max-w-4xl px-4">
|
<div class="w-full flex-grow md:w-4/5 lg:w-2/3 md:mx-auto md:max-w-4xl px-4">
|
||||||
|
|
||||||
<breadcrumb :path="breadcrumbs" />
|
<breadcrumb :path="breadcrumbs"/>
|
||||||
<div v-if="templatesLoading" class="text-center">
|
<div v-if="templatesLoading" class="text-center">
|
||||||
<loader class="h-6 w-6 text-nt-blue mx-auto" />
|
<loader class="h-6 w-6 text-nt-blue mx-auto"/>
|
||||||
|
</div>
|
||||||
|
<p v-else-if="template === null || !template">
|
||||||
|
Template does not exist.
|
||||||
|
</p>
|
||||||
|
<div v-else>
|
||||||
|
<div class="flex flex-wrap items-center mt-6 mb-4">
|
||||||
|
<h2 class="text-nt-blue text-3xl font-bold flex-grow">
|
||||||
|
{{ template.name }}
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<p v-else-if="template === null || !template">
|
<div class="mb-10">
|
||||||
Template does not exist.
|
<div class="w-full shadow-xl rounded-lg my-5 max-h-72 flex items-center justify-center overflow-hidden">
|
||||||
</p>
|
<img :src="template.image_url" alt="Template cover image" class="w-full object-cover"/>
|
||||||
<div v-else>
|
</div>
|
||||||
<div class="flex flex-wrap items-center mt-6 mb-4">
|
<div v-html="template.description"></div>
|
||||||
<h2 class="text-nt-blue text-3xl font-bold flex-grow">
|
<div class="mt-5 text-center">
|
||||||
{{ template.name }}
|
<v-button class="mt-4 sm:mt-0" :to="{path:'/forms/create?template='+template.slug}">
|
||||||
</h2>
|
Use this template
|
||||||
|
</v-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-10">
|
|
||||||
<img :src="template.image_url" alt="" class="w-full shadow-xl rounded-lg my-5"/>
|
|
||||||
<div v-html="template.description"></div>
|
|
||||||
<div class="mt-5 text-center">
|
|
||||||
<v-button class="mt-4 sm:mt-0" :to="{path:'/forms/create?template='+template.slug}">
|
|
||||||
Use this template
|
|
||||||
</v-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 class="text-center text-gray-500">Template Preview</h3>
|
<h3 class="text-center text-gray-500 mt-6 mb-2">Template Preview</h3>
|
||||||
<open-complete-form ref="open-complete-form" :form="form" :creating="true" class="my-5 p-4 bg-gray-50 rounded-lg"/>
|
<open-complete-form ref="open-complete-form" :form="form" :creating="true"
|
||||||
|
class="mb-4 p-4 bg-gray-50 rounded-lg overflow-hidden"/>
|
||||||
|
|
||||||
|
<div v-if="template.questions.length > 0" id="questions">
|
||||||
<h3 class="text-xl font-semibold mb-3">Frequently asked questions</h3>
|
<h3 class="text-xl font-semibold mb-3">Frequently asked questions</h3>
|
||||||
<div v-if="template.questions.length > 0" class="mt-5 pt-2">
|
<div class="mt-5 pt-2">
|
||||||
<div v-for="(ques,ques_key) in template.questions" :key="ques_key" class="my-3 border rounded-lg">
|
<div v-for="(ques,ques_key) in template.questions" :key="ques_key" class="my-3 border rounded-lg">
|
||||||
<h5 class="border-b p-2">{{ ques.question }}</h5>
|
<h5 class="border-b p-2">{{ ques.question }}</h5>
|
||||||
<div class="p-2" v-html="ques.answer"></div>
|
<div class="p-2" v-html="ques.answer"></div>
|
||||||
|
@ -37,64 +41,65 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<open-form-footer class="mt-8 border-t" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<open-form-footer class="mt-8 border-t"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import store from '~/store'
|
import store from '~/store'
|
||||||
import Form from 'vform'
|
import Form from 'vform'
|
||||||
import { mapGetters, mapState } from 'vuex'
|
import {mapGetters, mapState} from 'vuex'
|
||||||
import Fuse from 'fuse.js'
|
import Fuse from 'fuse.js'
|
||||||
import OpenFormFooter from '../../components/pages/OpenFormFooter'
|
import OpenFormFooter from '../../components/pages/OpenFormFooter'
|
||||||
import OpenCompleteForm from '../../components/open/forms/OpenCompleteForm'
|
import OpenCompleteForm from '../../components/open/forms/OpenCompleteForm'
|
||||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||||
|
|
||||||
const loadTemplates = function () {
|
const loadTemplates = function () {
|
||||||
store.commit('open/templates/startLoading')
|
store.commit('open/templates/startLoading')
|
||||||
store.dispatch('open/templates/loadIfEmpty').then(() => {
|
store.dispatch('open/templates/loadIfEmpty').then(() => {
|
||||||
store.commit('open/templates/stopLoading')
|
store.commit('open/templates/stopLoading')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {Breadcrumb, OpenFormFooter, OpenCompleteForm },
|
components: {Breadcrumb, OpenFormFooter, OpenCompleteForm},
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
beforeRouteEnter(to, from, next) {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
next()
|
next()
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
metaTitle: { type: String, default: 'Templates' },
|
metaTitle: {type: String, default: 'Templates'},
|
||||||
metaDescription: { type: String, default: 'Public templates for create form quickly!' }
|
metaDescription: {type: String, default: 'Public templates for create form quickly!'}
|
||||||
},
|
},
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
templatesLoading: state => state['open/templates'].loading
|
||||||
|
}),
|
||||||
|
breadcrumbs() {
|
||||||
|
if (!this.template) {
|
||||||
|
return [{route: {name: 'templates'}, label: 'Templates'}]
|
||||||
}
|
}
|
||||||
|
return [{route: {name: 'templates'}, label: 'Templates'}, {label: this.template.name}]
|
||||||
},
|
},
|
||||||
|
template() {
|
||||||
mounted () {},
|
return this.$store.getters['open/templates/getBySlug'](this.$route.params.slug)
|
||||||
|
},
|
||||||
methods: {},
|
form() {
|
||||||
|
return new Form(this.template.structure)
|
||||||
computed: {
|
|
||||||
...mapState({
|
|
||||||
templatesLoading: state => state['open/templates'].loading
|
|
||||||
}),
|
|
||||||
breadcrumbs () {
|
|
||||||
if (!this.template) {
|
|
||||||
return [{ route: { name: 'templates' }, label: 'Templates' }]
|
|
||||||
}
|
|
||||||
return [{ route: { name: 'templates' }, label: 'Templates' }, { label: this.template.name }]
|
|
||||||
},
|
|
||||||
template () {
|
|
||||||
return this.$store.getters['open/templates/getBySlug'](this.$route.params.slug)
|
|
||||||
},
|
|
||||||
form (){
|
|
||||||
return new Form(this.template.structure)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
}
|
||||||
|
</script>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
>it's free</span>.
|
>it's free</span>.
|
||||||
</h3>
|
</h3>
|
||||||
<div class="mt-6 flex justify-center">
|
<div class="mt-6 flex justify-center">
|
||||||
<v-button class="mr-1" :to="{ name: 'register' }" :arrow="true">
|
<v-button class="mr-1" :to="{ name: 'forms.create.guest' }" :arrow="true">
|
||||||
Create a form for FREE
|
Create a form for FREE
|
||||||
</v-button>
|
</v-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -73,7 +73,7 @@
|
||||||
<h4 class="font-semibold text-3xl text-white">Take your forms to the next level</h4>
|
<h4 class="font-semibold text-3xl text-white">Take your forms to the next level</h4>
|
||||||
<p class="text-gray-300 my-8">No trial. Generous, unlimited free plan.</p>
|
<p class="text-gray-300 my-8">No trial. Generous, unlimited free plan.</p>
|
||||||
<div class="mt-6 flex justify-center">
|
<div class="mt-6 flex justify-center">
|
||||||
<v-button :to="{ name: 'register' }" v-track.welcome_create_form_click :arrow="true" color="blue">
|
<v-button :to="{ name: 'forms.create.guest' }" v-track.welcome_create_form_click :arrow="true" color="blue">
|
||||||
Create a form for FREE
|
Create a form for FREE
|
||||||
</v-button>
|
</v-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,6 +8,7 @@ export default [
|
||||||
|
|
||||||
// Forms
|
// Forms
|
||||||
{ path: '/forms/create', name: 'forms.create', component: page('forms/create.vue') },
|
{ path: '/forms/create', name: 'forms.create', component: page('forms/create.vue') },
|
||||||
|
{ path: '/forms/create/guest', name: 'forms.create.guest', component: page('forms/create-guest.vue') },
|
||||||
{ path: '/forms/:slug/edit', name: 'forms.edit', component: page('forms/edit.vue') },
|
{ path: '/forms/:slug/edit', name: 'forms.edit', component: page('forms/edit.vue') },
|
||||||
{
|
{
|
||||||
path: '/forms/:slug/show',
|
path: '/forms/:slug/show',
|
||||||
|
|
Loading…
Reference in New Issue