@@ -15,6 +15,8 @@ import { Badge } from "@/components/ui/badge";
1515import { EventActions } from "./EventActions" ;
1616import { cn } from "@/lib/utils" ;
1717import { useTranslation } from "react-i18next" ;
18+ import { AddToGoogleCalendarLink } from "@/components/shared/AddToGoogleCalendarLink" ;
19+ import { combineDateAndTimeLocal , parseISODateLocal } from "@/lib/googleCalendar" ;
1820
1921interface EventCardProps {
2022 event : EventWithGroups ;
@@ -46,29 +48,60 @@ export function EventCard({
4648 timeDisplay = `${ t ( "events:endsAt" ) } ${ event . end_time } ` ;
4749 }
4850
51+ const calendarLabel = t ( "events:addToGoogleCalendar" ) ;
52+ const hasAnyTime = ! ! event . start_time || ! ! event . end_time ;
53+ const calendarStart = hasAnyTime
54+ ? ( ( event . start_time && combineDateAndTimeLocal ( event . date , event . start_time ) ) ??
55+ parseISODateLocal ( event . date ) ??
56+ new Date ( ) )
57+ : ( parseISODateLocal ( event . date ) ?? new Date ( ) ) ;
58+ const calendarEnd = hasAnyTime
59+ ? ( ( event . end_time && combineDateAndTimeLocal ( event . date , event . end_time ) ) ??
60+ new Date ( calendarStart . getTime ( ) + 60 * 60 * 1000 ) )
61+ : calendarStart ;
62+ const calendarDetails = [
63+ event . description ?? "" ,
64+ event . event_link ? `\n\n${ event . event_link } ` : "" ,
65+ ] . join ( "" ) ;
66+
4967 return (
5068 < Card className = { cn ( "relative" , event . visibility === "private" && "border-primary/30" ) } >
51- { isEditable && (
52- < EventActions
53- event = { event }
54- onEventUpdated = { onEventUpdated }
55- onDeleteEvent = { onDeleteEvent }
56- onCopyEvent = { onCopyEvent }
57- allGroups = { allGroups }
58- />
59- ) }
60-
6169 < CardHeader >
62- < CardTitle > { event . name } </ CardTitle >
63- < CardDescription className = "flex items-center gap-1" >
64- < Calendar className = "h-4 w-4" /> { formattedDate }
65- { hasTime && (
66- < >
67- < span className = "mx-1" > •</ span >
68- < Clock className = "h-4 w-4" /> { timeDisplay }
69- </ >
70- ) }
71- </ CardDescription >
70+ < div className = "flex items-start justify-between gap-3" >
71+ < div className = "min-w-0" >
72+ < CardTitle className = "truncate" > { event . name } </ CardTitle >
73+ < CardDescription className = "flex items-center gap-1" >
74+ < Calendar className = "h-4 w-4" /> { formattedDate }
75+ { hasTime && (
76+ < >
77+ < span className = "mx-1" > •</ span >
78+ < Clock className = "h-4 w-4" /> { timeDisplay }
79+ </ >
80+ ) }
81+ </ CardDescription >
82+ </ div >
83+ < div className = "flex shrink-0 items-center gap-2" >
84+ < AddToGoogleCalendarLink
85+ title = { event . name }
86+ start = { calendarStart }
87+ end = { calendarEnd }
88+ isAllDay = { ! hasAnyTime }
89+ details = { calendarDetails }
90+ label = { calendarLabel }
91+ className = "px-0"
92+ />
93+ { isEditable && (
94+ < EventActions
95+ className = "shrink-0"
96+ event = { event }
97+ onEventUpdated = { onEventUpdated }
98+ onDeleteEvent = { onDeleteEvent }
99+ onCopyEvent = { onCopyEvent }
100+ allGroups = { allGroups }
101+ />
102+ ) }
103+ </ div >
104+ </ div >
72105 </ CardHeader >
73106
74107 { event . description && (
@@ -77,36 +110,34 @@ export function EventCard({
77110 </ CardContent >
78111 ) }
79112
80- { ( event . event_link || ( event . groups && event . groups . length > 0 ) ) && (
81- < CardFooter className = "flex flex-wrap gap-2" >
82- { event . event_link && (
83- < a
84- href = { event . event_link }
85- target = "_blank"
86- rel = "noopener noreferrer"
87- className = "text-sm text-primary underline hover:no-underline"
88- >
89- { t ( "events:eventLinkText" ) }
90- </ a >
91- ) }
92-
93- { event . groups &&
94- event . groups . length > 0 &&
95- event . groups . map ( ( group ) =>
96- group ? (
97- < Badge key = { group . id } variant = "outline" >
98- { group . name }
99- </ Badge >
100- ) : null ,
101- ) }
113+ < CardFooter className = "flex flex-wrap gap-2" >
114+ { event . event_link && (
115+ < a
116+ href = { event . event_link }
117+ target = "_blank"
118+ rel = "noopener noreferrer"
119+ className = "text-sm text-primary underline hover:no-underline"
120+ >
121+ { t ( "events:eventLinkText" ) }
122+ </ a >
123+ ) }
102124
103- { event . visibility === "private" && (
104- < Badge variant = "secondary" className = "ml-auto" >
105- { t ( "events:private" ) }
106- </ Badge >
125+ { event . groups &&
126+ event . groups . length > 0 &&
127+ event . groups . map ( ( group ) =>
128+ group ? (
129+ < Badge key = { group . id } variant = "outline" >
130+ { group . name }
131+ </ Badge >
132+ ) : null ,
107133 ) }
108- </ CardFooter >
109- ) }
134+
135+ { event . visibility === "private" && (
136+ < Badge variant = "secondary" className = "ml-auto" >
137+ { t ( "events:private" ) }
138+ </ Badge >
139+ ) }
140+ </ CardFooter >
110141 </ Card >
111142 ) ;
112143}
0 commit comments