feat: implement frontend for calendar integration (Phase 3 & 4)

## Frontend Implementation

### Meeting Selection & Management
- Created MeetingSelection component for choosing between multiple active meetings
- Shows both active meetings and upcoming calendar events (30 min ahead)
- Displays meeting metadata with privacy controls (owner-only details)
- Supports creation of unscheduled meetings alongside calendar meetings

### Waiting Room
- Added waiting page for users joining before scheduled start time
- Shows countdown timer until meeting begins
- Auto-transitions to meeting when calendar event becomes active
- Handles early joining with proper routing

### Meeting Info Panel
- Created collapsible info panel showing meeting details
- Displays calendar metadata (title, description, attendees)
- Shows participant count and duration
- Privacy-aware: sensitive info only visible to room owners

### ICS Configuration UI
- Integrated ICS settings into room configuration dialog
- Test connection functionality with immediate feedback
- Manual sync trigger with detailed results
- Shows last sync time and ETag for monitoring
- Configurable sync intervals (1 min to 1 hour)

### Routing & Navigation
- New /room/{roomName} route for meeting selection
- Waiting room at /room/{roomName}/wait?eventId={id}
- Classic room page at /{roomName} with meeting info
- Uses sessionStorage to pass selected meeting between pages

### API Integration
- Added new endpoints for active/upcoming meetings
- Regenerated TypeScript client with latest OpenAPI spec
- Proper error handling and loading states
- Auto-refresh every 30 seconds for live updates

### UI/UX Improvements
- Color-coded badges for meeting status
- Attendee status indicators (accepted/declined/tentative)
- Responsive design with Chakra UI components
- Clear visual hierarchy between active and upcoming meetings
- Smart truncation for long attendee lists

This completes the frontend implementation for calendar integration,
enabling users to seamlessly join scheduled meetings from their
calendar applications.
This commit is contained in:
2025-08-18 19:29:56 -06:00
parent f286f0882c
commit 311d453e41
12 changed files with 2082 additions and 42 deletions

View File

@@ -16,6 +16,18 @@ import type {
V1RoomsDeleteResponse,
V1RoomsCreateMeetingData,
V1RoomsCreateMeetingResponse,
V1RoomsSyncIcsData,
V1RoomsSyncIcsResponse,
V1RoomsIcsStatusData,
V1RoomsIcsStatusResponse,
V1RoomsListMeetingsData,
V1RoomsListMeetingsResponse,
V1RoomsListUpcomingMeetingsData,
V1RoomsListUpcomingMeetingsResponse,
V1RoomsListActiveMeetingsData,
V1RoomsListActiveMeetingsResponse,
V1RoomsJoinMeetingData,
V1RoomsJoinMeetingResponse,
V1TranscriptsListData,
V1TranscriptsListResponse,
V1TranscriptsCreateData,
@@ -227,6 +239,146 @@ export class DefaultService {
});
}
/**
* Rooms Sync Ics
* @param data The data for the request.
* @param data.roomName
* @returns ICSSyncResult Successful Response
* @throws ApiError
*/
public v1RoomsSyncIcs(
data: V1RoomsSyncIcsData,
): CancelablePromise<V1RoomsSyncIcsResponse> {
return this.httpRequest.request({
method: "POST",
url: "/v1/rooms/{room_name}/ics/sync",
path: {
room_name: data.roomName,
},
errors: {
422: "Validation Error",
},
});
}
/**
* Rooms Ics Status
* @param data The data for the request.
* @param data.roomName
* @returns ICSStatus Successful Response
* @throws ApiError
*/
public v1RoomsIcsStatus(
data: V1RoomsIcsStatusData,
): CancelablePromise<V1RoomsIcsStatusResponse> {
return this.httpRequest.request({
method: "GET",
url: "/v1/rooms/{room_name}/ics/status",
path: {
room_name: data.roomName,
},
errors: {
422: "Validation Error",
},
});
}
/**
* Rooms List Meetings
* @param data The data for the request.
* @param data.roomName
* @returns CalendarEventResponse Successful Response
* @throws ApiError
*/
public v1RoomsListMeetings(
data: V1RoomsListMeetingsData,
): CancelablePromise<V1RoomsListMeetingsResponse> {
return this.httpRequest.request({
method: "GET",
url: "/v1/rooms/{room_name}/meetings",
path: {
room_name: data.roomName,
},
errors: {
422: "Validation Error",
},
});
}
/**
* Rooms List Upcoming Meetings
* @param data The data for the request.
* @param data.roomName
* @param data.minutesAhead
* @returns CalendarEventResponse Successful Response
* @throws ApiError
*/
public v1RoomsListUpcomingMeetings(
data: V1RoomsListUpcomingMeetingsData,
): CancelablePromise<V1RoomsListUpcomingMeetingsResponse> {
return this.httpRequest.request({
method: "GET",
url: "/v1/rooms/{room_name}/meetings/upcoming",
path: {
room_name: data.roomName,
},
query: {
minutes_ahead: data.minutesAhead,
},
errors: {
422: "Validation Error",
},
});
}
/**
* Rooms List Active Meetings
* List all active meetings for a room (supports multiple active meetings)
* @param data The data for the request.
* @param data.roomName
* @returns Meeting Successful Response
* @throws ApiError
*/
public v1RoomsListActiveMeetings(
data: V1RoomsListActiveMeetingsData,
): CancelablePromise<V1RoomsListActiveMeetingsResponse> {
return this.httpRequest.request({
method: "GET",
url: "/v1/rooms/{room_name}/meetings/active",
path: {
room_name: data.roomName,
},
errors: {
422: "Validation Error",
},
});
}
/**
* Rooms Join Meeting
* Join a specific meeting by ID
* @param data The data for the request.
* @param data.roomName
* @param data.meetingId
* @returns Meeting Successful Response
* @throws ApiError
*/
public v1RoomsJoinMeeting(
data: V1RoomsJoinMeetingData,
): CancelablePromise<V1RoomsJoinMeetingResponse> {
return this.httpRequest.request({
method: "POST",
url: "/v1/rooms/{room_name}/meetings/{meeting_id}/join",
path: {
room_name: data.roomName,
meeting_id: data.meetingId,
},
errors: {
422: "Validation Error",
},
});
}
/**
* Transcripts List
* @param data The data for the request.