Guest Disruptions

Comprehensive system for managing flight schedule changes and passenger impacts

Overview

The Guest Disruptions feature helps airline operations teams identify, analyze, and resolve passenger impacts caused by flight schedule changes. It provides a complete workflow from discovering disrupted itineraries to finding alternative flight pathways for affected passengers.

Architecture

The feature is located at /connections/guest-disruptions and consists of four integrated components:

Main Table

Searchable, filterable table of all disrupted itineraries

Schedule Changes

Timeline view of flight modifications

Impacted Dates

Calendar visualization with passenger/revenue metrics

Alternative Pathways

Available rebooking options

File Structure

guest-disruptions-table.tsx
guest-disruption-expanded-row.tsx
guest-disruption-filter-button.tsx
guest-disruption-filter-context.tsx

Data Models

Core Types

Prop

Type

Prop

Type

Route Segment Grouping:

Prop

Type

Prop

Type

Flight Segment:

Prop

Type

Complete Itinerary:

Prop

Type

With Comparison Data:

Prop

Type

API Types

Main Components

1. Guest Disruptions Table

The main table provides a comprehensive view of all disrupted itineraries with powerful filtering and sorting capabilities.

Features

  • Server-side pagination: Handles large datasets efficiently (10k+ disruptions)
  • Real-time filtering: Instant updates with React Query caching
  • Expandable rows: Click any row to see detailed analysis
  • Multi-column sorting: Sort by passengers, alternatives, or changes
  • Active filter badges: Visual indicators of applied filters

Table Columns

ColumnDescriptionWhy It Matters
O&D (Origin & Destination)Route with highlighted changed segmentsQuickly identify which part of the journey was disrupted
Disruption EventsSummary of schedule changesUnderstand the type of disruption at a glance
Impacted GuestsTotal passengers with group/premium breakdownPrioritize high-value or large-group bookings
Alternate PathsNumber of available alternativesAssess rebooking difficulty

Implementation Pattern

The table uses the @shared/ui DataTable component with custom column renderers:

components/dashboard/guest-disruptions/guest-disruptions-table.tsx
"use client";

import { DataTable, RouteDisplay } from "@shared/ui";
import { useGuestDisruptions } from "@/hooks/guest-disruptions/use-guest-disruptions";

export function GuestDisruptionsTable() {
  const { currentSchedule } = useCurrentSchedule();
  const { filters } = useGuestDisruptionFilters();

  const { data, isFetching } = useGuestDisruptions(currentSchedule?.id, {
    pathwayAvailability: filters.pathwayAvailability,
    connectivity: filters.connectivity,
    originStation: filters.originStation,
    // ... other filters
  });

  return (
    <DataTable
      columns={guestDisruptionColumns}
      data={data?.disruptions || []}
      loading={isFetching}
      pagination={{
        page,
        rowsPerPage,
        total: data?.total || 0,
        onPageChange: handleChangePage,
        onRowsPerPageChange: handleChangeRowsPerPage,
      }}
      expandable
      expandedRowContent={(row) => (
        <GuestDisruptionExpandedRow row={row} />
      )}
    />
  );
}

Why this pattern?

  • Server-side pagination prevents loading thousands of rows at once
  • React Query caching makes filter changes instant
  • Expandable rows keep the UI clean while providing deep details on demand

2. Filtering System

The filtering system uses a drawer with dynamic options loaded from the backend.

Available Filters

Filter State Management

Filters are managed with React Context for global access:

components/dashboard/guest-disruptions/guest-disruption-filter-context.tsx
"use client";

import { createContext, useContext } from "react";
import { useFilterDrawer } from "@shared/ui";

export function GuestDisruptionFilterProvider({ children }) {
  const filterDrawer = useFilterDrawer({
    defaults: guestDisruptionDefaults,
    schema: guestDisruptionFilterSchema,
  });

  return (
    <GuestDisruptionFilterContext.Provider value={filterDrawer}>
      {children}
    </GuestDisruptionFilterContext.Provider>
  );
}

