feat(seo): add meta tags and page titles
Configure default head meta and title template in nuxt.config.ts Add dynamic SEO meta tags and robots directives to all pages
This commit is contained in:
@@ -606,6 +606,12 @@ definePageMeta({
|
||||
middleware: 'auth'
|
||||
})
|
||||
|
||||
useSeoMeta({
|
||||
title: 'Bookings',
|
||||
description: 'Manage dinner ticket bookings, confirmations, receipts, and seat allocation.',
|
||||
robots: 'noindex,nofollow'
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
const apiClient = useApiClient()
|
||||
const auth = useAuth()
|
||||
|
||||
@@ -36,6 +36,15 @@ const statusColor = computed(() => booking.value.status === 'confirmed' ? 'succe
|
||||
const ticketLabel = computed(() => booking.value.ticketLabel || booking.value.ticketType.toUpperCase())
|
||||
const totalFormatted = computed(() => formatBookingCurrency(booking.value.totalPrice, locale.value))
|
||||
const receiptPath = computed(() => `/receipt/${booking.value.receiptToken}`)
|
||||
|
||||
useSeoMeta({
|
||||
title: () => `${t('confirm.title')} - ${booking.value.customerName}`,
|
||||
description: () => t('confirm.description'),
|
||||
ogTitle: () => `${t('confirm.title')} - ${booking.value.customerName}`,
|
||||
ogDescription: () => t('confirm.description'),
|
||||
robots: 'noindex,nofollow'
|
||||
})
|
||||
|
||||
const detailRows = computed(() => {
|
||||
const rows = [
|
||||
{
|
||||
|
||||
@@ -27,6 +27,20 @@ const [bookingConfig, contactsResponse] = await Promise.all([
|
||||
apiClient<{ contacts: PublicContact[] }>('/api/public/contacts')
|
||||
])
|
||||
|
||||
const seoDescription = computed(() => {
|
||||
return `${bookingConfig.event.dateLabel} · ${bookingConfig.event.timeLabel} · ${bookingConfig.event.venue}`
|
||||
})
|
||||
|
||||
useSeoMeta({
|
||||
title: () => bookingConfig.event.title,
|
||||
description: () => seoDescription.value,
|
||||
ogTitle: () => bookingConfig.event.title,
|
||||
ogDescription: () => seoDescription.value,
|
||||
twitterTitle: () => bookingConfig.event.title,
|
||||
twitterDescription: () => seoDescription.value,
|
||||
robots: 'index,follow'
|
||||
})
|
||||
|
||||
const eventDetails = computed(() => [
|
||||
{
|
||||
label: t('common.date'),
|
||||
|
||||
@@ -121,6 +121,12 @@ definePageMeta({
|
||||
middleware: 'guest'
|
||||
})
|
||||
|
||||
useSeoMeta({
|
||||
title: 'Staff Login',
|
||||
description: 'Staff login for the dinner ticket management system.',
|
||||
robots: 'noindex,nofollow'
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
const router = useRouter()
|
||||
const auth = useAuth()
|
||||
|
||||
@@ -255,6 +255,12 @@ definePageMeta({
|
||||
middleware: 'super-admin'
|
||||
})
|
||||
|
||||
useSeoMeta({
|
||||
title: 'User Management',
|
||||
description: 'Manage staff access for the dinner ticket system.',
|
||||
robots: 'noindex,nofollow'
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
const apiClient = useApiClient()
|
||||
const auth = useAuth()
|
||||
|
||||
@@ -52,6 +52,15 @@ const ticketLabel = computed(() => receipt.value.booking.ticketLabel || receipt.
|
||||
const statusColor = computed(() => receipt.value.booking.status === 'confirmed' ? 'success' : 'warning')
|
||||
const sharedSeats = computed(() => receipt.value.seats.filter((seat) => Boolean(seat.sharedAt)))
|
||||
const availableSeats = computed(() => receipt.value.seats.filter((seat) => !seat.sharedAt))
|
||||
|
||||
useSeoMeta({
|
||||
title: () => `${t('receipt.badge')} - ${eventDetails.value.title}`,
|
||||
description: () => `${receipt.value.booking.customerName} · ${ticketLabel.value} · ${receipt.value.booking.seatCount} ${t('common.seats')}`,
|
||||
ogTitle: () => `${t('receipt.badge')} - ${eventDetails.value.title}`,
|
||||
ogDescription: () => `${receipt.value.booking.customerName} · ${ticketLabel.value}`,
|
||||
robots: 'noindex,nofollow'
|
||||
})
|
||||
|
||||
const maxShareCount = computed(() => Math.max(availableSeats.value.length, 1))
|
||||
const normalizedShareCount = computed(() => {
|
||||
return Math.max(1, Math.min(Math.trunc(Number(shareForm.count) || 1), maxShareCount.value))
|
||||
|
||||
@@ -29,6 +29,14 @@ const receipt = ref(initialReceipt)
|
||||
const eventDetails = computed(() => receipt.value.booking.event)
|
||||
const ticketLabel = computed(() => receipt.value.booking.ticketLabel || receipt.value.booking.ticketType.toUpperCase())
|
||||
const totalFormatted = computed(() => formatBookingCurrency(receipt.value.booking.totalPrice))
|
||||
|
||||
useSeoMeta({
|
||||
title: () => `${getSeatLabel(receipt.value.seat.seatNumber)} - ${eventDetails.value.title}`,
|
||||
description: () => `${receipt.value.seat.recipientName || receipt.value.booking.customerName} · ${ticketLabel.value}`,
|
||||
ogTitle: () => `${getSeatLabel(receipt.value.seat.seatNumber)} - ${eventDetails.value.title}`,
|
||||
ogDescription: () => `${receipt.value.seat.recipientName || receipt.value.booking.customerName} · ${ticketLabel.value}`,
|
||||
robots: 'noindex,nofollow'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -114,6 +114,12 @@ definePageMeta({
|
||||
middleware: 'auth'
|
||||
})
|
||||
|
||||
useSeoMeta({
|
||||
title: 'Security Settings',
|
||||
description: 'Manage password and passkey settings for the dinner ticket system.',
|
||||
robots: 'noindex,nofollow'
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
const router = useRouter()
|
||||
const auth = useAuth()
|
||||
|
||||
@@ -6,6 +6,21 @@ export default defineNuxtConfig({
|
||||
devtools: { enabled: true },
|
||||
modules: ['@nuxt/ui'],
|
||||
css: ['~/assets/css/main.css'],
|
||||
app: {
|
||||
head: {
|
||||
title: 'Dinner Ticket System',
|
||||
titleTemplate: (titleChunk) => titleChunk && titleChunk !== 'Dinner Ticket System'
|
||||
? `${titleChunk} · Dinner Ticket System`
|
||||
: 'Dinner Ticket System',
|
||||
meta: [
|
||||
{ name: 'description', content: 'Dinner ticket booking, confirmation, receipt, and seat sharing system.' },
|
||||
{ property: 'og:site_name', content: 'Dinner Ticket System' },
|
||||
{ property: 'og:type', content: 'website' },
|
||||
{ name: 'twitter:card', content: 'summary' },
|
||||
{ name: 'theme-color', content: '#dc2626' }
|
||||
]
|
||||
}
|
||||
},
|
||||
runtimeConfig: {
|
||||
databaseUrl: '',
|
||||
redisUrl: '',
|
||||
|
||||
Reference in New Issue
Block a user