Finishing the templates pages
This commit is contained in:
parent
bb519907f6
commit
8e70d6be98
|
@ -1,51 +1,48 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="template">
|
<div>
|
||||||
<!-- <template v-if="displayAll">-->
|
<template v-if="displayAll">
|
||||||
<!-- <span v-if="template.is_new"-->
|
<span v-if="template.is_new"
|
||||||
<!-- class="inline-flex items-center gap-1 px-2 py-1 text-xs font-medium text-white bg-blue-500 rounded-full"-->
|
class="inline-flex items-center gap-1 px-2 py-1 text-xs font-medium text-white bg-blue-500 rounded-full"
|
||||||
<!-- >-->
|
>
|
||||||
<!-- <svg aria-hidden="true" class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"-->
|
<svg aria-hidden="true" class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
||||||
<!-- fill="currentColor"-->
|
fill="currentColor"
|
||||||
<!-- >-->
|
>
|
||||||
<!-- <path fill-rule="evenodd"-->
|
<path fill-rule="evenodd"
|
||||||
<!-- d="M5 2a1 1 0 011 1v1h1a1 1 0 010 2H6v1a1 1 0 01-2 0V6H3a1 1 0 010-2h1V3a1 1 0 011-1zm0 10a1 1 0 011 1v1h1a1 1 0 110 2H6v1a1 1 0 11-2 0v-1H3a1 1 0 110-2h1v-1a1 1 0 011-1zM12 2a1 1 0 01.967.744L14.146 7.2 17.5 9.134a1 1 0 010 1.732l-3.354 1.935-1.18 4.455a1 1 0 01-1.933 0L9.854 12.8 6.5 10.866a1 1 0 010-1.732l3.354-1.935 1.18-4.455A1 1 0 0112 2z"-->
|
d="M5 2a1 1 0 011 1v1h1a1 1 0 010 2H6v1a1 1 0 01-2 0V6H3a1 1 0 010-2h1V3a1 1 0 011-1zm0 10a1 1 0 011 1v1h1a1 1 0 110 2H6v1a1 1 0 11-2 0v-1H3a1 1 0 110-2h1v-1a1 1 0 011-1zM12 2a1 1 0 01.967.744L14.146 7.2 17.5 9.134a1 1 0 010 1.732l-3.354 1.935-1.18 4.455a1 1 0 01-1.933 0L9.854 12.8 6.5 10.866a1 1 0 010-1.732l3.354-1.935 1.18-4.455A1 1 0 0112 2z"
|
||||||
<!-- clip-rule="evenodd"-->
|
clip-rule="evenodd"
|
||||||
<!-- />-->
|
/>
|
||||||
<!-- </svg>-->
|
</svg>
|
||||||
<!-- New-->
|
New
|
||||||
<!-- </span>-->
|
</span>
|
||||||
<!-- <span v-for="item in types"-->
|
<span v-for="item in types" :key="item.slug"
|
||||||
<!-- class="inline-flex items-center rounded-full bg-gray-50 dark:bg-gray-800 dark:text-gray-400 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10"-->
|
class="inline-flex items-center rounded-full bg-gray-50 dark:bg-gray-800 dark:text-gray-400 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10"
|
||||||
<!-- >-->
|
>
|
||||||
<!-- {{ item }}-->
|
{{ item.name }}
|
||||||
<!-- </span>-->
|
</span>
|
||||||
<!-- <span v-for="item in industries"-->
|
<span v-for="item in industries" :key="item.slug"
|
||||||
<!-- class="inline-flex items-center rounded-full bg-blue-50 dark:bg-blue-900 dark:text-gray-400 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10"-->
|
class="inline-flex items-center rounded-full bg-blue-50 dark:bg-blue-900 dark:text-gray-400 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10"
|
||||||
<!-- >-->
|
>
|
||||||
<!-- {{ item }}-->
|
{{ item.name }}
|
||||||
<!-- </span>-->
|
</span>
|
||||||
<!-- </template>-->
|
</template>
|
||||||
<!-- <template v-else>-->
|
<template v-else>
|
||||||
<!-- <span v-if="types.length > 0"-->
|
<span v-if="types.length > 0"
|
||||||
<!-- class="inline-flex items-center rounded-full bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10"-->
|
class="inline-flex items-center rounded-full bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10"
|
||||||
<!-- >-->
|
>
|
||||||
<!-- {{ types[0] }} <template v-if="types.length > 1">+{{ types.length - 1 }}</template>-->
|
{{ types[0].name }} <template v-if="types.length > 1">+{{ types.length - 1 }}</template>
|
||||||
<!-- </span>-->
|
</span>
|
||||||
<!-- <span v-if="industries.length > 0"-->
|
<span v-if="industries.length > 0"
|
||||||
<!-- class="inline-flex items-center rounded-full bg-blue-50 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10"-->
|
class="inline-flex items-center rounded-full bg-blue-50 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10"
|
||||||
<!-- >-->
|
>
|
||||||
<!-- {{ industries[0] }} <template v-if="industries.length > 1">+{{ industries.length - 1 }}</template>-->
|
{{ industries[0].name }} <template v-if="industries.length > 1">+{{ industries.length - 1 }}</template>
|
||||||
<!-- </span>-->
|
</span>
|
||||||
<!-- </template>-->
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { computed } from 'vue'
|
|
||||||
import { useTemplatesStore } from '../../../stores/templates'
|
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: {
|
|
||||||
template: {
|
template: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
@ -54,31 +51,9 @@ export default {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
|
|
||||||
setup () {
|
const templatesStore = useTemplatesStore()
|
||||||
const templatesStore = useTemplatesStore()
|
const types = computed(() => templatesStore.getTemplateTypes(props.template.types))
|
||||||
return {
|
const industries = computed(() => templatesStore.getTemplateIndustries(props.template.industries))
|
||||||
templatesStore
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data: () => ({}),
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
// template () {
|
|
||||||
// return this.templatesStore.getByKey(this.slug)
|
|
||||||
// },
|
|
||||||
// types () {
|
|
||||||
// if (!this.template) return null
|
|
||||||
// return this.templatesStore.getTemplateTypes(this.template.types)
|
|
||||||
// },
|
|
||||||
// industries () {
|
|
||||||
// if (!this.template) return null
|
|
||||||
// return this.templatesStore.getTemplateIndustries(this.template.industries)
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 w-full md:max-w-xs">
|
<div class="flex-1 w-full md:max-w-xs">
|
||||||
<text-input autocomplete="off" name="search" :form="searchTemplate" placeholder="Search..."/>
|
<text-input autocomplete="off" name="search" v-model="search" placeholder="Search..."/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-8 mt-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
<div class="grid grid-cols-1 gap-8 mt-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
<router-link v-for="row in types" :key="row.slug"
|
<router-link v-for="row in types" :key="row.slug"
|
||||||
:to="{params:{slug:row.slug}, name:'templates-types'}"
|
:to="{params: {slug:row.slug}, name:'templates-types-slug'}"
|
||||||
:title="row.name"
|
:title="row.name"
|
||||||
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
|
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
|
||||||
>
|
>
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-8 mt-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
<div class="grid grid-cols-1 gap-8 mt-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
<router-link v-for="row in industries" :key="row.slug"
|
<router-link v-for="row in industries" :key="row.slug"
|
||||||
:to="{params:{slug:row.slug}, name:'templates-industries'}"
|
:to="{params:{slug:row.slug}, name:'templates-industries-slug'}"
|
||||||
:title="row.name"
|
:title="row.name"
|
||||||
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
|
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
|
||||||
>
|
>
|
||||||
|
@ -79,9 +79,9 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {computed} from 'vue'
|
import {computed} from 'vue'
|
||||||
import Form from 'vform'
|
|
||||||
import Fuse from 'fuse.js'
|
import Fuse from 'fuse.js'
|
||||||
import SingleTemplate from './SingleTemplate.vue'
|
import SingleTemplate from './SingleTemplate.vue'
|
||||||
|
import {refThrottled} from "@vueuse/core";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TemplatesList',
|
name: 'TemplatesList',
|
||||||
|
@ -100,23 +100,22 @@ export default {
|
||||||
setup() {
|
setup() {
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const templatesStore = useTemplatesStore()
|
const templatesStore = useTemplatesStore()
|
||||||
|
const search = ref('')
|
||||||
|
const throttledSearch = refThrottled(search, 1000)
|
||||||
return {
|
return {
|
||||||
|
search,
|
||||||
|
throttledSearch,
|
||||||
user: computed(() => authStore.user),
|
user: computed(() => authStore.user),
|
||||||
industries: computed(() => templatesStore.industries),
|
industries: computed(() => [...templatesStore.industries.values()]),
|
||||||
types: computed(() => templatesStore.types)
|
types: computed(() => [...templatesStore.types.values()])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
selectedType: 'all',
|
selectedType: 'all',
|
||||||
selectedIndustry: 'all',
|
selectedIndustry: 'all',
|
||||||
searchTemplate: new Form({
|
|
||||||
search: ''
|
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
watch: {},
|
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
industriesOptions() {
|
industriesOptions() {
|
||||||
return [{name: 'All Industries', value: 'all'}].concat(Object.values(this.industries).map((industry) => {
|
return [{name: 'All Industries', value: 'all'}].concat(Object.values(this.industries).map((industry) => {
|
||||||
|
@ -151,7 +150,8 @@ export default {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.searchTemplate.search === '' || this.searchTemplate.search === null) {
|
console.log(this.throttledSearch, '---inode')
|
||||||
|
if (!this.throttledSearch || this.throttledSearch === '' || this.throttledSearch === null) {
|
||||||
return enrichedTemplates
|
return enrichedTemplates
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ export default {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
const fuse = new Fuse(enrichedTemplates, fuzeOptions)
|
const fuse = new Fuse(enrichedTemplates, fuzeOptions)
|
||||||
return fuse.search(this.searchTemplate.search).map((res) => {
|
return fuse.search(this.throttledSearch).map((res) => {
|
||||||
return res.item
|
return res.item
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,9 +45,9 @@
|
||||||
<p class="mt-2 text-lg font-normal text-gray-600">
|
<p class="mt-2 text-lg font-normal text-gray-600">
|
||||||
{{ cleanQuotes(template.short_description) }}
|
{{ cleanQuotes(template.short_description) }}
|
||||||
</p>
|
</p>
|
||||||
<!-- <template-tags :slug="template.slug" :display-all="true"-->
|
<template-tags :template="template" :display-all="true"
|
||||||
<!-- class="flex flex-wrap items-center justify-center gap-3 mt-4 md:justify-start"-->
|
class="flex flex-wrap items-center justify-center gap-3 mt-4 md:justify-start"
|
||||||
<!-- />-->
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -226,6 +226,7 @@ const {data: relatedTemplatesData} = await useAsyncData('related-templates', ()
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
templatesStore.save(relatedTemplatesData.value)
|
templatesStore.save(relatedTemplatesData.value)
|
||||||
|
templatesStore.initTypesAndIndustries()
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const showFormTemplateModal = ref(false)
|
const showFormTemplateModal = ref(false)
|
||||||
|
@ -235,7 +236,7 @@ const breadcrumbs = computed(() => {
|
||||||
if (!template.value) {
|
if (!template.value) {
|
||||||
return [{route: {name: 'templates'}, label: 'Templates'}]
|
return [{route: {name: 'templates'}, label: 'Templates'}]
|
||||||
}
|
}
|
||||||
return [{route: {name: 'templates'}, label: 'Templates'}, {label: template.name}]
|
return [{route: {name: 'templates'}, label: 'Templates'}, {label: template.value.name}]
|
||||||
})
|
})
|
||||||
const relatedTemplates = computed(() => templatesStore.getByKey(template?.value?.related_templates))
|
const relatedTemplates = computed(() => templatesStore.getByKey(template?.value?.related_templates))
|
||||||
const canEditTemplate = computed(() => authStore.authenticated && template.value && (authStore.user.admin || authStore.user.template_editor || template.creator_id === authStore.user.id))
|
const canEditTemplate = computed(() => authStore.authenticated && template.value && (authStore.user.admin || authStore.user.template_editor || template.creator_id === authStore.user.id))
|
||||||
|
|
|
@ -31,6 +31,7 @@ const templatesStore = useTemplatesStore()
|
||||||
|
|
||||||
if (!templatesStore.allLoaded) {
|
if (!templatesStore.allLoaded) {
|
||||||
templatesStore.startLoading()
|
templatesStore.startLoading()
|
||||||
|
templatesStore.initTypesAndIndustries()
|
||||||
const {data} = await fetchAllTemplates()
|
const {data} = await fetchAllTemplates()
|
||||||
templatesStore.set(data.value)
|
templatesStore.set(data.value)
|
||||||
templatesStore.allLoaded = true
|
templatesStore.allLoaded = true
|
||||||
|
|
|
@ -76,7 +76,7 @@
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-8 mt-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
<div class="grid grid-cols-1 gap-8 mt-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
<router-link v-for="row in otherIndustries" :key="row.slug"
|
<router-link v-for="row in otherIndustries" :key="row.slug"
|
||||||
:to="{params:{slug:row.slug}, name:'templates-industries'}"
|
:to="{params:{slug:row.slug}, name:'templates-industries-slug'}"
|
||||||
:title="row.name"
|
:title="row.name"
|
||||||
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
|
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
|
||||||
>
|
>
|
||||||
|
@ -96,12 +96,12 @@
|
||||||
import Form from 'vform'
|
import Form from 'vform'
|
||||||
import Fuse from 'fuse.js'
|
import Fuse from 'fuse.js'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useAuthStore } from '../../stores/auth'
|
import { useAuthStore } from '../../../stores/auth.js'
|
||||||
import { useTemplatesStore } from '../../stores/templates'
|
import { useTemplatesStore } from '../../../stores/templates.js'
|
||||||
import SeoMeta from '../../mixins/seo-meta.js'
|
import SeoMeta from '../../../mixins/seo-meta.js'
|
||||||
import OpenFormFooter from '../../components/pages/OpenFormFooter.vue'
|
import OpenFormFooter from '../../../components/pages/OpenFormFooter.vue'
|
||||||
import Breadcrumb from '~/components/global/Breadcrumb.vue'
|
import Breadcrumb from '~/components/global/Breadcrumb.vue'
|
||||||
import SingleTemplate from '../../components/pages/templates/SingleTemplate.vue'
|
import SingleTemplate from '../../../components/pages/templates/SingleTemplate.vue'
|
||||||
|
|
||||||
const loadTemplates = function () {
|
const loadTemplates = function () {
|
||||||
const templatesStore = useTemplatesStore()
|
const templatesStore = useTemplatesStore()
|
|
@ -1,238 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="flex flex-col min-h-full">
|
|
||||||
<breadcrumb :path="breadcrumbs" />
|
|
||||||
|
|
||||||
<div v-if="templatesLoading" class="text-center my-4">
|
|
||||||
<Loader class="h-6 w-6 text-nt-blue mx-auto" />
|
|
||||||
</div>
|
|
||||||
<p v-else-if="type === null || !type" class="text-center my-4">
|
|
||||||
We could not find this type.
|
|
||||||
</p>
|
|
||||||
<template v-else>
|
|
||||||
<section class="py-12 sm:py-16 bg-gray-50 border-b border-gray-200">
|
|
||||||
<div class="px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto">
|
|
||||||
<div class="text-center mx-auto">
|
|
||||||
<div class="font-semibold sm:w-full text-blue-500 mb-3">
|
|
||||||
{{ type.name }}
|
|
||||||
</div>
|
|
||||||
<h1 class="text-3xl sm:text-4xl lg:text-5xl font-bold tracking-tight text-gray-900">
|
|
||||||
{{ type.meta_title }}
|
|
||||||
</h1>
|
|
||||||
<p class="max-w-xl mx-auto text-gray-600 mt-4 text-lg font-normal">
|
|
||||||
{{ type.meta_description }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="bg-white py-12 sm:py-16">
|
|
||||||
<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="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>
|
|
||||||
|
|
||||||
<section class="py-12 bg-white border-t border-gray-200 sm:py-16">
|
|
||||||
<div class="px-4 mx-auto sm:px-6 lg:px-8 max-w-7xl">
|
|
||||||
<p class="text-gray-600 font-normal">
|
|
||||||
{{ type.description }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="py-12 bg-white border-t border-gray-200 sm:py-16">
|
|
||||||
<div class="px-4 mx-auto sm:px-6 lg:px-8 max-w-7xl">
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<h4 class="text-xl font-bold tracking-tight text-gray-900 sm:text-2xl">
|
|
||||||
Other Types
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<v-button :to="{name:'templates'}" color="white" size="small" :arrow="true">
|
|
||||||
View All Templates
|
|
||||||
</v-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-8 mt-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
|
||||||
<router-link v-for="row in otherTypes" :key="row.slug"
|
|
||||||
:to="{params:{slug:row.slug}, name:'templates-types'}"
|
|
||||||
:title="row.name"
|
|
||||||
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
|
|
||||||
>
|
|
||||||
{{ row.name }}
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<open-form-footer class="mt-8 border-t"/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Form from 'vform'
|
|
||||||
import Fuse from 'fuse.js'
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { useAuthStore } from '../../stores/auth'
|
|
||||||
import { useTemplatesStore } from '../../stores/templates'
|
|
||||||
import SeoMeta from '../../mixins/seo-meta.js'
|
|
||||||
import OpenFormFooter from '../../components/pages/OpenFormFooter.vue'
|
|
||||||
import Breadcrumb from '~/components/global/Breadcrumb.vue'
|
|
||||||
import SingleTemplate from '../../components/pages/templates/SingleTemplate.vue'
|
|
||||||
|
|
||||||
const loadTemplates = function () {
|
|
||||||
const templatesStore = useTemplatesStore()
|
|
||||||
templatesStore.startLoading()
|
|
||||||
templatesStore.loadIfEmpty().then(() => {
|
|
||||||
templatesStore.stopLoading()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: { Breadcrumb, OpenFormFooter, SingleTemplate },
|
|
||||||
mixins: [SeoMeta],
|
|
||||||
|
|
||||||
beforeRouteEnter (to, from, next) {
|
|
||||||
loadTemplates()
|
|
||||||
next()
|
|
||||||
},
|
|
||||||
|
|
||||||
setup () {
|
|
||||||
const authStore = useAuthStore()
|
|
||||||
const templatesStore = useTemplatesStore()
|
|
||||||
return {
|
|
||||||
authenticated : computed(() => authStore.check),
|
|
||||||
user : computed(() => authStore.user),
|
|
||||||
templates : computed(() => templatesStore.content),
|
|
||||||
templatesLoading : computed(() => templatesStore.loading),
|
|
||||||
industries : computed(() => templatesStore.industries),
|
|
||||||
types : computed(() => templatesStore.types)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
selectedIndustry: 'all',
|
|
||||||
searchTemplate: new Form({
|
|
||||||
search: ''
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
breadcrumbs () {
|
|
||||||
if (!this.type) {
|
|
||||||
return [{ route: { name: 'templates' }, label: 'Templates' }]
|
|
||||||
}
|
|
||||||
return [{ route: { name: 'templates' }, label: 'Templates' }, { label: this.type.name }]
|
|
||||||
},
|
|
||||||
type () {
|
|
||||||
return Object.values(this.types).find((type) => {
|
|
||||||
return type.slug === this.$route.params.slug
|
|
||||||
})
|
|
||||||
},
|
|
||||||
industriesOptions () {
|
|
||||||
return [{ name: 'All Industries', value: 'all' }].concat(Object.values(this.industries).map((industry) => {
|
|
||||||
return {
|
|
||||||
name: industry.name,
|
|
||||||
value: industry.slug
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
otherTypes() {
|
|
||||||
return Object.values(this.types).filter((type) => {
|
|
||||||
return type.slug !== this.$route.params.slug
|
|
||||||
})
|
|
||||||
},
|
|
||||||
enrichedTemplates () {
|
|
||||||
let enrichedTemplates = this.templates
|
|
||||||
|
|
||||||
// Filter by current Type only
|
|
||||||
enrichedTemplates = enrichedTemplates.filter((item) => {
|
|
||||||
return (item.types && item.types.length > 0) ? item.types.includes(this.$route.params.slug) : 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
|
|
||||||
})
|
|
||||||
},
|
|
||||||
metaTitle () {
|
|
||||||
return this.type ? this.type.meta_title : 'Form Template Type'
|
|
||||||
},
|
|
||||||
metaDescription () {
|
|
||||||
if (!this.type) return null
|
|
||||||
return this.type.meta_description.substring(0, 140)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang='scss'>
|
|
||||||
.nf-text {
|
|
||||||
@apply space-y-4;
|
|
||||||
h2 {
|
|
||||||
@apply text-sm font-normal tracking-widest text-gray-500 uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
@apply font-normal leading-7 text-gray-900 dark:text-gray-100;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol {
|
|
||||||
@apply list-decimal list-inside;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
@apply list-disc list-inside;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col min-h-full">
|
||||||
|
<breadcrumb :path="breadcrumbs"/>
|
||||||
|
|
||||||
|
<div v-if="templatesLoading" class="text-center my-4">
|
||||||
|
<Loader class="h-6 w-6 text-nt-blue mx-auto"/>
|
||||||
|
</div>
|
||||||
|
<p v-else-if="type === null || !type" class="text-center my-4">
|
||||||
|
We could not find this type.
|
||||||
|
</p>
|
||||||
|
<template v-else>
|
||||||
|
<section class="py-12 sm:py-16 bg-gray-50 border-b border-gray-200">
|
||||||
|
<div class="px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto">
|
||||||
|
<div class="text-center mx-auto">
|
||||||
|
<div class="font-semibold sm:w-full text-blue-500 mb-3">
|
||||||
|
{{ type.name }}
|
||||||
|
</div>
|
||||||
|
<h1 class="text-3xl sm:text-4xl lg:text-5xl font-bold tracking-tight text-gray-900">
|
||||||
|
{{ type.meta_title }}
|
||||||
|
</h1>
|
||||||
|
<p class="max-w-xl mx-auto text-gray-600 mt-4 text-lg font-normal">
|
||||||
|
{{ type.meta_description }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="bg-white py-12 sm:py-16">
|
||||||
|
<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="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>
|
||||||
|
|
||||||
|
<section class="py-12 bg-white border-t border-gray-200 sm:py-16">
|
||||||
|
<div class="px-4 mx-auto sm:px-6 lg:px-8 max-w-7xl">
|
||||||
|
<p class="text-gray-600 font-normal">
|
||||||
|
{{ type.description }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="py-12 bg-white border-t border-gray-200 sm:py-16">
|
||||||
|
<div class="px-4 mx-auto sm:px-6 lg:px-8 max-w-7xl">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h4 class="text-xl font-bold tracking-tight text-gray-900 sm:text-2xl">
|
||||||
|
Other Types
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<v-button :to="{name:'templates'}" color="white" size="small" :arrow="true">
|
||||||
|
View All Templates
|
||||||
|
</v-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-8 mt-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
|
<router-link v-for="row in otherTypes" :key="row.slug"
|
||||||
|
:to="{params:{slug:row.slug}, name:'templates-types-slug'}"
|
||||||
|
:title="row.name"
|
||||||
|
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
|
||||||
|
>
|
||||||
|
{{ row.name }}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<open-form-footer class="mt-8 border-t"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {computed} from 'vue'
|
||||||
|
import SeoMeta from '../../../mixins/seo-meta.js'
|
||||||
|
import OpenFormFooter from '../../../components/pages/OpenFormFooter.vue'
|
||||||
|
import Breadcrumb from '~/components/global/Breadcrumb.vue'
|
||||||
|
import SingleTemplate from '../../../components/pages/templates/SingleTemplate.vue'
|
||||||
|
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
const templatesStore = useTemplatesStore()
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
// State
|
||||||
|
const selectedIndustry = ref('all')
|
||||||
|
const searchTemplate = useForm({
|
||||||
|
search: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// Computed
|
||||||
|
const authenticated = computed(() => authStore.check)
|
||||||
|
const user = computed(() => authStore.user)
|
||||||
|
const templates = computed(() => templatesStore.getAll)
|
||||||
|
const breadcrumbs = computed(() => {
|
||||||
|
if (!type) {
|
||||||
|
return [{route: {name: 'templates'}, label: 'Templates'}]
|
||||||
|
}
|
||||||
|
return [{route: {name: 'templates'}, label: 'Templates'}, {label: type.name}]
|
||||||
|
})
|
||||||
|
const type = computed(() => templatesStore.types.value.get(route.params.slug))
|
||||||
|
const types = computed(() => [...templatesStore.types.value.values()])
|
||||||
|
const industriesOptions = computed(() => [{name: 'All Industries', value: 'all'}].concat(
|
||||||
|
[...templatesStore.types.values.values()].map((industry) => {
|
||||||
|
return {
|
||||||
|
name: industry.name,
|
||||||
|
value: industry.slug
|
||||||
|
}
|
||||||
|
})))
|
||||||
|
|
||||||
|
// enrichedTemplates()
|
||||||
|
// {
|
||||||
|
// let enrichedTemplates = this.templates
|
||||||
|
//
|
||||||
|
// // Filter by current Type only
|
||||||
|
// enrichedTemplates = enrichedTemplates.filter((item) => {
|
||||||
|
// return (item.types && item.types.length > 0) ? item.types.includes(this.$route.params.slug) : 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
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// ,
|
||||||
|
// metaTitle()
|
||||||
|
// {
|
||||||
|
// return this.type ? this.type.meta_title : 'Form Template Type'
|
||||||
|
// }
|
||||||
|
// ,
|
||||||
|
// metaDescription()
|
||||||
|
// {
|
||||||
|
// if (!this.type) return null
|
||||||
|
// return this.type.meta_description.substring(0, 140)
|
||||||
|
// }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss'>
|
||||||
|
.nf-text {
|
||||||
|
@apply space-y-4;
|
||||||
|
h2 {
|
||||||
|
@apply text-sm font-normal tracking-widest text-gray-500 uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
@apply font-normal leading-7 text-gray-900 dark:text-gray-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
@apply list-decimal list-inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
@apply list-disc list-inside;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import {defineStore} from 'pinia'
|
import {defineStore} from 'pinia'
|
||||||
import {useContentStore} from "~/composables/stores/useContentStore.js";
|
import {useContentStore} from "~/composables/stores/useContentStore.js";
|
||||||
|
import templateTypes from "~/data/forms/templates/types.json"
|
||||||
|
import industryTypes from "~/data/forms/templates/industries.json"
|
||||||
|
|
||||||
const templatesEndpoint = 'templates'
|
const templatesEndpoint = 'templates'
|
||||||
export const useTemplatesStore = defineStore('templates', () => {
|
export const useTemplatesStore = defineStore('templates', () => {
|
||||||
|
@ -7,29 +9,26 @@ export const useTemplatesStore = defineStore('templates', () => {
|
||||||
const contentStore = useContentStore('slug')
|
const contentStore = useContentStore('slug')
|
||||||
|
|
||||||
const allLoaded = ref(false)
|
const allLoaded = ref(false)
|
||||||
const industries = ref({})
|
const industries = ref(new Map)
|
||||||
const types = ref({})
|
const types = ref(new Map)
|
||||||
|
|
||||||
const getTemplateTypes = computed((state) => (slugs) => {
|
const getTemplateTypes = (slugs) => {
|
||||||
if (state.types.length === 0) return null
|
return slugs.map((slug) => {
|
||||||
return Object.values(state.types).filter((val) => slugs.includes(val.slug)).map((item) => {
|
return types.value.get(slug)
|
||||||
return item.name
|
}).filter((item) => item !== undefined)
|
||||||
})
|
}
|
||||||
// todo: use map
|
const getTemplateIndustries =(slugs) => {
|
||||||
})
|
return slugs.map((slug) => {
|
||||||
const getTemplateIndustries = computed((state) => (slugs) => {
|
return industries.value.get(slug)
|
||||||
if (state.industries.length === 0) return null
|
}).filter((item) => item !== undefined)
|
||||||
return Object.values(state.industries).filter((val) => slugs.includes(val.slug)).map((item) => {
|
}
|
||||||
return item.name
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const loadTypesAndIndustries = function() {
|
const initTypesAndIndustries = () => {
|
||||||
if (Object.keys(this.industries).length === 0 || Object.keys(this.types).length === 0) {
|
if (types.value.size === 0) {
|
||||||
// const files = import.meta.glob('~/data/forms/templates/*.json')
|
types.value = new Map(Object.entries(templateTypes))
|
||||||
// console.log(await files['/data/forms/templates/industries.json']())
|
}
|
||||||
// this.industries = await files['/data/forms/templates/industries.json']()
|
if (industries.value.size === 0) {
|
||||||
// this.types = await files['/data/forms/templates/types.json']()
|
industries.value = new Map(Object.entries(industryTypes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ export const useTemplatesStore = defineStore('templates', () => {
|
||||||
types,
|
types,
|
||||||
getTemplateTypes,
|
getTemplateTypes,
|
||||||
getTemplateIndustries,
|
getTemplateIndustries,
|
||||||
loadTypesAndIndustries,
|
initTypesAndIndustries
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -51,3 +50,8 @@ export const fetchAllTemplates = () => {
|
||||||
return useOpnApi(templatesEndpoint)
|
return useOpnApi(templatesEndpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const loadTypesAndIndustries = () => {
|
||||||
|
// Load the json files
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue