Clean metaInfo SEO (#40)

* Clean metaInfo SEO

* fix path

Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
Chirag 2022-12-22 16:25:17 +05:30 committed by GitHub
parent 549506ab8f
commit 286b82b3ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 128 additions and 156 deletions

25
resources/js/base.js vendored
View File

@ -4,36 +4,13 @@
import debounce from 'debounce' import debounce from 'debounce'
export default { export default {
computed: { computed: {
$crisp () { $crisp () {
return window.$crisp return window.$crisp
} }
}, },
metaInfo () {
const info = {
meta: this.metaTags ?? []
}
if (this.metaTitle) {
info.title = this.metaTitle
info.meta = [
...info.meta,
{ vmid: 'og:title', property: 'og:title', content: this.metaTitle },
{ vmid: 'twitter:title', property: 'twitter:title', content: this.metaTitle }
]
}
if (this.metaDescription) {
info.meta = [
...info.meta,
{ vmid: 'description', name: 'description', content: this.metaDescription },
{ vmid: 'og:description', property: 'og:description', content: this.metaDescription },
{ vmid: 'twitter:description', property: 'twitter:description', content: this.metaDescription }
]
}
return info
},
methods: { methods: {
/** /**
* Creates a debounced function that delays invoking a callback. * Creates a debounced function that delays invoking a callback.

View File

@ -45,6 +45,7 @@ import Amplitude from './service/Amplitude'
import Crisp from './service/Crisp' import Crisp from './service/Crisp'
import StopImpersonation from './pages/StopImpersonation' import StopImpersonation from './pages/StopImpersonation'
import Notifications from "./common/Notifications" import Notifications from "./common/Notifications"
import SeoMeta from '../mixins/seo-meta'
// Load layout components dynamically. // Load layout components dynamically.
const requireContext = require.context('~/layouts', false, /.*\.vue$/) const requireContext = require.context('~/layouts', false, /.*\.vue$/)
@ -70,7 +71,11 @@ export default {
Loading Loading
}, },
mixins: [SeoMeta],
data: () => ({ data: () => ({
metaTitle: 'OpnForm',
metaDescription: 'Create beautiful forms for free. Unlimited fields, unlimited submissions. It\'s free and it takes less than 1 minute to create your first form.',
layout: null, layout: null,
defaultLayout: 'default', defaultLayout: 'default',
announcement: false, announcement: false,
@ -83,26 +88,6 @@ export default {
} }
}), }),
metaInfo () {
const { appName } = window.config
const description = "Create beautiful forms for free. Unlimited fields, unlimited submissions. It's free and it takes less than 1 minute to create your first form."
return {
title: appName,
titleTemplate: `%s · ${appName}`,
meta: [
{ vmid: 'description', name: 'description', content: description },
{ vmid: 'og:title', property: 'og:title', content: appName },
{ vmid: 'og:description', property: 'og:description', content: description },
{ vmid: 'og:image', property: 'og:image', content: this.asset('img/social-preview.jpg') },
{ vmid: 'twitter:title', property: 'twitter:title', content: appName },
{ vmid: 'twitter:description', property: 'twitter:description', content: description },
{ vmid: 'twitter:image', property: 'twitter:image', content: this.asset('img/social-preview.jpg') },
{ vmid: 'twitter:card', property: 'twitter:card', content: 'summary_large_image' }
]
}
},
mounted () { mounted () {
this.$loading = this.$refs.loading this.$loading = this.$refs.loading
}, },

22
resources/js/mixins/seo-meta.js vendored Normal file
View File

@ -0,0 +1,22 @@
export default {
metaInfo () {
const title = this.metaTitle ?? 'OpnForm'
const description = this.metaDescription ?? "Create beautiful forms for free. Unlimited fields, unlimited submissions. It's free and it takes less than 1 minute to create your first form."
const image = this.metaImage ?? this.asset('img/social-preview.jpg')
return {
title: title,
titleTemplate: '%s · OpnForm',
meta: [
...(this.metaTags ?? []),
{ vmid: 'og:title', property: 'og:title', content: title },
{ vmid: 'twitter:title', property: 'twitter:title', content: title },
{ vmid: 'description', name: 'description', content: description },
{ vmid: 'og:description', property: 'og:description', content: description },
{ vmid: 'twitter:description', property: 'twitter:description', content: description },
{ vmid: 'twitter:image', property: 'twitter:image', content: image },
{ vmid: 'og:image', property: 'og:image', content: image }
]
}
}
}

View File

@ -59,6 +59,7 @@
import OpenFormFooter from '../../components/pages/OpenFormFooter' import OpenFormFooter from '../../components/pages/OpenFormFooter'
import Testimonials from '../../components/pages/welcome/Testimonials' import Testimonials from '../../components/pages/welcome/Testimonials'
import LoginForm from './components/LoginForm' import LoginForm from './components/LoginForm'
import SeoMeta from '../../mixins/seo-meta'
export default { export default {
components: { components: {
@ -69,12 +70,10 @@ export default {
middleware: 'guest', middleware: 'guest',
metaInfo () { mixins: [SeoMeta],
return { title: this.$t('login') }
},
data: () => ({ data: () => ({
metaTitle: 'Login',
}), }),
methods: { methods: {

View File

@ -25,17 +25,18 @@
<script> <script>
import Form from 'vform' import Form from 'vform'
import OpenFormFooter from '../../../components/pages/OpenFormFooter' import OpenFormFooter from '../../../components/pages/OpenFormFooter'
import SeoMeta from '../../../mixins/seo-meta'
export default { export default {
middleware: 'guest', middleware: 'guest',
components: { components: {
OpenFormFooter OpenFormFooter
}, },
metaInfo () {
return { title: this.$t('reset_password') } mixins: [SeoMeta],
},
data: () => ({ data: () => ({
metaTitle: 'Reset Password',
status: '', status: '',
form: new Form({ form: new Form({
email: '' email: ''

View File

@ -35,17 +35,18 @@
<script> <script>
import Form from 'vform' import Form from 'vform'
import OpenFormFooter from '../../../components/pages/OpenFormFooter' import OpenFormFooter from '../../../components/pages/OpenFormFooter'
import SeoMeta from '../../../mixins/seo-meta'
export default { export default {
middleware: 'guest', middleware: 'guest',
components: { components: {
OpenFormFooter OpenFormFooter
}, },
metaInfo () {
return { title: this.$t('reset_password') } mixins: [SeoMeta],
},
data: () => ({ data: () => ({
metaTitle: 'Reset Password',
status: '', status: '',
form: new Form({ form: new Form({
token: '', token: '',

View File

@ -59,6 +59,7 @@
import OpenFormFooter from '../../components/pages/OpenFormFooter' import OpenFormFooter from '../../components/pages/OpenFormFooter'
import Testimonials from '../../components/pages/welcome/Testimonials' import Testimonials from '../../components/pages/welcome/Testimonials'
import RegisterForm from './components/RegisterForm' import RegisterForm from './components/RegisterForm'
import SeoMeta from '../../mixins/seo-meta'
export default { export default {
components: { components: {
@ -69,12 +70,10 @@ export default {
middleware: 'guest', middleware: 'guest',
metaInfo () { mixins: [SeoMeta],
return { title: this.$t('register') }
},
data: () => ({ data: () => ({
metaTitle: 'Register',
}), }),
computed: { computed: {

View File

@ -25,15 +25,14 @@
<script> <script>
import Form from 'vform' import Form from 'vform'
import SeoMeta from '../../../mixins/seo-meta'
export default { export default {
middleware: 'guest', middleware: 'guest',
mixins: [SeoMeta],
metaInfo () {
return { title: this.$t('verify_email') }
},
data: () => ({ data: () => ({
metaTitle: 'Verify Email',
status: '', status: '',
form: new Form({ form: new Form({
email: '' email: ''

View File

@ -28,6 +28,7 @@
<script> <script>
import axios from 'axios' import axios from 'axios'
import SeoMeta from '../../../mixins/seo-meta'
const qs = (params) => Object.keys(params).map(key => `${key}=${params[key]}`).join('&') const qs = (params) => Object.keys(params).map(key => `${key}=${params[key]}`).join('&')
@ -47,12 +48,10 @@ export default {
}, },
middleware: 'guest', middleware: 'guest',
mixins: [SeoMeta],
metaInfo () {
return { title: this.$t('verify_email') }
},
data: () => ({ data: () => ({
metaTitle: 'Verify Email',
error: '', error: '',
success: '' success: ''
}) })

View File

@ -29,6 +29,7 @@ import Form from 'vform'
import {mapState, mapActions} from 'vuex' import {mapState, mapActions} from 'vuex'
import QuickRegister from '../auth/components/QuickRegister' import QuickRegister from '../auth/components/QuickRegister'
import initForm from "../../mixins/form_editor/initForm" import initForm from "../../mixins/form_editor/initForm"
import SeoMeta from '../../mixins/seo-meta'
const loadTemplates = function () { const loadTemplates = function () {
store.commit('open/templates/startLoading') store.commit('open/templates/startLoading')
@ -39,17 +40,13 @@ const loadTemplates = function () {
export default { export default {
name: 'CreateFormGuest', name: 'CreateFormGuest',
mixins: [initForm], mixins: [initForm, SeoMeta],
components: { components: {
QuickRegister QuickRegister
}, },
middleware: 'guest', middleware: 'guest',
metaInfo() {
return {title: 'Create a new Form as Guest'}
},
beforeRouteEnter(to, from, next) { beforeRouteEnter(to, from, next) {
loadTemplates() loadTemplates()
next() next()
@ -57,6 +54,7 @@ export default {
data() { data() {
return { return {
metaTitle: 'Create a new Form as Guest',
stateReady: false, stateReady: false,
loading: false, loading: false,
error: '', error: '',

View File

@ -22,6 +22,7 @@ import store from '~/store'
import Form from 'vform' import Form from 'vform'
import {mapState, mapActions} from 'vuex' import {mapState, mapActions} from 'vuex'
import initForm from "../../mixins/form_editor/initForm"; import initForm from "../../mixins/form_editor/initForm";
import SeoMeta from '../../mixins/seo-meta'
const loadTemplates = function () { const loadTemplates = function () {
store.commit('open/templates/startLoading') store.commit('open/templates/startLoading')
@ -33,13 +34,9 @@ const loadTemplates = function () {
export default { export default {
name: 'CreateForm', name: 'CreateForm',
mixins: [initForm], mixins: [initForm, SeoMeta],
components: {}, components: {},
metaInfo() {
return {title: 'Create a new Form'}
},
beforeRouteEnter(to, from, next) { beforeRouteEnter(to, from, next) {
loadTemplates() loadTemplates()
next() next()
@ -49,6 +46,7 @@ export default {
data() { data() {
return { return {
metaTitle: 'Create a new Form',
stateReady: false, stateReady: false,
loading: false, loading: false,
error: '', error: '',

View File

@ -20,10 +20,9 @@
import axios from 'axios' import axios from 'axios'
import store from '~/store' import store from '~/store'
import Breadcrumb from '../../components/common/Breadcrumb' import Breadcrumb from '../../components/common/Breadcrumb'
import Form from 'vform' import Form from 'vform'
import { mapState } from 'vuex' import { mapState } from 'vuex'
import SeoMeta from '../../mixins/seo-meta'
const loadForms = function () { const loadForms = function () {
store.commit('open/forms/startLoading') store.commit('open/forms/startLoading')
@ -43,6 +42,7 @@ export default {
next() next()
}, },
middleware: 'auth', middleware: 'auth',
mixins: [SeoMeta],
data () { data () {
return { return {
@ -70,7 +70,10 @@ export default {
}, },
pageLoaded () { pageLoaded () {
return !this.loading && this.updatedForm !== null return !this.loading && this.updatedForm !== null
} },
metaTitle () {
return 'Edit ' + (this.form ? this.form.title : 'Your Form')
},
}, },
watch: { watch: {
@ -95,10 +98,6 @@ export default {
} }
}, },
metaInfo () {
return { title: 'Edit ' + (this.form ? this.form.title : 'Your Form') }
},
methods: { methods: {
/** /**
* Compute max height of editor * Compute max height of editor

View File

@ -9,23 +9,25 @@
<script> <script>
import FormStats from '../../../components/open/forms/components/FormStats' import FormStats from '../../../components/open/forms/components/FormStats'
import SeoMeta from '../../../mixins/seo-meta'
export default { export default {
components: {FormStats}, components: {FormStats},
props: { props: {
form: { type: Object, required: true } form: { type: Object, required: true }
}, },
mixins: [SeoMeta],
metaInfo() {
return {title: (this.form) ? 'Form Analytics - '+this.form.title : 'Form Analytics'}
},
data: () => ({ data: () => ({
}), }),
mounted() {}, mounted() {},
computed: {}, computed: {
metaTitle() {
return (this.form) ? 'Form Analytics - '+this.form.title : 'Form Analytics'
},
},
methods: { methods: {

View File

@ -107,6 +107,7 @@ import {mapGetters, mapState} from 'vuex'
import ProTag from '../../../components/common/ProTag' import ProTag from '../../../components/common/ProTag'
import VButton from "../../../components/common/Button"; import VButton from "../../../components/common/Button";
import ExtraMenu from '../../../components/pages/forms/show/ExtraMenu' import ExtraMenu from '../../../components/pages/forms/show/ExtraMenu'
import SeoMeta from '../../../mixins/seo-meta'
const loadForms = function () { const loadForms = function () {
store.commit('open/forms/startLoading') store.commit('open/forms/startLoading')
@ -122,6 +123,7 @@ export default {
ProTag, ProTag,
ExtraMenu ExtraMenu
}, },
mixins: [SeoMeta],
beforeRouteEnter(to, from, next) { beforeRouteEnter(to, from, next) {
loadForms() loadForms()
@ -136,6 +138,7 @@ export default {
data() { data() {
return { return {
metaTitle: 'Home',
tabsList: [ tabsList: [
{ {
name: 'Submissions', name: 'Submissions',
@ -205,10 +208,6 @@ export default {
} }
}, },
metaInfo() {
return {title: this.$t('home')}
},
methods: { methods: {
openCrisp() { openCrisp() {
window.$crisp.push(['do', 'chat:show']) window.$crisp.push(['do', 'chat:show'])

View File

@ -17,6 +17,7 @@ import ShareLink from '../../../components/pages/forms/show/ShareLink'
import EmbedCode from '../../../components/pages/forms/show/EmbedCode' import EmbedCode from '../../../components/pages/forms/show/EmbedCode'
import UrlFormPrefill from '../../../components/pages/forms/show/UrlFormPrefill' import UrlFormPrefill from '../../../components/pages/forms/show/UrlFormPrefill'
import RegenerateFormLink from '../../../components/pages/forms/show/RegenerateFormLink' import RegenerateFormLink from '../../../components/pages/forms/show/RegenerateFormLink'
import SeoMeta from '../../../mixins/seo-meta'
export default { export default {
components: { components: {
@ -28,17 +29,18 @@ export default {
props: { props: {
form: { type: Object, required: true } form: { type: Object, required: true }
}, },
mixins: [SeoMeta],
metaInfo() {
return {title: (this.form) ? 'Form Share - '+this.form.title : 'Form Share'}
},
data: () => ({ data: () => ({
}), }),
mounted() {}, mounted() {},
computed: {}, computed: {
metaTitle() {
return (this.form) ? 'Form Share - '+this.form.title : 'Form Share'
},
},
methods: { methods: {

View File

@ -6,23 +6,25 @@
<script> <script>
import FormSubmissions from '../../../components/open/forms/components/FormSubmissions' import FormSubmissions from '../../../components/open/forms/components/FormSubmissions'
import SeoMeta from '../../../mixins/seo-meta'
export default { export default {
components: {FormSubmissions}, components: {FormSubmissions},
props: { props: {
form: { type: Object, required: true } form: { type: Object, required: true }
}, },
mixins: [SeoMeta],
metaInfo() {
return {title: (this.form) ? 'Form Submissions - '+this.form.title : 'Form Submissions'}
},
data: () => ({ data: () => ({
}), }),
mounted() {}, mounted() {},
computed: {}, computed: {
metaTitle() {
return (this.form) ? 'Form Submissions - '+this.form.title : 'Form Submissions'
},
},
methods: { methods: {

View File

@ -14,16 +14,15 @@
<script> <script>
import OpenFormFooter from '../../components/pages/OpenFormFooter' import OpenFormFooter from '../../components/pages/OpenFormFooter'
import SeoMeta from '../../mixins/seo-meta'
export default { export default {
components: { OpenFormFooter}, components: { OpenFormFooter},
layout: 'default', layout: 'default',
mixins: [SeoMeta],
metaInfo () {
return { title: 'Privacy Policy' }
},
data: () => ({ data: () => ({
metaTitle: 'Privacy Policy',
}), }),
computed: {}, computed: {},

View File

@ -14,16 +14,15 @@
<script> <script>
import OpenFormFooter from '../../components/pages/OpenFormFooter' import OpenFormFooter from '../../components/pages/OpenFormFooter'
import SeoMeta from '../../mixins/seo-meta'
export default { export default {
components: { OpenFormFooter }, components: { OpenFormFooter },
layout: 'default', layout: 'default',
mixins: [SeoMeta],
metaInfo () {
return { title: 'Terms & Conditions' }
},
data: () => ({ data: () => ({
metaTitle: 'Terms & Conditions',
}), }),
computed: {}, computed: {},

View File

@ -18,15 +18,14 @@
<script> <script>
import Form from 'vform' import Form from 'vform'
import axios from 'axios' import axios from 'axios'
import SeoMeta from '../../mixins/seo-meta'
export default { export default {
scrollToTop: false, scrollToTop: false,
mixins: [SeoMeta],
metaInfo () {
return { title: 'Account' }
},
data: () => ({ data: () => ({
metaTitle: 'Account',
form: new Form({ form: new Form({
identifier: '' identifier: ''
}), }),

View File

@ -37,17 +37,16 @@
<script> <script>
import Form from 'vform' import Form from 'vform'
import axios from 'axios' import axios from 'axios'
import SeoMeta from '../../mixins/seo-meta'
export default { export default {
components: { }, components: { },
middleware: 'admin', middleware: 'admin',
scrollToTop: false, scrollToTop: false,
mixins: [SeoMeta],
metaInfo () {
return { title: 'Admin' }
},
data: () => ({ data: () => ({
metaTitle: 'Admin',
form: new Form({ form: new Form({
identifier: '' identifier: ''
}), }),

View File

@ -17,16 +17,15 @@
<script> <script>
import axios from 'axios' import axios from 'axios'
import VButton from '../../components/common/Button' import VButton from '../../components/common/Button'
import SeoMeta from '../../mixins/seo-meta'
export default { export default {
components: { VButton }, components: { VButton },
scrollToTop: false, scrollToTop: false,
mixins: [SeoMeta],
metaInfo () {
return { title: 'Billing' }
},
data: () => ({ data: () => ({
metaTitle: 'Billing',
billingLoading: false billingLoading: false
}), }),

View File

@ -24,15 +24,14 @@
<script> <script>
import Form from 'vform' import Form from 'vform'
import SeoMeta from '../../mixins/seo-meta'
export default { export default {
scrollToTop: false, scrollToTop: false,
mixins: [SeoMeta],
metaInfo () {
return { title: this.$t('settings') }
},
data: () => ({ data: () => ({
metaTitle: 'Password',
form: new Form({ form: new Form({
password: '', password: '',
password_confirmation: '' password_confirmation: ''

View File

@ -21,15 +21,14 @@
<script> <script>
import Form from 'vform' import Form from 'vform'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import SeoMeta from '../../mixins/seo-meta'
export default { export default {
scrollToTop: false, scrollToTop: false,
mixins: [SeoMeta],
metaInfo () {
return { title: this.$t('settings') }
},
data: () => ({ data: () => ({
metaTitle: 'Profile',
form: new Form({ form: new Form({
name: '', name: '',
email: '' email: ''

View File

@ -75,17 +75,15 @@
<script> <script>
import Form from 'vform' import Form from 'vform'
import {mapActions, mapState} from 'vuex' import {mapActions, mapState} from 'vuex'
import SeoMeta from '../../mixins/seo-meta'
export default { export default {
components: {}, components: {},
scrollToTop: false, scrollToTop: false,
mixins: [SeoMeta],
metaInfo() {
return {title: 'Workspaces'}
},
data: () => ({ data: () => ({
metaTitle: 'Workspaces',
form: new Form({ form: new Form({
name: '', name: '',
emoji: '' emoji: ''

View File

@ -2,17 +2,17 @@
<script> <script>
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import SeoMeta from '../../mixins/seo-meta'
export default { export default {
components: { }, components: { },
layout: 'default', layout: 'default',
middleware: 'auth', middleware: 'auth',
mixins: [SeoMeta],
metaInfo () { data: () => ({
return { title: 'Error' } metaTitle: 'Error',
}, }),
data: () => ({}),
mounted () { mounted () {
this.$router.push({ name: 'pricing' }) this.$router.push({ name: 'pricing' })

View File

@ -18,17 +18,16 @@
<script> <script>
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import OpenFormFooter from '../../components/pages/OpenFormFooter' import OpenFormFooter from '../../components/pages/OpenFormFooter'
import SeoMeta from '../../mixins/seo-meta'
export default { export default {
components: { OpenFormFooter }, components: { OpenFormFooter },
layout: 'default', layout: 'default',
middleware: 'auth', middleware: 'auth',
mixins: [SeoMeta],
metaInfo () {
return { title: 'Subscription Success' }
},
data: () => ({ data: () => ({
metaTitle: 'Subscription Success',
interval: null interval: null
}), }),

View File

@ -108,18 +108,18 @@ import Features from '~/components/pages/welcome/Features'
import MoreFeatures from '~/components/pages/welcome/MoreFeatures' import MoreFeatures from '~/components/pages/welcome/MoreFeatures'
import OpenFormFooter from '../components/pages/OpenFormFooter' import OpenFormFooter from '../components/pages/OpenFormFooter'
import Testimonials from '../components/pages/welcome/Testimonials' import Testimonials from '../components/pages/welcome/Testimonials'
import SeoMeta from '../mixins/seo-meta'
export default { export default {
components: {Testimonials, OpenFormFooter, Features, MoreFeatures}, components: {Testimonials, OpenFormFooter, Features, MoreFeatures},
layout: 'default', layout: 'default',
metaInfo() { mixins: [SeoMeta],
return {title: 'Create beautiful & open-source forms for free'}
},
data: () => ({ data: () => ({
title: window.config.appName title: window.config.appName,
metaTitle: 'Create beautiful & open-source forms for free',
}), }),
mounted() { mounted() {