"use client"; export const dynamic = "force-dynamic"; import { useState, useEffect, useRef, useCallback } from "react"; import Link from "next/link"; import { ArrowLeft, Settings as SettingsIcon, Loader2, Check, Upload, Image as ImageIcon, Type, Share2, Sparkles, Info, } from "lucide-react"; import { getAllSettingsForEditor, saveBranding, saveFooter, saveSocial, } from "./actions"; import { DEFAULT_BRANDING, DEFAULT_FOOTER, DEFAULT_SOCIAL, type BrandingSettings, type FooterSettings, type SocialSettings, } from "@/lib/siteSettingsTypes"; type Tab = "branding" | "footer" | "social"; export default function SiteSettingsPage() { const [tab, setTab] = useState("branding"); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [savedFlash, setSavedFlash] = useState(null); const [branding, setBranding] = useState(DEFAULT_BRANDING); const [footer, setFooter] = useState(DEFAULT_FOOTER); const [social, setSocial] = useState(DEFAULT_SOCIAL); const [autoTranslateFooter, setAutoTranslateFooter] = useState(false); const flash = (id: string) => { setSavedFlash(id); setTimeout(() => setSavedFlash(null), 1500); }; const load = useCallback(async () => { setLoading(true); const res = await getAllSettingsForEditor(); if (res.success) { if (res.branding) setBranding(res.branding); if (res.footer) setFooter(res.footer); if (res.social) setSocial(res.social); } setLoading(false); }, []); useEffect(() => { load(); }, [load]); const onSaveBranding = async () => { setSaving(true); await saveBranding(branding); setSaving(false); flash("branding"); }; const onSaveFooter = async () => { setSaving(true); await saveFooter(footer, autoTranslateFooter); setSaving(false); flash("footer"); }; const onSaveSocial = async () => { setSaving(true); await saveSocial(social); setSaving(false); flash("social"); }; return (
Back to Dashboard
Site Settings

Branding & Global Content.

Favicon, logo, footer, social links — applies site-wide across all locales.

{/* Tabs */}
{[ { id: "branding", label: "Branding", icon: ImageIcon }, { id: "footer", label: "Footer", icon: Type }, { id: "social", label: "Social", icon: Share2 }, ].map((t) => ( ))}
{loading ? (
Loading settings…
) : tab === "branding" ? ( ) : tab === "footer" ? ( ) : ( )}
); } // ─── Branding tab ──────────────────────────────────────────────── function BrandingTab({ value, onChange, onSave, saving, justSaved, }: { value: BrandingSettings; onChange: (v: BrandingSettings) => void; onSave: () => void; saving: boolean; justSaved: boolean; }) { return (
Upload images to set the site favicon and logo. Recommended sizes are shown next to each field. Changes appear on the live site within 60 seconds, no rebuild needed. onChange({ ...value, faviconUrl: url })} /> onChange({ ...value, appleTouchIconUrl: url })} /> onChange({ ...value, logoUrl: url })} /> onChange({ ...value, logoEmailUrl: url })} /> onChange({ ...value, ogImageUrl: url })} />
onChange({ ...value, themeColor: e.target.value })} className="w-14 h-10 rounded-lg cursor-pointer bg-transparent" /> onChange({ ...value, themeColor: e.target.value })} className="bg-black/40 border border-white/10 text-white text-sm rounded-lg px-3 py-2 outline-none focus:border-[#00F0FF]/40 font-mono" /> Used for browser address bar tint on iOS / Android
); } // ─── Footer tab ────────────────────────────────────────────────── function FooterTab({ value, onChange, autoTranslate, onAutoTranslateChange, onSave, saving, justSaved, }: { value: FooterSettings; onChange: (v: FooterSettings) => void; autoTranslate: boolean; onAutoTranslateChange: (v: boolean) => void; onSave: () => void; saving: boolean; justSaved: boolean; }) { return (
Footer text appears at the bottom of every page. Auto-translate sends the text to AI for IT, VEC, ES, DE versions. onChange({ ...value, ctaTitle1: v })} /> onChange({ ...value, ctaTitle2: v })} /> onChange({ ...value, ctaSubtitle: v })} multiline /> onChange({ ...value, hqAddress: v })} /> onChange({ ...value, hqCity: v })} /> onChange({ ...value, hqRegion: v })} /> onChange({ ...value, hqCountry: v })} /> onChange({ ...value, copyrightHolder: v })} /> onChange({ ...value, privacyUrl: v })} /> onChange({ ...value, termsUrl: v })} />
); } // ─── Social tab ────────────────────────────────────────────────── function SocialTab({ value, onChange, onSave, saving, justSaved, }: { value: SocialSettings; onChange: (v: SocialSettings) => void; onSave: () => void; saving: boolean; justSaved: boolean; }) { return (
Add the URL of each profile. Leave blank to hide. Email is shown as a mailto link. onChange({ ...value, linkedin: v })} placeholder="https://linkedin.com/company/flux-srl" /> onChange({ ...value, instagram: v })} placeholder="https://instagram.com/..." /> onChange({ ...value, youtube: v })} placeholder="https://youtube.com/@..." /> onChange({ ...value, email: v })} placeholder="info@rf-flux.com" />
); } // ─── Reusable bits ────────────────────────────────────────────── function Tip({ children }: { children: React.ReactNode }) { return (
{children}
); } function FieldGroup({ title, children }: { title: string; children: React.ReactNode }) { return (
{title}
{children}
); } function TextField({ label, value, onChange, placeholder, multiline, }: { label: string; value: string; onChange: (v: string) => void; placeholder?: string; multiline?: boolean; }) { return (
{multiline ? (