remove rocket.new leaderboard banner; re-add admin nav guard

- src/app/home-page/components/LeaderboardAd.tsx: drop the hardcoded
  img.rocket.new image (rocket_gen_img_1bebfd3c7) at the top of the
  homepage. Replaced with <AdSlot zone='homepage-top' size='728x90' />
  to keep the slot for the eventual ad-server wiring while removing the
  rocket.new dependency entirely.
- src/components/Header.tsx: re-add the isAdmin gate that was reverted
  in 0d51dad. Reads bb.user_profiles.role and only renders the desktop
  admin nav block (WP Import, Articles, Editorial, Users, Analytics,
  Audit Log, Notifications, Newsletter, Forum Seed, Company Tracker,
  Show Calendar, AI Console, Banned Terms, Dashboard) and the mobile WP
  Import link when role is in (administrator, admin, super_admin,
  editor, support). 'support' is admin-tier per the new RBAC matrix.
  Page-level auth checks on each /admin/* route remain authoritative;
  this only suppresses menu visibility for non-admin/anonymous.
This commit is contained in:
Ryan Salazar
2026-05-08 15:03:16 +00:00
parent f1e1d5ea76
commit 7c9d2c8c79
2 changed files with 79 additions and 67 deletions

View File

@@ -1,22 +1,12 @@
import React from "react"; import React from "react";
import AppImage from "@/components/ui/AppImage"; import AdSlot from "@/components/AdSlot";
// DO NOT OVERRIDE — Leaderboard ad placeholder layout
export default function LeaderboardAd() { export default function LeaderboardAd() {
return ( return (
<div className="bg-ice-blue border-b border-border py-3"> <div className="bg-ice-blue border-b border-border py-3">
<div className="max-w-container mx-auto px-4 flex justify-center"> <div className="max-w-container mx-auto px-4 flex justify-center">
{/* Leaderboard ad unit — 728x90 — stub links to advertise page */} <AdSlot zone="homepage-top" size="728x90" />
<a href="/advertise" aria-label="Advertisement — click to learn about advertising on BroadcastBeat" className="block">
<AppImage
src="https://img.rocket.new/generatedImages/rocket_gen_img_1bebfd3c7-1765090260606.png"
alt="BroadcastBeat sponsor advertisement banner 728x90"
width={728}
height={90}
className="max-w-full h-auto" />
</a>
</div> </div>
</div>); </div>
);
} }

View File

