This commit is contained in:
Julien Nahum
2023-10-13 12:29:38 +02:00
15 changed files with 307 additions and 145 deletions

View File

@@ -68,6 +68,15 @@
My Forms
</router-link>
<router-link v-if="userOnboarded" :to="{ name: 'my_templates' }"
class="block block px-4 py-2 text-md text-gray-700 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:text-white dark:hover:bg-gray-600 flex items-center"
>
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 mr-2" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
</svg>
My Templates
</router-link>
<router-link :to="{ name: 'settings.profile' }"
class="block block px-4 py-2 text-md text-gray-700 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:text-white dark:hover:bg-gray-600 flex items-center"
>

View File

@@ -18,13 +18,13 @@
</template>
<div class="p-4">
<p v-if="!template">
New template will be create from your form <span class="font-semibold">{{ form.title }}</span>.
New template will be create from your form: <span class="font-semibold">{{ form.title }}</span>.
</p>
<form v-if="templateForm" class="mt-6" @submit.prevent="onSubmit" @keydown="templateForm.onKeydown($event)">
<div class="-m-6">
<div class="border-t py-4 px-6">
<toggle-switch-input name="publicly_listed" :form="templateForm" class="mt-4" label="Publicly Listed?" />
<toggle-switch-input v-if="user && (user.admin || user.template_editor)" name="publicly_listed" :form="templateForm" class="mt-4" label="Publicly Listed?" />
<text-input name="name" :form="templateForm" class="mt-4" label="Title" :required="true" />
<text-input name="slug" :form="templateForm" class="mt-4" label="Slug" :required="true" />
<text-area-input name="short_description" :form="templateForm" class="mt-4" label="Short Description"
@@ -74,7 +74,7 @@
<script>
import Form from 'vform'
import store from '~/store'
import { mapState } from 'vuex'
import { mapState, mapGetters } from 'vuex'
import axios from 'axios'
import QuestionsEditor from './QuestionsEditor.vue'
@@ -93,7 +93,7 @@ export default {
mounted () {
this.templateForm = new Form(this.template ?? {
publicly_listed: true,
publicly_listed: false,
name: '',
slug: '',
short_description: '',
@@ -113,6 +113,9 @@ export default {
industries: state => state['open/templates'].industries,
types: state => state['open/templates'].types
}),
...mapGetters({
user: 'auth/user'
}),
typesOptions () {
return Object.values(this.types).map((type) => {
return {
@@ -153,6 +156,7 @@ export default {
if (response.data.message) {
this.alertSuccess(response.data.message)
}
this.$store.commit('open/templates/addOrUpdate', response.data.data)
this.$emit('close')
})
},
@@ -162,7 +166,7 @@ export default {
if (response.data.message) {
this.alertSuccess(response.data.message)
}
this.$store.dispatch('open/templates/addOrUpdate', response.data.data)
this.$store.commit('open/templates/addOrUpdate', response.data.data)
this.$emit('close')
})
},
@@ -173,7 +177,7 @@ export default {
this.alertSuccess(response.data.message)
}
this.$router.push({ name: 'templates' })
this.$store.dispatch('open/templates/remove', response.data.data)
this.$store.commit('open/templates/remove', this.template)
this.$emit('close')
})
}

View File

@@ -20,7 +20,7 @@
</svg>
</v-button>
</template>
<router-link v-if="isMainPage" :to="{name:'forms.show_public', params: {slug: form.slug}}" target="_blank"
<router-link v-if="isMainPage && user" :to="{name:'forms.show_public', params: {slug: form.slug}}" target="_blank"
class="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"
v-track.view_form_click="{form_id:form.id, form_slug:form.slug}"
>
@@ -67,7 +67,7 @@
</svg>
Duplicate form
</a>
<a href="#" v-if="user && user.template_editor"
<a href="#" v-if="!isMainPage" v-track.create_template_click="{form_id:form.id, form_slug:form.slug}"
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="showFormTemplateModal=true"
>
@@ -117,7 +117,7 @@
</div>
</modal>
<form-template-modal :form="form" :show="showFormTemplateModal" @close="showFormTemplateModal=false"/>
<form-template-modal v-if="!isMainPage && user" :form="form" :show="showFormTemplateModal" @close="showFormTemplateModal=false"/>
</div>
</template>

