1313 * │ Subtitle line 2 │
1414 * │ [Tag] [Tag] │ ← tags (optional)
1515 * │ ───────────────────────────────── │
16- * │ [ RSVP ] [ Add to Calendar ] │ ← actions (each conditional)
16+ * │ [ RSVP / Apply ] [ Add to Calendar ] │ ← actions (each conditional)
1717 * └───────────────────────────────────────┘
1818 *
19- * Uses design system components: DateBadge, Tag, Button
20- * Uses design system SVG: bookmark-filled.svg (always filled — this is the
21- * bookmarks view so every card is already saved)
19+ * Subtitle variants (Figma annotations):
20+ * string[] → event with time + location: ['4:00–5:30 pm', 'Hollister Hall 312']
21+ * string → informative summary or edge-case "Click to see original email"
22+ * undefined → no subtitle shown
2223 *
23- * Subtitle variants (from Figma annotations):
24- * events → string[] e.g. ['4:00 pm – 5:30 pm', 'Hollister Hall 312']
25- * informative → string e.g. 'For early career designers and developers'
26- * edge case → string e.g. 'Click to see original email'
24+ * When `onSubtitleClick` is provided (edge-case event), clicking the subtitle
25+ * opens OriginalEmailView. The cursor changes to pointer to signal interactivity.
2726 */
2827
2928import type { ComponentPropsWithoutRef } from "react" ;
@@ -54,30 +53,42 @@ export interface BookmarkCardProps extends ComponentPropsWithoutRef<"div"> {
5453 thumbnailVariant ?: ThumbnailVariant ;
5554 /** Day-of-month for the "date" thumbnail (e.g. 24). */
5655 day ?: number | string ;
57- /** Abbreviated month for the "date" thumbnail (e.g. "Mar "). */
56+ /** Abbreviated month for the "date" thumbnail (e.g. "Apr "). */
5857 month ?: string ;
5958 /** Event title — DM Sans SemiBold 14 px, Neutral/900. */
6059 title : string ;
6160 /**
62- * Subtitle shown below the title in the event row.
61+ * Subtitle shown below the title.
62+ * string[] → multiple lines (e.g. time + location)
6363 * string → single line (informative summary or edge-case message)
64- * string[] → multiple lines, e.g. ['4:00 pm – 5:30 pm', 'Hollister Hall 312']
6564 */
6665 subtitle ?: string | string [ ] ;
66+ /**
67+ * When provided, clicking the subtitle triggers this handler.
68+ * Used for edge-case events where the subtitle reads "Click to see original email".
69+ */
70+ onSubtitleClick ?: ( ) => void ;
6771 /** Neutral/200 category tags shown beneath the event row. */
6872 tags ?: string [ ] ;
6973 /**
70- * When provided, an RSVP button is rendered.
71- * Figma annotation: "appears only if there's an RSVP link".
74+ * Primary action button (RSVP / Apply / Register).
75+ * Figma annotation: "appears only if there's a primary link".
76+ * Prefer `links[]` → `getPrimaryLink()` to derive this from EventItem.
7277 */
73- onRsvp ?: ( ) => void ;
78+ primaryAction ?: { label : string ; onClick : ( ) => void } ;
7479 /**
7580 * When provided, an Add to Calendar button is rendered.
7681 * Figma annotation: "appears only when there's specific date and time".
7782 */
7883 onAddToCalendar ?: ( ) => void ;
7984 /** Called when the filled bookmark icon is pressed to remove the save. */
8085 onUnbookmark ?: ( ) => void ;
86+ /**
87+ * Hover handlers wired to GCal grid highlight (passed from BookmarkView).
88+ * noop when not on a GCal page.
89+ */
90+ onPreviewEnter ?: ( ) => void ;
91+ onPreviewLeave ?: ( ) => void ;
8192}
8293
8394// ── Component ──────────────────────────────────────────────────────────────
@@ -90,17 +101,21 @@ export function BookmarkCard({
90101 month,
91102 title,
92103 subtitle,
104+ onSubtitleClick,
93105 tags,
94- onRsvp ,
106+ primaryAction ,
95107 onAddToCalendar,
96108 onUnbookmark,
109+ onPreviewEnter,
110+ onPreviewLeave,
97111 className,
98112 ...rest
99113} : BookmarkCardProps ) {
100114 const subtitleLines : string [ ] =
101115 subtitle == null ? [ ] : Array . isArray ( subtitle ) ? subtitle : [ subtitle ] ;
102116
103- const hasActions = onRsvp != null || onAddToCalendar != null ;
117+ const hasActions = primaryAction != null || onAddToCalendar != null ;
118+ const subtitleClickable = onSubtitleClick != null && subtitleLines . length > 0 ;
104119
105120 return (
106121 < div
@@ -114,6 +129,8 @@ export function BookmarkCard({
114129 ]
115130 . filter ( Boolean )
116131 . join ( " " ) }
132+ onMouseEnter = { onPreviewEnter }
133+ onMouseLeave = { onPreviewLeave }
117134 { ...rest }
118135 >
119136 { /* ── Org header ── */ }
@@ -155,28 +172,43 @@ export function BookmarkCard({
155172 { /* ── Middle: event row + tags ── */ }
156173 < div className = "flex w-full flex-col gap-[var(--space-2)]" >
157174 { /* Event row — DateBadge + text + filled bookmark */ }
158- < div className = "flex w-full items-center gap-[var(--space-3)] rounded-[var(--radius-input)] bg-[var(--color-surface)] p-[var(--space-1-5)]" >
175+ < div className = "flex w-full items-start gap-[var(--space-3)] rounded-[var(--radius-input)] bg-[var(--color-surface)] p-[var(--space-1-5)]" >
159176 < DateBadge variant = { thumbnailVariant } day = { day } month = { month } />
160177
161178 { /* Title + subtitle */ }
162179 < div className = "flex min-w-0 flex-1 flex-col gap-[3px]" >
163180 < p
164181 className = {
165182 BODY2_SEMIBOLD +
166- " w-full truncate text-[var(--color-neutral-900)]"
183+ " w-full text-[var(--color-neutral-900)]"
167184 }
168185 style = { { fontVariationSettings : "'opsz' 14" } }
169186 >
170187 { title }
171188 </ p >
172189
173190 { subtitleLines . length > 0 && (
174- < div className = "flex flex-col" >
191+ < div
192+ className = { [
193+ "flex flex-col gap-[1px]" ,
194+ subtitleClickable
195+ ? "cursor-pointer hover:underline"
196+ : "" ,
197+ ] . join ( " " ) }
198+ onClick = { subtitleClickable ? onSubtitleClick : undefined }
199+ role = { subtitleClickable ? "button" : undefined }
200+ tabIndex = { subtitleClickable ? 0 : undefined }
201+ onKeyDown = {
202+ subtitleClickable
203+ ? ( e ) => e . key === "Enter" && onSubtitleClick ?.( )
204+ : undefined
205+ }
206+ >
175207 { subtitleLines . map ( ( line , i ) => (
176208 < p
177209 key = { i }
178210 className = {
179- BODY3 + " w-full truncate text-[var(--color-neutral-700)]"
211+ BODY3 + " w-full text-[var(--color-neutral-700)]"
180212 }
181213 style = { { fontVariationSettings : "'opsz' 14" } }
182214 >
@@ -217,14 +249,14 @@ export function BookmarkCard({
217249 { /* ── Action buttons ── */ }
218250 { hasActions && (
219251 < div className = "flex w-full gap-[var(--space-2)]" >
220- { onRsvp && (
252+ { primaryAction && (
221253 < Button
222254 variant = "secondary"
223255 size = "cta"
224256 className = "flex-1"
225- onClick = { onRsvp }
257+ onClick = { primaryAction . onClick }
226258 >
227- RSVP
259+ { primaryAction . label }
228260 </ Button >
229261 ) }
230262 { onAddToCalendar && (
0 commit comments