dere-cbai
Clean deployment with countdown functionality
6d65f0c
import { Globe, Tag, Clock } from "lucide-react";
import { Conference } from "@/types/conference";
import { isValid, isPast } from "date-fns";
import ConferenceDialog from "./ConferenceDialog";
import CountdownTimer from "./CountdownTimer";
import { useState } from "react";
import { getDeadlineInLocalTime } from '@/utils/dateUtils';
const ConferenceCard = ({
title,
full_name,
year,
date,
deadline,
timezone,
tags = [],
link,
note,
abstract_deadline,
city,
country,
venue,
...conferenceProps
}: Conference) => {
const [dialogOpen, setDialogOpen] = useState(false);
const deadlineDate = getDeadlineInLocalTime(deadline, timezone);
// Create location string by concatenating city and country
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);
}
};
const handleTagClick = (e: React.MouseEvent, tag: string) => {
e.stopPropagation();
const searchParams = new URLSearchParams(window.location.search);
const currentTags = searchParams.get('tags')?.split(',') || [];
let newTags;
if (currentTags.includes(tag)) {
newTags = currentTags.filter(t => t !== tag);
} else {
newTags = [...currentTags, tag];
}
if (newTags.length > 0) {
searchParams.set('tags', newTags.join(','));
} else {
searchParams.delete('tags');
}
const newUrl = `${window.location.pathname}${searchParams.toString() ? `?${searchParams.toString()}` : ''}`;
window.history.pushState({}, '', newUrl);
window.dispatchEvent(new CustomEvent('urlchange', { detail: { tag } }));
};
return (
<>
<div
className="bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow p-4 flex flex-col cursor-pointer"
onClick={handleCardClick}
>
<div className="flex justify-between items-start mb-2">
<div className="flex-1">
<h3 className="text-lg font-semibold text-primary">
{title} {year}
{link && (
<a
href={link}
target="_blank"
rel="noopener noreferrer"
className="ml-2 hover:underline"
onClick={(e) => e.stopPropagation()}
>
<Globe className="h-4 w-4 inline flex-shrink-0" />
</a>
)}
</h3>
<CountdownTimer
deadline={deadlineDate}
className="mt-1"
/>
{full_name && (
<p className="text-sm text-gray-600 mt-1">
{full_name}
</p>
)}
<div className="mt-2 text-sm text-gray-600">
{date}. {location}
</div>
{note && (
<div className="text-xs text-gray-500 mt-1">
Note: {note.replace(/<[^>]*>/g, '')}
</div>
)}
<div className="flex items-center text-gray-600 mt-2">
<Clock className="h-4 w-4 mr-2 flex-shrink-0" />
<span className="text-sm">
Deadline: {deadline === 'TBD' ? 'TBD' : deadlineDate?.toLocaleString('en-US', {
weekday: 'short',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
timeZoneName: 'short'
})}
</span>
</div>
</div>
</div>
<div className="flex flex-col gap-2 mb-3">
</div>
{Array.isArray(tags) && tags.length > 0 && (
<div className="flex flex-wrap gap-2">
{tags.map((tag) => (
<button
key={tag}
className="tag tag-button"
onClick={(e) => handleTagClick(e, tag)}
>
<Tag className="h-3 w-3 mr-1" />
{tag}
</button>
))}
</div>
)}
</div>
<ConferenceDialog
conference={{
title,
full_name,
year,
date,
deadline,
timezone,
tags,
link,
note,
abstract_deadline,
city,
country,
venue,
...conferenceProps
}}
open={dialogOpen}
onOpenChange={setDialogOpen}
/>
</>
);
};
export default ConferenceCard;