Skip to main content

Frontend Interaction

This document describes how the frontend interacts with the flight search system through the conversational AI interface.

Search Flow

API Endpoints

POST /api/trip-searches
Content-Type: application/json

{
"origin": "YUL",
"destination": "CDG",
"departure_date": "2025-03-15",
"return_date": "2025-03-22",
"passengers": {
"adults": 1,
"children": 0,
"infants": 0
},
"cabin_class": "ECONOMY"
}

Get Flight Options

GET /api/trip-searches/{trip_search_id}/options

Returns ranked flight options with insights:

{
"options": [
{
"id": "uuid",
"rank": 1,
"total_score": 85.5,
"flight": {
"validating_carrier": "Air France",
"total_price": 850.00,
"currency": "CAD",
"legs": [
{
"departure_airport": "YUL",
"arrival_airport": "CDG",
"departure_time": "2025-03-15T22:30:00",
"arrival_time": "2025-03-16T11:45:00",
"duration_minutes": 435
}
]
},
"insights": [
{
"type": "POSITIVE",
"message": "Direct flight with no layovers"
},
{
"type": "POSITIVE",
"message": "Price is within your budget"
}
]
}
]
}

Frontend Components

Search Form

interface FlightSearchFormProps {
onSubmit: (criteria: SearchCriteria) => Promise<void>;
isLoading: boolean;
}

const FlightSearchForm: React.FC<FlightSearchFormProps> = ({
onSubmit,
isLoading
}) => {
// Form implementation with origin, destination, dates, passengers
};

Results Display

interface FlightResultsProps {
options: FlightOption[];
onSelect: (option: FlightOption) => void;
}

const FlightResults: React.FC<FlightResultsProps> = ({
options,
onSelect
}) => {
return (
<div className="space-y-4">
{options.map((option) => (
<FlightCard
key={option.id}
option={option}
onClick={() => onSelect(option)}
/>
))}
</div>
);
};

Insight Display

Insights are color-coded by type:

  • POSITIVE (green): Option meets or exceeds preferences
  • NEUTRAL (gray): Informational insight
  • NEGATIVE (red): Option doesn't meet preferences
  • POLICY_VIOLATION (red with warning): Violates company policy
const InsightBadge: React.FC<{ insight: Insight }> = ({ insight }) => {
const colorMap = {
POSITIVE: 'bg-green-100 text-green-800',
NEUTRAL: 'bg-gray-100 text-gray-800',
NEGATIVE: 'bg-red-100 text-red-800',
POLICY_VIOLATION: 'bg-red-200 text-red-900',
};

return (
<span className={`px-2 py-1 rounded ${colorMap[insight.type]}`}>
{insight.message}
</span>
);
};

State Management

The frontend uses React contexts for state management:

const FlightSearchContext = createContext<FlightSearchState | null>(null);

interface FlightSearchState {
tripSearch: TripSearch | null;
options: FlightOption[];
isSearching: boolean;
selectedOption: FlightOption | null;

createTripSearch: (criteria: SearchCriteria) => Promise<void>;
selectOption: (option: FlightOption) => void;
}

Error Handling

try {
await createTripSearch(criteria);
} catch (error) {
if (error instanceof APIError) {
if (error.status === 422) {
// Validation error - show field errors
setFieldErrors(error.details);
} else if (error.status === 429) {
// Rate limited - show retry message
showToast('Too many requests. Please try again later.');
}
}
}