Spaces:
Running
Running
| import { | |
| Dialog, | |
| DialogContent, | |
| DialogHeader, | |
| DialogTitle, | |
| } from "@/components/ui/dialog"; | |
| import { CalendarDays, Globe, Tag, Clock, AlarmClock } from "lucide-react"; | |
| import { Conference } from "@/types/conference"; | |
| import { formatDistanceToNow, parseISO, isValid } from "date-fns"; | |
| interface ConferenceDialogProps { | |
| conference: Conference; | |
| open: boolean; | |
| onOpenChange: (open: boolean) => void; | |
| } | |
| const ConferenceDialog = ({ conference, open, onOpenChange }: ConferenceDialogProps) => { | |
| const deadlineDate = conference.deadline && conference.deadline !== 'TBD' ? parseISO(conference.deadline) : null; | |
| const daysLeft = deadlineDate && isValid(deadlineDate) ? formatDistanceToNow(deadlineDate, { addSuffix: true }) : 'TBD'; | |
| const getCountdownColor = () => { | |
| if (!deadlineDate || !isValid(deadlineDate)) return "text-neutral-600"; | |
| const daysRemaining = Math.ceil((deadlineDate.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24)); | |
| if (daysRemaining <= 7) return "text-red-600"; | |
| if (daysRemaining <= 30) return "text-orange-600"; | |
| return "text-green-600"; | |
| }; | |
| return ( | |
| <Dialog open={open} onOpenChange={onOpenChange}> | |
| <DialogContent className="max-w-md"> | |
| <DialogHeader> | |
| <DialogTitle className="text-xl font-bold"> | |
| {conference.title} | |
| </DialogTitle> | |
| {conference.full_name && ( | |
| <p className="text-sm text-neutral-600">{conference.full_name}</p> | |
| )} | |
| </DialogHeader> | |
| <div className="space-y-4 py-4"> | |
| <div className="flex flex-col gap-3"> | |
| <div className="flex items-center text-neutral"> | |
| <CalendarDays className="h-5 w-5 mr-3 flex-shrink-0" /> | |
| <span>{conference.date}</span> | |
| </div> | |
| <div className="flex items-center text-neutral"> | |
| <Globe className="h-5 w-5 mr-3 flex-shrink-0" /> | |
| <span>{conference.place}</span> | |
| </div> | |
| <div className="flex items-center text-neutral"> | |
| <Clock className="h-5 w-5 mr-3 flex-shrink-0" /> | |
| <div className="flex flex-col"> | |
| <span>Deadline: {conference.deadline === 'TBD' ? 'TBD' : conference.deadline}</span> | |
| {conference.timezone && ( | |
| <span className="text-sm text-neutral-500">Timezone: {conference.timezone}</span> | |
| )} | |
| </div> | |
| </div> | |
| <div className="flex items-center"> | |
| <AlarmClock className={`h-5 w-5 mr-3 flex-shrink-0 ${getCountdownColor()}`} /> | |
| <span className={`font-medium ${getCountdownColor()}`}> | |
| {daysLeft} | |
| </span> | |
| </div> | |
| </div> | |
| {conference.abstract_deadline && ( | |
| <div className="text-sm text-neutral-600"> | |
| Abstract Deadline: {conference.abstract_deadline} | |
| </div> | |
| )} | |
| {Array.isArray(conference.tags) && conference.tags.length > 0 && ( | |
| <div className="flex flex-wrap gap-2"> | |
| {conference.tags.map((tag) => ( | |
| <span key={tag} className="tag"> | |
| <Tag className="h-3 w-3 mr-1" /> | |
| {tag} | |
| </span> | |
| ))} | |
| </div> | |
| )} | |
| {conference.note && ( | |
| <div | |
| className="text-sm text-neutral-600 mt-2 p-3 bg-neutral-50 rounded-lg" | |
| dangerouslySetInnerHTML={{ __html: conference.note }} | |
| /> | |
| )} | |
| {conference.link && ( | |
| <div className="pt-2"> | |
| <a | |
| href={conference.link} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="text-primary hover:underline" | |
| > | |
| Visit Conference Website → | |
| </a> | |
| </div> | |
| )} | |
| </div> | |
| </DialogContent> | |
| </Dialog> | |
| ); | |
| }; | |
| export default ConferenceDialog; | |