All files / src/components/public/Event CountdownTimer.tsx

100% Statements 6/6
100% Branches 8/8
100% Functions 1/1
100% Lines 6/6

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50                        1x 13x           13x     13x 3x     10x                                              
/**
 * CountdownTimer Component (Story 4.1.3)
 * Displays countdown to upcoming event if within 30 days
 */
 
import { differenceInCalendarDays } from 'date-fns';
import { useTranslation } from 'react-i18next';
 
interface CountdownTimerProps {
  eventDate: Date;
}
 
export const CountdownTimer = ({ eventDate }: CountdownTimerProps) => {
  const { t } = useTranslation('common');
  // differenceInCalendarDays (not differenceInDays): "today / tomorrow / N days" is a
  // calendar-boundary question, not a 24h-period one. differenceInDays truncates whole
  // 24h spans, so an event at 00:00 two calendar days out read as 1 → "Tomorrow!" (the
  // BATbern59 2026-06-19 bug seen on 2026-06-17). Calendar-day diff is timezone-local,
  // matching how a visitor reads the date on the page.
  const daysUntil = differenceInCalendarDays(eventDate, new Date());
 
  // Only show if event is within 30 days and in the future
  if (daysUntil < 0 || daysUntil > 30) {
    return null;
  }
 
  return (
    <div className="flex items-center gap-3 animate-in fade-in slide-in-from-top-2 duration-500">
      {/* Pulsing blue dot */}
      <div className="relative">
        <div className="h-3 w-3 rounded-full bg-primary animate-pulse" />
        <div className="absolute inset-0 h-3 w-3 rounded-full bg-primary opacity-75 animate-ping" />
      </div>
 
      {/* Badge */}
      <div className="text-sm font-light text-zinc-300">
        <span className="text-primary font-medium">{t('countdown.nextEvent')}</span>
        {' • '}
        {daysUntil === 0 ? (
          <span className="text-green-400 font-medium">{t('countdown.today')}</span>
        ) : daysUntil === 1 ? (
          <span className="text-orange-400 font-medium">{t('countdown.tomorrow')}</span>
        ) : (
          <span>{t('countdown.daysUntil', { count: daysUntil })}</span>
        )}
      </div>
    </div>
  );
};