B8f7a improve templates pages for seo (#5)
* Templates * access templates without login also * Set required on UI * Improve templates pages for SEO * test case for Templates * Refactor SitemapController * Cosmetic changes to templates Co-authored-by: Julien Nahum <jhumanj@MacBook-Pro-de-Julien.local>
This commit is contained in:
@@ -51,12 +51,21 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '~/store'
|
||||
import Form from 'vform'
|
||||
import {mapState, mapActions} from 'vuex'
|
||||
import saveUpdateAlert from '../../mixins/forms/saveUpdateAlert'
|
||||
import clonedeep from 'clone-deep'
|
||||
|
||||
const FormEditor = () => import('../../components/open/forms/components/FormEditor')
|
||||
|
||||
const loadTemplates = function () {
|
||||
store.commit('open/templates/startLoading')
|
||||
store.dispatch('open/templates/loadIfEmpty').then(() => {
|
||||
store.commit('open/templates/stopLoading')
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'CreateForm',
|
||||
components: {
|
||||
@@ -69,6 +78,11 @@ export default {
|
||||
|
||||
mixins: [saveUpdateAlert],
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
loadTemplates()
|
||||
next()
|
||||
},
|
||||
|
||||
middleware: 'auth',
|
||||
|
||||
data() {
|
||||
@@ -126,7 +140,16 @@ export default {
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.initForm()
|
||||
if(this.$route.query.template !== undefined && this.$route.query.template){
|
||||
let template = this.$store.getters['open/templates/getBySlug'](this.$route.query.template)
|
||||
if(template && template.structure){
|
||||
this.form = new Form(template.structure)
|
||||
}else{
|
||||
this.initForm()
|
||||
}
|
||||
}else{
|
||||
this.initForm()
|
||||
}
|
||||
this.closeAlert()
|
||||
this.loadWorkspaces()
|
||||
|
||||
|
||||
@@ -164,6 +164,20 @@
|
||||
<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 -->
|
||||
@@ -260,6 +274,8 @@
|
||||
</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">
|
||||
@@ -282,6 +298,7 @@ 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'
|
||||
|
||||
@@ -294,7 +311,7 @@ const loadForms = function () {
|
||||
|
||||
export default {
|
||||
name: 'EditForm',
|
||||
components: { UrlFormPrefillModal, ProTag, Breadcrumb, ShareFormUrl, EmbedFormCode, FormStats, FormSubmissions },
|
||||
components: { UrlFormPrefillModal, CreateTemplateModal, ProTag, Breadcrumb, ShareFormUrl, EmbedFormCode, FormStats, FormSubmissions },
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
loadForms()
|
||||
@@ -315,7 +332,8 @@ export default {
|
||||
showNotionEmbedModal: false,
|
||||
showShareEmbedFormModal: false,
|
||||
showUrlFormPrefillModal: false,
|
||||
showGenerateFormLinkModal: false
|
||||
showGenerateFormLinkModal: false,
|
||||
showCreateTemplateModal: false
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
100
resources/js/pages/templates/show.vue
Normal file
100
resources/js/pages/templates/show.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div class="flex flex-col min-h-full mt-6">
|
||||
<div class="w-full flex-grow md:w-4/5 lg:w-2/3 md:mx-auto md:max-w-4xl px-4">
|
||||
|
||||
<breadcrumb :path="breadcrumbs" />
|
||||
<div v-if="templatesLoading" class="text-center">
|
||||
<loader class="h-6 w-6 text-nt-blue mx-auto" />
|
||||
</div>
|
||||
<p v-else-if="template === null || !template">
|
||||
Template does not exist.
|
||||
</p>
|
||||
<div v-else>
|
||||
<div class="flex flex-wrap items-center mt-6 mb-4">
|
||||
<h2 class="text-nt-blue text-3xl font-bold flex-grow">
|
||||
{{ template.name }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="mb-10">
|
||||
<img :src="template.image_url" alt="" class="w-full shadow-xl rounded-lg my-5"/>
|
||||
<div v-html="template.description"></div>
|
||||
<div class="mt-5 text-center">
|
||||
<fancy-link class="mt-4 sm:mt-0" :to="{path:'/forms/create?template='+template.slug}" color="nt-blue">
|
||||
Use this template
|
||||
</fancy-link>
|
||||
</div>
|
||||
|
||||
<h3 class="text-center text-gray-500">Template Preview</h3>
|
||||
<open-complete-form ref="open-complete-form" :form="form" :creating="true" class="my-5 p-4 bg-gray-50 rounded-lg"/>
|
||||
|
||||
<h3 class="text-xl font-semibold mb-3">Frequently asked questions</h3>
|
||||
<div v-if="template.questions.length > 0" class="mt-5 pt-2">
|
||||
<div v-for="(ques,ques_key) in template.questions" :key="ques_key" class="my-3 border rounded-lg">
|
||||
<h5 class="border-b p-2">{{ ques.question }}</h5>
|
||||
<div class="p-2" v-html="ques.answer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<open-form-footer class="mt-8 border-t" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '~/store'
|
||||
import Form from 'vform'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import Fuse from 'fuse.js'
|
||||
import OpenFormFooter from '../../components/pages/OpenFormFooter'
|
||||
import OpenCompleteForm from '../../components/open/forms/OpenCompleteForm'
|
||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||
|
||||
const loadTemplates = function () {
|
||||
store.commit('open/templates/startLoading')
|
||||
store.dispatch('open/templates/loadIfEmpty').then(() => {
|
||||
store.commit('open/templates/stopLoading')
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {Breadcrumb, OpenFormFooter, OpenCompleteForm },
|
||||
|
||||
beforeRouteEnter (to, from, next) {
|
||||
loadTemplates()
|
||||
next()
|
||||
},
|
||||
|
||||
props: {
|
||||
metaTitle: { type: String, default: 'Templates' },
|
||||
metaDescription: { type: String, default: 'Public templates for create form quickly!' }
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {},
|
||||
|
||||
methods: {},
|
||||
|
||||
computed: {
|
||||
...mapState({
|
||||
templatesLoading: state => state['open/templates'].loading
|
||||
}),
|
||||
breadcrumbs () {
|
||||
if (!this.template) {
|
||||
return [{ route: { name: 'templates' }, label: 'Templates' }]
|
||||
}
|
||||
return [{ route: { name: 'templates' }, label: 'Templates' }, { label: this.template.name }]
|
||||
},
|
||||
template () {
|
||||
return this.$store.getters['open/templates/getBySlug'](this.$route.params.slug)
|
||||
},
|
||||
form (){
|
||||
return new Form(this.template.structure)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
91
resources/js/pages/templates/templates.vue
Normal file
91
resources/js/pages/templates/templates.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="flex flex-col min-h-full mt-6">
|
||||
<div class="w-full flex-grow md:w-4/5 lg:w-2/3 md:mx-auto md:max-w-4xl px-4">
|
||||
<div>
|
||||
<div class="flex flex-wrap items-center mt-6 mb-4">
|
||||
<h2 class="text-nt-blue text-3xl font-bold flex-grow">
|
||||
Templates
|
||||
</h2>
|
||||
</div>
|
||||
<div v-if="templatesLoading" class="text-center">
|
||||
<loader class="h-6 w-6 text-nt-blue mx-auto"/>
|
||||
</div>
|
||||
<p v-else-if="templates.length === 0">
|
||||
No any templates found.
|
||||
</p>
|
||||
<div v-else class="mb-4">
|
||||
<div v-if="templates && templates.length"
|
||||
class="grid max-w-3xl grid-cols-1 mx-auto text-center sm:text-left sm:grid-cols-2 gap-y-8 gap-x-8 lg:gap-x-20">
|
||||
<div class="relative group" v-for="(template, index) in templates" :key="template.id">
|
||||
<div class="overflow-hidden rounded-lg aspect-w-16 aspect-h-9">
|
||||
<img class="object-cover w-full h-full transition-all duration-300 transform group-hover:scale-125"
|
||||
:src="template.image_url" alt=""/>
|
||||
</div>
|
||||
<p class="mt-3 mb-2 text-sm font-normal text-gray-600 font-pj">
|
||||
{{ formatCreatedDate(template.created_at) }}</p>
|
||||
<p class="text-xl font-bold text-gray-900 font-pj">{{ template.name }}</p>
|
||||
<router-link :to="{params:{slug:template.slug},name:'templates.show'}" title="">
|
||||
<span class="absolute inset-0" aria-hidden="true"></span>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<open-form-footer class="mt-8 border-t"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '~/store'
|
||||
import {mapGetters, mapState} from 'vuex'
|
||||
import Fuse from 'fuse.js'
|
||||
import OpenFormFooter from '../../components/pages/OpenFormFooter'
|
||||
|
||||
const loadTemplates = function () {
|
||||
store.commit('open/templates/startLoading')
|
||||
store.dispatch('open/templates/load').then(() => {
|
||||
store.commit('open/templates/stopLoading')
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {OpenFormFooter},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
loadTemplates()
|
||||
next()
|
||||
},
|
||||
|
||||
props: {
|
||||
metaTitle: {type: String, default: 'Templates'},
|
||||
metaDescription: {type: String, default: 'Public templates for create form quickly!'}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
},
|
||||
|
||||
methods: {
|
||||
formatCreatedDate(createdDate) {
|
||||
const date = new Date(createdDate)
|
||||
const dateTimeFormat = new Intl.DateTimeFormat('en', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})
|
||||
return dateTimeFormat.format(date)
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState({
|
||||
templates: state => state['open/templates'].content,
|
||||
templatesLoading: state => state['open/templates'].loading
|
||||
}),
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user