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
+38 -2
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,6 +333,38 @@ 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">
{isMultiDay ? (
event.showWithoutTime ? (
<>
<div className="font-medium text-foreground">
{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>
</>
)
) : (
<>
<span className="font-medium text-foreground"> <span className="font-medium text-foreground">
{formatEventDate(startDate)} {formatEventDate(startDate)}
</span> </span>
@@ -342,6 +376,8 @@ export function EventDetailPopover({
<span className="ml-1.5 text-xs">({formatDurationDisplay(durationMinutes)})</span> <span className="ml-1.5 text-xs">({formatDurationDisplay(durationMinutes)})</span>
</div> </div>
)} )}
</>
)}
</div> </div>
</div> </div>
+67 -1
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,6 +621,32 @@ export function EventModal({
</div> </div>
</div> </div>
{(() => {
const displayEnd = getEventDisplayEndDate(event);
const multiDay = !isSameDay(startD, displayEnd);
if (multiDay && event.showWithoutTime) {
return (
<div className="text-sm">
<div className="font-medium">{formatEventDate(startD)} </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"> <div className="text-sm">
<span className="font-medium">{formatEventDate(startD)}</span> <span className="font-medium">{formatEventDate(startD)}</span>
{!event.showWithoutTime && ( {!event.showWithoutTime && (
@@ -629,6 +655,8 @@ export function EventModal({
</span> </span>
)} )}
</div> </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,6 +770,41 @@ 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">
{(() => {
const displayEnd = getEventDisplayEndDate(event);
const multiDay = !isSameDay(startD, displayEnd);
if (multiDay && event.showWithoutTime) {
return (
<>
<div className="font-medium text-foreground">{formatEventDate(startD)} </div>
<div className="font-medium text-foreground">{formatEventDate(displayEnd)}</div>
<div className="text-muted-foreground">{t("events.all_day")}</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"> <span className="font-medium text-foreground">
{formatEventDate(startD)} {formatEventDate(startD)}
</span> </span>
@@ -753,6 +816,9 @@ export function EventModal({
<span className="ml-1.5 text-xs">({formatDurationDisplay(durMin)})</span> <span className="ml-1.5 text-xs">({formatDurationDisplay(durMin)})</span>
</div> </div>
)} )}
</>
);
})()}
</div> </div>
</div> </div>