Skip to content

Commit cf42a8a

Browse files
committed
feat: add and integrate a new announcement banner component into the main layout.
1 parent fa2fe37 commit cf42a8a

2 files changed

Lines changed: 81 additions & 0 deletions

File tree

app/(main)/layout.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useState, useEffect } from "react";
44
import { Header } from "@/components/layout/header";
55
import { Sidebar } from "@/components/layout/sidebar";
6+
import { AnnouncementBanner } from "@/components/AnnouncementBanner";
67
import { cn, parseMajor } from "@/lib/utils";
78

89
interface LayoutProps {
@@ -108,6 +109,13 @@ const Layout = ({ children }: LayoutProps) => {
108109
isCollapsed ? "sm:pl-20" : "sm:pl-72"
109110
)}
110111
>
112+
<AnnouncementBanner
113+
message="Got questions about internships or placements?"
114+
linkText="Book a free 1:1 guidance call"
115+
linkHref="https://parthsali.com/#book-call"
116+
showAlways={true} // Set to true to show every time page loads
117+
118+
/>
111119
<Header isCollapsed={isCollapsed} setIsCollapsed={setIsCollapsed} />
112120
<main className="flex-1 p-6">
113121
<div className="mx-auto">{children}</div>

components/AnnouncementBanner.tsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"use client";
2+
3+
import { useState, useEffect } from "react";
4+
import { X } from "lucide-react";
5+
import Link from "next/link";
6+
import { cn } from "@/lib/utils";
7+
8+
interface AnnouncementBannerProps {
9+
message: string;
10+
linkText?: string;
11+
linkHref?: string;
12+
storageKey?: string;
13+
showAlways?: boolean; // If true, ignores storageKey and always shows (unless dismissed in current session if we want to support session dismissal, but usually 'everytime' means ignore history)
14+
}
15+
16+
export function AnnouncementBanner({
17+
message,
18+
linkText,
19+
linkHref,
20+
storageKey = "announcement-banner",
21+
showAlways = false,
22+
}: AnnouncementBannerProps) {
23+
const [isVisible, setIsVisible] = useState(false);
24+
25+
useEffect(() => {
26+
// If showAlways is true, we always show it initially (until user dismisses it in this session?)
27+
// Or maybe "everytime when page loads" means it shows up every reload, but can be closed.
28+
// "Show only once" means if closed, it remembers forever.
29+
30+
if (showAlways) {
31+
setIsVisible(true);
32+
return;
33+
}
34+
35+
// Check localStorage
36+
const dismissed = localStorage.getItem(storageKey);
37+
if (!dismissed) {
38+
setIsVisible(true);
39+
}
40+
}, [storageKey, showAlways]);
41+
42+
const handleDismiss = () => {
43+
setIsVisible(false);
44+
if (!showAlways) {
45+
localStorage.setItem(storageKey, "true");
46+
}
47+
};
48+
49+
if (!isVisible) return null;
50+
51+
return (
52+
<div className="relative w-full bg-gradient-to-r from-violet-100/80 to-purple-100/80 dark:from-violet-900/20 dark:to-purple-900/20 border-b px-4 py-2.5 transition-all">
53+
<div className="mx-auto flex max-w-7xl items-center justify-center gap-2 text-sm font-medium">
54+
<span className="text-foreground/90">{message}</span>
55+
{linkText && linkHref && (
56+
<Link
57+
href={linkHref}
58+
className="group flex items-center gap-1 font-semibold text-primary hover:underline hover:underline-offset-4"
59+
>
60+
{linkText} <span className="transition-transform group-hover:translate-x-0.5"></span>
61+
</Link>
62+
)}
63+
</div>
64+
<button
65+
onClick={handleDismiss}
66+
className="absolute right-4 top-1/2 -translate-y-1/2 rounded-full p-1 text-muted-foreground transition-colors hover:bg-black/5 hover:text-foreground dark:hover:bg-white/10"
67+
aria-label="Dismiss announcement"
68+
>
69+
<X className="h-4 w-4" />
70+
</button>
71+
</div>
72+
);
73+
}

0 commit comments

Comments
 (0)