135 lines
5.4 KiB
JavaScript
135 lines
5.4 KiB
JavaScript
|
|
import React from "react";
|
|
import {
|
|
HoverCard,
|
|
HoverCardContent,
|
|
HoverCardTrigger,
|
|
} from "@/components/ui/hover-card";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Calendar, MapPin, Users, FileText } from "lucide-react";
|
|
import { format, parseISO, isValid } from "date-fns";
|
|
|
|
const statusColors = {
|
|
Draft: "bg-slate-100 text-slate-700 border border-slate-300",
|
|
Active: "bg-emerald-50 text-emerald-700 border border-emerald-200",
|
|
Pending: "bg-purple-50 text-purple-700 border border-purple-200",
|
|
Confirmed: "bg-[#0A39DF]/10 text-[#0A39DF] border border-[#0A39DF]/30",
|
|
Assigned: "bg-amber-50 text-amber-700 border border-amber-200",
|
|
Completed: "bg-slate-100 text-slate-600 border border-slate-300",
|
|
Canceled: "bg-red-50 text-red-700 border border-red-200",
|
|
Cancelled: "bg-red-50 text-red-700 border border-red-200"
|
|
};
|
|
|
|
// Helper function to safely format dates
|
|
const safeFormatDate = (dateString, formatStr) => {
|
|
if (!dateString) return "-";
|
|
try {
|
|
const date = typeof dateString === 'string' ? parseISO(dateString) : new Date(dateString);
|
|
if (!isValid(date)) return "-";
|
|
return format(date, formatStr);
|
|
} catch {
|
|
return "-";
|
|
}
|
|
};
|
|
|
|
export default function EventHoverCard({ event, children }) {
|
|
const assignedCount = event.assigned_staff?.length || event.assigned || 0;
|
|
const requestedCount = event.requested || 0;
|
|
const remainingSlots = requestedCount - assignedCount;
|
|
|
|
return (
|
|
<HoverCard openDelay={200}>
|
|
<HoverCardTrigger asChild>
|
|
{children}
|
|
</HoverCardTrigger>
|
|
<HoverCardContent className="w-96 p-0" side="right" align="start">
|
|
<div className="bg-gradient-to-br from-slate-50 to-white p-4 border-b border-slate-200">
|
|
<div className="flex items-start justify-between mb-3">
|
|
<div>
|
|
<h3 className="font-bold text-lg text-[#1C323E]">{event.event_name}</h3>
|
|
<p className="text-sm text-slate-600">{event.business_name || "Company Name"}</p>
|
|
</div>
|
|
<Badge className={`${statusColors[event.status]} font-medium`}>
|
|
{event.status}
|
|
</Badge>
|
|
</div>
|
|
|
|
<div className="space-y-2 text-sm">
|
|
<div className="flex items-center gap-2 text-slate-700">
|
|
<Calendar className="w-4 h-4 text-[#0A39DF]" />
|
|
<span className="font-semibold">{safeFormatDate(event.date, "MMMM dd, yyyy")}</span>
|
|
</div>
|
|
|
|
{event.event_location && (
|
|
<div className="flex items-center gap-2 text-slate-700">
|
|
<MapPin className="w-4 h-4 text-[#0A39DF]" />
|
|
<span className="truncate">{event.event_location}</span>
|
|
</div>
|
|
)}
|
|
|
|
{event.po && (
|
|
<div className="flex items-center gap-2 text-slate-700">
|
|
<FileText className="w-4 h-4 text-[#0A39DF]" />
|
|
<span>PO: {event.po}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-4 space-y-3">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<Users className="w-4 h-4 text-slate-600" />
|
|
<span className="text-sm font-medium text-slate-700">Staff Assignment</span>
|
|
</div>
|
|
<Badge variant="outline" className="border-[#0A39DF] text-[#0A39DF]">
|
|
{assignedCount} / {requestedCount}
|
|
</Badge>
|
|
</div>
|
|
|
|
{remainingSlots > 0 && (
|
|
<div className="bg-amber-50 border border-amber-200 rounded-lg p-2 text-xs text-amber-800">
|
|
<strong>{remainingSlots}</strong> staff member{remainingSlots !== 1 ? 's' : ''} still needed
|
|
</div>
|
|
)}
|
|
|
|
{remainingSlots === 0 && requestedCount > 0 && (
|
|
<div className="bg-emerald-50 border border-emerald-200 rounded-lg p-2 text-xs text-emerald-800">
|
|
✓ Fully staffed
|
|
</div>
|
|
)}
|
|
|
|
{event.assigned_staff && event.assigned_staff.length > 0 && (
|
|
<div className="space-y-2 max-h-40 overflow-y-auto">
|
|
<p className="text-xs font-semibold text-slate-600 uppercase">Assigned Staff:</p>
|
|
{event.assigned_staff.map((staff, idx) => (
|
|
<div key={idx} className="flex items-center gap-2 text-sm">
|
|
<div className="w-6 h-6 bg-[#0A39DF] rounded flex items-center justify-center text-white text-xs font-bold">
|
|
{staff.staff_name?.charAt(0) || '?'}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="font-medium text-slate-900 truncate">{staff.staff_name}</p>
|
|
{staff.position && <p className="text-xs text-slate-500 truncate">{staff.position}</p>}
|
|
</div>
|
|
{staff.confirmed && (
|
|
<Badge variant="outline" className="text-xs border-emerald-500 text-emerald-700">
|
|
Confirmed
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{event.notes && (
|
|
<div className="pt-2 border-t border-slate-200">
|
|
<p className="text-xs font-semibold text-slate-600 uppercase mb-1">Notes:</p>
|
|
<p className="text-xs text-slate-600">{event.notes}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</HoverCardContent>
|
|
</HoverCard>
|
|
);
|
|
}
|