feat: use friendly event date labels

This commit is contained in:
2026-04-09 17:41:37 -04:00
parent 12f2fd95dc
commit e01a7ed1ad
4 changed files with 94 additions and 31 deletions

View File

@@ -18,6 +18,7 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { formatEventRangeLabel } from "@/lib/event-date-format";
import type { CalendarEvent } from "@/lib/types";
interface EventCardProps {
@@ -27,21 +28,6 @@ interface EventCardProps {
}
export const EventCard = ({ event, onEdit, onDelete }: EventCardProps) => {
const formatDateTime = (dateStr: string, allDay: boolean | undefined) => {
return allDay
? 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 = () => {
onEdit({
id: event.id,
@@ -52,12 +38,10 @@ export const EventCard = ({ event, onEdit, onDelete }: EventCardProps) => {
start: event.start,
end: event.end || "",
allDay: event.allDay || false,
recurrenceRule: event.recurrenceRule,
});
};
const endDate =
event.end && !event.allDay ? formatDateTime(event.end, event.allDay) : null;
return (
<motion.div
layout
@@ -66,15 +50,13 @@ export const EventCard = ({ event, onEdit, onDelete }: EventCardProps) => {
exit={{ opacity: 0, y: -8, transition: { duration: 0.15 } }}
transition={{ duration: 0.2 }}
>
<div className="glass-card p-4 group cursor-pointer hover:bg-accent/50 transition-colors duration-150">
<div className="glass-card group cursor-pointer p-4 transition-colors duration-150 hover:bg-accent/50">
<div className="flex items-start gap-3">
<div className="flex-1 min-w-0 space-y-1.5">
<h3 className="font-medium text-sm leading-snug truncate">
{event.title}
</h3>
<div className="min-w-0 flex-1 space-y-1.5">
<h3 className="truncate text-sm font-medium leading-snug">{event.title}</h3>
{event.description && (
<p className="text-xs text-muted-foreground line-clamp-2 leading-relaxed">
<p className="line-clamp-2 text-xs leading-relaxed text-muted-foreground">
{event.description}
</p>
)}
@@ -82,9 +64,7 @@ export const EventCard = ({ event, onEdit, onDelete }: EventCardProps) => {
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground">
<span className="inline-flex items-center gap-1">
<Clock className="h-3 w-3 shrink-0" />
{formatDateTime(event.start, event.allDay)}
{endDate && <span className="text-muted-foreground/50">-</span>}
{endDate}
{formatEventRangeLabel(event)}
</span>
{event.location && (
@@ -98,24 +78,24 @@ export const EventCard = ({ event, onEdit, onDelete }: EventCardProps) => {
<Button
variant="link"
size="sm"
className="gap-1 h-auto p-0 text-xs text-primary/70 hover:text-primary"
className="h-auto gap-1 p-0 text-xs text-primary/70 hover:text-primary"
asChild
>
<a
href={event.url}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
onClick={(currentEvent) => currentEvent.stopPropagation()}
>
<ExternalLink className="h-3 w-3" />
<span className="truncate max-w-[120px]">Link</span>
<span className="max-w-[120px] truncate">Link</span>
</a>
</Button>
)}
</div>
{event.recurrenceRule && (
<RRuleDisplay rrule={event.recurrenceRule} />
<RRuleDisplay rrule={event.recurrenceRule} start={event.start} />
)}
</div>

View File

@@ -0,0 +1,38 @@
import {
format,
isSameDay,
isToday,
isTomorrow,
parseISO,
} from "date-fns";
import type { CalendarEvent } from "@/lib/types";
const getFriendlyDayLabel = (value: Date): string => {
if (isToday(value)) return "Today";
if (isTomorrow(value)) return "Tomorrow";
return format(value, "MMM d, yyyy");
};
export const formatEventStartLabel = (start: string, allDay?: boolean): string => {
const parsed = parseISO(start);
const dayLabel = getFriendlyDayLabel(parsed);
if (allDay) return dayLabel;
return `${dayLabel} · ${format(parsed, "HH:mm")}`;
};
export const formatEventRangeLabel = (event: Pick<CalendarEvent, "start" | "end" | "allDay">): string => {
const startDate = parseISO(event.start);
const startLabel = getFriendlyDayLabel(startDate);
if (event.allDay || !event.end) {
return event.allDay ? startLabel : `${startLabel} · ${format(startDate, "HH:mm")}`;
}
const endDate = parseISO(event.end);
if (isSameDay(startDate, endDate)) {
return `${startLabel} · ${format(startDate, "HH:mm")}${format(endDate, "HH:mm")}`;
}
return `${startLabel} · ${format(startDate, "HH:mm")}${getFriendlyDayLabel(endDate)} · ${format(endDate, "HH:mm")}`;
};