This commit is contained in:
Your Name committed 2026-09-29 13:46:02 +07:00
1 parent e87176dba3
commit 80e715da76
3 files changed
+59 -8

No files matched your search

+52 -3
View File
@@ -1,9 +1,12 @@
import { useEffect } from 'react' import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { API_BASE } from '@homaroma/types' import { API_BASE } from '@homaroma/types'
export default function ThemeInjector() { export default function ThemeInjector() {
const { i18n } = useTranslation()
useEffect(() => { useEffect(() => {
const lang = localStorage.getItem('app_lang') || 'en' const lang = i18n.language || localStorage.getItem('app_lang') || 'en'
fetch(`${API_BASE}/settings?lang=${lang}`) fetch(`${API_BASE}/settings?lang=${lang}`)
.then(res => res.ok ? res.json() : null) .then(res => res.ok ? res.json() : null)
@@ -11,6 +14,52 @@ export default function ThemeInjector() {
if (!settings) return if (!settings) return
const root = document.documentElement const root = document.documentElement
// --- Global SEO & Document Title ---
const pageTitle = settings.defaultSeoTitle || settings.brandName
if (pageTitle) {
document.title = pageTitle
}
if (settings.defaultSeoDescription) {
let metaDesc = document.querySelector('meta[name="description"]')
if (!metaDesc) {
metaDesc = document.createElement('meta')
metaDesc.setAttribute('name', 'description')
document.head.appendChild(metaDesc)
}
metaDesc.setAttribute('content', settings.defaultSeoDescription)
}
if (pageTitle) {
let ogTitle = document.querySelector('meta[property="og:title"]')
if (!ogTitle) {
ogTitle = document.createElement('meta')
ogTitle.setAttribute('property', 'og:title')
document.head.appendChild(ogTitle)
}
ogTitle.setAttribute('content', pageTitle)
}
if (settings.defaultSeoDescription) {
let ogDesc = document.querySelector('meta[property="og:description"]')
if (!ogDesc) {
ogDesc = document.createElement('meta')
ogDesc.setAttribute('property', 'og:description')
document.head.appendChild(ogDesc)
}
ogDesc.setAttribute('content', settings.defaultSeoDescription)
}
if (settings.defaultOgImage) {
let ogImg = document.querySelector('meta[property="og:image"]')
if (!ogImg) {
ogImg = document.createElement('meta')
ogImg.setAttribute('property', 'og:image')
document.head.appendChild(ogImg)
}
ogImg.setAttribute('content', settings.defaultOgImage)
}
// --- Theme tokens --- // --- Theme tokens ---
const tokens = settings?.themeTokens const tokens = settings?.themeTokens
if (tokens && typeof tokens === 'object') { if (tokens && typeof tokens === 'object') {
@@ -48,8 +97,8 @@ export default function ThemeInjector() {
root.style.setProperty('--scroll-reveal-style', style) root.style.setProperty('--scroll-reveal-style', style)
root.style.setProperty('--scroll-reveal-duration', `${duration}ms`) root.style.setProperty('--scroll-reveal-duration', `${duration}ms`)
}) })
.catch(err => console.error('Failed to inject theme in SPA:', err)) .catch(err => console.error('Failed to inject theme/SEO in SPA:', err))
}, []) }, [i18n.language])
return null return null
} }
+4 -3
View File
@@ -12,20 +12,21 @@ import type { CSSProperties } from 'react';
export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise<Metadata> { export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise<Metadata> {
const { lang } = await params; const { lang } = await params;
const settings = await getStoreSettings(lang); const settings = await getStoreSettings(lang);
const brandName = settings?.brandName || ''; const brandName = settings?.brandName || 'HomAroma';
const defaultTitle = settings?.defaultSeoTitle || brandName;
const description = settings?.defaultSeoDescription || settings?.footerDescription || 'Pure Origins, Artisan Craft, Zero Compromise'; const description = settings?.defaultSeoDescription || settings?.footerDescription || 'Pure Origins, Artisan Craft, Zero Compromise';
const ogImage = settings?.defaultOgImage; const ogImage = settings?.defaultOgImage;
return { return {
title: { title: {
template: `%s | ${brandName}`, template: `%s | ${brandName}`,
default: brandName, default: defaultTitle,
}, },
description, description,
metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'), metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'),
openGraph: ogImage openGraph: ogImage
? { ? {
title: brandName, title: defaultTitle,
description, description,
images: [ogImage], images: [ogImage],
} }
+3 -2
View File
@@ -15,10 +15,11 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
getStoreSettings(lang), getStoreSettings(lang),
]) ])
const title = page?.seoTitle || page?.title || undefined const title = page?.seoTitle || settings?.defaultSeoTitle || (page?.title && page.title !== 'Home' && page.title !== 'หน้าแรก' ? page.title : undefined) || settings?.brandName || undefined
const description = const description =
page?.seoDescription || page?.seoDescription ||
settings?.defaultSeoDescription || settings?.defaultSeoDescription ||
settings?.footerDescription ||
(settings?.brandName ? `${settings.brandName} - Pure Origins, Artisan Craft` : 'Pure Origins, Artisan Craft') (settings?.brandName ? `${settings.brandName} - Pure Origins, Artisan Craft` : 'Pure Origins, Artisan Craft')
const ogImage = page?.ogImageUrl || settings?.defaultOgImage const ogImage = page?.ogImageUrl || settings?.defaultOgImage
@@ -29,7 +30,7 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
lang, lang,
openGraph: ogImage openGraph: ogImage
? { ? {
title: page?.seoTitle || page?.title || settings?.brandName || undefined, title,
description, description,
images: [ogImage], images: [ogImage],
} }