From c80322f20a4c75ec0cdb6ad043859b2af00ec56f Mon Sep 17 00:00:00 2001 From: Dmytro Stanchiev Date: Wed, 8 Apr 2026 00:56:28 -0400 Subject: [PATCH] feat(event-card): redesign with motion layout animations, improved date formatting, and contextual metadata - Wrap card in motion.div with layout/enter/exit animations - Replace Card/CardHeader/CardContent with flat glass-card div - Add hover-reveal action menu with opacity transition - Improve date formatting with locale options for month/day/time - Show end time inline next to start - Add ExternalLink for event URLs - Add icons to dropdown menu items with DropdownMenuSeparator --- src/components/event-card.tsx | 129 ++++++++++++++++++++++++---------- 1 file changed, 90 insertions(+), 39 deletions(-) diff --git a/src/components/event-card.tsx b/src/components/event-card.tsx index c866345..caf1a1a 100644 --- a/src/components/event-card.tsx +++ b/src/components/event-card.tsx @@ -1,11 +1,21 @@ -import { Clock, LucideMapPin, MoreHorizontal } from "lucide-react"; +"use client"; + +import { motion } from "framer-motion"; +import { + Clock, + ExternalLink, + LucideMapPin, + MoreHorizontal, + Pencil, + Trash2, +} from "lucide-react"; import { RRuleDisplay } from "@/components/rrule-display"; import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader } from "@/components/ui/card"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, + DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import type { CalendarEvent } from "@/lib/types"; @@ -19,8 +29,17 @@ interface EventCardProps { export const EventCard = ({ event, onEdit, onDelete }: EventCardProps) => { const formatDateTime = (dateStr: string, allDay: boolean | undefined) => { return allDay - ? new Date(dateStr).toLocaleDateString() - : new Date(dateStr).toLocaleString(); + ? new Date(dateStr).toLocaleDateString(undefined, { + month: "short", + day: "numeric", + year: "numeric", + }) + : new Date(dateStr).toLocaleString(undefined, { + month: "short", + day: "numeric", + hour: "numeric", + minute: "2-digit", + }); }; const handleEdit = () => { @@ -36,60 +55,92 @@ export const EventCard = ({ event, onEdit, onDelete }: EventCardProps) => { }); }; + const endDate = + event.end && !event.allDay ? formatDateTime(event.end, event.allDay) : null; + return ( - - -
-
-

+ +
+
+
+

{event.title}

- {event.recurrenceRule && ( -
- -
- )} + {event.description && ( -

+

{event.description}

)} + +
+ + + {formatDateTime(event.start, event.allDay)} + {endDate && -} + {endDate} + + + {event.location && ( + + + {event.location} + + )} + + {event.url && ( + e.stopPropagation()} + > + + Link + + )} +
+ + {event.recurrenceRule && ( + + )}
+ - - - Edit + + + + Edit + + onDelete(event.id)} - className="text-destructive" + className="text-destructive focus:text-destructive" > + Delete
- - - -
-
- - {formatDateTime(event.start, event.allDay)} -
- - {event.location && ( -
- - {event.location} -
- )} -
-
- +
+
); };