Frontend Interaction
This document describes how the frontend interacts with the flight search system through the conversational AI interface.
Search Flow
API Endpoints
Create Trip Search
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.');
}
}
}