"use client"; import { useEffect, useState, useRef } from "react"; import { fetchSettingByCode } from "@/api/serversetting"; import { X, Eclipse } from "lucide-react"; import { ChatInputBox } from "@/components/ChatInputBox/ChatInputBox"; export const HOME_BANNER_CODE = "homeBanner"; /** CTA config for banner */ export interface HomeBannerCTAConfig { label?: string; href?: string; [key: string]: unknown; } /** Configuration payload for `HomeBanner` */ export interface HomeBannerConfig { eyebrow?: string; title?: string; subtitle?: string; description?: string; backgroundImage?: string; cta?: HomeBannerCTAConfig; ctaText?: string; ctaLink?: string; [key: string]: unknown; } /** * Renders the home banner with optional background image and CTA. * Pulls configuration from server settings keyed by `HOME_BANNER_CODE`. * @returns React.ReactElement | null */ export default function HomeBanner() { const [config, setConfig] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [isFlying, setIsFlying] = useState(false); const autoCollapseTimerRef = useRef | null>(null); const handleDismiss = () => { setIsFlying(true); }; const handleBannerClick = () => { if (isFlying) { setIsFlying(false); } }; // Auto collapse after mount (3s) useEffect(() => { if (autoCollapseTimerRef.current) return; autoCollapseTimerRef.current = setTimeout(() => { setIsFlying(true); }, 3000); return () => { if (autoCollapseTimerRef.current) { clearTimeout(autoCollapseTimerRef.current); autoCollapseTimerRef.current = null; } }; }, []); useEffect(() => { let active = true; async function load() { setLoading(true); setError(null); try { const value = await fetchSettingByCode(HOME_BANNER_CODE); if (!active) return; setConfig(value ?? null); } catch (err) { if (!active) return; const message = err instanceof Error ? err.message : "Failed to load home banner"; setError(message); setConfig(null); } finally { if (active) { setLoading(false); } } } load(); return () => { active = false; }; }, []); if (loading || error || !config) { // Render nothing until configuration schema is finalized. return null; } const title = typeof config.title === "string" ? config.title : undefined; const subtitle = typeof config.subtitle === "string" ? config.subtitle : undefined; const description = typeof config.description === "string" ? config.description : undefined; const eyebrow = typeof config.eyebrow === "string" ? config.eyebrow : undefined; const backgroundImage = typeof config.backgroundImage === "string" ? config.backgroundImage : undefined; let ctaLabel: string | undefined; let ctaHref: string | undefined; if (config.cta && typeof config.cta === "object") { const { label, href } = config.cta as Record; if (typeof label === "string") ctaLabel = label; if (typeof href === "string") ctaHref = href; } // Support legacy field names until the API contract is confirmed. if (!ctaLabel && typeof config.ctaText === "string") { ctaLabel = config.ctaText; } if (!ctaHref && typeof config.ctaLink === "string") { ctaHref = config.ctaLink; } const hasContent = Boolean(title || subtitle || description || ctaLabel || backgroundImage); if (!hasContent) { return null; } return (
{/* Banner overlay - stacked above */}
{backgroundImage ? ( ) : null} {backgroundImage ? (
) : null}
{/* Dismiss button */}
{isFlying ? ( ) : null} {eyebrow ? ( {eyebrow} ) : null} {title ? (

{title}

) : null} {subtitle ? (

{subtitle}

) : null} {description ? (

{description}

) : null} {ctaLabel ? ( ctaHref ? ( {ctaLabel} ) : ( ) ) : null}
{/* Base content - always present under the banner */}
); }