feat: add footer configuration (address, phone, email, social URLs, tagline) to admin panel

This commit is contained in:
2026-06-28 23:21:41 -05:00
parent abf46b9075
commit a1d88af78b
3 changed files with 79 additions and 2 deletions
+76
View File
@@ -1294,6 +1294,28 @@ function BrandSection({ images, onToast }: { images: Image[]; onToast: (m: strin
facebook_url: '', instagram_url: '', whatsapp_url: '', facebook_url: '', instagram_url: '', whatsapp_url: '',
address: '', phone: '', email: '', address: '', phone: '', email: '',
}); });
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
useEffect(() => {
loadConfig();
}, []);
const loadConfig = async () => {
try {
const res = await fetch('/api/site-assets');
if (res.ok) {
const data = await res.json();
if (data.data) {
setConfig(prev => ({ ...prev, ...data.data }));
}
}
} catch (err) {
console.error('Failed to load config:', err);
} finally {
setLoading(false);
}
};
const handleUpload = async (key: keyof FooterConfig) => { const handleUpload = async (key: keyof FooterConfig) => {
const input = document.createElement('input'); const input = document.createElement('input');
@@ -1318,6 +1340,28 @@ function BrandSection({ images, onToast }: { images: Image[]; onToast: (m: strin
input.click(); input.click();
}; };
const saveConfig = async () => {
setSaving(true);
try {
const res = await fetch('/api/site-assets', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ values: config }),
});
if (res.ok) {
onToast('Configuration saved!', 'success');
} else {
onToast('Failed to save', 'error');
}
} catch {
onToast('Failed to save', 'error');
} finally {
setSaving(false);
}
};
if (loading) return <div style={{ padding: 20, color: C.textLight }}>Loading...</div>;
return ( return (
<div> <div>
<SecHead title="🎨 Brand & Identity" /> <SecHead title="🎨 Brand & Identity" />
@@ -1340,6 +1384,38 @@ function BrandSection({ images, onToast }: { images: Image[]; onToast: (m: strin
<I value={config.tagline} onChange={(v: string) => setConfig(prev => ({ ...prev, tagline: v }))} placeholder="Your hotel tagline" /> <I value={config.tagline} onChange={(v: string) => setConfig(prev => ({ ...prev, tagline: v }))} placeholder="Your hotel tagline" />
</div> </div>
</div> </div>
<SecHead title="📞 Contact & Social" />
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: '16px', marginBottom: 20 }}>
<div style={{ background: C.card, borderRadius: 16, padding: '16px', boxShadow: C.shadow, border: `1px solid ${C.border}` }}>
<div style={{ fontSize: '13px', fontWeight: 600, color: C.text, marginBottom: '8px' }}>📍 Address</div>
<I value={config.address} onChange={(v: string) => setConfig(prev => ({ ...prev, address: v }))} placeholder="Full address" />
</div>
<div style={{ background: C.card, borderRadius: 16, padding: '16px', boxShadow: C.shadow, border: `1px solid ${C.border}` }}>
<div style={{ fontSize: '13px', fontWeight: 600, color: C.text, marginBottom: '8px' }}>📞 Phone</div>
<I value={config.phone} onChange={(v: string) => setConfig(prev => ({ ...prev, phone: v }))} placeholder="+593 99 999 9999" />
</div>
<div style={{ background: C.card, borderRadius: 16, padding: '16px', boxShadow: C.shadow, border: `1px solid ${C.border}` }}>
<div style={{ fontSize: '13px', fontWeight: 600, color: C.text, marginBottom: '8px' }}> Email</div>
<I value={config.email} onChange={(v: string) => setConfig(prev => ({ ...prev, email: v }))} placeholder="contact@lahuasca.com" />
</div>
<div style={{ background: C.card, borderRadius: 16, padding: '16px', boxShadow: C.shadow, border: `1px solid ${C.border}` }}>
<div style={{ fontSize: '13px', fontWeight: 600, color: C.text, marginBottom: '8px' }}>📘 Facebook URL</div>
<I value={config.facebook_url} onChange={(v: string) => setConfig(prev => ({ ...prev, facebook_url: v }))} placeholder="https://facebook.com/..." />
</div>
<div style={{ background: C.card, borderRadius: 16, padding: '16px', boxShadow: C.shadow, border: `1px solid ${C.border}` }}>
<div style={{ fontSize: '13px', fontWeight: 600, color: C.text, marginBottom: '8px' }}>📷 Instagram URL</div>
<I value={config.instagram_url} onChange={(v: string) => setConfig(prev => ({ ...prev, instagram_url: v }))} placeholder="https://instagram.com/..." />
</div>
<div style={{ background: C.card, borderRadius: 16, padding: '16px', boxShadow: C.shadow, border: `1px solid ${C.border}` }}>
<div style={{ fontSize: '13px', fontWeight: 600, color: C.text, marginBottom: '8px' }}>💬 WhatsApp URL</div>
<I value={config.whatsapp_url} onChange={(v: string) => setConfig(prev => ({ ...prev, whatsapp_url: v }))} placeholder="https://wa.me/..." />
</div>
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: '16px' }}>
<Btn onClick={saveConfig} disabled={saving}>{saving ? 'Saving...' : '💾 Save Configuration'}</Btn>
</div>
</div> </div>
); );
} }
+1 -1
View File
@@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from 'next/server';
import { requireRole } from '@/lib/auth'; import { requireRole } from '@/lib/auth';
import { db } from '@/lib/db'; import { db } from '@/lib/db';
const PUBLIC_KEYS = ['logo','favicon','facebook_url','instagram_url','whatsapp_url','address','phone','email','wifi_password']; const PUBLIC_KEYS = ['logo','favicon','tagline','facebook_url','instagram_url','whatsapp_url','address','phone','email','wifi_password'];
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
try { try {
+2 -1
View File
@@ -4,6 +4,7 @@ import { useEffect, useState } from 'react';
import { FacebookIcon, InstagramIcon, WhatsAppIcon } from './icons'; import { FacebookIcon, InstagramIcon, WhatsAppIcon } from './icons';
interface Config { interface Config {
tagline?: string;
facebook_url?: string; facebook_url?: string;
instagram_url?: string; instagram_url?: string;
whatsapp_url?: string; whatsapp_url?: string;
@@ -47,7 +48,7 @@ export default function Footer() {
<div> <div>
<h3 style={{ color: gold, fontSize: '18px', margin: '0 0 1rem', fontStyle: 'italic' }}>La Huasca</h3> <h3 style={{ color: gold, fontSize: '18px', margin: '0 0 1rem', fontStyle: 'italic' }}>La Huasca</h3>
<p style={{ color: 'rgba(245,240,232,0.75)', lineHeight: 1.55, margin: 0 }}> <p style={{ color: 'rgba(245,240,232,0.75)', lineHeight: 1.55, margin: 0 }}>
A quiet corner of the Ecuadorian Andes. {config.tagline || 'A quiet corner of the Ecuadorian Andes.'}
</p> </p>
</div> </div>