fix: show end date in event popover for multi-day events #318

This commit is contained in:
Linus Rath
2026-05-25 16:28:03 +02:00
parent 534894c38d
commit 42ec34be21
2 changed files with 133 additions and 31 deletions
+47 -11
View File
@@ -8,11 +8,11 @@ import {
X, Clock, MapPin, Video, Users, Repeat, Bell, AlignLeft, X, Clock, MapPin, Video, Users, Repeat, Bell, AlignLeft,
Pencil, Trash2, Copy, Send, Check, Pencil, Trash2, Copy, Send, Check,
} from "lucide-react"; } from "lucide-react";
import { format, parseISO } from "date-fns"; import { format, isSameDay, parseISO } from "date-fns";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import type { CalendarEvent, Calendar, CalendarParticipant } from "@/lib/jmap/types"; import type { CalendarEvent, Calendar, CalendarParticipant } from "@/lib/jmap/types";
import { parseDuration, getEventColor } from "./event-card"; import { parseDuration, getEventColor } from "./event-card";
import { getEventEndDate, getEventStartDate } from "@/lib/calendar-utils"; import { getEventDisplayEndDate, getEventEndDate, getEventStartDate } from "@/lib/calendar-utils";
import { import {
isOrganizer, isOrganizer,
getUserParticipantId, getUserParticipantId,
@@ -143,6 +143,8 @@ export function EventDetailPopover({
const startDate = getEventStartDate(event); const startDate = getEventStartDate(event);
const durationMinutes = parseDuration(event.duration); const durationMinutes = parseDuration(event.duration);
const endDate = getEventEndDate(event); const endDate = getEventEndDate(event);
const displayEndDate = getEventDisplayEndDate(event);
const isMultiDay = !isSameDay(startDate, displayEndDate);
const locationName = useMemo(() => { const locationName = useMemo(() => {
if (!event.locations) return null; if (!event.locations) return null;
@@ -331,16 +333,50 @@ export function EventDetailPopover({
<div className="flex items-start gap-2.5"> <div className="flex items-start gap-2.5">
<Clock className="w-4 h-4 text-muted-foreground mt-0.5 flex-shrink-0" /> <Clock className="w-4 h-4 text-muted-foreground mt-0.5 flex-shrink-0" />
<div className="text-sm"> <div className="text-sm">
<span className="font-medium text-foreground"> {isMultiDay ? (
{formatEventDate(startDate)} event.showWithoutTime ? (
</span> <>
{event.showWithoutTime ? ( <div className="font-medium text-foreground">
<span className="text-muted-foreground ml-1.5">{t("events.all_day")}</span> {formatEventDate(startDate)}
</div>
<div className="font-medium text-foreground">
{formatEventDate(displayEndDate)}
</div>
<div className="text-muted-foreground">{t("events.all_day")}</div>
</>
) : (
<>
<div className="font-medium text-foreground">
{formatEventDate(startDate)}
<span className="ml-1.5 font-normal text-muted-foreground">
{formatTime(startDate)}
</span>
</div>
<div className="font-medium text-foreground">
{formatEventDate(endDate)}
<span className="ml-1.5 font-normal text-muted-foreground">
{formatTime(endDate)}
</span>
</div>
<div className="text-muted-foreground text-xs">
({formatDurationDisplay(durationMinutes)})
</div>
</>
)
) : ( ) : (
<div className="text-muted-foreground"> <>
{formatTime(startDate)} {formatTime(endDate)} <span className="font-medium text-foreground">
<span className="ml-1.5 text-xs">({formatDurationDisplay(durationMinutes)})</span> {formatEventDate(startDate)}
</div> </span>
{event.showWithoutTime ? (
<span className="text-muted-foreground ml-1.5">{t("events.all_day")}</span>
) : (
<div className="text-muted-foreground">
{formatTime(startDate)} {formatTime(endDate)}
<span className="ml-1.5 text-xs">({formatDurationDisplay(durationMinutes)})</span>
</div>
)}
</>
)} )}
</div> </div>
</div> </div>
+86 -20
View File
@@ -5,7 +5,7 @@ import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; 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 { 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 type { CalendarEvent, Calendar, CalendarParticipant, CalendarEventAlert } from "@/lib/jmap/types";
import { parseDuration, getEventColor } from "./event-card"; import { parseDuration, getEventColor } from "./event-card";
import { buildAllDayDuration, getEventDisplayEndDate, getEventEndDate, getEventStartDate, getPrimaryCalendarId } from "@/lib/calendar-utils"; import { buildAllDayDuration, getEventDisplayEndDate, getEventEndDate, getEventStartDate, getPrimaryCalendarId } from "@/lib/calendar-utils";
@@ -621,14 +621,42 @@ export function EventModal({
</div> </div>
</div> </div>
<div className="text-sm"> {(() => {
<span className="font-medium">{formatEventDate(startD)}</span> const displayEnd = getEventDisplayEndDate(event);
{!event.showWithoutTime && ( const multiDay = !isSameDay(startD, displayEnd);
<span className="text-muted-foreground ml-2"> if (multiDay && event.showWithoutTime) {
{format(startD, timeDisplayFmt)} {format(endD, timeDisplayFmt)} return (
</span> <div className="text-sm">
)} <div className="font-medium">{formatEventDate(startD)} </div>
</div> <div className="font-medium">{formatEventDate(displayEnd)}</div>
</div>
);
}
if (multiDay) {
return (
<div className="text-sm">
<div>
<span className="font-medium">{formatEventDate(startD)}</span>
<span className="text-muted-foreground ml-2">{format(startD, timeDisplayFmt)}</span>
</div>
<div>
<span className="font-medium">{formatEventDate(endD)}</span>
<span className="text-muted-foreground ml-2">{format(endD, timeDisplayFmt)}</span>
</div>
</div>
);
}
return (
<div className="text-sm">
<span className="font-medium">{formatEventDate(startD)}</span>
{!event.showWithoutTime && (
<span className="text-muted-foreground ml-2">
{format(startD, timeDisplayFmt)} {format(endD, timeDisplayFmt)}
</span>
)}
</div>
);
})()}
{event.description && ( {event.description && (
<p className="text-sm text-muted-foreground">{event.description}</p> <p className="text-sm text-muted-foreground">{event.description}</p>
@@ -742,17 +770,55 @@ export function EventModal({
<div className="flex items-start gap-2.5"> <div className="flex items-start gap-2.5">
<Clock className="w-4 h-4 text-muted-foreground mt-0.5 flex-shrink-0" /> <Clock className="w-4 h-4 text-muted-foreground mt-0.5 flex-shrink-0" />
<div className="text-sm"> <div className="text-sm">
<span className="font-medium text-foreground"> {(() => {
{formatEventDate(startD)} const displayEnd = getEventDisplayEndDate(event);
</span> const multiDay = !isSameDay(startD, displayEnd);
{event.showWithoutTime ? ( if (multiDay && event.showWithoutTime) {
<span className="text-muted-foreground ml-1.5">{t("events.all_day")}</span> return (
) : ( <>
<div className="text-muted-foreground"> <div className="font-medium text-foreground">{formatEventDate(startD)} </div>
{format(startD, timeDisplayFmt)} {format(endD, timeDisplayFmt)} <div className="font-medium text-foreground">{formatEventDate(displayEnd)}</div>
<span className="ml-1.5 text-xs">({formatDurationDisplay(durMin)})</span> <div className="text-muted-foreground">{t("events.all_day")}</div>
</div> </>
)} );
}
if (multiDay) {
return (
<>
<div className="font-medium text-foreground">
{formatEventDate(startD)}
<span className="ml-1.5 font-normal text-muted-foreground">
{format(startD, timeDisplayFmt)}
</span>
</div>
<div className="font-medium text-foreground">
{formatEventDate(endD)}
<span className="ml-1.5 font-normal text-muted-foreground">
{format(endD, timeDisplayFmt)}
</span>
</div>
<div className="text-muted-foreground text-xs">
({formatDurationDisplay(durMin)})
</div>
</>
);
}
return (
<>
<span className="font-medium text-foreground">
{formatEventDate(startD)}
</span>
{event.showWithoutTime ? (
<span className="text-muted-foreground ml-1.5">{t("events.all_day")}</span>
) : (
<div className="text-muted-foreground">
{format(startD, timeDisplayFmt)} {format(endD, timeDisplayFmt)}
<span className="ml-1.5 text-xs">({formatDurationDisplay(durMin)})</span>
</div>
)}
</>
);
})()}
</div> </div>
</div> </div>