Finish reworking most templates pages

This commit is contained in:
Julien Nahum 2023-12-19 17:10:07 +01:00
parent eda1af9b1b
commit aac4d1da04
5 changed files with 99 additions and 333 deletions

View File

@ -5,12 +5,12 @@
<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"
<select-input v-model="selectedType" name="type" v-if="filterTypes"
: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"
<select-input v-model="selectedIndustry" name="industry" v-if="filterIndustries"
:options="industriesOptions" class="w-full sm:w-auto md:w-56"
/>
</div>
@ -34,16 +34,20 @@
</div>
</section>
<slot name="before-lists"/>
<section class="py-12 bg-white border-t border-gray-200 sm:py-16">
<section class="py-12 bg-white border-t border-gray-200 sm:py-16" v-if="showTypes">
<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">
All 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">
<div class="grid grid-cols-1 gap-x-8 gap-y-4 mt-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<router-link v-for="row in types" :key="row.slug"
:to="{params: {slug:row.slug}, name:'templates-types-slug'}"
:title="row.name"
@ -55,15 +59,18 @@
</div>
</section>
<section class="py-12 bg-white border-t border-gray-200 sm:py-16">
<section class="py-12 bg-white border-t border-gray-200 sm:py-16" v-if="showIndustries">
<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">
All Industries
</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">
<div class="grid grid-cols-1 gap-x-8 gap-y-4 mt-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<router-link v-for="row in industries" :key="row.slug"
:to="{params:{slug:row.slug}, name:'templates-industries-slug'}"
:title="row.name"
@ -94,6 +101,22 @@ export default {
loading: {
type: Boolean,
default: false
},
showTypes: {
type: Boolean,
default: true
},
filterTypes: {
type: Boolean,
default: true
},
showIndustries: {
type: Boolean,
default: true
},
filterIndustries: {
type: Boolean,
default: true
}
},
@ -137,14 +160,14 @@ export default {
let enrichedTemplates = this.templates
// Filter by Selected Type
if (this.selectedType && this.selectedType !== 'all') {
if (this.filterTypes && 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') {
if (this.filterIndustries && this.selectedIndustry && this.selectedIndustry !== 'all') {
enrichedTemplates = enrichedTemplates.filter((item) => {
return (item.industries && item.industries.length > 0) ? item.industries.includes(this.selectedIndustry) : false
})

View File

@ -20,7 +20,7 @@
</template>
<script setup>
import {fetchAllTemplates} from "~/stores/templates.js";
import {loadAllTemplates} from "~/stores/templates.js";
// props: {
// metaTitle: { type: String, default: 'Templates' },
@ -28,14 +28,7 @@ import {fetchAllTemplates} from "~/stores/templates.js";
// },
const templatesStore = useTemplatesStore()
if (!templatesStore.allLoaded) {
templatesStore.startLoading()
templatesStore.initTypesAndIndustries()
const {data} = await fetchAllTemplates()
templatesStore.set(data.value)
templatesStore.allLoaded = true
}
loadAllTemplates(templatesStore)
const templates = computed(() => templatesStore.getAll)
</script>

View File

@ -1,12 +1,9 @@
<template>
<div class="flex flex-col min-h-full">
<breadcrumb :path="breadcrumbs" />
<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="industry === null || !industry" class="text-center my-4">
We could not find this industry.
<p v-if="industry === null || !industry" 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">
@ -25,195 +22,51 @@
</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="selectedType" name="type"
:options="typesOptions" class="w-full sm:w-auto md:w-56"
/>
</div>
<templates-list :templates="templates" :filter-industries="false" :show-types="false">
<template #before-lists>
<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">
{{ industry.description }}
</p>
</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">
{{ industry.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 Industries
</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 otherIndustries" :key="row.slug"
:to="{params:{slug:row.slug}, name:'templates-industries-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>
</section>
</template>
</templates-list>
</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.js'
import { useTemplatesStore } from '../../../stores/templates.js'
import SeoMeta from '../../../mixins/seo-meta.js'
<script setup>
import {computed} from 'vue'
import OpenFormFooter from '../../../components/pages/OpenFormFooter.vue'
import Breadcrumb from '~/components/global/Breadcrumb.vue'
import SingleTemplate from '../../../components/pages/templates/SingleTemplate.vue'
import {loadAllTemplates} from "~/stores/templates.js";
const loadTemplates = function () {
const templatesStore = useTemplatesStore()
templatesStore.startLoading()
templatesStore.loadIfEmpty().then(() => {
templatesStore.stopLoading()
})
}
const route = useRoute()
const authStore = useAuthStore()
const templatesStore = useTemplatesStore()
export default {
components: { Breadcrumb, OpenFormFooter, SingleTemplate },
mixins: [SeoMeta],
loadAllTemplates(templatesStore)
beforeRouteEnter (to, from, next) {
loadTemplates()
next()
},
// Computed
const authenticated = computed(() => authStore.check)
const user = computed(() => authStore.user)
const templates = computed(() => templatesStore.getAll.filter((item) => {
return (item.industries && item.industries.length > 0) ? item.industries.includes(route.params.slug) : false
}))
const breadcrumbs = computed(() => {
if (!industry) {
return [{route: {name: 'templates'}, label: 'Templates'}]
}
return [{route: {name: 'templates'}, label: 'Templates'}, {label: industry.value.name}]
})
setup () {
const authStore = useAuthStore()
const templatesStore = useTemplatesStore()
return {
authStore,
authenticated : computed(() => authStore.check),
user : computed(() => authStore.user),
templates : computed(() => templatesStore.content),
templatesLoading : computed(() => templatesStore.loading),
industries : computed(() => templatesStore.industries),
types : computed(() => templatesStore.types)
}
},
const industry = computed(() => templatesStore.industries.get(route.params.slug))
data () {
return {
selectedType: 'all',
searchTemplate: new Form({
search: ''
})
}
},
mounted () {},
computed: {
breadcrumbs () {
if (!this.industry) {
return [{ route: { name: 'templates' }, label: 'Templates' }]
}
return [{ route: { name: 'templates' }, label: 'Templates' }, { label: this.industry.name }]
},
industry () {
return Object.values(this.industries).find((industry) => {
return industry.slug === this.$route.params.slug
})
},
typesOptions () {
return [{ name: 'All Types', value: 'all' }].concat(Object.values(this.types).map((type) => {
return {
name: type.name,
value: type.slug
}
}))
},
otherIndustries() {
return Object.values(this.industries).filter((industry) => {
return industry.slug !== this.$route.params.slug
})
},
enrichedTemplates () {
let enrichedTemplates = this.templates
// Filter by current Industry only
enrichedTemplates = enrichedTemplates.filter((item) => {
return (item.industries && item.industries.length > 0) ? item.industries.includes(this.$route.params.slug) : false
})
// 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
})
}
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.industry ? this.industry.meta_title : 'Form Template Industry'
},
metaDescription () {
if (!this.industry) return null
return this.industry.meta_description.substring(0, 140)
}
},
methods: {}
}
</script>
<style lang='scss'>
@ -236,3 +89,4 @@ export default {
}
}
</style>

View File

@ -2,10 +2,7 @@
<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">
<p v-if="type === null || !type" class="text-center my-4">
We could not find this type.
</p>
<template v-else>
@ -25,156 +22,51 @@
</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>
<templates-list :templates="templates" :filter-types="false" :show-industries="false">
<template #before-lists>
<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>
<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>
</section>
</template>
</templates-list>
</template>
<open-form-footer class="mt-8 border-t"/>
</div>
</template>
<script>
<script setup>
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'
import {loadAllTemplates} from "~/stores/templates.js";
const route = useRoute()
const authStore = useAuthStore()
const templatesStore = useTemplatesStore()
const route = useRoute()
// State
const selectedIndustry = ref('all')
const searchTemplate = useForm({
search: ''
})
loadAllTemplates(templatesStore)
// Computed
const authenticated = computed(() => authStore.check)
const user = computed(() => authStore.user)
const templates = computed(() => templatesStore.getAll)
const templates = computed(() => templatesStore.getAll.filter((item) => {
return (item.types && item.types.length > 0) ? item.types.includes(route.params.slug) : false
}))
const breadcrumbs = computed(() => {
if (!type) {
return [{route: {name: 'templates'}, label: 'Templates'}]
}
return [{route: {name: 'templates'}, label: 'Templates'}, {label: type.name}]
return [{route: {name: 'templates'}, label: 'Templates'}, {label: type.value.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)
// }
const type = computed(() => templatesStore.types.get(route.params.slug))
</script>
<style lang='scss'>

View File

@ -17,7 +17,7 @@ export const useTemplatesStore = defineStore('templates', () => {
return types.value.get(slug)
}).filter((item) => item !== undefined)
}
const getTemplateIndustries =(slugs) => {
const getTemplateIndustries = (slugs) => {
return slugs.map((slug) => {
return industries.value.get(slug)
}).filter((item) => item !== undefined)
@ -50,8 +50,12 @@ export const fetchAllTemplates = () => {
return useOpnApi(templatesEndpoint)
}
export const loadTypesAndIndustries = () => {
// Load the json files
export const loadAllTemplates = async (store) => {
if (!store.allLoaded) {
store.startLoading()
store.initTypesAndIndustries()
const {data} = await fetchAllTemplates()
store.set(data.value)
store.allLoaded = true
}
}