Why Context?

  • Global filter state: Filters persist across component re-renders
  • Shared between components: Filter button, table, and URL params all sync
  • Type-safe: Zod schema validation ensures correct filter values

3. Schedule Changes View

Displayed in the expanded row, this component shows a detailed timeline of schedule modifications.

What It Shows

  • Original vs. New Times: Side-by-side comparison of departure/arrival times
  • Time Differences: Calculated in minutes for quick assessment
  • Effective Dates: When the schedule change is valid
  • Flight Frequency: Which days of the week are affected
  • Multiple Segments: For multi-leg itineraries, shows changes for each segment

Why This Matters

Operations teams need to understand:

  1. Magnitude of change: Is it a 10-minute delay or a 3-hour shift?
  2. Validity period: Is this a one-time change or ongoing?
  3. Segment impact: Did the first leg change, causing a missed connection?
components/dashboard/guest-disruptions/schedule-changes/schedule-changes-table.tsx
export function ScheduleChangesTable({ route }: { route: string }) {
  const { currentSchedule } = useCurrentSchedule();
  const { data, isLoading } = useScheduleChanges(
    currentSchedule?.id || "",
    route
  );

  return (
    <Stack spacing={3}>
      {data?.routeSegments.map((segment) => (
        <Card key={segment.itineraryId}>
          <CardHeader title={segment.disruptionMessage} />
          <Table>
            {segment.segments.map((change) => (
              <TableRow key={`${change.leg}-${change.effectiveDate}`}>
                <TableCell>{change.route}</TableCell>
                <TableCell>{change.flightNumber}</TableCell>
                <TableCell>
                  <TimeComparison
                    original={change.originalDeparture}
                    new={change.newDeparture}
                    difference={change.departureChange}
                  />
                </TableCell>
                {/* ... */}
              </TableRow>
            ))}
          </Table>
        </Card>
      ))}
    </Stack>
  );
}

4. Impacted Dates View

Visualizes passenger and revenue impact across dates with a combination of chart and table.

Components

  1. Chart Visualization: Bar chart showing passengers and revenue by date
  2. Data Table: Detailed breakdown with passenger segmentation
  3. Date Details: Expandable rows with booking class breakdown

Key Metrics

MetricDescriptionWhy It's Important
Total PassengersCount of impacted passengersVolume assessment
Group %Percentage of group bookings (10+)Prioritization (groups are harder to rebook)
Premium %Percentage of premium classRevenue protection
Booked RevenueConfirmed bookings revenueActual revenue at risk
Predicted RevenueML-predicted revenueFuture revenue impact
ConfidencePrediction confidence (0-1)Trust in predictions

Chart Features

  • Interactive tooltips: Hover to see detailed breakdown
  • Dual-axis: Passengers (left) and revenue (right)
  • Color coding: Confirmed (solid) vs. predicted (pattern fill)
  • Date range selection: Focus on specific time periods
components/dashboard/guest-disruptions/impacted-dates/impacted-dates.tsx
export function ImpactedDates({ route }: { route: string }) {
  const { currentSchedule } = useCurrentSchedule();
  const { data, isLoading } = useImpactedDates(
    currentSchedule?.id || "",
    route
  );

  return (
    <Stack spacing={3}>
      {/* Chart Visualization */}
      <Card>
        <CardHeader title="Impact Over Time" />
        <ImpactedDatesChart data={data?.impactedDates || []} />
      </Card>

      {/* Detailed Table */}
      <Card>
        <CardHeader title="Date Details" />
        <ImpactedDatesDataTable data={data?.impactedDates || []} />
      </Card>
    </Stack>
  );
}

Why both chart and table?

  • Chart: Quick visual pattern recognition (e.g., "Fridays are heavily impacted")
  • Table: Precise numbers for reporting and decision-making

5. Alternative Pathways View

Displays available alternative flight options for rebooking disrupted passengers.

