From 42ec34be21b82e5a564e030de741d85b143a9c0f Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Mon, 25 May 2026 16:28:03 +0200 Subject: [PATCH] fix: show end date in event popover for multi-day events #318 --- components/calendar/event-detail-popover.tsx | 58 ++++++++-- components/calendar/event-modal.tsx | 106 +++++++++++++++---- 2 files changed, 133 insertions(+), 31 deletions(-) diff --git a/components/calendar/event-detail-popover.tsx b/components/calendar/event-detail-popover.tsx index 78de3c28..1f0e4f67 100644 --- a/components/calendar/event-detail-popover.tsx +++ b/components/calendar/event-detail-popover.tsx @@ -8,11 +8,11 @@ import { X, Clock, MapPin, Video, Users, Repeat, Bell, AlignLeft, Pencil, Trash2, Copy, Send, Check, } from "lucide-react"; -import { format, parseISO } from "date-fns"; +import { format, isSameDay, parseISO } from "date-fns"; import { cn } from "@/lib/utils"; import type { CalendarEvent, Calendar, CalendarParticipant } from "@/lib/jmap/types"; import { parseDuration, getEventColor } from "./event-card"; -import { getEventEndDate, getEventStartDate } from "@/lib/calendar-utils"; +import { getEventDisplayEndDate, getEventEndDate, getEventStartDate } from "@/lib/calendar-utils"; import { isOrganizer, getUserParticipantId, @@ -143,6 +143,8 @@ export function EventDetailPopover({ const startDate = getEventStartDate(event); const durationMinutes = parseDuration(event.duration); const endDate = getEventEndDate(event); + const displayEndDate = getEventDisplayEndDate(event); + const isMultiDay = !isSameDay(startDate, displayEndDate); const locationName = useMemo(() => { if (!event.locations) return null; @@ -331,16 +333,50 @@ export function EventDetailPopover({
- - {formatEventDate(startDate)} - - {event.showWithoutTime ? ( - {t("events.all_day")} + {isMultiDay ? ( + event.showWithoutTime ? ( + <> +
+ {formatEventDate(startDate)} – +
+
+ {formatEventDate(displayEndDate)} +
+
{t("events.all_day")}
+ + ) : ( + <> +
+ {formatEventDate(startDate)} + + {formatTime(startDate)} + +
+
+ {formatEventDate(endDate)} + + {formatTime(endDate)} + +
+
+ ({formatDurationDisplay(durationMinutes)}) +
+ + ) ) : ( -
- {formatTime(startDate)} – {formatTime(endDate)} - ({formatDurationDisplay(durationMinutes)}) -
+ <> + + {formatEventDate(startDate)} + + {event.showWithoutTime ? ( + {t("events.all_day")} + ) : ( +
+ {formatTime(startDate)} – {formatTime(endDate)} + ({formatDurationDisplay(durationMinutes)}) +
+ )} + )}
diff --git a/components/calendar/event-modal.tsx b/components/calendar/event-modal.tsx index 1d815207..c0d7c87a 100644 --- a/components/calendar/event-modal.tsx +++ b/components/calendar/event-modal.tsx @@ -5,7 +5,7 @@ import { useTranslations } from "next-intl"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { X, Trash2, Check, Users, CalendarDays, Copy, Pencil, Clock, MapPin, Video, Repeat, Bell, AlignLeft, Plus } from "lucide-react"; -import { format, parseISO, addHours, addDays } from "date-fns"; +import { format, parseISO, addHours, addDays, isSameDay } from "date-fns"; import type { CalendarEvent, Calendar, CalendarParticipant, CalendarEventAlert } from "@/lib/jmap/types"; import { parseDuration, getEventColor } from "./event-card"; import { buildAllDayDuration, getEventDisplayEndDate, getEventEndDate, getEventStartDate, getPrimaryCalendarId } from "@/lib/calendar-utils"; @@ -621,14 +621,42 @@ export function EventModal({ -
- {formatEventDate(startD)} - {!event.showWithoutTime && ( - - {format(startD, timeDisplayFmt)} – {format(endD, timeDisplayFmt)} - - )} -
+ {(() => { + const displayEnd = getEventDisplayEndDate(event); + const multiDay = !isSameDay(startD, displayEnd); + if (multiDay && event.showWithoutTime) { + return ( +
+
{formatEventDate(startD)} –
+
{formatEventDate(displayEnd)}
+
+ ); + } + if (multiDay) { + return ( +
+
+ {formatEventDate(startD)} + {format(startD, timeDisplayFmt)} +
+
+ {formatEventDate(endD)} + {format(endD, timeDisplayFmt)} +
+
+ ); + } + return ( +
+ {formatEventDate(startD)} + {!event.showWithoutTime && ( + + {format(startD, timeDisplayFmt)} – {format(endD, timeDisplayFmt)} + + )} +
+ ); + })()} {event.description && (

{event.description}

@@ -742,17 +770,55 @@ export function EventModal({
- - {formatEventDate(startD)} - - {event.showWithoutTime ? ( - {t("events.all_day")} - ) : ( -
- {format(startD, timeDisplayFmt)} – {format(endD, timeDisplayFmt)} - ({formatDurationDisplay(durMin)}) -
- )} + {(() => { + const displayEnd = getEventDisplayEndDate(event); + const multiDay = !isSameDay(startD, displayEnd); + if (multiDay && event.showWithoutTime) { + return ( + <> +
{formatEventDate(startD)} –
+
{formatEventDate(displayEnd)}
+
{t("events.all_day")}
+ + ); + } + if (multiDay) { + return ( + <> +
+ {formatEventDate(startD)} + + {format(startD, timeDisplayFmt)} + +
+
+ {formatEventDate(endD)} + + {format(endD, timeDisplayFmt)} + +
+
+ ({formatDurationDisplay(durMin)}) +
+ + ); + } + return ( + <> + + {formatEventDate(startD)} + + {event.showWithoutTime ? ( + {t("events.all_day")} + ) : ( +
+ {format(startD, timeDisplayFmt)} – {format(endD, timeDisplayFmt)} + ({formatDurationDisplay(durMin)}) +
+ )} + + ); + })()}