Skip to content

Add event location and online meeting details#70

Merged
felixgollnhuber merged 1 commit intomainfrom
codex/add-event-location-details
Apr 27, 2026
Merged

Add event location and online meeting details#70
felixgollnhuber merged 1 commit intomainfrom
codex/add-event-location-details

Conversation

@felixgollnhuber
Copy link
Copy Markdown
Owner

Summary

  • Add optional event location metadata and online meeting support to event creation and storage.
  • Hide location when an event is marked as online and carry the meeting link through snapshots, manage views, notifications, and ICS export.
  • Update the create-event UI to use an explicit event format choice and render the appropriate fields without layout shift.

Testing

  • Added and updated unit tests for validation, API payloads, snapshot propagation, and ICS output.
  • Ran local UI verification in the browser for the /new flow to confirm the new event format control and conditional fields render correctly.

Copilot AI review requested due to automatic review settings April 27, 2026 21:56
@felixgollnhuber felixgollnhuber merged commit b48dbe2 into main Apr 27, 2026
4 checks passed
@felixgollnhuber felixgollnhuber deleted the codex/add-event-location-details branch April 27, 2026 22:00
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for optional event location metadata and online meeting details throughout event creation, persistence, snapshots, UI surfaces, notifications, and ICS export.

Changes:

  • Extend validation, types, and persistence to support location, isOnlineMeeting, and meetingLink.
  • Propagate the new metadata through snapshots/views and surface it via a shared EventMetaDetails component.
  • Update ICS export to reflect online meetings (hide physical location, include meeting link in description) and expand unit/UI tests accordingly.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/lib/validators.ts Adds optional text schema + meeting-link URL validation and drops irrelevant fields via transform.
src/lib/validators.test.ts Adds validation tests for location/online meeting/link URL requirements.
src/lib/types.ts Extends event create input and snapshot types with location/online-meeting fields.
src/lib/ics.ts Adds online-meeting-aware LOCATION/description content and filters optional ICS lines.
src/lib/i18n/messages.ts Adds EN/DE strings for event format/location/meeting link labels and validation messages.
src/lib/event-service.ts Persists online meeting fields and carries them into snapshots.
src/lib/event-service.test.ts Updates event creation tests to include isOnlineMeeting.
src/lib/availability.ts Includes new fields in snapshot building; enforces hiding location for online events.
src/lib/availability-notifications.tsx Ensures notification snapshots include the new fields.
src/components/public-event-client.tsx Renders shared event meta details on the public event page.
src/components/public-event-client.test.tsx Updates snapshot test helpers to include new snapshot fields.
src/components/manage-event-client.tsx Renders shared event meta details on the manage page.
src/components/manage-event-client.test.tsx Updates manage-view test helpers to include new snapshot fields.
src/components/full-day-availability.tsx Renders shared event meta details for full-day view.
src/components/event-meta-details.tsx New component to display location/online status/meeting link consistently.
src/components/event-heatmap.tsx Renders shared event meta details in the heatmap header.
src/components/create-event-form.tsx Adds “event format” selection UI and conditional location/meeting-link fields + payload wiring.
src/components/create-event-form.test.tsx Adds UI tests for event format control and payload behavior for online vs in-person.
src/app/api/events/route.test.ts Updates API tests for default isOnlineMeeting + new online-meeting payload passthrough.
src/app/api/events/[slug]/ics/route.ts Passes snapshot location/online-meeting/link into ICS builder.
src/app/api/events/[slug]/ics/route.test.ts Validates ICS output for online meetings (LOCATION + meeting link in description).
prisma/schema.prisma Adds new columns to the Event model.
prisma/migrations/20260427143000_add_event_location_details/migration.sql Migration to add location, isOnlineMeeting, and meetingLink columns.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +467 to +515
<div className="space-y-2">
<Label id={eventFieldIds.isOnlineMeeting}>
{messages.createEvent.eventFormatLabel}
</Label>
<div
role="radiogroup"
aria-labelledby={eventFieldIds.isOnlineMeeting}
className="grid grid-cols-1 gap-2 sm:grid-cols-2"
>
<button
type="button"
role="radio"
aria-checked={!isOnlineMeeting}
className={cn(
"flex h-10 items-center justify-center gap-2 rounded-md border px-3 text-sm font-medium transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
!isOnlineMeeting
? "border-primary bg-primary/10 text-foreground"
: "border-input bg-background text-muted-foreground",
)}
onClick={() => {
setIsOnlineMeeting(false);
setMeetingLink("");
clearErrors("isOnlineMeeting", "meetingLink");
}}
>
<MapPinIcon className="size-4" aria-hidden="true" />
{messages.createEvent.inPersonLabel}
</button>
<button
type="button"
role="radio"
aria-checked={isOnlineMeeting}
className={cn(
"flex h-10 items-center justify-center gap-2 rounded-md border px-3 text-sm font-medium transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
isOnlineMeeting
? "border-primary bg-primary/10 text-foreground"
: "border-input bg-background text-muted-foreground",
)}
onClick={() => {
setIsOnlineMeeting(true);
setLocation("");
clearErrors("isOnlineMeeting", "location");
}}
>
<VideoIcon className="size-4" aria-hidden="true" />
{messages.createEvent.onlineMeetingLabel}
</button>
</div>
</div>
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new event-format control uses ARIA role="radiogroup"/role="radio" on <div>/<button> but doesn’t implement the required radio-group keyboard behavior (roving tabIndex, arrow-key navigation, Space/Enter activation). This will be confusing for keyboard and assistive-technology users because both buttons remain in the tab order and arrow keys won’t work as expected. Consider switching to native <input type="radio"> elements (or a shared RadioGroup component) and implementing the standard keyboard interactions.

