fix: replace hardcoded site name with dynamic settings

- Add public /api/settings endpoint (force-dynamic, no auth) exposing
  site_name, site_description, footer_copyright, footer_links
- Navbar, login, register pages fetch site_name via useEffect
- Homepage hero and footer read site_name and site_description from DB
- Fix admin settings form silently ignoring API errors on save
This commit is contained in:
2026-05-19 11:30:05 +02:00
parent 9797519e5c
commit ea5fca6561
6 changed files with 68 additions and 11 deletions
+8 -1
View File
@@ -52,12 +52,19 @@ export default function AdminSettingsPage() {
e.preventDefault()
setSaving(true)
setError('')
setMessage('')
for (const key of ALL_KEYS) {
await fetch('/api/admin/settings', {
const res = await fetch('/api/admin/settings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key, value: values[key] }),
})
if (!res.ok) {
const data = await res.json().catch(() => ({}))
setSaving(false)
setError(data.error || `Errore nel salvare "${key}" (${res.status})`)
return
}
}
setSaving(false)
setMessage('Impostazioni salvate!')
+14
View File
@@ -0,0 +1,14 @@
import { NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
export const dynamic = 'force-dynamic'
const PUBLIC_KEYS = ['site_name', 'site_description', 'footer_copyright', 'footer_links'] as const
export async function GET() {
const rows = await prisma.siteSettings.findMany({
where: { key: { in: [...PUBLIC_KEYS] } },
})
const settings = Object.fromEntries(rows.map((r) => [r.key, r.value]))
return NextResponse.json({ settings })
}
+10 -2
View File
@@ -1,6 +1,6 @@
'use client'
import { Suspense, useState } from 'react'
import { Suspense, useState, useEffect } from 'react'
import Link from 'next/link'
import { useRouter, useSearchParams } from 'next/navigation'
import { Input } from '@/components/ui/Input'
@@ -21,11 +21,19 @@ function LoginForm() {
const [password, setPassword] = useState('')
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const [siteName, setSiteName] = useState('ShopX')
const router = useRouter()
const searchParams = useSearchParams()
const redirect = searchParams.get('redirect') || '/'
const { refreshUser } = useUser()
useEffect(() => {
fetch('/api/settings')
.then((r) => r.json())
.then((data) => { if (data.settings?.site_name) setSiteName(data.settings.site_name as string) })
.catch(() => {})
}, [])
async function handleSubmit(e: React.FormEvent) {
e.preventDefault()
setError('')
@@ -66,7 +74,7 @@ function LoginForm() {
<div className="w-full max-w-md">
<div className="bg-white rounded-lg border border-gray-200 shadow-sm p-8">
<div className="text-center mb-8">
<Link href="/" className="text-2xl font-bold text-gray-900">ShopX</Link>
<Link href="/" className="text-2xl font-bold text-gray-900">{siteName}</Link>
<h1 className="text-xl font-semibold mt-4">Welcome back</h1>
<p className="text-gray-600 text-sm mt-1">Sign in to your account</p>
</div>
+13 -4
View File
@@ -14,8 +14,17 @@ async function getFeaturedProducts() {
})
}
async function getSiteSettings() {
const rows = await prisma.siteSettings.findMany({
where: { key: { in: ['site_name', 'site_description'] } },
})
return Object.fromEntries(rows.map((r) => [r.key, r.value as string]))
}
export default async function HomePage() {
const products = await getFeaturedProducts()
const [products, settings] = await Promise.all([getFeaturedProducts(), getSiteSettings()])
const siteName = settings.site_name || 'ShopX'
const siteDescription = settings.site_description || 'Discover our curated collection of products'
return (
<div>
@@ -24,9 +33,9 @@ export default async function HomePage() {
{/* Hero */}
<section className="bg-blue-600 text-white py-20">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<h1 className="text-4xl font-bold mb-4">Welcome to ShopX</h1>
<h1 className="text-4xl font-bold mb-4">Welcome to {siteName}</h1>
<p className="text-xl text-blue-100 mb-8">
Discover our curated collection of products
{siteDescription}
</p>
<Link
href="/products"
@@ -90,7 +99,7 @@ export default async function HomePage() {
<footer className="bg-gray-800 text-gray-300 py-8">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center text-sm">
<p>&copy; {new Date().getFullYear()} ShopX. All rights reserved.</p>
<p>&copy; {new Date().getFullYear()} {siteName}. All rights reserved.</p>
</div>
</footer>
</div>
+11 -3
View File
@@ -1,6 +1,6 @@
'use client'
import { useState } from 'react'
import { useState, useEffect } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { Input } from '@/components/ui/Input'
@@ -14,9 +14,17 @@ export default function RegisterPage() {
const [password, setPassword] = useState('')
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const [siteName, setSiteName] = useState('ShopX')
const router = useRouter()
const { refreshUser } = useUser()
useEffect(() => {
fetch('/api/settings')
.then((r) => r.json())
.then((data) => { if (data.settings?.site_name) setSiteName(data.settings.site_name as string) })
.catch(() => {})
}, [])
async function handleSubmit(e: React.FormEvent) {
e.preventDefault()
setError('')
@@ -50,9 +58,9 @@ export default function RegisterPage() {
<div className="w-full max-w-md">
<div className="bg-white rounded-lg border border-gray-200 shadow-sm p-8">
<div className="text-center mb-8">
<Link href="/" className="text-2xl font-bold text-gray-900">ShopX</Link>
<Link href="/" className="text-2xl font-bold text-gray-900">{siteName}</Link>
<h1 className="text-xl font-semibold mt-4">Create Account</h1>
<p className="text-gray-600 text-sm mt-1">Join ShopX today</p>
<p className="text-gray-600 text-sm mt-1">Join {siteName} today</p>
</div>
{error && <Alert variant="error" className="mb-4">{error}</Alert>}
+12 -1
View File
@@ -7,6 +7,7 @@ import { useUser } from '@/context/UserContext'
export function Navbar() {
const [cartCount, setCartCount] = useState(0)
const [siteName, setSiteName] = useState('ShopX')
const { user, refreshUser } = useUser()
const router = useRouter()
@@ -16,6 +17,16 @@ export function Navbar() {
setCartCount(count)
}, [])
useEffect(() => {
fetch('/api/settings')
.then((r) => r.json())
.then((data) => {
const name = data.settings?.site_name
if (name) setSiteName(name as string)
})
.catch(() => {})
}, [])
async function handleLogout() {
await fetch('/api/auth/logout', { method: 'POST' })
await refreshUser()
@@ -28,7 +39,7 @@ export function Navbar() {
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<Link href="/" className="text-xl font-bold text-gray-900">
ShopX
{siteName}
</Link>
<nav className="flex items-center gap-6 text-sm">