Initial commit
Next.js + Express event management app for Hope Family Church.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { apiFetch } from "@/lib/api";
|
||||
|
||||
type BannerType = "info" | "warning" | "success" | "danger";
|
||||
|
||||
interface Banner {
|
||||
message: string;
|
||||
type: BannerType;
|
||||
liveFrom: string | null;
|
||||
liveTill: string | null;
|
||||
}
|
||||
|
||||
const STYLES: Record<BannerType, string> = {
|
||||
info: "bg-blue-600 text-white",
|
||||
warning: "bg-amber-400 text-amber-950",
|
||||
success: "bg-emerald-600 text-white",
|
||||
danger: "bg-red-600 text-white",
|
||||
};
|
||||
|
||||
function dismissKey(banner: Banner) {
|
||||
return `banner_dismissed_${banner.message}_${banner.liveFrom}_${banner.liveTill}`;
|
||||
}
|
||||
|
||||
export function BannerBar() {
|
||||
const [banner, setBanner] = useState<Banner | null>(null);
|
||||
const [dismissed, setDismissed] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
apiFetch<Banner>("/api/banner")
|
||||
.then(b => {
|
||||
if (!b?.message) return;
|
||||
setBanner(b);
|
||||
const key = dismissKey(b);
|
||||
if (typeof window !== "undefined" && localStorage.getItem(key) === "1") {
|
||||
setDismissed(true);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
if (!banner || !banner.message || dismissed) return null;
|
||||
|
||||
const now = Date.now();
|
||||
const from = banner.liveFrom ? new Date(banner.liveFrom).getTime() : null;
|
||||
const till = banner.liveTill ? new Date(banner.liveTill).getTime() : null;
|
||||
|
||||
if (from !== null && now < from) return null;
|
||||
if (till !== null && now > till) return null;
|
||||
|
||||
const dismiss = () => {
|
||||
if (typeof window !== "undefined") {
|
||||
localStorage.setItem(dismissKey(banner), "1");
|
||||
}
|
||||
setDismissed(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`w-full px-4 py-2.5 flex items-center justify-center gap-3 text-sm font-medium ${STYLES[banner.type ?? "info"]}`}>
|
||||
<span className="flex-1 text-center">{banner.message}</span>
|
||||
<button
|
||||
onClick={dismiss}
|
||||
aria-label="Dismiss banner"
|
||||
className="shrink-0 opacity-70 hover:opacity-100 transition-opacity text-lg leading-none"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user