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
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
| Column | Description | Why It Matters |
|---|---|---|
| O&D (Origin & Destination) | Route with highlighted changed segments | Quickly identify which part of the journey was disrupted |
| Disruption Events | Summary of schedule changes | Understand the type of disruption at a glance |
| Impacted Guests | Total passengers with group/premium breakdown | Prioritize high-value or large-group bookings |
| Alternate Paths | Number of available alternatives | Assess rebooking difficulty |
Implementation Pattern
The table uses the @shared/ui DataTable component with custom column renderers:
"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:
"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:
- Magnitude of change: Is it a 10-minute delay or a 3-hour shift?
- Validity period: Is this a one-time change or ongoing?
- Segment impact: Did the first leg change, causing a missed connection?
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
- Chart Visualization: Bar chart showing passengers and revenue by date
- Data Table: Detailed breakdown with passenger segmentation
- Date Details: Expandable rows with booking class breakdown
Key Metrics
| Metric | Description | Why It's Important |
|---|---|---|
| Total Passengers | Count of impacted passengers | Volume assessment |
| Group % | Percentage of group bookings (10+) | Prioritization (groups are harder to rebook) |
| Premium % | Percentage of premium class | Revenue protection |
| Booked Revenue | Confirmed bookings revenue | Actual revenue at risk |
| Predicted Revenue | ML-predicted revenue | Future revenue impact |
| Confidence | Prediction 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
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:
- Route Overview: Station codes with connecting points
- Departure Time: With difference from original (e.g., "+45 min")
- Total Duration: Flight time in HH:MM format
- Flight Numbers: All flights in the pathway
- 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
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.