Features

  • Pathway Cards: Each alternative shown as a card with flight segments
  • Time Comparison: Shows departure time difference from original
  • Duration Display: Total flight time for each alternative
  • Segment Details: Expandable view of each flight leg
  • Filter Options: Filter by departure time offset and duration

Pathway Card Layout

Each alternative pathway card shows:

  1. Route Overview: Station codes with connecting points
  2. Departure Time: With difference from original (e.g., "+45 min")
  3. Total Duration: Flight time in HH:MM format
  4. Flight Numbers: All flights in the pathway
  5. Segment Details: Expandable list of each flight leg with:
    • Departure/arrival times (local timezone)
    • Aircraft type and seat capacity
    • Connection time between flights

Filter Parameters

components/dashboard/guest-disruptions/alternative-pathways/alternative-pathways.tsx
export function AlternativePathways({ route }: { route: string }) {
  const { currentSchedule } = useCurrentSchedule();
  const [filters, setFilters] = useState({
    departureOffsetMin: -180,
    departureOffsetMax: 180,
    durationMin: 0,
    durationMax: 1440,
  });

  const { data, isLoading } = useAlternativePathways(
    currentSchedule?.id || "",
    route,
    filters
  );

  return (
    <Stack spacing={2}>
      {/* Filter Controls */}
      <PathwayFilters filters={filters} onChange={setFilters} />

      {/* Pathway Cards */}
      {data?.pathways.map((pathway, index) => (
              <PathwayFlightCard
          key={index}
          pathway={pathway}
          originalDeparture={data.originalItinerary.departureDatetime}
        />
      ))}
    </Stack>
  );
}

Why this approach?

  • Card layout: Easy to scan multiple options
  • Time comparison: Helps assess passenger convenience
  • Expandable details: Progressive disclosure keeps UI clean

Data Flow

The feature follows a consistent data flow pattern:

Caching Strategy

All data hooks use React Query with optimized cache times:

// Main disruptions list - 5 min cache
useGuestDisruptions(scheduleId, filters, {
  staleTime: 5 * 60 * 1000,
  gcTime: 15 * 60 * 1000,
  placeholderData: (previousData) => previousData, // Smooth filter transitions
})

// Detail views - 5 min cache
useScheduleChanges(scheduleId, route, {
  staleTime: 5 * 60 * 1000,
  gcTime: 15 * 60 * 1000,
})

Why these cache times?

  • 5 min staleTime: Data doesn't change frequently, avoid unnecessary refetches
  • 15 min gcTime: Keep data in memory longer for tab switching
  • placeholderData: Show previous results while fetching new ones (smooth UX)

Performance Optimizations

Server-Side Pagination

The table implements server-side pagination to handle large datasets:

const [page, setPage] = useState(0)
const [rowsPerPage, setRowsPerPage] = useState(10)
const offset = page * rowsPerPage

const { data } = useGuestDisruptions(scheduleId, {
  ...filters,
  limit: rowsPerPage,
  offset: offset,
})

Benefits:

  • Only loads 10-50 rows at a time
  • Fast initial render
  • Smooth scrolling and interactions

Lazy Loading Detail Views

Schedule changes, impacted dates, and alternative pathways are only fetched when the row is expanded:

export function GuestDisruptionExpandedRow({ row }) {
  // Data is NOT fetched until this component renders
  return (
    <Tabs>
      <Tab label="Schedule Changes">
        <ScheduleChangesTable route={row.route} />
      </Tab>
      {/* ... */}
    </Tabs>
  );
}

Benefits:

  • Reduces initial load time
  • Saves bandwidth for unused data
  • Improves perceived performance

Optimistic Filter Updates

Filters use placeholderData to show previous results while fetching:

useGuestDisruptions(scheduleId, filters, {
  placeholderData: (previousData) => previousData,
})

Benefits:

  • No loading spinner when changing filters
  • Smooth transitions
  • Better user experience

Next: Explore Airlines Performance for schedule analysis and metrics.