@@ -48,6 +48,7 @@ export default function Header() {
const [searchFocused, setSearchFocused] = useState(false); const [searchFocused, setSearchFocused] = useState(false);
const [currentUserId, setCurrentUserId] = useState<string | null>(null); const [currentUserId, setCurrentUserId] = useState<string | null>(null);
const [currentUserEmail, setCurrentUserEmail] = useState<string | null>(null); const [currentUserEmail, setCurrentUserEmail] = useState<string | null>(null);
const [isAdmin, setIsAdmin] = useState(false);
const [subEmail, setSubEmail] = useState(""); const [subEmail, setSubEmail] = useState("");
const [subStatus, setSubStatus] = useState<"idle" | "loading" | "success" | "error">("idle"); const [subStatus, setSubStatus] = useState<"idle" | "loading" | "success" | "error">("idle");
const [subMessage, setSubMessage] = useState(""); const [subMessage, setSubMessage] = useState("");
@@ -65,14 +66,29 @@ export default function Header() {
useEffect(() => { useEffect(() => {
const supabase = createClient(); const supabase = createClient();
const ADMIN_ROLES = new Set([
"administrator", "admin", "super_admin", "editor", "support",
]);
const refreshAdmin = async (userId: string | null) => {
if (!userId) { setIsAdmin(false); return; }
const { data } = await supabase
.from("user_profiles")
.select("role")
.eq("id", userId)
.maybeSingle();
setIsAdmin(!!data?.role && ADMIN_ROLES.has(data.role));
};
supabase.auth.getUser().then(({ data: { user } }) => { supabase.auth.getUser().then(({ data: { user } }) => {
setCurrentUserId(user?.id || null); setCurrentUserId(user?.id || null);
setCurrentUserEmail(user?.email || null); setCurrentUserEmail(user?.email || null);
refreshAdmin(user?.id || null);
}); });
const supabase2 = createClient(); const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
const { data: { subscription } } = supabase2.auth.onAuthStateChange((_event, session) => {
setCurrentUserId(session?.user?.id || null); setCurrentUserId(session?.user?.id || null);
setCurrentUserEmail(session?.user?.email || null); setCurrentUserEmail(session?.user?.email || null);
refreshAdmin(session?.user?.id || null);
}); });
return () => subscription.unsubscribe(); return () => subscription.unsubscribe();
}, []); }, []);
@@ -237,48 +253,52 @@ export default function Header() {
My Profile My Profile
</Link> </Link>
)} )}
<Link href="/admin/import" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> {isAdmin && (
WP Import <>
</Link> <Link href="/admin/import" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
<Link href="/admin/articles" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> WP Import
Articles </Link>
</Link> <Link href="/admin/articles" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
<Link href="/dashboard/editorial" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> Articles
Editorial </Link>
</Link> <Link href="/dashboard/editorial" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
<Link href="/admin/users" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> Editorial
Users </Link>
</Link> <Link href="/admin/users" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
<Link href="/admin/analytics" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> Users
Analytics </Link>
</Link> <Link href="/admin/analytics" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
<Link href="/admin/audit-log" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> Analytics
Audit Log </Link>
</Link> <Link href="/admin/audit-log" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
<Link href="/admin/notifications" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> Audit Log
Notifications </Link>
</Link> <Link href="/admin/notifications" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
<Link href="/admin/newsletter" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> Notifications
Newsletter </Link>
</Link> <Link href="/admin/newsletter" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
<Link href="/admin/forum-seed" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> Newsletter
Forum Seed </Link>
</Link> <Link href="/admin/forum-seed" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
<Link href="/admin/company-tracker" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> Forum Seed
Company Tracker </Link>
</Link> <Link href="/admin/company-tracker" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
<Link href="/dashboard/show-calendar" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> Company Tracker
Show Calendar </Link>
</Link> <Link href="/dashboard/show-calendar" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
<Link href="/dashboard/ai-console" className="text-[#3b82f6] hover:text-blue-300 focus:text-blue-300 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> Show Calendar
AI Console </Link>
</Link> <Link href="/dashboard/ai-console" className="text-[#3b82f6] hover:text-blue-300 focus:text-blue-300 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
<Link href="/admin/banned-terms" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> AI Console
Banned Terms </Link>
</Link> <Link href="/admin/banned-terms" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
<Link href="/admin" className="text-[#3b82f6] hover:text-blue-300 focus:text-blue-300 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"> Banned Terms
Dashboard </Link>
</Link> <Link href="/admin" className="text-[#3b82f6] hover:text-blue-300 focus:text-blue-300 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
Dashboard
</Link>
</>
)}
{/* Inline Email Subscription Form */} {/* Inline Email Subscription Form */}
{subStatus === "success" ? ( {subStatus === "success" ? (
<span className="text-[#3b82f6] text-xs font-body font-bold whitespace-nowrap">{subMessage}</span> <span className="text-[#3b82f6] text-xs font-body font-bold whitespace-nowrap">{subMessage}</span>
@@ -448,15 +468,17 @@ export default function Header() {
Account Account
</Link> </Link>
{/* WP Import mobile link */} {/* WP Import mobile link */}
<Link {isAdmin && (
href="/admin/import" <Link
className="px-2 py-2.5 text-sm font-body font-bold uppercase tracking-wide text-[#cccccc] hover:text-[#3b82f6] hover:bg-[#1a1a1a] border-b border-[#222] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#3b82f6] flex items-center gap-2" href="/admin/import"
onClick={() => setMobileOpen(false)}> className="px-2 py-2.5 text-sm font-body font-bold uppercase tracking-wide text-[#cccccc] hover:text-[#3b82f6] hover:bg-[#1a1a1a] border-b border-[#222] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#3b82f6] flex items-center gap-2"
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true"> onClick={() => setMobileOpen(false)}>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><polyline points="7 10 12 15 17 10" /><line x1="12" y1="15" x2="12" y2="3" /> <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
</svg> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><polyline points="7 10 12 15 17 10" /><line x1="12" y1="15" x2="12" y2="3" />
WP Import </svg>
</Link> WP Import
</Link>
)}
</div> </div>
<div className="flex flex-col gap-0 border-t border-[#252525] mt-1"> <div className="flex flex-col gap-0 border-t border-[#252525] mt-1">
{categoryLinks?.map((link) => ( {categoryLinks?.map((link) => (