Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 1 addition & 74 deletions src/components/CatalogueContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import { FilterProvider, useFilters } from "@/context/filterContext";
import EmptyState from "./ui/EmptyState";
import SidebarButton from "./SidebarButton";
import SortComponent from "./ui/sorting";
import Announcement from "./ui/announcement/Announcement";
import { getSubjectEvent, EVENTS, type EventData } from "@/config/events";

const CatalogueContentInner = ({ subject }: { subject: string | null }) => {
const [isMounted, setIsMounted] = useState(false);
Expand Down Expand Up @@ -237,60 +235,11 @@ const CatalogueContentInner = ({ subject }: { subject: string | null }) => {
setAppliedFilters,
]);

const [activeEvent, setActiveEvent] = useState<EventData | null>(null);
const [isGridEventsDismissed, setIsGridEventsDismissed] = useState<boolean>(false);

useEffect(() => {
try {
setIsGridEventsDismissed(
window.sessionStorage.getItem("announcement:global-grid-card:dismissed") === "true",
);
} catch {
setIsGridEventsDismissed(false);
}
}, []);

const handleDismissGridEvents = () => {
setIsGridEventsDismissed(true);
try {
window.sessionStorage.setItem(
"announcement:global-grid-card:dismissed",
"true",
);
} catch {}
};

useEffect(() => {
setActiveEvent(getSubjectEvent(subject));
}, [subject]);

