Meetings System
Overview
The meetings system allows company users to create, manage, and analyze meetings. It supports standalone meetings (not tied to interviews) with attendee management, personalized join links, AI-powered meeting insights, and a calendar view.
URL: /company/meetings (list + calendar) | /company/meetings/[meetingId] (detail)
Architecture
File Map
| Path | Purpose |
|---|---|
app/company/meetings/page.tsx | Main list/calendar page with compact stats, tab-based view switching |
app/company/meetings/[meetingId]/page.tsx | Meeting detail page with attendees, actions, AI insights |
components/meetings/CreateMeetingModal.tsx | Create meeting form with recurrence options |
components/meetings/UpdateMeetingModal.tsx | Edit existing meeting details |
components/meetings/CancelMeetingModal.tsx | Cancel meeting with optional reason |
components/meetings/MeetingsCalendar.tsx | Custom monthly calendar grid component |
components/meetings/index.ts | Barrel exports |
components/meetingInsights/ | AI insight viewer, generation button, history modal |
graphql/operations/interview/queries.ts | GET_MY_MEETINGS, GET_MEETING_BY_ID, GET_MY_MEETING_LINK |
graphql/operations/interview/mutations.ts | CREATE_MEETING, UPDATE_MEETING, CANCEL_MEETING, COMPLETE_MEETING, attendee mutations |
graphql/operations/meetingInsights/ | Insight queries and mutations |
lib/hooks/useMeetingInsights.ts | Insight generation hook with WebSocket subscription |
types/meetingInsights.ts | TypeScript types for meeting insights |
Data Flow
GET_MY_MEETINGS (Apollo) → meetings[]
├─ List View: MeetingCard components with pagination
└─ Calendar View: MeetingsCalendar (groups by day, renders grid)
GET_MEETING_BY_ID → meeting detail
├─ Schedule, attendees, organizer info
├─ Action buttons (edit, complete, cancel)
└─ AI Insights (when COMPLETED)
GraphQL Operations
Queries
| Query | Variables | Returns |
|---|---|---|
GET_MY_MEETINGS | { input: { limit, includeAttendedMeetings } } | id, title, status, scheduledAt, duration, meetingUrl, roomName, interviewBookingId |
GET_MEETING_BY_ID | { id } | Full meeting with organizer, attendees, latestInsight, meetingInsights[] |
GET_MY_MEETING_LINK | { meetingId } | { meetingUrl } — personalized join link |
Mutations
| Mutation | Purpose |
|---|---|
CREATE_MEETING | Create meeting with title, type, scheduledAt, duration, timezone, attendees, metadata |
UPDATE_MEETING | Update title, description, scheduledAt, duration |
CANCEL_MEETING | Cancel with optional reason |
COMPLETE_MEETING | Mark as completed (enables AI insights) |
ADD_MEETING_ATTENDEES | Add attendees post-creation |
REMOVE_MEETING_ATTENDEE | Remove single attendee by email |
Meeting Types & Statuses
Types: GENERAL, ONE_ON_ONE, ALL_HANDS, TRAINING, PANEL
Statuses:
SCHEDULED— Created, upcoming (primary color)ONGOING— Currently active (amber)COMPLETED— Finished, AI insights available (green)CANCELLED— Cancelled with optional reason (red)
Views
List View (default)
- Compact stats bar showing meeting counts by status
- Search + status filter + Create Meeting button
MeetingCardcomponents in vertical list with pagination- Each card shows: title, room name, status badge, date/time, duration, recurrence indicator
- "View Details" and "Join" action buttons
Calendar View
Custom monthly calendar grid (MeetingsCalendar.tsx), no external library.
Features:
- Month navigation (prev/next) with "Today" quick button
- 7-column day grid with day-of-week headers
- Meeting indicators: colored bars with truncated titles (desktop), dots (mobile)
- Status-color coded: primary (scheduled), amber (ongoing), green (completed), red (cancelled)
- Click a day to expand detail panel below showing all meetings for that day
- Meetings grouped client-side via
Map<"YYYY-M-D", Meeting[]>
Responsive:
- Desktop:
min-h-[80px]cells, shows up to 2 meeting titles + "+N more" - Mobile:
min-h-[60px]cells, shows up to 4 colored dots
Tab Switching
Uses icon-only toggle buttons (List/Calendar icons) in a segmented control. Both views share the same filteredMeetings data from the Apollo query.
Recurring Meetings
Recurrence is a frontend-only UI feature — data is stored in the metadata JSON field of the meeting.
UI (CreateMeetingModal & UpdateMeetingModal)
- Repeat type: "Does not repeat" | "Daily" | "Weekly" | "Bi-weekly" | "Monthly"
- Day selection (Weekly/Bi-weekly only): Circular day-of-week toggles (Sun–Sat) to choose which days the meeting repeats on
- End condition (when repeat != NONE):
- "After N occurrences" — number input (1-365)
- "Until date" — date picker
- Monthly recurrence shows a note: "Repeats on day N of each month" (based on selected date)
Metadata Shape
{
"createdFrom": "frontend",
"createdAt": "2026-03-12T...",
"recurrence": {
"type": "WEEKLY",
"days": [1, 3, 5],
"endType": "OCCURRENCES",
"occurrences": 10
}
}
daysis an array of weekday indices (0=Sun, 1=Mon, ... 6=Sat), only present for WEEKLY/BIWEEKLY with specific days selected.
Or with end date:
{
"recurrence": {
"type": "MONTHLY",
"endType": "UNTIL",
"endDate": "2026-06-15"
}
}
Calendar Expansion
The MeetingsCalendar component expands recurring meetings across the visible month:
- Daily: Shows on every day from start until end condition
- Weekly + days: Shows on each selected weekday, every week from start
- Bi-weekly + days: Shows on each selected weekday, every other week from start
- Monthly: Shows on the same day-of-month each month
Display
Meeting cards show a RotateIcon + label (e.g., "Weekly") when metadata.recurrence.type is not "NONE".
Join Meeting Flow
Both the list page and detail page use GET_MY_MEETING_LINK (lazy query, network-only) to fetch a personalized meeting link per user:
const [fetchLink] = useLazyQuery(GET_MY_MEETING_LINK, { fetchPolicy: "network-only" })
// onClick:
const { data } = await fetchLink({ variables: { meetingId } })
window.open(data.getMyMeetingLink.meetingUrl, "_blank")
This replaces the previous pattern of directly opening meeting.meetingUrl.
AI Meeting Insights
Available only for COMPLETED meetings. Located on the meeting detail page.
Components: InsightGenerationButton, InsightViewer, InsightHistoryModal (in components/meetingInsights/)
Hook: useMeetingInsights — manages generation via WebSocket subscription to background-tasks service.
Flow:
- User clicks "Generate AI Insights" on completed meeting
INITIALIZE_MEETING_INSIGHTmutation creates a GENERATING record- WebSocket subscription (
GenerateMeetingInsight) streams progress 0-100% - On completion,
InsightViewershows tabbed results: Overview, Skills, Communication, Topics, Concerns, Full Report
Insight types: SkillsAssessment, CommunicationAnalysis, KeyTopicsSummary, RedFlagsAndConcerns, OverallRecommendation
Key Component Props
MeetingsCalendar
interface MeetingsCalendarProps {
meetings: any[] // Array of meeting objects from GET_MY_MEETINGS
onViewDetails: (id: string) => void // Navigate to meeting detail
}
CreateMeetingModal
interface CreateMeetingModalProps {
isOpen: boolean
onClose: () => void
onSuccess?: (meetingId: string) => void
}
MeetingCard (internal to page.tsx)
// Props: { meeting: any, onViewDetails: (id: string) => void }
// Memoized component with useLazyQuery for personalized join links