Suggested change
<div className="space-y-2">
<Label id={eventFieldIds.isOnlineMeeting}>
{messages.createEvent.eventFormatLabel}
</Label>
<div
role="radiogroup"
aria-labelledby={eventFieldIds.isOnlineMeeting}
className="grid grid-cols-1 gap-2 sm:grid-cols-2"
>
<button
type="button"
role="radio"
aria-checked={!isOnlineMeeting}
className={cn(
"flex h-10 items-center justify-center gap-2 rounded-md border px-3 text-sm font-medium transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
!isOnlineMeeting
? "border-primary bg-primary/10 text-foreground"
: "border-input bg-background text-muted-foreground",
)}
onClick={() => {
setIsOnlineMeeting(false);
setMeetingLink("");
clearErrors("isOnlineMeeting", "meetingLink");
}}
>
<MapPinIcon className="size-4" aria-hidden="true" />
{messages.createEvent.inPersonLabel}
</button>
<button
type="button"
role="radio"
aria-checked={isOnlineMeeting}
className={cn(
"flex h-10 items-center justify-center gap-2 rounded-md border px-3 text-sm font-medium transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
isOnlineMeeting
? "border-primary bg-primary/10 text-foreground"
: "border-input bg-background text-muted-foreground",
)}
onClick={() => {
setIsOnlineMeeting(true);
setLocation("");
clearErrors("isOnlineMeeting", "location");
}}
>
<VideoIcon className="size-4" aria-hidden="true" />
{messages.createEvent.onlineMeetingLabel}
</button>
</div>
</div>
<fieldset className="space-y-2">
<legend
id={eventFieldIds.isOnlineMeeting}
className="text-sm font-medium leading-none"
>
{messages.createEvent.eventFormatLabel}
</legend>
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
<label
className={cn(
"flex h-10 cursor-pointer items-center justify-center gap-2 rounded-md border px-3 text-sm font-medium transition-colors hover:bg-muted/40",
"focus-within:outline-none focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
!isOnlineMeeting
? "border-primary bg-primary/10 text-foreground"
: "border-input bg-background text-muted-foreground",
)}
>
<input
type="radio"
name="event-format"
className="sr-only"
checked={!isOnlineMeeting}
onChange={() => {
setIsOnlineMeeting(false);
setMeetingLink("");
clearErrors("isOnlineMeeting", "meetingLink");
}}
/>
<MapPinIcon className="size-4" aria-hidden="true" />
{messages.createEvent.inPersonLabel}
</label>
<label
className={cn(
"flex h-10 cursor-pointer items-center justify-center gap-2 rounded-md border px-3 text-sm font-medium transition-colors hover:bg-muted/40",
"focus-within:outline-none focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
isOnlineMeeting
? "border-primary bg-primary/10 text-foreground"
: "border-input bg-background text-muted-foreground",
)}
>
<input
type="radio"
name="event-format"
className="sr-only"
checked={isOnlineMeeting}
onChange={() => {
setIsOnlineMeeting(true);
setLocation("");
clearErrors("isOnlineMeeting", "location");
}}
/>
<VideoIcon className="size-4" aria-hidden="true" />
{messages.createEvent.onlineMeetingLabel}
</label>
</div>
</fieldset>

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants