Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
65 changes: 42 additions & 23 deletions components/campaigns/AnnouncementHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ interface IAnnouncementHeroProps {
*/
export default function AnnouncementHero({ className = '', small = false }: IAnnouncementHeroProps) {
const [activeIndex, setActiveIndex] = useState(0);
const [isBannerVisible, setIsBannerVisible] = useState(true);

// Load banner visibility from localStorage
useEffect(() => {
const hidden = localStorage.getItem('bannerClosed');
if (hidden === 'true') setIsBannerVisible(false);
}, []);

const visibleBanners = useMemo(() => banners.filter((banner) => shouldShowBanner(banner.cfpDeadline)), [banners]);
const numberOfVisibleBanners = visibleBanners.length;
Expand All @@ -36,15 +43,17 @@ export default function AnnouncementHero({ className = '', small = false }: IAnn
setActiveIndex(index);
};

const handleClose = () => {
setIsBannerVisible(false);
localStorage.setItem('bannerClosed', 'true');
};
Comment on lines 57 to 74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Consider banner-specific dismissal instead of global dismissal.

The current implementation dismisses ALL banners permanently when the close button is clicked, even if the user only intended to dismiss the currently visible banner. Once closed, the banner(s) remain hidden indefinitely until localStorage is manually cleared, which prevents users from seeing new conference announcements.

Consider implementing one of these approaches:

  1. Store dismissal state per banner (e.g., using banner ID/title as key)
  2. Add a time-based reset (e.g., show again after 7 days)
  3. Store the specific conference that was dismissed

Example implementation for banner-specific dismissal:

const handleClose = () => {
  const currentBanner = visibleBanners[activeIndex];
  const dismissedBanners = JSON.parse(localStorage.getItem('dismissedBanners') || '[]');
  dismissedBanners.push(currentBanner.eventName);
  localStorage.setItem('dismissedBanners', JSON.stringify(dismissedBanners));
  
  // Filter out dismissed banners
  const remainingBanners = visibleBanners.filter(
    b => !dismissedBanners.includes(b.eventName)
  );
  
  if (remainingBanners.length === 0) {
    setIsBannerVisible(false);
  } else {
    // Move to next banner or wrap around
    setActiveIndex(prev => prev % remainingBanners.length);
  }
};

Then update the initial load effect to filter dismissed banners from visibleBanners.

🤖 Prompt for AI Agents
In components/campaigns/AnnouncementHero.tsx around lines 46-49, the close
handler currently sets a global 'bannerClosed' flag which hides all banners
permanently; change it to store dismissal per banner (e.g., by eventName or id)
in localStorage (use a JSON array like 'dismissedBanners'), update handleClose
to add the current banner's identifier to that array and then filter
visibleBanners to remove dismissed ones (and adjust activeIndex safely or hide
the banner container if none remain), and modify the component's initial
load/effect to read and filter visibleBanners by that dismissed list (optionally
include a timestamp per entry if you prefer time-based reset).


useEffect(() => {
const interval = setInterval(() => setActiveIndex((index) => (index + 1) % numberOfVisibleBanners), 10000);

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

if (numberOfVisibleBanners === 0) {
if (numberOfVisibleBanners === 0 || !isBannerVisible) {
return null;
}

Expand All @@ -60,33 +69,42 @@ export default function AnnouncementHero({ className = '', small = false }: IAnn
<ArrowLeft className='text-white' />
</div>
)}

<div className='relative flex w-4/5 md:w-5/6 flex-col items-center justify-center gap-2'>
<div className='relative flex min-h-72 w-full justify-center overflow-hidden lg:h-[17rem] lg:w-[38rem]'>
{visibleBanners.map((banner, index) => {
// Only render the active banner
const isVisible = index === activeIndex;

if (!isVisible) return null;
if (index !== activeIndex) return null;

return (
<Banner
key={index}
title={banner.title}
dateLocation={banner.dateLocation}
cfaText={banner.cfaText}
eventName={banner.eventName}
cfpDeadline={banner.cfpDeadline}
link={banner.link}
city={banner.city}
activeBanner={isVisible}
className={className}
small={small}
/>
<div key={index} className='relative w-full flex justify-center'>
{/* Close Button inside the card */}
<button
onClick={handleClose}
aria-label='Close announcement banner'
className='absolute top-[1.7rem] right-4 z-20 flex h-6 w-6 items-center justify-center rounded border border-gray-400 bg-white text-gray-600 hover:bg-gray-100 hover:text-gray-800'
>
×
</button>

<Banner
title={banner.title}
dateLocation={banner.dateLocation}
cfaText={banner.cfaText}
eventName={banner.eventName}
cfpDeadline={banner.cfpDeadline}
link={banner.link}
city={banner.city}
activeBanner={index === activeIndex}
className={className}
small={small}
/>
</div>
);
})}
</div>

<div className='m-auto flex justify-center'>
{visibleBanners.map((banner, index) => (
{visibleBanners.map((_, index) => (
<div
key={index}
className={`mx-1 size-2 cursor-pointer rounded-full ${
Expand All @@ -97,6 +115,7 @@ export default function AnnouncementHero({ className = '', small = false }: IAnn
))}
</div>
</div>

{numberOfVisibleBanners > 1 && (
<div
className={`absolute right-0 top-1/2 z-10 mb-2 size-8 -translate-y-1/2 cursor-pointer
Expand Down
6 changes: 6 additions & 0 deletions next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify if this auto-generated file should be version controlled.

This file is auto-generated by Next.js and typically should not be manually edited or committed to version control (usually listed in .gitignore). However, some projects do commit it for consistency.

Run this script to check if next-env.d.ts is already in .gitignore:


🏁 Script executed:

#!/bin/bash
# Check if next-env.d.ts should be in .gitignore
grep -n "next-env.d.ts" .gitignore || echo "next-env.d.ts not found in .gitignore"

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Check if next-env.d.ts exists and is tracked
echo "=== Checking if next-env.d.ts exists ==="
ls -la next-env.d.ts 2>/dev/null || echo "File does not exist"

echo ""
echo "=== Checking git status of next-env.d.ts ==="
git ls-files --cached next-env.d.ts 2>/dev/null && echo "File is tracked in git" || echo "File is not tracked"

echo ""
echo "=== Checking .gitignore content for auto-generated patterns ==="
cat .gitignore | head -50

Length of output: 234


🏁 Script executed:

#!/bin/bash
# Check git diff for changes to next-env.d.ts
echo "=== Git diff for next-env.d.ts ==="
git diff HEAD -- next-env.d.ts 2>/dev/null | head -50 || echo "No diff found"

echo ""
echo "=== Check if file is added (untracked) ==="
git status --short 2>/dev/null | grep -i "next-env" || echo "No untracked next-env files"

echo ""
echo "=== Check if this is a Next.js project ==="
test -f package.json && echo "package.json exists" || echo "No package.json"
test -f next.config.js && echo "next.config.js exists" || echo "No next.config.js"
test -f tsconfig.json && echo "tsconfig.json exists" || echo "No tsconfig.json"

echo ""
echo "=== List TypeScript declaration files ==="
find . -maxdepth 2 -name "*.d.ts" 2>/dev/null | head -10

Length of output: 268


Add next-env.d.ts to .gitignore.

This auto-generated file should not be committed to version control. Add next-env.d.ts to .gitignore so it's regenerated locally during builds instead of being versioned.

🤖 Prompt for AI Agents
In next-env.d.ts around lines 1 to 6, the auto-generated Next.js type file is
currently tracked in git; update the repository's .gitignore to include
"next-env.d.ts" so this file is not committed. Edit the .gitignore at the repo
root and add a line with next-env.d.ts, then remove the tracked file from the
index with git rm --cached next-env.d.ts (or equivalent) so it will be
regenerated locally during builds.

Loading