mission-control/components/calendar/EventDetailModal.tsx

187 lines
5.5 KiB
TypeScript

"use client";
import { useState } from "react";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Calendar, Clock, MapPin, ExternalLink, FileText, X } from "lucide-react";
import { format, parseISO } from "date-fns";
import { cn } from "@/lib/utils";
interface CalendarEvent {
id: string;
summary: string;
description?: string;
start: {
dateTime?: string;
date?: string;
};
end: {
dateTime?: string;
date?: string;
};
location?: string;
calendarId: string;
calendarName: string;
htmlLink?: string;
}
interface EventDetailModalProps {
event: CalendarEvent | null;
isOpen: boolean;
onClose: () => void;
}
function getEventDate(event: { start: { dateTime?: string; date?: string } }): Date {
if (event.start.dateTime) {
return parseISO(event.start.dateTime);
}
if (event.start.date) {
return parseISO(event.start.date);
}
return new Date();
}
function getEventEndDate(event: { end: { dateTime?: string; date?: string } }): Date | null {
if (event.end.dateTime) {
return parseISO(event.end.dateTime);
}
if (event.end.date) {
return parseISO(event.end.date);
}
return null;
}
function isAllDayEvent(event: { start: { dateTime?: string; date?: string } }): boolean {
return !!event.start.date && !event.start.dateTime;
}
function getCalendarColor(calendarName: string): string {
const colors: Record<string, string> = {
personal: "bg-pink-500 text-white",
work: "bg-blue-500 text-white",
health: "bg-green-500 text-white",
social: "bg-purple-500 text-white",
family: "bg-yellow-500 text-black",
task: "bg-orange-500 text-white",
primary: "bg-blue-600 text-white",
};
const lower = calendarName.toLowerCase();
for (const [key, color] of Object.entries(colors)) {
if (lower.includes(key)) return color;
}
return "bg-muted text-muted-foreground";
}
function formatEventTime(event: CalendarEvent): string {
if (isAllDayEvent(event)) {
return "All day";
}
const start = getEventDate(event);
const end = getEventEndDate(event);
if (end) {
return `${format(start, "h:mm a")} - ${format(end, "h:mm a")}`;
}
return format(start, "h:mm a");
}
function formatEventDate(event: CalendarEvent): string {
const date = getEventDate(event);
return format(date, "EEEE, MMMM d, yyyy");
}
export function EventDetailModal({ event, isOpen, onClose }: EventDetailModalProps) {
if (!event) return null;
const eventUrl = event.htmlLink || `https://calendar.google.com/calendar/event?eid=${btoa(event.id)}`;
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<div className="flex items-start gap-3">
<Badge className={cn("shrink-0", getCalendarColor(event.calendarName))}>
{event.calendarName}
</Badge>
<Button
variant="ghost"
size="icon"
className="shrink-0 -mr-2 -mt-2"
onClick={onClose}
>
<X className="w-4 h-4" />
</Button>
</div>
<DialogTitle className="text-xl mt-2">{event.summary}</DialogTitle>
</DialogHeader>
<div className="space-y-4 mt-2">
{/* Date and Time */}
<div className="flex items-start gap-3">
<div className="w-8 h-8 rounded-lg bg-secondary flex items-center justify-center shrink-0">
<Calendar className="w-4 h-4 text-muted-foreground" />
</div>
<div>
<p className="font-medium">{formatEventDate(event)}</p>
<p className="text-sm text-muted-foreground flex items-center gap-1">
<Clock className="w-3 h-3" />
{formatEventTime(event)}
</p>
</div>
</div>
{/* Location */}
{event.location && (
<div className="flex items-start gap-3">
<div className="w-8 h-8 rounded-lg bg-secondary flex items-center justify-center shrink-0">
<MapPin className="w-4 h-4 text-muted-foreground" />
</div>
<div>
<p className="font-medium">Location</p>
<p className="text-sm text-muted-foreground">{event.location}</p>
</div>
</div>
)}
{/* Description */}
{event.description && (
<div className="flex items-start gap-3">
<div className="w-8 h-8 rounded-lg bg-secondary flex items-center justify-center shrink-0">
<FileText className="w-4 h-4 text-muted-foreground" />
</div>
<div className="flex-1 min-w-0">
<p className="font-medium">Description</p>
<div className="text-sm text-muted-foreground mt-1 whitespace-pre-wrap break-words max-h-40 overflow-y-auto">
{event.description}
</div>
</div>
</div>
)}
{/* Actions */}
<div className="flex gap-2 pt-4 border-t">
<Button
variant="outline"
className="flex-1 gap-2"
onClick={() => window.open(eventUrl, "_blank")}
>
<ExternalLink className="w-4 h-4" />
Open in Google Calendar
</Button>
</div>
</div>
</DialogContent>
</Dialog>
);
}