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-router": "^3.5.2",
|
||||
"vue-tailwind": "^2.5.0",
|
||||
"vue-tour": "^2.0.0",
|
||||
"vue2-editor": "^2.10.3",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"vuex": "^3.6.2",
|
||||
|
|
|
@ -6,8 +6,6 @@ import App from '~/components/App'
|
|||
import LoadScript from 'vue-plugin-load-script'
|
||||
import Base from './base'
|
||||
|
||||
import VueTour from 'vue-tour'
|
||||
|
||||
import '~/plugins'
|
||||
import '~/components'
|
||||
|
||||
|
@ -16,10 +14,6 @@ Vue.config.productionTip = false
|
|||
Vue.mixin(Base)
|
||||
Vue.use(LoadScript)
|
||||
|
||||
/* Vue Tour */
|
||||
require('vue-tour/dist/vue-tour.css')
|
||||
Vue.use(VueTour)
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
i18n,
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
<workspace-dropdown class="ml-6"/>
|
||||
</div>
|
||||
<div class="hidden md:block ml-auto relative">
|
||||
<!-- <router-link :to="{name:'integrations'}"-->
|
||||
<!-- class="text-sm text-gray-600 dark:text-white hover:text-gray-800 cursor-pointer mt-1 mr-8">-->
|
||||
<!-- Integrations-->
|
||||
<!-- </router-link>-->
|
||||
<router-link :to="{name:'templates'}"
|
||||
class="text-sm text-gray-600 dark:text-white hover:text-gray-800 cursor-pointer mt-1 mr-8">
|
||||
Templates
|
||||
</router-link>
|
||||
<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"
|
||||
>
|
||||
|
@ -99,7 +99,7 @@
|
|||
{{ $t('login') }}
|
||||
</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
|
||||
</v-button>
|
||||
|
||||
|
|
|
@ -7,17 +7,17 @@
|
|||
</div>
|
||||
<modal :show="showPremiumModal" @close="showPremiumModal=false">
|
||||
<h2 class="text-nt-blue">
|
||||
OpenForm PRO
|
||||
OpnForm PRO
|
||||
</h2>
|
||||
<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>.
|
||||
<br><br>
|
||||
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.
|
||||
</h4>
|
||||
<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>.
|
||||
</h4>
|
||||
<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"
|
||||
>
|
||||
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
|
||||
the form editor, but you can't use them in your real forms</b>. You can subscribe now to gain unlimited access
|
||||
to
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
>
|
||||
<template #submit-btn="{submitForm}">
|
||||
<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 }}
|
||||
</open-form-button>
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
<template>
|
||||
<div v-if="form" id="form-editor" class="w-full flex flex-grow relative overflow-x-hidden">
|
||||
<!-- 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="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
|
||||
to preview your form changes.
|
||||
</div>
|
||||
<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">
|
||||
<path d="M5 9L1 5L5 1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
|
||||
stroke-linejoin="round"/>
|
||||
|
@ -88,6 +87,11 @@ export default {
|
|||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isGuest: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@ -123,7 +127,7 @@ export default {
|
|||
{
|
||||
target: '#v-step-0',
|
||||
header: {
|
||||
title: 'Welcome to the OpenForm Editor!'
|
||||
title: 'Welcome to the OpnForm Editor!'
|
||||
},
|
||||
content: 'Discover <strong>your form Editor</strong>!'
|
||||
},
|
||||
|
@ -160,20 +164,16 @@ export default {
|
|||
|
||||
mounted() {
|
||||
this.$emit('mounted')
|
||||
this.startTour()
|
||||
},
|
||||
|
||||
methods: {
|
||||
startTour() {
|
||||
if (!this.user.has_forms) {
|
||||
this.$tours.tutorial.start()
|
||||
}
|
||||
},
|
||||
showValidationErrors() {
|
||||
this.showFormErrorModal = true
|
||||
},
|
||||
saveForm() {
|
||||
if (this.isEdit) {
|
||||
if(this.isGuest) {
|
||||
this.saveFormGuest()
|
||||
} else if (this.isEdit) {
|
||||
this.saveFormEdit()
|
||||
} else {
|
||||
this.saveFormCreate()
|
||||
|
@ -230,6 +230,9 @@ export default {
|
|||
}).finally(() => {
|
||||
this.updateFormLoading = false
|
||||
})
|
||||
},
|
||||
saveFormGuest() {
|
||||
this.$emit('openRegister')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -266,7 +266,7 @@ export default {
|
|||
];
|
||||
},
|
||||
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()
|
||||
} else {
|
||||
this.formFields = clonedeep(this.form.properties).map((field) => {
|
||||
|
|
|
@ -47,6 +47,17 @@
|
|||
</svg>
|
||||
Duplicate form
|
||||
</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="#"
|
||||
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}"
|
||||
|
@ -60,17 +71,6 @@
|
|||
</svg>
|
||||
Delete form
|
||||
</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>
|
||||
|
||||
<create-template-modal :form="form" :show="showCreateTemplateModal" @close="showCreateTemplateModal=false"/>
|
||||
|
@ -127,4 +127,3 @@ export default {
|
|||
}
|
||||
}
|
||||
</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>
|
||||
<div>
|
||||
<forgot-password-modal :show="showForgotModal" @close="showForgotModal=false" />
|
||||
|
||||
<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:w-1/2 md:p-6">
|
||||
|
@ -11,35 +9,7 @@
|
|||
</h2>
|
||||
<small>Welcome back! Please enter your details.</small>
|
||||
|
||||
<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? <router-link :to="{name:'register'}" class="font-semibold ml-1">Sign Up</router-link>
|
||||
</p>
|
||||
</form>
|
||||
<login-form />
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 md:p-6 mt-8 md:mt-0 ">
|
||||
|
@ -86,17 +56,15 @@
|
|||
</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'
|
||||
import LoginForm from './components/LoginForm'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
OpenFormFooter,
|
||||
Testimonials,
|
||||
ForgotPasswordModal
|
||||
LoginForm
|
||||
},
|
||||
|
||||
middleware: 'guest',
|
||||
|
@ -106,42 +74,11 @@ export default {
|
|||
},
|
||||
|
||||
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 () {
|
||||
const intendedUrl = Cookies.get('intended_url')
|
||||
|
||||
if (intendedUrl) {
|
||||
Cookies.remove('intended_url')
|
||||
this.$router.push({ path: intendedUrl })
|
||||
} else {
|
||||
this.$router.push({ name: 'home' })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -9,43 +9,7 @@
|
|||
</h2>
|
||||
<small>Sign up in less than 2 minutes.</small>
|
||||
|
||||
<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? <router-link :to="{name:'login'}" class="font-semibold ml-1">Log In</router-link>
|
||||
</p>
|
||||
|
||||
<!-- GitHub Register Button -->
|
||||
<login-with-github />
|
||||
</form>
|
||||
<register-form />
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full hidden lg:block lg:w-1/2 md:p-6 mt-8 md:mt-0 ">
|
||||
|
@ -92,19 +56,15 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Form from 'vform'
|
||||
import LoginWithGithub from '~/components/LoginWithGithub'
|
||||
import SelectInput from '../../components/forms/SelectInput'
|
||||
import OpenFormFooter from '../../components/pages/OpenFormFooter'
|
||||
import { initCrisp } from '../../middleware/check-auth'
|
||||
import Testimonials from '../../components/pages/welcome/Testimonials'
|
||||
import RegisterForm from './components/RegisterForm'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Testimonials,
|
||||
SelectInput,
|
||||
LoginWithGithub,
|
||||
OpenFormFooter
|
||||
OpenFormFooter,
|
||||
RegisterForm
|
||||
},
|
||||
|
||||
middleware: 'guest',
|
||||
|
@ -114,62 +74,13 @@ export default {
|
|||
},
|
||||
|
||||
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 home.
|
||||
this.$router.push({ name: 'forms.create' })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</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,6 +21,7 @@
|
|||
import store from '~/store'
|
||||
import Form from 'vform'
|
||||
import {mapState, mapActions} from 'vuex'
|
||||
import initForm from "../../mixins/form_editor/initForm";
|
||||
|
||||
const FormEditor = () => import('../../components/open/forms/components/FormEditor')
|
||||
|
||||
|
@ -33,6 +34,8 @@ const loadTemplates = function () {
|
|||
|
||||
export default {
|
||||
name: 'CreateForm',
|
||||
|
||||
mixins: [initForm],
|
||||
components: {
|
||||
FormEditor,
|
||||
},
|
||||
|
@ -75,12 +78,6 @@ export default {
|
|||
workspace() {
|
||||
return this.$store.getters['open/workspaces/getCurrent']()
|
||||
},
|
||||
fromOnboarding() {
|
||||
return this.$route.params.from_onboarding
|
||||
},
|
||||
fbGroupLink() {
|
||||
return window.config.links.facebook_group
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
@ -95,15 +92,12 @@ export default {
|
|||
},
|
||||
|
||||
mounted() {
|
||||
this.initForm()
|
||||
if (this.$route.query.template !== undefined && this.$route.query.template) {
|
||||
let template = this.$store.getters['open/templates/getBySlug'](this.$route.query.template)
|
||||
const template = this.$store.getters['open/templates/getBySlug'](this.$route.query.template)
|
||||
if (template && template.structure) {
|
||||
this.form = new Form(template.structure)
|
||||
}else{
|
||||
this.initForm()
|
||||
this.form = new Form({...this.form.data(), ...template.structure})
|
||||
}
|
||||
}else{
|
||||
this.initForm()
|
||||
}
|
||||
this.closeAlert()
|
||||
this.loadWorkspaces()
|
||||
|
@ -122,50 +116,6 @@ export default {
|
|||
...mapActions({
|
||||
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
|
||||
*/
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
</h2>
|
||||
</div>
|
||||
<div class="mb-10">
|
||||
<img :src="template.image_url" alt="" class="w-full shadow-xl rounded-lg my-5"/>
|
||||
<div class="w-full shadow-xl rounded-lg my-5 max-h-72 flex items-center justify-center overflow-hidden">
|
||||
<img :src="template.image_url" alt="Template cover image" class="w-full object-cover"/>
|
||||
</div>
|
||||
<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}">
|
||||
|
@ -24,11 +26,13 @@
|
|||
</v-button>
|
||||
</div>
|
||||
|
||||
<h3 class="text-center text-gray-500">Template Preview</h3>
|
||||
<open-complete-form ref="open-complete-form" :form="form" :creating="true" class="my-5 p-4 bg-gray-50 rounded-lg"/>
|
||||
<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="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>
|
||||
<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">
|
||||
<h5 class="border-b p-2">{{ ques.question }}</h5>
|
||||
<div class="p-2" v-html="ques.answer"></div>
|
||||
|
@ -37,6 +41,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<open-form-footer class="mt-8 border-t"/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -71,11 +76,11 @@
|
|||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
return {}
|
||||
},
|
||||
|
||||
mounted () {},
|
||||
mounted() {
|
||||
},
|
||||
|
||||
methods: {},
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
>it's free</span>.
|
||||
</h3>
|
||||
<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
|
||||
</v-button>
|
||||
</div>
|
||||
|
@ -73,7 +73,7 @@
|
|||
<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>
|
||||
<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
|
||||
</v-button>
|
||||
</div>
|
||||
|
|
|
@ -8,6 +8,7 @@ export default [
|
|||
|
||||
// Forms
|
||||
{ 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/show',
|
||||
|
|
Loading…
Reference in New Issue