NielsRogge commited on
Commit
0312464
·
unverified ·
2 Parent(s): b5b0120be08b38

Merge pull request #118 from laitifranz/feature/deadline-days-countdown

Browse files
src/components/ConferenceDialog.tsx CHANGED
@@ -17,7 +17,7 @@ import {
17
  } from "@/components/ui/dropdown-menu";
18
  import { useState, useEffect } from "react";
19
  import { getDeadlineInLocalTime } from '@/utils/dateUtils';
20
- import { getAllDeadlines, getNextUpcomingDeadline, getUpcomingDeadlines } from '@/utils/deadlineUtils';
21
 
22
  interface ConferenceDialogProps {
23
  conference: Conference;
@@ -218,7 +218,7 @@ END:VCALENDAR`;
218
  return (
219
  <Dialog open={open} onOpenChange={onOpenChange}>
220
  <DialogContent
221
- className="max-w-md w-full"
222
  >
223
  <DialogHeader>
224
  <DialogTitle className="text-2xl font-bold text-blue-600">
@@ -257,15 +257,24 @@ END:VCALENDAR`;
257
  {upcomingDeadlines.length > 0 ? (
258
  upcomingDeadlines.map((deadline, index) => {
259
  const isNext = nextDeadline && deadline.date === nextDeadline.date && deadline.type === nextDeadline.type;
 
 
260
  return (
261
  <div
262
  key={`${deadline.type}-${index}`}
263
  className={`rounded-md p-2 ${isNext ? 'bg-blue-100 border border-blue-200' : 'bg-gray-100'}`}
264
  >
265
- <p className={isNext ? 'font-medium text-blue-800' : ''}>
266
- {deadline.label}: {formatDeadlineDate(deadline.date)}
267
- {isNext && <span className="ml-2 text-xs">(Next)</span>}
268
- </p>
 
 
 
 
 
 
 
269
  </div>
270
  );
271
  })
 
17
  } from "@/components/ui/dropdown-menu";
18
  import { useState, useEffect } from "react";
19
  import { getDeadlineInLocalTime } from '@/utils/dateUtils';
20
+ import { getAllDeadlines, getNextUpcomingDeadline, getUpcomingDeadlines, getDaysRemaining, getCountdownColorClass } from '@/utils/deadlineUtils';
21
 
22
  interface ConferenceDialogProps {
23
  conference: Conference;
 
218
  return (
219
  <Dialog open={open} onOpenChange={onOpenChange}>
220
  <DialogContent
221
+ className="max-w-lg w-full"
222
  >
223
  <DialogHeader>
224
  <DialogTitle className="text-2xl font-bold text-blue-600">
 
257
  {upcomingDeadlines.length > 0 ? (
258
  upcomingDeadlines.map((deadline, index) => {
259
  const isNext = nextDeadline && deadline.date === nextDeadline.date && deadline.type === nextDeadline.type;
260
+ const daysRemaining = getDaysRemaining(deadline, conference.timezone);
261
+ const daysColorClass = getCountdownColorClass(daysRemaining);
262
  return (
263
  <div
264
  key={`${deadline.type}-${index}`}
265
  className={`rounded-md p-2 ${isNext ? 'bg-blue-100 border border-blue-200' : 'bg-gray-100'}`}
266
  >
267
+ <div className="flex items-center justify-between gap-2">
268
+ <p className={`flex-1 ${isNext ? 'font-medium text-blue-800' : ''}`}>
269
+ {deadline.label}: {formatDeadlineDate(deadline.date)}
270
+ {isNext && <span className="ml-2 text-xs">(Next)</span>}
271
+ </p>
272
+ {daysRemaining !== null && daysRemaining > 0 && (
273
+ <span className={`text-xs font-medium whitespace-nowrap ${daysColorClass}`}>
274
+ {daysRemaining} {daysRemaining === 1 ? 'day' : 'days'}
275
+ </span>
276
+ )}
277
+ </div>
278
  </div>
279
  );
280
  })
src/utils/deadlineUtils.ts CHANGED
@@ -193,3 +193,27 @@ export function getUpcomingDeadlines(conference: Conference): Deadline[] {
193
 
194
  return upcomingDeadlines;
195
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
  return upcomingDeadlines;
195
  }
196
+
197
+ /**
198
+ * Get the number of days remaining until a deadline
199
+ * Returns null if the deadline date is invalid
200
+ */
201
+ export function getDaysRemaining(deadline: Deadline, fallbackTimezone?: string): number | null {
202
+ const deadlineDate = getDeadlineInLocalTime(deadline.date, deadline.timezone || fallbackTimezone);
203
+ if (!deadlineDate || !isValid(deadlineDate)) return null;
204
+
205
+ const now = new Date();
206
+ const diff = deadlineDate.getTime() - now.getTime();
207
+ return Math.ceil(diff / (1000 * 60 * 60 * 24));
208
+ }
209
+
210
+ /**
211
+ * Get the color class for a countdown based on days remaining
212
+ */
213
+ export function getCountdownColorClass(daysRemaining: number | null): string {
214
+ if (daysRemaining === null) return "text-neutral-600";
215
+ if (daysRemaining <= 0) return "text-neutral-600";
216
+ if (daysRemaining <= 7) return "text-red-600";
217
+ if (daysRemaining <= 30) return "text-orange-600";
218
+ return "text-green-600";
219
+ }