Spaces:
Running
Running
| import { CalendarDays, Globe, Tag } from "lucide-react"; | |
| import { Conference } from "@/types/conference"; | |
| import { format, isValid } from "date-fns"; | |
| import ConferenceDialog from "./ConferenceDialog"; | |
| import { useState } from "react"; | |
| import { getDeadlineInLocalTime } from '@/utils/dateUtils'; | |
| import { getAllDeadlines } from '@/utils/deadlineUtils'; | |
| type FindConferenceCardProps = Conference; | |
| const FindConferenceCard = ({ | |
| title, | |
| full_name, | |
| year, | |
| id, | |
| date, | |
| deadline, | |
| timezone, | |
| tags = [], | |
| link, | |
| note, | |
| abstract_deadline, | |
| city, | |
| country, | |
| venue, | |
| ...conferenceProps | |
| }: FindConferenceCardProps) => { | |
| const [dialogOpen, setDialogOpen] = useState(false); | |
| const conference = { | |
| title, full_name, year, id, date, deadline, timezone, tags, link, note, | |
| abstract_deadline, city, country, venue, ...conferenceProps | |
| }; | |
| const allDeadlines = getAllDeadlines(conference); | |
| // Find the paper submission deadline | |
| const paperDeadlineTypes = new Set(["paper", "submission", "paper_submission"]); | |
| const paperDeadline = allDeadlines.find((d) => paperDeadlineTypes.has(d.type)); | |
| const paperDeadlineDate = paperDeadline | |
| ? getDeadlineInLocalTime(paperDeadline.date, paperDeadline.timezone || timezone) | |
| : null; | |
| const formatLocalDeadline = (deadlineDate: Date | null) => { | |
| if (!deadlineDate || !isValid(deadlineDate)) return "TBD"; | |
| return format(deadlineDate, "MMM d, yyyy HH:mm"); | |
| }; | |
| // Create location string | |
| const location = [city, country].filter(Boolean).join(", "); | |
| const handleCardClick = (e: React.MouseEvent) => { | |
| if (!(e.target as HTMLElement).closest('a') && | |
| !(e.target as HTMLElement).closest('.tag-button')) { | |
| setDialogOpen(true); | |
| } | |
| }; | |
| return ( | |
| <> | |
| <div | |
| className="bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow flex flex-col cursor-pointer p-4" | |
| onClick={handleCardClick} | |
| > | |
| {/* Header */} | |
| <div className="flex justify-between items-start mb-3"> | |
| <div className="flex flex-col"> | |
| <h3 className="text-lg font-semibold text-foreground"> | |
| {title} {year} | |
| </h3> | |
| {full_name && ( | |
| <p className="text-sm italic text-neutral-500"> | |
| {full_name} | |
| </p> | |
| )} | |
| </div> | |
| {link && ( | |
| <a | |
| href={link} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="text-violet-600 hover:text-violet-700" | |
| onClick={(e) => e.stopPropagation()} | |
| > | |
| <Globe className="h-5 w-5" /> | |
| </a> | |
| )} | |
| </div> | |
| {/* Paper Submission Deadline - Highlighted */} | |
| <div className="mb-3 p-3 bg-violet-50 rounded-md border border-violet-200"> | |
| <div className="text-xs font-medium text-violet-700 mb-1"> | |
| Paper Submission Deadline | |
| </div> | |
| <div className="text-lg font-bold text-violet-900"> | |
| {formatLocalDeadline(paperDeadlineDate)} | |
| </div> | |
| </div> | |
| {/* Conference Date */} | |
| {date && ( | |
| <div className="flex items-center gap-2 mb-2 text-sm text-neutral-600"> | |
| <CalendarDays className="h-4 w-4" /> | |
| <span>{date}</span> | |
| </div> | |
| )} | |
| {/* Location */} | |
| {location && ( | |
| <div className="flex items-center gap-2 mb-3 text-sm text-neutral-600"> | |
| <Globe className="h-4 w-4" /> | |
| <span>{location}</span> | |
| </div> | |
| )} | |
| {/* Tags */} | |
| {tags.length > 0 && ( | |
| <div className="flex flex-wrap gap-1.5 mt-auto pt-3 border-t border-neutral-100"> | |
| {tags.map((tag) => ( | |
| <span | |
| key={tag} | |
| className="inline-flex items-center gap-1 px-2 py-0.5 bg-neutral-100 text-neutral-700 rounded text-xs tag-button hover:bg-neutral-200 transition-colors" | |
| > | |
| <Tag className="h-3 w-3" /> | |
| {tag.split("-").map(word => | |
| word.charAt(0).toUpperCase() + word.slice(1) | |
| ).join(" ")} | |
| </span> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| <ConferenceDialog | |
| conference={conference} | |
| open={dialogOpen} | |
| onOpenChange={setDialogOpen} | |
| /> | |
| </> | |
| ); | |
| }; | |
| export default FindConferenceCard; | |