const renderGridItems = (paperList: IPaper[]) => {
if (paperList.length === 0) return null;
const currentEvent = activeEvent ?? EVENTS[0]!;

const items: React.ReactNode[] = [];
paperList.forEach((paper, index) => {
if (index === 2 && !isGridEventsDismissed) {
items.push(
<Announcement
key={`grid-event-${subject ?? "all"}`}
id={`grid-card-${currentEvent.id}`}
variant="card"
title={currentEvent.title}
message={currentEvent.description}
badge={currentEvent.badge}
logoType={currentEvent.logoType}
imageUrl={currentEvent.imageUrl}
accent={currentEvent.accent}
ctaLabel="Register Now"
href={currentEvent.registrationUrl}
secondaryCtaLabel="Learn More"
secondaryHref={currentEvent.landingPageUrl}
dismissible={true}
onDismiss={handleDismissGridEvents}
/>,
);
}
paperList.forEach((paper) => {
items.push(
<Card
key={paper._id}
Expand All @@ -301,28 +250,6 @@ const CatalogueContentInner = ({ subject }: { subject: string | null }) => {
);
});

if (paperList.length > 0 && paperList.length < 2 && !isGridEventsDismissed) {
items.push(
<Announcement
key={`grid-event-${subject ?? "all"}`}
id={`grid-card-${currentEvent.id}`}
variant="card"
title={currentEvent.title}
message={currentEvent.description}
badge={currentEvent.badge}
logoType={currentEvent.logoType}
imageUrl={currentEvent.imageUrl}
accent={currentEvent.accent}
ctaLabel="Register Now"
href={currentEvent.registrationUrl}
secondaryCtaLabel="Learn More"
secondaryHref={currentEvent.landingPageUrl}
dismissible={true}
onDismiss={handleDismissGridEvents}
/>,
);
}

return items;
};

Expand Down
99 changes: 47 additions & 52 deletions src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,48 +23,58 @@ import { useCourses } from "@/context/courseContext";
import PinnedModal from "./ui/PinnedModal";
import RequestModal from "./ui/RequestModal";
import Announcement from "./ui/announcement/Announcement";
import { EVENTS } from "@/config/events";
import { getSubjectEvent, type EventData } from "@/config/events";

function Navbar() {
const pathname: string = usePathname() ?? "/";

const [dropdownOpen, setDropdownOpen] = useState<boolean>(false);
const [currentBannerIndex, setCurrentBannerIndex] = useState<number>(0);
const [isPaused, setIsPaused] = useState<boolean>(false);
const [isTopBannerDismissed, setIsTopBannerDismissed] = useState<boolean>(false);
const { courses } = useCourses();

useEffect(() => {
try {
setIsTopBannerDismissed(
window.sessionStorage.getItem("announcement:global-top-banner:dismissed") === "true",
);
const storageKey = "announcement:global-top-banner:dismissed";
const timestampKey = `${storageKey}:time`;
const isDismissed = window.localStorage.getItem(storageKey) === "true";
const dismissedAt = window.localStorage.getItem(timestampKey);
const TWELVE_HOURS = 12 * 60 * 60 * 1000;

if (isDismissed && dismissedAt) {
const timePassed = Date.now() - parseInt(dismissedAt, 10);
if (timePassed < TWELVE_HOURS) {
setIsTopBannerDismissed(true);
} else {
window.localStorage.removeItem(storageKey);
window.localStorage.removeItem(timestampKey);
setIsTopBannerDismissed(false);
}
} else if (isDismissed) {
setIsTopBannerDismissed(true);
} else {
setIsTopBannerDismissed(false);
}
} catch {
setIsTopBannerDismissed(false);
}
}, []);

useEffect(() => {
if (isPaused || EVENTS.length <= 1) return;

const interval = setInterval(() => {
setCurrentBannerIndex((prevIndex) => (prevIndex + 1) % EVENTS.length);
}, 5000);

return () => clearInterval(interval);
}, [isPaused]);

const handleDismissTopBanner = (e?: React.MouseEvent) => {
e?.stopPropagation();
setIsTopBannerDismissed(true);
try {
window.sessionStorage.setItem(
"announcement:global-top-banner:dismissed",
"true",
);
const storageKey = "announcement:global-top-banner:dismissed";
window.localStorage.setItem(storageKey, "true");
window.localStorage.setItem(`${storageKey}:time`, Date.now().toString());
} catch {}
};

const [currentEvent, setCurrentEvent] = useState<EventData | null>(null);

useEffect(() => {
setCurrentEvent(getSubjectEvent());
}, []);

const renderHomePageButtons = () => (
<>
{/* <Link href="/pinned" className="ml-2">
Expand All @@ -90,38 +100,23 @@ function Navbar() {

return (
<div className="sticky top-0 z-[50] w-full bg-[#B2B8FF] dark:bg-[#130E1F]">
{EVENTS.length > 0 && !isTopBannerDismissed && (
<div
className="relative w-full overflow-hidden"
onMouseEnter={() => setIsPaused(true)}
onMouseLeave={() => setIsPaused(false)}
>
<div
className="flex w-full transition-transform duration-700 ease-in-out"
style={{ transform: `translateX(-${currentBannerIndex * 100}%)` }}
>
{EVENTS.map((event) => (
<div key={event.id} className="w-full min-w-full flex-shrink-0">
<Announcement
id={`top-banner-${event.id}`}
variant="banner"
title="Registrations are Live at Gravitas!"
message={event.tagline}
badge={event.badge}
logoType={event.logoType}
imageUrl={event.imageUrl}
accent={event.accent}
ctaLabel="Register Now"
href={event.registrationUrl}
secondaryCtaLabel="Event Details"
secondaryHref={event.landingPageUrl}
dismissible={true}
onDismiss={handleDismissTopBanner}
/>
</div>
))}
</div>
</div>
{currentEvent && !isTopBannerDismissed && (
<Announcement
id={`top-banner-${currentEvent.id}`}
variant="banner"
title="Registrations are Live at Gravitas!"
message={currentEvent.tagline}
badge={currentEvent.badge}
logoType={currentEvent.logoType}
imageUrl={currentEvent.imageUrl}
accent={currentEvent.accent}
ctaLabel="Register Now"
href={currentEvent.registrationUrl}
secondaryCtaLabel="Event Details"
secondaryHref={currentEvent.landingPageUrl}
dismissible={true}
onDismiss={handleDismissTopBanner}
/>
)}

<div className="flex items-center justify-between bg-inherit px-4 py-4 md:px-8 md:py-5">
Expand Down
45 changes: 32 additions & 13 deletions src/components/ui/announcement/Announcement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,16 @@ const accentStyles: Record<
badgeBg: "bg-[#EFEAFF] text-[#562EE7] dark:bg-[#2E1A47] dark:text-[#C4B5FD]",
},
amber: {
cardBorder: "border-[#D97706] dark:border-[#B45309]",
cardSurface: "bg-white dark:bg-[#1C1305] hover:bg-[#FEF3C7] hover:dark:bg-[#2B1B07]",
bannerSurface: "bg-[#FEF3C7] dark:bg-[#2A1C07]",
chip: "bg-[#FDE8B8] dark:bg-[#3D290A]",
chipIcon: "text-[#92600B] dark:text-[#FCD34D]",
accentText: "text-[#92600B] dark:text-[#FBBF24]",
cta: "bg-[#D97706] text-white hover:bg-[#B8630A] dark:bg-[#D97706] dark:hover:bg-[#B45309]",
secondaryCta: "border border-[#D97706] text-[#92600B] hover:bg-[#FEF3C7] dark:border-[#F59E0B] dark:text-[#FCD34D] dark:hover:bg-[#3D290A]",
tagText: "text-[#92600B] dark:text-[#FCD34D]",
badgeBg: "bg-[#FEF3C7] text-[#92600B] dark:bg-[#3D290A] dark:text-[#FCD34D]",
cardBorder: "border-[#734DFF] dark:border-[#562EE7]",
cardSurface: "bg-white dark:bg-[#120B24] hover:bg-[#EFEAFF] hover:dark:bg-[#1D1438]",
bannerSurface: "bg-[#EFEAFF] dark:bg-[#1A1133]",
chip: "bg-[#EFEAFF] dark:bg-[#231845]",
chipIcon: "text-[#562EE7] dark:text-[#C4B5FD]",
accentText: "text-[#562EE7] dark:text-[#A78BFA]",
cta: "bg-[#734DFF] text-white hover:bg-[#5F3FE0] dark:bg-[#6D28D9] dark:hover:bg-[#5B21B6]",
secondaryCta: "border border-[#734DFF] text-[#562EE7] hover:bg-[#EFEAFF] dark:border-[#7C3AED] dark:text-[#C4B5FD] dark:hover:bg-[#2E1A47]",
tagText: "text-[#734DFF] dark:text-[#C4B5FD]",
badgeBg: "bg-[#EFEAFF] text-[#562EE7] dark:bg-[#2E1A47] dark:text-[#C4B5FD]",
},
green: {
cardBorder: "border-[#16A34A] dark:border-[#15803D]",
Expand Down Expand Up @@ -167,7 +167,25 @@ export default function Announcement({
setMounted(true);
if (dismissible) {
try {
setDismissed(window.sessionStorage.getItem(storageKey) === "true");
const timestampKey = `${storageKey}:time`;
const isDismissed = window.localStorage.getItem(storageKey) === "true";
const dismissedAt = window.localStorage.getItem(timestampKey);
const TWELVE_HOURS = 12 * 60 * 60 * 1000;

if (isDismissed && dismissedAt) {
const timePassed = Date.now() - parseInt(dismissedAt, 10);
if (timePassed < TWELVE_HOURS) {
setDismissed(true);
} else {
window.localStorage.removeItem(storageKey);
window.localStorage.removeItem(timestampKey);
setDismissed(false);
}
} else if (isDismissed) {
setDismissed(true);
} else {
setDismissed(false);
}
} catch {
setDismissed(false);
}
Expand All @@ -178,7 +196,8 @@ export default function Announcement({
e.stopPropagation();
setDismissed(true);
try {
window.sessionStorage.setItem(storageKey, "true");
window.localStorage.setItem(storageKey, "true");
window.localStorage.setItem(`${storageKey}:time`, Date.now().toString());
} catch {
// ignore quota / security error
}
Expand Down Expand Up @@ -214,7 +233,7 @@ export default function Announcement({
className,
)}
>
<div className="relative mx-auto flex max-w-screen-2xl items-center justify-between gap-1.5 px-2 py-1.5 sm:gap-3 sm:px-4 sm:py-2.5 md:px-8">
<div className="relative flex w-full items-center justify-between gap-3 px-4 py-2.5 md:px-8">
{/* Info section - grouped neatly right next to the logo */}
<div className="flex min-w-0 flex-1 items-center gap-2 sm:gap-3">
{imageUrl ? (
Expand Down
16 changes: 8 additions & 8 deletions src/components/ui/announcement/EventLogo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function EventLogo({ type = "default", className, size = "md" }: EventLog
return (
<div
className={cn(
"relative flex items-center justify-center rounded-2xl bg-gradient-to-br from-amber-950 via-orange-900 to-slate-900 p-2 shadow-lg shadow-orange-900/30 ring-1 ring-orange-500/30 transition-transform duration-300 hover:scale-105",
"relative flex items-center justify-center rounded-2xl bg-gradient-to-br from-indigo-950 via-purple-900 to-slate-900 p-2 shadow-lg shadow-purple-900/30 ring-1 ring-purple-500/30 transition-transform duration-300 hover:scale-105",
sizeClasses[size],
className
)}
Expand All @@ -75,13 +75,13 @@ export function EventLogo({ type = "default", className, size = "md" }: EventLog
viewBox="0 0 100 100"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="h-full w-full drop-shadow-[0_0_8px_rgba(249,115,22,0.6)]"
className="h-full w-full drop-shadow-[0_0_8px_rgba(168,85,247,0.6)]"
>
<defs>
<linearGradient id="cookFlame" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" stopColor="#EA580C" />
<stop offset="50%" stopColor="#F97316" />
<stop offset="100%" stopColor="#FBBF24" />
<stop offset="0%" stopColor="#734DFF" />
<stop offset="50%" stopColor="#A855F7" />
<stop offset="100%" stopColor="#C4B5FD" />
</linearGradient>
<linearGradient id="hatGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#FFFFFF" />
Expand All @@ -95,7 +95,7 @@ export function EventLogo({ type = "default", className, size = "md" }: EventLog
fill="url(#hatGrad)"
/>
{/* Hat Ribbon / Code Bar */}
<rect x="30" y="66" width="40" height="12" rx="4" fill="#EA580C" />
<rect x="30" y="66" width="40" height="12" rx="4" fill="#734DFF" />
<path d="M38 72 L43 72 M48 72 L58 72" stroke="#FFFFFF" strokeWidth="3" strokeLinecap="round" />

{/* Central Coding Flame */}
Expand All @@ -105,8 +105,8 @@ export function EventLogo({ type = "default", className, size = "md" }: EventLog
/>

{/* Sparkles */}
<path d="M18 40 L20 43 L23 45 L20 47 L18 50 L16 47 L13 45 L16 43 Z" fill="#FBBF24" />
<path d="M80 34 L81.5 36.5 L84 38 L81.5 39.5 L80 42 L78.5 39.5 L76 38 L78.5 36.5 Z" fill="#F97316" />
<path d="M18 40 L20 43 L23 45 L20 47 L18 50 L16 47 L13 45 L16 43 Z" fill="#C4B5FD" />
<path d="M80 34 L81.5 36.5 L84 38 L81.5 39.5 L80 42 L78.5 39.5 L76 38 L78.5 36.5 Z" fill="#A855F7" />
</svg>
</div>
);
Expand Down
8 changes: 4 additions & 4 deletions src/components/ui/banners/bannerDismiss.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ interface BannerProps {

export default function Banner({
bannerId,
bgColor = "#fef3c7",
textColor = "#5a3000",
iconColor = "#d97706",
accentColor = "#78350f",
bgColor = "#EFEAFF",
textColor = "#120B24",
iconColor = "#562EE7",
accentColor = "#562EE7",
message,
title,
}: BannerProps) {
Expand Down
2 changes: 1 addition & 1 deletion src/config/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const EVENTS: EventData[] = [
tagline: "Largest Competitive Coding Contest",
description: "Test your algorithmic problem solving skills against top coders. Win prizes, certificates, and glory at Gravitas !",
badge: "CookOff 11",
accent: "amber",
accent: "purple",
registrationUrl: "https://gravitas.vit.ac.in/events/0629102b-cc5f-4992-8921-2e55df1cb2f8",
landingPageUrl: "https://gravitas.codechefvit.com/",
dates: "Gravitas '26",
Expand Down
Loading