Add admin-configurable branding (colors, logo, favicon) and generic default fallbacks
Site Settings -> Branding now supports a Primary/Secondary/Accent brand color system applied site-wide (buttons, nav, hover states, links) and to outgoing email header/CTA colors, plus a favicon upload alongside the existing logo upload, a live preview panel (website/email x desktop/mobile), and logo-based color suggestions. The setup wizard's Branding step got the same treatment. Fixes two related bugs found along the way: the setup wizard's logo/favicon upload was missing its auth token, and a static favicon.ico in Next's special app/ convention path was silently overriding the dynamic one. Also replaces every "Hope Events"/"Hope Family Church" default (org name, email subjects, WhatsApp messages, report metadata, API docs) with a neutral "Cross Code" placeholder, and the optional legal settings (operator name, IO details, website URL, effective date) with obviously-generic placeholders instead of defaulting to real personal/organisational details -- since this platform is deployed for multiple organisations. Adds SETTINGS.md documenting every setting's default behaviour. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,8 +9,14 @@ export type SiteSettings = {
|
||||
org_email?: string;
|
||||
org_phone?: string;
|
||||
org_address?: string;
|
||||
// Branding — 3-color system. accent_color is the tertiary/highlight color;
|
||||
// primary_color falls back to accent_color's legacy value if unset (see
|
||||
// settingsController.js PUBLIC_KEYS comment).
|
||||
primary_color?: string;
|
||||
secondary_color?: string;
|
||||
accent_color?: string;
|
||||
logo_url?: string;
|
||||
favicon_url?: string;
|
||||
setup_complete?: string;
|
||||
// Legal pages
|
||||
legal_operator_name?: string;
|
||||
@@ -32,9 +38,15 @@ const SiteSettingsContext = createContext<SiteSettingsContextValue>({
|
||||
reload: () => {},
|
||||
});
|
||||
|
||||
export function SiteSettingsProvider({ children }: { children: React.ReactNode }) {
|
||||
const [settings, setSettings] = useState<SiteSettings>({});
|
||||
const [loading, setLoading] = useState(true);
|
||||
export function SiteSettingsProvider({
|
||||
children, initialSettings,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
/** Server-fetched settings from layout.tsx's generateMetadata/RootLayout — seeds state so there's no loading flash for anything reading useSiteSettings() (e.g. the navbar logo). */
|
||||
initialSettings?: SiteSettings;
|
||||
}) {
|
||||
const [settings, setSettings] = useState<SiteSettings>(initialSettings || {});
|
||||
const [loading, setLoading] = useState(!initialSettings);
|
||||
|
||||
const load = async () => {
|
||||
try {
|
||||
@@ -47,6 +59,9 @@ export function SiteSettingsProvider({ children }: { children: React.ReactNode }
|
||||
}
|
||||
};
|
||||
|
||||
// Always refreshes in the background (cheap, 60s-cached server-side) even
|
||||
// when seeded from initialSettings — self-heals if the SSR fetch in
|
||||
// layout.tsx failed, and picks up any change since that request.
|
||||
useEffect(() => { load(); }, []);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user