View File

@@ -44,7 +44,7 @@ export default {
watch: {
user () {
this.form.email = this.user.email
this.updateUser()
},
show () {
// Wait for modal to open and focus on first field
@@ -59,13 +59,16 @@ export default {
},
mounted () {
if (this.user) {
this.form.name = this.user.name
this.form.email = this.user.email
}
this.updateUser()
},
methods: {
updateUser() {
if (this.user) {
this.form.name = this.user.name
this.form.email = this.user.email
}
},
saveDetails () {
if (this.form.busy) return
this.form.put('api/subscription/update-customer-details').then(() => {

View File

@@ -0,0 +1,141 @@
<template>
<section class="bg-white py-12">
<div class="px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 sm:gap-6 relative z-20">
<div class="flex items-center gap-4">
<div class="flex-1 sm:flex-none">
<select-input v-model="selectedType" name="type"
:options="typesOptions" class="w-full sm:w-auto md:w-56"
/>
</div>
<div class="flex-1 sm:flex-none">
<select-input v-model="selectedIndustry" name="industry"
:options="industriesOptions" class="w-full sm:w-auto md:w-56"
/>
</div>
</div>
<div class="flex-1 w-full md:max-w-xs">
<text-input name="search" :form="searchTemplate" placeholder="Search..." />
</div>
</div>
<div v-if="templatesLoading" class="text-center mt-4">
<loader class="h-6 w-6 text-nt-blue mx-auto" />
</div>
<p v-else-if="enrichedTemplates.length === 0" class="text-center mt-4">
No templates found.
</p>
<div v-else class="relative z-10">
<div class="grid grid-cols-1 mt-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8 sm:gap-y-12">
<single-template v-for="template in enrichedTemplates" :key="template.id" :slug="template.slug" />
</div>
</div>
</div>
</section>
</template>
<script>
import store from '~/store'
import { mapGetters, mapState } from 'vuex'
import Form from 'vform'
import Fuse from 'fuse.js'
import SingleTemplate from './SingleTemplate.vue'
const loadTemplates = function () {
store.commit('open/templates/startLoading')
store.dispatch('open/templates/loadIfEmpty').then(() => {
store.commit('open/templates/stopLoading')
})
}
export default {
name: 'TemplatesList',
components: { SingleTemplate },
props: {
onlyMy: {
type: Boolean,
required: false
}
},
data: () => ({
selectedType: 'all',
selectedIndustry: 'all',
searchTemplate: new Form({
search: ''
})
}),
watch: {},
mounted () {
loadTemplates()
},
computed: {
...mapState({
templates: state => state['open/templates'].content,
templatesLoading: state => state['open/templates'].loading,
industries: state => state['open/templates'].industries,
types: state => state['open/templates'].types
}),
...mapGetters({
user: 'auth/user'
}),
industriesOptions () {
return [{ name: 'All Industries', value: 'all' }].concat(Object.values(this.industries).map((industry) => {
return {
name: industry.name,
value: industry.slug
}
}))
},
typesOptions () {
return [{ name: 'All Types', value: 'all' }].concat(Object.values(this.types).map((type) => {
return {
name: type.name,
value: type.slug
}
}))
},
enrichedTemplates () {
let enrichedTemplates = (this.onlyMy && this.user) ? this.templates.filter((item) => { return item.creator_id === this.user.id}) : this.templates
// Filter by Selected Type
if (this.selectedType && this.selectedType !== 'all') {
enrichedTemplates = enrichedTemplates.filter((item) => {
return (item.types && item.types.length > 0) ? item.types.includes(this.selectedType) : false
})
}
// Filter by Selected Industry
if (this.selectedIndustry && this.selectedIndustry !== 'all') {
enrichedTemplates = enrichedTemplates.filter((item) => {
return (item.industries && item.industries.length > 0) ? item.industries.includes(this.selectedIndustry) : false
})
}
if (this.searchTemplate.search === '' || this.searchTemplate.search === null) {
return enrichedTemplates
}
// Fuze search
const fuzeOptions = {
keys: [
'name',
'slug',
'description',
'short_description'
]
}
const fuse = new Fuse(enrichedTemplates, fuzeOptions)
return fuse.search(this.searchTemplate.search).map((res) => {
return res.item
})
}
},
methods: {}
}
</script>