Merge main and resolve conflicts
This commit is contained in:
78
resources/js/pages/auth/ForgotPasswordModal.vue
Normal file
78
resources/js/pages/auth/ForgotPasswordModal.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<!-- Forgot password modal -->
|
||||
<modal :show="show" @close="close" max-width="lg">
|
||||
<template #icon>
|
||||
<template v-if="isMailSent">
|
||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect class="text-blue-50" width="56" height="56" rx="28" fill="currentColor"/>
|
||||
<path d="M16.3333 22.1666L25.859 28.8346C26.6304 29.3746 27.016 29.6446 27.4356 29.7492C27.8061 29.8415 28.1937 29.8415 28.5643 29.7492C28.9838 29.6446 29.3695 29.3746 30.1408 28.8346L39.6666 22.1666M21.9333 37.3333H34.0666C36.0268 37.3333 37.0069 37.3333 37.7556 36.9518C38.4141 36.6163 38.9496 36.0808 39.2851 35.4223C39.6666 34.6736 39.6666 33.6935 39.6666 31.7333V24.2666C39.6666 22.3064 39.6666 21.3264 39.2851 20.5777C38.9496 19.9191 38.4141 19.3837 37.7556 19.0481C37.0069 18.6666 36.0268 18.6666 34.0666 18.6666H21.9333C19.9731 18.6666 18.993 18.6666 18.2443 19.0481C17.5857 19.3837 17.0503 19.9191 16.7147 20.5777C16.3333 21.3264 16.3333 22.3064 16.3333 24.2666V31.7333C16.3333 33.6935 16.3333 34.6736 16.7147 35.4223C17.0503 36.0808 17.5857 36.6163 18.2443 36.9518C18.993 37.3333 19.9731 37.3333 21.9333 37.3333Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</template>
|
||||
<template v-else>
|
||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect class="text-blue-50" width="56" height="56" rx="28" fill="currentColor"/>
|
||||
<path d="M33.8333 24.4999C33.8333 23.9028 33.6055 23.3057 33.1499 22.8501C32.6943 22.3945 32.0972 22.1667 31.5 22.1667M31.5 31.5C35.366 31.5 38.5 28.366 38.5 24.5C38.5 20.634 35.366 17.5 31.5 17.5C27.634 17.5 24.5 20.634 24.5 24.5C24.5 24.8193 24.5214 25.1336 24.5628 25.4415C24.6309 25.948 24.6649 26.2013 24.642 26.3615C24.6181 26.5284 24.5877 26.6184 24.5055 26.7655C24.4265 26.9068 24.2873 27.046 24.009 27.3243L18.0467 33.2866C17.845 33.4884 17.7441 33.5893 17.6719 33.707C17.608 33.8114 17.5608 33.9252 17.5322 34.0442C17.5 34.1785 17.5 34.3212 17.5 34.6065V36.6333C17.5 37.2867 17.5 37.6134 17.6272 37.863C17.739 38.0825 17.9175 38.261 18.137 38.3728C18.3866 38.5 18.7133 38.5 19.3667 38.5H22.1667V36.1667H24.5V33.8333H26.8333L28.6757 31.991C28.954 31.7127 29.0932 31.5735 29.2345 31.4945C29.3816 31.4123 29.4716 31.3819 29.6385 31.358C29.7987 31.3351 30.052 31.3691 30.5585 31.4372C30.8664 31.4786 31.1807 31.5 31.5 31.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</template>
|
||||
</template>
|
||||
<template #title>
|
||||
<template v-if="isMailSent">Check your email</template>
|
||||
<template v-else>Forgot password?</template>
|
||||
</template>
|
||||
<template v-if="isMailSent">
|
||||
<div class="text-center">We sent a password reset link to <br/><span>{{form.email}}</span></div>
|
||||
<div class="w-full p-4 text-center">
|
||||
<span class="mt-4">Didn't receive the email? <a href="#" class="ml-1" @click.prevent="send">Click to resend</a></span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="text-center">No worries, we'll send you reset instructions.</div>
|
||||
<form @submit.prevent="send" @keydown="form.onKeydown($event)" class="p-4">
|
||||
<text-input name="email" :form="form" label="Email" placeholder="Your email address" :required="true" />
|
||||
|
||||
<div class="w-full mt-6">
|
||||
<v-button :loading="form.busy" class="w-full my-3">Reset password</v-button>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
<div class="w-full text-center">
|
||||
<a href="#" @click.prevent="close" class="text-xs hover:underline text-gray-500 sm:text-sm hover:text-gray-700">
|
||||
<svg class="inline mr-1" width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.3334 6.99996H1.66669M1.66669 6.99996L7.50002 12.8333M1.66669 6.99996L7.50002 1.16663" stroke="#475467" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
Back to log in
|
||||
</a>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Form from 'vform'
|
||||
|
||||
export default {
|
||||
name: 'ForgotPasswordModal',
|
||||
components: { },
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
isMailSent: false,
|
||||
form: new Form({
|
||||
email: ''
|
||||
})
|
||||
}),
|
||||
methods: {
|
||||
async send () {
|
||||
const { data } = await this.form.post('/api/password/email')
|
||||
this.isMailSent = true
|
||||
},
|
||||
close () {
|
||||
this.$emit('close')
|
||||
this.isMailSent = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,5 +1,7 @@
|
||||
<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">
|
||||
@@ -25,9 +27,9 @@
|
||||
</v-checkbox>
|
||||
|
||||
<div class="w-full md:w-1/2 text-right">
|
||||
<router-link :to="{ name: 'password.request' }" class="text-xs hover:underline text-gray-500 sm:text-sm hover:text-gray-700">
|
||||
<a href="#" @click.prevent="showForgotModal=true" class="text-xs hover:underline text-gray-500 sm:text-sm hover:text-gray-700">
|
||||
Forgot your password?
|
||||
</router-link>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -88,11 +90,13 @@ 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 {
|
||||
components: {
|
||||
OpenFormFooter,
|
||||
Testimonials
|
||||
Testimonials,
|
||||
ForgotPasswordModal
|
||||
},
|
||||
|
||||
middleware: 'guest',
|
||||
@@ -106,7 +110,8 @@ export default {
|
||||
email: '',
|
||||
password: ''
|
||||
}),
|
||||
remember: false
|
||||
remember: false,
|
||||
showForgotModal: false
|
||||
}),
|
||||
|
||||
methods: {
|
||||
|
||||
@@ -1,481 +0,0 @@
|
||||
<template>
|
||||
<div class="flex mt-6">
|
||||
<div class="w-full md:w-4/5 lg:w-3/5 md:mx-auto md:max-w-4xl px-4">
|
||||
<breadcrumb class="sm:px-6" :path="breadcrumbs" />
|
||||
<div v-if="form" class="sm:px-6">
|
||||
<h2 class="text-nt-blue text-3xl font-bold z-10 mt-6 mb-3">
|
||||
{{ form.title }}
|
||||
<span v-if="form.visibility=='draft'" class="float-right text-white p-2 text-xs inline rounded-lg font-semibold mr-2 bg-gray-400 dark:bg-gray-700">Draft (not public)</span>
|
||||
</h2>
|
||||
|
||||
<p class="mb-3">
|
||||
<span v-if="form.views_count">This form has been seen
|
||||
<span class="font-semibold">{{ form.views_count }}</span> time{{ form.views_count > 0 ? 's' : '' }}
|
||||
and it has received
|
||||
<span class="font-semibold">{{ form.submissions_count }}</span> submission{{ form.submissions_count > 0 ? 's' : '' }}.</span>
|
||||
</p>
|
||||
|
||||
<p v-if="form.closes_at" class="text-yellow-500">
|
||||
<span v-if="form.is_closed"> This form stopped accepting submissions on the {{ displayClosesDate }} </span>
|
||||
<span v-else> This form will stop accepting submissions on the {{ displayClosesDate }} </span>
|
||||
</p>
|
||||
|
||||
<p v-if="form.max_submissions_count > 0" class="text-yellow-500">
|
||||
<span v-if="form.max_number_of_submissions_reached"> The form is now closed because it reached its limit of {{ form.max_submissions_count }} submissions. </span>
|
||||
<span v-else> This form will stop accepting submissions after {{ form.max_submissions_count }} submissions. </span>
|
||||
</p>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<share-form-url :form="form" :link="true" />
|
||||
</div>
|
||||
|
||||
<!-- Open Form -->
|
||||
<div class="flex flex-wrap -mx-2">
|
||||
<!-- Edit Form -->
|
||||
<div class="w-full sm:w-1/2 px-2 flex">
|
||||
<div v-track.edit_form_click="{form_id:form.id, form_slug:form.slug}"
|
||||
class="group relative transition-all mt-4 flex items-center p-3 px-6 w-full rounded-md bg-gray-50 dark:bg-gray-700 hover:bg-blue-50 dark:hover:bg-blue-900 cursor-pointer hover:text-blue-500"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-4 "
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="font-semibold group relative-hover:text-blue-500">
|
||||
Edit form
|
||||
</span>
|
||||
<router-link :to="{name:'forms.edit',params:{slug:form.slug}}" class="absolute inset-0" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- Open Form -->
|
||||
<div class="w-full sm:w-1/2 px-2 flex">
|
||||
<div
|
||||
v-track.open_form_click="{form_id:form.id, form_slug:form.slug}" class="group relative transition-all mt-4 flex items-center p-3 px-6 w-full rounded-md bg-gray-50 dark:bg-gray-700
|
||||
hover:bg-blue-50 dark:hover:bg-blue-500 cursor-pointer hover:text-blue-500 dark:hover:text-white"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-4 "
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
|
||||
/>
|
||||
</svg>
|
||||
<span class="font-semibold group relative-hover:text-blue-500">
|
||||
Open form
|
||||
</span>
|
||||
<a target="_blank" :href="form.share_url" class="absolute inset-0" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Share/Embed form table -->
|
||||
<div class="w-full sm:w-1/2 px-2 flex">
|
||||
<div
|
||||
v-track.share_embed_form_click="{form_id:form.id, form_slug:form.slug}"
|
||||
class="group relative transition-all mt-4 flex items-center p-3 px-6 w-full rounded-md bg-gray-50 dark:bg-gray-700 hover:bg-blue-50 dark:hover:bg-blue-900 cursor-pointer hover:text-blue-500"
|
||||
@click.prevent="showShareEmbedFormModal=true"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-4 "
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="font-semibold group relative-hover:text-blue-500">
|
||||
Share/Embed form
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Regenerate form link -->
|
||||
<div class="w-full sm:w-1/2 px-2 flex">
|
||||
<div v-track.regenerate_form_link_click="{form_id:form.id, form_slug:form.slug}"
|
||||
class="group relative transition-all mt-4 flex items-center p-3 px-6 w-full rounded-md bg-gray-50 dark:bg-gray-700 hover:bg-blue-50 dark:hover:bg-blue-900 cursor-pointer hover:text-blue-500"
|
||||
@click="showGenerateFormLinkModal=true"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-4 "
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"
|
||||
/>
|
||||
</svg>
|
||||
<span class="font-semibold group relative-hover:text-blue-500">
|
||||
Regenerate form link
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full sm:w-1/2 px-2 flex">
|
||||
<div v-track.url_form_prefill_click="{form_id:form.id, form_slug:form.slug}"
|
||||
class="group relative transition-all mt-4 flex items-center p-3 px-6 w-full rounded-md bg-gray-50 dark:bg-gray-700 hover:bg-blue-50 dark:hover:bg-blue-900 cursor-pointer hover:text-blue-500"
|
||||
@click="showUrlFormPrefillModal=true"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-4" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M17 16v2a2 2 0 01-2 2H5a2 2 0 01-2-2v-7a2 2 0 012-2h2m3-4H9a2 2 0 00-2 2v7a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-1m-1 4l-3 3m0 0l-3-3m3 3V3"
|
||||
/>
|
||||
</svg>
|
||||
<span class="font-semibold group relative-hover:text-blue-500">
|
||||
Url form pre-fill <pro-tag class="ml-2" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full sm:w-1/2 px-2 flex">
|
||||
<div v-track.duplicate_form_click="{form_id:form.id, form_slug:form.slug}"
|
||||
class="group relative transition-all mt-4 flex items-center p-3 px-6 w-full rounded-md bg-gray-50 dark:bg-gray-700 hover:bg-blue-50 dark:hover:bg-blue-900 cursor-pointer hover:text-blue-500"
|
||||
@click="duplicateForm"
|
||||
>
|
||||
<template v-if="!loadingDuplicate">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-4 "
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2"
|
||||
/>
|
||||
</svg>
|
||||
<span class="font-semibold group relative-hover:text-blue-500">
|
||||
Duplicate form
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<loader class="h-6 w-6 text-nt-blue mx-auto" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full sm:w-1/2 px-2 flex mb-5">
|
||||
<div v-track.delete_form_click="{form_id:form.id, form_slug:form.slug}"
|
||||
class="group relative transition-all mt-4 flex items-center p-3 px-6 w-full rounded-md bg-gray-50 dark:bg-gray-700 hover:bg-red-50 dark:hover:bg-red-900 cursor-pointer hover:text-red-500"
|
||||
@click="alertConfirm('Do you really want to delete this form?',deleteForm)"
|
||||
>
|
||||
<template v-if="!loadingDelete">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-4"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
/>
|
||||
</svg>
|
||||
<span class="font-semibold group relative-hover:text-red-500">
|
||||
Delete form
|
||||
</span>
|
||||
</template>
|
||||
<loader v-else class="h-6 w-6 text-nt-blue mx-auto" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full sm:w-1/2 px-2 flex mb-5" v-if="user.admin">
|
||||
<div class="group relative transition-all mt-4 flex items-center p-3 px-6 w-full rounded-md bg-gray-50 dark:bg-gray-700 hover:bg-blue-50 dark:hover:bg-blue-900 cursor-pointer hover:text-blue-500"
|
||||
@click="showCreateTemplateModal=true"
|
||||
>
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-4" 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>
|
||||
<span class="font-semibold group relative-hover:text-blue-500">
|
||||
Create template
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Submissions -->
|
||||
<div class="pt-5 mt-5 border-t" id="table-page" v-if="form">
|
||||
<form-submissions />
|
||||
</div>
|
||||
|
||||
<!-- Form Analytics -->
|
||||
<div class="pt-5 mt-5 border-t">
|
||||
<h3 class="font-semibold">
|
||||
Form Analytics (last 30 days)
|
||||
</h3>
|
||||
<form-stats :form="form" />
|
||||
</div>
|
||||
|
||||
<!-- Share/Embed form modal -->
|
||||
<modal :show="showShareEmbedFormModal" @close="showShareEmbedFormModal=false">
|
||||
<div class="px-4">
|
||||
<h2 class="text-nt-blue text-3xl font-bold mb-6">
|
||||
Share/Embed your form
|
||||
</h2>
|
||||
|
||||
<!-- Link -->
|
||||
<h3 class="font-bold text-xl border-t pt-4">
|
||||
Share
|
||||
</h3>
|
||||
<p>Share your form using the link below:</p>
|
||||
<share-form-url :form="form" />
|
||||
|
||||
<!-- Embed -->
|
||||
<h3 class="font-bold text-xl border-t pt-4">
|
||||
Embed
|
||||
</h3>
|
||||
<p>
|
||||
Embed your form on your website by copying the html code below.
|
||||
</p>
|
||||
<embed-form-code :form="form" />
|
||||
|
||||
<div class="flex justify-end mt-4">
|
||||
<v-button color="gray" shade="light" @click="showShareEmbedFormModal=false">Close</v-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</modal>
|
||||
|
||||
<!-- Regenerate form link modal -->
|
||||
<modal :show="showGenerateFormLinkModal" @close="showGenerateFormLinkModal=false">
|
||||
<div class="-m-6">
|
||||
<div class="p-6">
|
||||
<h2 class="text-nt-blue text-3xl font-bold mb-6">
|
||||
Generate new form link
|
||||
</h2>
|
||||
<p>
|
||||
You can choose between two different URL formats for your form. <span class="font-semibold">Be careful, changing your form URL
|
||||
is not a reversible operation</span>. Make sure to udpate your form URL everywhere where it's used.
|
||||
</p>
|
||||
</div>
|
||||
<div class="border-t py-4 mt-4 px-6">
|
||||
<h3 class="text-xl text-gray-700 font-semibold">
|
||||
Human Readable URL
|
||||
</h3>
|
||||
<p>If your users are going to see this url, you might want to make nice and readable. Example:</p>
|
||||
<p class="text-gray-600 p-4 bg-gray-100 rounded-md mt-4">
|
||||
https://opnform.com/forms/contact
|
||||
</p>
|
||||
<div class="text-center mt-4">
|
||||
<v-button :loading="loadingNewLink" @click="regenerateLink('slug')">
|
||||
Generate a Human Readable URL
|
||||
</v-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-t pt-4 mt-4 px-6 pb-10">
|
||||
<h3 class="text-xl text-gray-700 font-semibold">
|
||||
Random ID URL
|
||||
</h3>
|
||||
<p>
|
||||
If your user are not going to see your form url (if it's embedded), and if you prefer to have a random
|
||||
non-guessable URL. Example:
|
||||
</p>
|
||||
<p class="text-gray-600 p-4 bg-gray-100 rounded-md mt-4">
|
||||
https://opnform.com/forms/b4417f9c-34ae-4421-8006-832ee47786e7
|
||||
</p>
|
||||
<div class="text-center mt-4">
|
||||
<v-button :loading="loadingNewLink" @click="regenerateLink('uuid')">
|
||||
Generate a Random ID URL
|
||||
</v-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end mt-4 pb-5 px-6">
|
||||
<v-button color="gray" shade="light" @click="showGenerateFormLinkModal=false">Close</v-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</modal>
|
||||
|
||||
<create-template-modal :form="form" :show="showCreateTemplateModal" @close="showCreateTemplateModal=false" />
|
||||
|
||||
<url-form-prefill-modal :form="form" :show="showUrlFormPrefillModal" @close="showUrlFormPrefillModal=false" />
|
||||
</div>
|
||||
<div v-else-if="loading" class="text-center w-full p-5">
|
||||
<loader class="h-6 w-6 mx-auto" />
|
||||
</div>
|
||||
<div v-else>
|
||||
Form not found.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import store from '~/store'
|
||||
import Form from 'vform'
|
||||
import ShareFormUrl from '../../components/open/forms/components/ShareFormUrl'
|
||||
import EmbedFormCode from '../../components/open/forms/components/EmbedFormCode'
|
||||
import Breadcrumb from '../../components/common/Breadcrumb'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import ProTag from '../../components/common/ProTag'
|
||||
import UrlFormPrefillModal from '../../components/pages/forms/UrlFormPrefillModal'
|
||||
import CreateTemplateModal from '../../components/pages/forms/CreateTemplateModal'
|
||||
import FormStats from '../../components/open/forms/components/FormStats'
|
||||
import FormSubmissions from '../../components/open/forms/components/FormSubmissions'
|
||||
|
||||
const loadForms = function () {
|
||||
store.commit('open/forms/startLoading')
|
||||
store.dispatch('open/workspaces/loadIfEmpty').then(() => {
|
||||
store.dispatch('open/forms/loadIfEmpty', store.state['open/workspaces'].currentId)
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'EditForm',
|
||||
components: { UrlFormPrefillModal, CreateTemplateModal, ProTag, Breadcrumb, ShareFormUrl, EmbedFormCode, FormStats, FormSubmissions },
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
loadForms()
|
||||
next()
|
||||
},
|
||||
|
||||
beforeRouteLeave (to, from, next) {
|
||||
this.workingForm = null
|
||||
next()
|
||||
},
|
||||
middleware: 'auth',
|
||||
|
||||
data () {
|
||||
return {
|
||||
loadingDuplicate: false,
|
||||
loadingDelete: false,
|
||||
loadingNewLink: false,
|
||||
showNotionEmbedModal: false,
|
||||
showShareEmbedFormModal: false,
|
||||
showUrlFormPrefillModal: false,
|
||||
showGenerateFormLinkModal: false,
|
||||
showCreateTemplateModal: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
user: 'auth/user'
|
||||
}),
|
||||
...mapState({
|
||||
formsLoading: state => state['open/forms'].loading,
|
||||
workspacesLoading: state => state['open/workspaces'].loading
|
||||
}),
|
||||
workingForm: {
|
||||
get () {
|
||||
return this.$store.state['open/working_form'].content
|
||||
},
|
||||
set (value) {
|
||||
this.$store.commit('open/working_form/set', value)
|
||||
}
|
||||
},
|
||||
workspace () {
|
||||
if (!this.form) return null
|
||||
return this.$store.getters['open/workspaces/getById'](this.form.workspace_id)
|
||||
},
|
||||
form () {
|
||||
return this.$store.getters['open/forms/getBySlug'](this.$route.params.slug)
|
||||
},
|
||||
formEndpoint: () => '/api/open/forms/{id}',
|
||||
breadcrumbs () {
|
||||
if (!this.form) {
|
||||
return [{ route: { name: 'home' }, label: 'Your Forms' }]
|
||||
}
|
||||
return [{ route: { name: 'home' }, label: 'Your Forms' }, { label: this.form.title }]
|
||||
},
|
||||
loading () {
|
||||
return this.formsLoading || this.workspacesLoading
|
||||
},
|
||||
displayClosesDate(){
|
||||
if(this.form.closes_at){
|
||||
let dateObj = new Date(this.form.closes_at)
|
||||
return dateObj.getFullYear() + "-" +
|
||||
String(dateObj.getMonth() + 1).padStart(2, '0') + "-" +
|
||||
String(dateObj.getDate()).padStart(2, '0') + " " +
|
||||
String(dateObj.getHours()).padStart(2, '0') + ":" +
|
||||
String(dateObj.getMinutes()).padStart(2, '0')
|
||||
}
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
form () {
|
||||
this.workingForm = new Form(this.form)
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.updatedForm = new Form(this.form)
|
||||
|
||||
if (this.$route.params.hasOwnProperty('new_form') && this.$route.params.new_form) {
|
||||
// if (!this.user.is_subscribed && !this.user.has_customer_id) {
|
||||
// // Crisp offer
|
||||
// this.$getCrisp().push(['set', 'session:event', [[['first_form_created', { form_id: this.form.id, form_slug: this.form.slug }, 'blue']]]])
|
||||
//
|
||||
// setTimeout(
|
||||
// function () {
|
||||
// window.$crisp.push(['do', 'chat:show'])
|
||||
// window.$crisp.push(['do', 'chat:open'])
|
||||
// window.$crisp.push([
|
||||
// 'do',
|
||||
// 'message:show',
|
||||
// ['text',
|
||||
// 'Hey there! I\m Julien the founder of NotionForms. Congrats on setting up your first OpnForm 🎉']
|
||||
// ])
|
||||
// setTimeout(
|
||||
// function () {
|
||||
// window.$crisp.push(['do', 'chat:show'])
|
||||
// window.$crisp.push(['do', 'chat:open'])
|
||||
// window.$crisp.push([
|
||||
// 'do',
|
||||
// 'message:show',
|
||||
// ['text',
|
||||
// 'A small gift to congratulate you? 🎁 I\'d be happy to offer you a 40% discount on your first month of a Pro subscription. Let me know if you\'re interested!']
|
||||
// ])
|
||||
// setTimeout(
|
||||
// function () {
|
||||
// window.$crisp.push(['do', 'chat:show'])
|
||||
// window.$crisp.push(['do', 'chat:open'])
|
||||
// window.$crisp.push([
|
||||
// 'do',
|
||||
// 'message:show',
|
||||
// ['text',
|
||||
// 'Just use the code "FIRSTFORM40" in the next 24 hours to get the discount! 🎉']
|
||||
// ])
|
||||
// }, 20000)
|
||||
// }, 4000)
|
||||
// }, 4000)
|
||||
// }
|
||||
}
|
||||
},
|
||||
|
||||
metaInfo () {
|
||||
return { title: this.$t('home') }
|
||||
},
|
||||
|
||||
methods: {
|
||||
openCrisp () {
|
||||
window.$crisp.push(['do', 'chat:show'])
|
||||
window.$crisp.push(['do', 'chat:open'])
|
||||
},
|
||||
duplicateForm () {
|
||||
if (this.loadingDuplicate) return
|
||||
this.loadingDuplicate = true
|
||||
axios.post(this.formEndpoint.replace('{id}', this.form.id) + '/duplicate').then((response) => {
|
||||
this.$store.commit('open/forms/addOrUpdate', response.data.new_form)
|
||||
this.$router.push({ name: 'forms.show', params: { slug: response.data.new_form.slug } })
|
||||
this.alertSuccess('Form was successfully duplicated.')
|
||||
this.loadingDuplicate = false
|
||||
})
|
||||
},
|
||||
regenerateLink (option) {
|
||||
if (this.loadingNewLink) return
|
||||
this.loadingNewLink = true
|
||||
axios.put(this.formEndpoint.replace('{id}', this.form.id) + '/regenerate-link/' + option).then((response) => {
|
||||
this.$store.commit('open/forms/addOrUpdate', response.data.form)
|
||||
this.$router.push({ name: 'forms.show', params: { slug: response.data.form.slug } })
|
||||
this.alertSuccess(response.data.message)
|
||||
this.loadingNewLink = false
|
||||
}).finally(() => {
|
||||
this.showGenerateFormLinkModal = false
|
||||
})
|
||||
},
|
||||
deleteForm () {
|
||||
if (this.loadingDelete) return
|
||||
this.loadingDelete = true
|
||||
axios.delete(this.formEndpoint.replace('{id}', this.form.id)).then(() => {
|
||||
this.$store.commit('open/forms/remove', this.form)
|
||||
this.$router.push({ name: 'home' })
|
||||
this.alertSuccess('Form was deleted.')
|
||||
this.loadingDelete = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
34
resources/js/pages/forms/show/analytics.vue
Normal file
34
resources/js/pages/forms/show/analytics.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3 class="font-semibold mt-4 text-xl">
|
||||
Form Analytics (last 30 days)
|
||||
</h3>
|
||||
<form-stats :form="form"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FormStats from '../../../components/open/forms/components/FormStats'
|
||||
|
||||
export default {
|
||||
components: {FormStats},
|
||||
props: {
|
||||
form: { type: Object, required: true }
|
||||
},
|
||||
|
||||
metaInfo() {
|
||||
return {title: (this.form) ? 'Form Analytics - '+this.form.title : 'Form Analytics'}
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
}),
|
||||
|
||||
mounted() {},
|
||||
|
||||
computed: {},
|
||||
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
225
resources/js/pages/forms/show/index.vue
Normal file
225
resources/js/pages/forms/show/index.vue
Normal file
@@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<div class="bg-white">
|
||||
<template v-if="form">
|
||||
<div class="flex bg-gray-50">
|
||||
<div class="w-full md:w-4/5 lg:w-3/5 md:mx-auto md:max-w-4xl px-4">
|
||||
<div class="pt-4 pb-0">
|
||||
<a href="#" @click.prevent="goBack" 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"/>
|
||||
</svg>
|
||||
Go back
|
||||
</a>
|
||||
|
||||
<div class="flex">
|
||||
<h2 class="flex-grow text-gray-900 truncate">
|
||||
{{ form.title }}
|
||||
</h2>
|
||||
<div class="flex">
|
||||
<extra-menu :form="form" />
|
||||
|
||||
<v-button target="_blank" :to="{name:'forms.show_public', params: {slug: form.slug}}"
|
||||
color="white" class="mr-2 text-blue-600 hidden sm:block"
|
||||
v-track.view_form_click="{form_id:form.id, form_slug:form.slug}">
|
||||
<svg class="w-6 h-6 inline -mt-1" viewBox="0 0 24 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 12C1 12 5 4 12 4C19 4 23 12 23 12C23 12 19 20 12 20C5 20 1 12 1 12Z"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path
|
||||
d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</v-button>
|
||||
<v-button class="text-white" @click="openEdit">
|
||||
<svg class="inline mr-1 -mt-1" width="18" height="17" viewBox="0 0 18 17" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8.99998 15.6662H16.5M1.5 15.6662H2.89545C3.3031 15.6662 3.50693 15.6662 3.69874 15.6202C3.8688 15.5793 4.03138 15.512 4.1805 15.4206C4.34869 15.3175 4.49282 15.1734 4.78107 14.8852L15.25 4.4162C15.9404 3.72585 15.9404 2.60656 15.25 1.9162C14.5597 1.22585 13.4404 1.22585 12.75 1.9162L2.28105 12.3852C1.9928 12.6734 1.84867 12.8175 1.7456 12.9857C1.65422 13.1348 1.58688 13.2974 1.54605 13.4675C1.5 13.6593 1.5 13.8631 1.5 14.2708V15.6662Z"
|
||||
stroke="currentColor" stroke-width="1.67" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
Edit form
|
||||
</v-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="flex text-gray-500">
|
||||
<li class="pr-1">{{ form.views_count }} view{{ form.views_count > 0 ? 's' : '' }}</li>
|
||||
<li class="list-disc ml-6 pr-1">{{ form.submissions_count }}
|
||||
submission{{ form.submissions_count > 0 ? 's' : '' }}
|
||||
</li>
|
||||
<li class="list-disc ml-6 pr-1 text-blue-500" v-if="form.visibility=='draft'">Draft (not public)</li>
|
||||
<li class="list-disc ml-6">Edited {{ form.last_edited_human }}</li>
|
||||
</ul>
|
||||
|
||||
<p v-if="form.closes_at" class="text-yellow-500">
|
||||
<span v-if="form.is_closed"> This form stopped accepting submissions on the {{
|
||||
displayClosesDate
|
||||
}} </span>
|
||||
<span v-else> This form will stop accepting submissions on the {{ displayClosesDate }} </span>
|
||||
</p>
|
||||
<p v-if="form.max_submissions_count > 0" class="text-yellow-500">
|
||||
<span v-if="form.max_number_of_submissions_reached"> The form is now closed because it reached its limit of {{
|
||||
form.max_submissions_count
|
||||
}} submissions. </span>
|
||||
<span v-else> This form will stop accepting submissions after {{ form.max_submissions_count }} submissions. </span>
|
||||
</p>
|
||||
|
||||
<div class="mt-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center">
|
||||
<li class="mr-6" v-for="(tab, i) in tabsList" :key="i+1">
|
||||
<router-link :to="{ name: tab.route }"
|
||||
class="hover:no-underline inline-block py-4 rounded-t-lg border-b-2 border-transparent text-gray-500 hover:text-gray-600"
|
||||
active-class="text-blue-600 hover:text-blue-900 dark:text-blue-500 dark:hover:text-blue-500 border-blue-600 dark:border-blue-500"
|
||||
>{{tab.name}}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex bg-white">
|
||||
<div class="w-full md:w-4/5 lg:w-3/5 md:mx-auto md:max-w-4xl px-4">
|
||||
<div class="py-4">
|
||||
<transition name="fade" mode="out-in">
|
||||
<router-view :form="form" />
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else-if="loading" class="text-center w-full p-5">
|
||||
<loader class="h-6 w-6 mx-auto"/>
|
||||
</div>
|
||||
<div v-else class="text-center w-full p-5">
|
||||
Form not found.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import store from '~/store'
|
||||
import Form from 'vform'
|
||||
import {mapGetters, mapState} from 'vuex'
|
||||
import ProTag from '../../../components/common/ProTag'
|
||||
import VButton from "../../../components/common/Button";
|
||||
import ExtraMenu from '../../../components/pages/forms/show/ExtraMenu'
|
||||
|
||||
const loadForms = function () {
|
||||
store.commit('open/forms/startLoading')
|
||||
store.dispatch('open/workspaces/loadIfEmpty').then(() => {
|
||||
store.dispatch('open/forms/loadIfEmpty', store.state['open/workspaces'].currentId)
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'ShowForm',
|
||||
components: {
|
||||
VButton,
|
||||
ProTag,
|
||||
ExtraMenu
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
loadForms()
|
||||
next()
|
||||
},
|
||||
|
||||
beforeRouteLeave(to, from, next) {
|
||||
this.workingForm = null
|
||||
next()
|
||||
},
|
||||
middleware: 'auth',
|
||||
|
||||
data() {
|
||||
return {
|
||||
tabsList: [
|
||||
{
|
||||
name: 'Submissions',
|
||||
route: 'forms.show'
|
||||
},
|
||||
{
|
||||
name: 'Analytics',
|
||||
route: 'forms.show.analytics'
|
||||
},
|
||||
{
|
||||
name: 'Share',
|
||||
route: 'forms.show.share'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
user: 'auth/user'
|
||||
}),
|
||||
...mapState({
|
||||
formsLoading: state => state['open/forms'].loading,
|
||||
workspacesLoading: state => state['open/workspaces'].loading
|
||||
}),
|
||||
workingForm: {
|
||||
get() {
|
||||
return this.$store.state['open/working_form'].content
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('open/working_form/set', value)
|
||||
}
|
||||
},
|
||||
workspace() {
|
||||
if (!this.form) return null
|
||||
return this.$store.getters['open/workspaces/getById'](this.form.workspace_id)
|
||||
},
|
||||
form() {
|
||||
return this.$store.getters['open/forms/getBySlug'](this.$route.params.slug)
|
||||
},
|
||||
formEndpoint: () => '/api/open/forms/{id}',
|
||||
loading() {
|
||||
return this.formsLoading || this.workspacesLoading
|
||||
},
|
||||
displayClosesDate() {
|
||||
if (this.form.closes_at) {
|
||||
let dateObj = new Date(this.form.closes_at)
|
||||
return dateObj.getFullYear() + "-" +
|
||||
String(dateObj.getMonth() + 1).padStart(2, '0') + "-" +
|
||||
String(dateObj.getDate()).padStart(2, '0') + " " +
|
||||
String(dateObj.getHours()).padStart(2, '0') + ":" +
|
||||
String(dateObj.getMinutes()).padStart(2, '0')
|
||||
}
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
form() {
|
||||
this.workingForm = new Form(this.form)
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.form) {
|
||||
this.workingForm = new Form(this.form)
|
||||
}
|
||||
},
|
||||
|
||||
metaInfo() {
|
||||
return {title: this.$t('home')}
|
||||
},
|
||||
|
||||
methods: {
|
||||
openCrisp() {
|
||||
window.$crisp.push(['do', 'chat:show'])
|
||||
window.$crisp.push(['do', 'chat:open'])
|
||||
},
|
||||
goBack() {
|
||||
this.$router.push({name: 'home'})
|
||||
},
|
||||
openEdit() {
|
||||
this.$router.push({name: 'forms.edit', params: {slug: this.form.slug}})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
47
resources/js/pages/forms/show/share.vue
Normal file
47
resources/js/pages/forms/show/share.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div>
|
||||
<share-link class="mt-4" :form="form" />
|
||||
|
||||
<embed-code class="mt-6" :form="form" />
|
||||
|
||||
<div class="mt-6 pt-6 border-t w-full flex">
|
||||
<regenerate-form-link class="sm:w-1/2 mr-4" :form="form" />
|
||||
|
||||
<url-form-prefill class="sm:w-1/2" :form="form" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ShareLink from '../../../components/pages/forms/show/ShareLink'
|
||||
import EmbedCode from '../../../components/pages/forms/show/EmbedCode'
|
||||
import UrlFormPrefill from '../../../components/pages/forms/show/UrlFormPrefill'
|
||||
import RegenerateFormLink from '../../../components/pages/forms/show/RegenerateFormLink'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ShareLink,
|
||||
EmbedCode,
|
||||
UrlFormPrefill,
|
||||
RegenerateFormLink
|
||||
},
|
||||
props: {
|
||||
form: { type: Object, required: true }
|
||||
},
|
||||
|
||||
metaInfo() {
|
||||
return {title: (this.form) ? 'Form Share - '+this.form.title : 'Form Share'}
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
}),
|
||||
|
||||
mounted() {},
|
||||
|
||||
computed: {},
|
||||
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
31
resources/js/pages/forms/show/submissions.vue
Normal file
31
resources/js/pages/forms/show/submissions.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div id="table-page">
|
||||
<form-submissions/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FormSubmissions from '../../../components/open/forms/components/FormSubmissions'
|
||||
|
||||
export default {
|
||||
components: {FormSubmissions},
|
||||
props: {
|
||||
form: { type: Object, required: true }
|
||||
},
|
||||
|
||||
metaInfo() {
|
||||
return {title: (this.form) ? 'Form Submissions - '+this.form.title : 'Form Submissions'}
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
}),
|
||||
|
||||
mounted() {},
|
||||
|
||||
computed: {},
|
||||
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,18 +1,18 @@
|
||||
<template>
|
||||
<card title="Account" class="bg-gray-50 dark:bg-notion-dark-light">
|
||||
<h3 class="text-lg font-semibold mb-4">
|
||||
Your Account
|
||||
</h3>
|
||||
|
||||
<p class="text-gray-800 dark:text-gray-200">
|
||||
You can delete your account. All your data will be removed. <span class="font-semibold">This cannot be undone.</span>
|
||||
<div>
|
||||
<h3 class="font-semibold text-2xl text-gray-900">Danger zone</h3>
|
||||
<p class="text-gray-600 text-sm mt-2">
|
||||
This will permanently delete your entire account. All your forms, submissions and workspaces will be deleted.
|
||||
<span class="text-red-500">
|
||||
This cannot be undone.
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<v-button :loading="loading" class="mt-4" color="red" @click="alertConfirm('Do you really want to delete your account?',deleteAccount)">
|
||||
Delete my account
|
||||
Delete account
|
||||
</v-button>
|
||||
</card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<template>
|
||||
<card title="Admin" class="bg-gray-50 dark:bg-notion-dark-light">
|
||||
<h3 class="text-lg font-semibold mb-4">
|
||||
<div>
|
||||
<h3 class="font-semibold text-2xl text-gray-900">Admin settings</h3>
|
||||
<small class="text-gray-600">Manage settings.</small>
|
||||
|
||||
|
||||
<h3 class="mt-3 text-lg font-semibold mb-4">
|
||||
Tools
|
||||
</h3>
|
||||
<div class="flex flex-wrap mb-10">
|
||||
<div class="flex flex-wrap mb-5">
|
||||
<a href="/stats">
|
||||
<v-button class="mx-1" color="gray" shade="lighter">
|
||||
Stats
|
||||
@@ -25,11 +29,9 @@
|
||||
/>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<v-button :loading="loading" class="mt-4 w-full">
|
||||
Impersonate User
|
||||
</v-button>
|
||||
<v-button :loading="loading" class="mt-4">Impersonate User</v-button>
|
||||
</form>
|
||||
</card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
<template>
|
||||
<card title="Billing" class="bg-gray-50 dark:bg-notion-dark-light">
|
||||
<v-button color="gray" shade="light" :loading="billingLoading" @click.prevent="openBillingDashboard">
|
||||
Manage Subscription
|
||||
</v-button>
|
||||
<v-button color="red" class="mt-3" @click.prevent="cancelSubscription">
|
||||
Cancel Subscription
|
||||
</v-button>
|
||||
</card>
|
||||
<div>
|
||||
<h3 class="font-semibold text-2xl text-gray-900">Billing details</h3>
|
||||
<small class="text-gray-600">Manage your billing.</small>
|
||||
|
||||
<div class="mt-4">
|
||||
<v-button color="gray" shade="light" :loading="billingLoading" @click.prevent="openBillingDashboard">
|
||||
Manage Subscription
|
||||
</v-button>
|
||||
<v-button color="red" class="mt-3" @click.prevent="cancelSubscription">
|
||||
Cancel Subscription
|
||||
</v-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,80 +1,38 @@
|
||||
<template>
|
||||
<div class="flex flex-wrap mt-6 md:max-w-3xl w-full md:mx-auto">
|
||||
<div class="w-full md:w-1/3 md:pr-4">
|
||||
<card :padding="false" class="bg-gray-50 dark:bg-notion-dark-light">
|
||||
<ul>
|
||||
<li v-for="tab in tabs" :key="tab.route">
|
||||
<router-link :to="{ name: tab.route }"
|
||||
class="px-6 py-4 flex items-center text-gray-600 dark:text-gray-400 dark:hover:text-gray-300 hover:text-gray-900 hover:bg-gray-50 dark:hover:bg-gray-900 rounded"
|
||||
active-class="text-nt-blue bg-indigo-50 dark:bg-gray-800 hover:bg-blue-50"
|
||||
>
|
||||
<template v-if="tab.route == 'settings.profile'">
|
||||
<svg class="w-6 h-6 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M5.121 17.804A13.937 13.937 0 0112 16c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0zm6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
<template v-else-if="tab.route == 'settings.account'">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7a4 4 0 11-8 0 4 4 0 018 0zM9 14a6 6 0 00-6 6v1h12v-1a6 6 0 00-6-6zM21 12h-6" />
|
||||
</svg>
|
||||
</template>
|
||||
<template v-else-if="tab.route == 'settings.workspaces'">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" />
|
||||
</svg>
|
||||
</template>
|
||||
<template v-else-if="tab.route == 'settings.billing'">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mr-2" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
<template v-else-if="tab.route == 'settings.password'">
|
||||
<svg class="w-6 h-6 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
<span class="ml-2">
|
||||
{{ tab.name }}
|
||||
</span>
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="user.admin">
|
||||
<router-link :to="{ name: 'settings.admin' }"
|
||||
class="px-6 py-4 flex items-center text-gray-600 dark:text-gray-400 dark:hover:text-gray-300 hover:text-gray-900 hover:bg-gray-50 dark:hover:bg-gray-900 rounded"
|
||||
active-class="text-nt-blue bg-indigo-50 dark:bg-gray-800 hover:bg-blue-50"
|
||||
>
|
||||
<svg class="w-6 h-6 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="ml-2">
|
||||
Admin
|
||||
</span>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</card>
|
||||
</div>
|
||||
<div class="bg-white">
|
||||
<div class="flex bg-gray-50">
|
||||
<div class="w-full md:w-4/5 lg:w-3/5 md:mx-auto md:max-w-4xl px-4">
|
||||
<div class="pt-4 pb-0">
|
||||
<div class="flex">
|
||||
<h2 class="flex-grow text-gray-900">
|
||||
My Account
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="flex text-gray-500">
|
||||
<li>{{ user.email }}</li>
|
||||
</ul>
|
||||
|
||||
<div class="w-full md:w-2/3">
|
||||
<transition name="fade" mode="out-in">
|
||||
<router-view />
|
||||
</transition>
|
||||
<div class="mt-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center">
|
||||
<li class="mr-6" v-for="(tab, i) in tabsList" :key="i+1">
|
||||
<router-link :to="{ name: tab.route }"
|
||||
class="hover:no-underline inline-block py-4 rounded-t-lg border-b-2 border-transparent text-gray-500 hover:text-gray-600"
|
||||
active-class="text-blue-600 hover:text-blue-900 dark:text-blue-500 dark:hover:text-blue-500 border-blue-600 dark:border-blue-500"
|
||||
>{{tab.name}}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex bg-white">
|
||||
<div class="w-full md:w-4/5 lg:w-3/5 md:mx-auto md:max-w-4xl px-4">
|
||||
<div class="mt-8 pb-0">
|
||||
<transition name="fade" mode="out-in">
|
||||
<router-view />
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -85,19 +43,27 @@ import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
middleware: 'auth',
|
||||
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
tabs () {
|
||||
...mapGetters({
|
||||
user: 'auth/user'
|
||||
}),
|
||||
tabsList () {
|
||||
const tabs = [
|
||||
{
|
||||
name: 'Workspaces',
|
||||
route: 'settings.workspaces'
|
||||
},
|
||||
{
|
||||
name: this.$t('profile'),
|
||||
name: 'Profile',
|
||||
route: 'settings.profile'
|
||||
},
|
||||
{
|
||||
name: this.$t('password'),
|
||||
name: 'Workspace Settings',
|
||||
route: 'settings.workspaces'
|
||||
},
|
||||
{
|
||||
name: 'Password',
|
||||
route: 'settings.password'
|
||||
},
|
||||
{
|
||||
@@ -113,11 +79,18 @@ export default {
|
||||
})
|
||||
}
|
||||
|
||||
if(this.user.admin){
|
||||
tabs.push({
|
||||
name: 'Admin',
|
||||
route: 'settings.admin'
|
||||
})
|
||||
}
|
||||
|
||||
return tabs
|
||||
},
|
||||
...mapGetters({
|
||||
user: 'auth/user'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
<template>
|
||||
<card :title="$t('your_password')" class="bg-gray-50 dark:bg-notion-dark-light">
|
||||
<form @submit.prevent="update" @keydown="form.onKeydown($event)">
|
||||
<div>
|
||||
<h3 class="font-semibold text-2xl text-gray-900">Password</h3>
|
||||
<small class="text-gray-600">Manage your password.</small>
|
||||
|
||||
<form @submit.prevent="update" @keydown="form.onKeydown($event)" class="mt-3">
|
||||
<alert-success class="mb-5" :form="form" :message="$t('password_updated')" />
|
||||
|
||||
<!-- Password -->
|
||||
<text-input class="mt-8" native-type="password"
|
||||
<text-input native-type="password"
|
||||
name="password" :form="form" :label="$t('password')" :required="true"
|
||||
/>
|
||||
|
||||
<!-- Password Confirmation-->
|
||||
<text-input class="mt-8" native-type="password"
|
||||
<text-input native-type="password"
|
||||
name="password_confirmation" :form="form" :label="$t('confirm_password')" :required="true"
|
||||
/>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<v-button :loading="form.busy" class="mt-4 w-full">
|
||||
{{ $t('update') }}
|
||||
</v-button>
|
||||
<v-button :loading="form.busy" class="mt-4">Update password</v-button>
|
||||
</form>
|
||||
</card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<card title="Profile" class="bg-gray-50 dark:bg-notion-dark-light">
|
||||
<form @submit.prevent="update" @keydown="form.onKeydown($event)">
|
||||
<div>
|
||||
<h3 class="font-semibold text-2xl text-gray-900">Profile details</h3>
|
||||
<small class="text-gray-600">Update your username and manage your account details.</small>
|
||||
|
||||
<form @submit.prevent="update" @keydown="form.onKeydown($event)" class="mt-3">
|
||||
<alert-success class="mb-5" :form="form" :message="$t('info_updated')" />
|
||||
|
||||
<!-- Name -->
|
||||
@@ -10,11 +13,9 @@
|
||||
<text-input name="email" :form="form" :label="$t('email')" :required="true" />
|
||||
|
||||
<!-- Submit Button -->
|
||||
<v-button :loading="form.busy" class="mt-4 w-full">
|
||||
{{ $t('update') }}
|
||||
</v-button>
|
||||
<v-button :loading="form.busy" class="mt-4">Save changes</v-button>
|
||||
</form>
|
||||
</card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,37 +1,41 @@
|
||||
<template>
|
||||
<card title="Workspaces" class="bg-gray-50 dark:bg-notion-dark-light">
|
||||
<div>
|
||||
<h3 class="font-semibold text-2xl text-gray-900">Workspace settings</h3>
|
||||
<small class="text-gray-600">Manage your workspaces.</small>
|
||||
|
||||
<div v-if="loading" class="w-full text-blue-500 text-center">
|
||||
<loader class="h-10 w-10 p-5"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-for="workspace in workspaces" :key="workspace.id"
|
||||
class="border border-nt-blue-light shadow rounded-md p-4 mb-5 max-w-sm w-full flex group mx-auto bg-white dark:bg-notion-dark items-center"
|
||||
class="mt-4 p-4 flex group bg-white hover:bg-gray-50 dark:bg-notion-dark items-center"
|
||||
>
|
||||
<div class="flex space-x-4 flex-grow cursor-pointer" role="button" @click.prevent="switchWorkspace(workspace)">
|
||||
<div class="flex space-x-4 flex-grow items-center cursor-pointer" role="button" @click.prevent="switchWorkspace(workspace)">
|
||||
<img v-if="isUrl(workspace.icon)" :src="workspace.icon" :alt="workspace.name + ' icon'"
|
||||
class="rounded-full h-12 w-12"
|
||||
class="rounded-full h-12 w-12"
|
||||
>
|
||||
<div v-else class="rounded-full bg-nt-blue-lighter h-12 w-12 text-2xl pt-2 text-center overflow-hidden"
|
||||
v-text="workspace.icon"
|
||||
<div v-else class="rounded-2xl bg-gray-100 h-12 w-12 text-2xl pt-2 text-center overflow-hidden"
|
||||
v-text="workspace.icon"
|
||||
/>
|
||||
<div class="flex-1 flex items-center space-y-4 py-1">
|
||||
<p class="font-bold truncate" v-text="workspace.name"/>
|
||||
<div class="space-y-4 py-1">
|
||||
<div class="font-bold truncate">{{workspace.name}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="workspaces.length > 1"
|
||||
class="block md:hidden group-hover:block text-red-500 p-2 rounded hover:bg-red-50" role="button"
|
||||
@click="deleteWorkspace(workspace)">
|
||||
class="block md:hidden group-hover:block text-red-500 p-2 rounded hover:bg-red-50" role="button"
|
||||
@click="deleteWorkspace(workspace)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max-w-sm w-full mx-auto mt-4">
|
||||
<v-button :loading="loading" class="w-full" @click="workspaceModal=true">
|
||||
Create a new workspace
|
||||
</v-button>
|
||||
</div>
|
||||
<v-button :loading="loading" class="mt-4" @click="workspaceModal=true">
|
||||
<svg class="inline text-white mr-1 h-4 w-4" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.99996 1.16699V12.8337M1.16663 7.00033H12.8333" stroke="currentColor" stroke-width="1.67" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
Create new workspace
|
||||
</v-button>
|
||||
</div>
|
||||
|
||||
<!-- Workspace modal -->
|
||||
@@ -65,7 +69,7 @@
|
||||
</div>
|
||||
</modal>
|
||||
|
||||
</card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
Reference in New Issue
Block a user