ai-deadlines / src /components /FindConferenceCard.tsx
kenza-ily's picture
find a conference with better display
d300e9f
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;