Files
hope-events/frontend/src/components/layout/BottomNav.tsx
T
joshuaandClaude Sonnet 5 86af7093ac 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>
2026-08-20 14:49:51 +02:00

89 lines
3.5 KiB
TypeScript

"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useAuth } from "@/hooks/useAuth";
import { Home, Calendar, Phone, LogIn, UserPlus, LayoutDashboard, LogOut } from "lucide-react";
import { useBrandColorVar } from "@/lib/theme";
export const BottomNav = () => {
const { user, loading: authLoading, logout } = useAuth();
const pathname = usePathname();
// Raw SVG needs a literal hex value — reads the live admin-configurable
// primary color off the CSS custom property (see lib/theme.ts).
const accentColor = useBrandColorVar("--brand-600");
const handleLogout = () => {
logout();
window.location.href = "/";
};
const isActive = (href: string) => {
if (href.includes("#")) return false;
if (href === "/") return pathname === "/";
return pathname.startsWith(href);
};
type NavItem = { href: string; label: string; icon: React.ElementType; onClick?: () => void };
// Auth items are only known once the token has been validated — omitting
// them entirely while loading avoids a Login/Register -> Dashboard/Logout
// flash on every load (see Navbar for the same pattern).
const items: NavItem[] = [
{ href: "/", label: "Home", icon: Home },
{ href: "/events", label: "Events", icon: Calendar },
{ href: "/contact", label: "Contact", icon: Phone },
...(authLoading
? []
: user
? [
{ href: "/dashboard", label: "Dashboard", icon: LayoutDashboard },
{ href: "#", label: "Logout", icon: LogOut, onClick: handleLogout },
]
: [
{ href: "/login", label: "Login", icon: LogIn },
{ href: "/register", label: "Register", icon: UserPlus },
]),
];
return (
<nav
className="md:hidden fixed bottom-0 inset-x-0 z-50 bg-white border-t border-gray-200"
style={{ paddingBottom: "env(safe-area-inset-bottom)" }}
>
<div className="flex items-stretch h-16">
{items.map((item) => {
const Icon = item.icon;
const active = isActive(item.href);
const color = active ? accentColor : "#9ca3af";
if (item.onClick) {
return (
<button
key={item.label}
onClick={item.onClick}
className="flex-1 flex flex-col items-center justify-center gap-0.5 text-[10px] font-medium"
style={{ color }}
>
<Icon size={22} strokeWidth={1.8} />
<span>{item.label}</span>
</button>
);
}
return (
<Link
key={item.href}
href={item.href}
className="flex-1 flex flex-col items-center justify-center gap-0.5 text-[10px] font-medium"
style={{ color }}
>
<Icon size={22} strokeWidth={1.8} />
<span>{item.label}</span>
</Link>
);
})}
</div>
</nav>
);
};