Skip to main content

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

PathPurpose
app/company/meetings/page.tsxMain list/calendar page with compact stats, tab-based view switching
app/company/meetings/[meetingId]/page.tsxMeeting detail page with attendees, actions, AI insights
components/meetings/CreateMeetingModal.tsxCreate meeting form with recurrence options
components/meetings/UpdateMeetingModal.tsxEdit existing meeting details
components/meetings/CancelMeetingModal.tsxCancel meeting with optional reason
components/meetings/MeetingsCalendar.tsxCustom monthly calendar grid component
components/meetings/index.tsBarrel exports
components/meetingInsights/AI insight viewer, generation button, history modal
graphql/operations/interview/queries.tsGET_MY_MEETINGS, GET_MEETING_BY_ID, GET_MY_MEETING_LINK
graphql/operations/interview/mutations.tsCREATE_MEETING, UPDATE_MEETING, CANCEL_MEETING, COMPLETE_MEETING, attendee mutations
graphql/operations/meetingInsights/Insight queries and mutations
lib/hooks/useMeetingInsights.tsInsight generation hook with WebSocket subscription
types/meetingInsights.tsTypeScript 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

QueryVariablesReturns
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

MutationPurpose
CREATE_MEETINGCreate meeting with title, type, scheduledAt, duration, timezone, attendees, metadata
UPDATE_MEETINGUpdate title, description, scheduledAt, duration
CANCEL_MEETINGCancel with optional reason
COMPLETE_MEETINGMark as completed (enables AI insights)
ADD_MEETING_ATTENDEESAdd attendees post-creation
REMOVE_MEETING_ATTENDEERemove 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
  • MeetingCard components 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
}
}
  • days is 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:

  1. User clicks "Generate AI Insights" on completed meeting
  2. INITIALIZE_MEETING_INSIGHT mutation creates a GENERATING record
  3. WebSocket subscription (GenerateMeetingInsight) streams progress 0-100%
  4. On completion, InsightViewer shows 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