import { ThemeType, useTheme } from "../../theme"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { StyleProp, StyleSheet, TouchableOpacity, View, ViewStyle, TextStyle, } from "react-native"; import { NumericStepper, NumericStepperProps } from "../NumericStepper"; import RNPickerSelect from "react-native-picker-select"; import { Ionicons } from "@expo/vector-icons"; import { ThemedText as Text } from "../ui"; export interface PassengerConfig { key: "adults" | "children" | "infants"; name: string; description: string; defaultValue: number; minValue: number; maxValue?: number; // Age range for children/infants ageRange?: { min: number; max: number }; defaultAge?: number; } // Represents an individual child or infant with a selectable age export interface IndividualPassenger { id: string; age: number; } // Updated FlightSelection to handle individual passengers export interface FlightSelection { flightType: string; flightClass: string; passengers: { adults: number; children: IndividualPassenger[]; infants: IndividualPassenger[]; }; } // --- CONFIGURATION --- const DEFAULT_PASSENGERS: PassengerConfig[] = [ { key: "adults", name: "Adults", description: "12+ years old", defaultValue: 1, minValue: 1, maxValue: 9, }, { key: "children", name: "Children", description: "2 - 11 years old", defaultValue: 0, minValue: 0, maxValue: 6, ageRange: { min: 2, max: 11 }, defaultAge: 5, }, { key: "infants", name: "Infants", description: "0 - 1 years old", defaultValue: 0, minValue: 0, maxValue: 4, ageRange: { min: 0, max: 1 }, defaultAge: 0, }, ]; const DEFAULT_FLIGHT_TYPES = [ { value: "ROUND_TRIP", name: "Round trip" }, { value: "ONE_WAY", name: "One way" }, ]; const DEFAULT_FLIGHT_CLASSES = [ { value: "ECONOMY", name: "Economy" }, { value: "BUSINESS", name: "Business" }, { value: "FIRST", name: "First" }, ]; // --- HELPER FUNCTION --- const getInitialPassengerState = ( config: PassengerConfig[] ): FlightSelection["passengers"] => { return { adults: config.find((p) => p.key === "adults")?.defaultValue ?? 1, children: Array.from( { length: config.find((p) => p.key === "children")?.defaultValue ?? 0 }, (_, i) => ({ id: `child_${i}_${Date.now()}`, age: config.find((p) => p.key === "children")?.defaultAge ?? 2, }) ), infants: Array.from( { length: config.find((p) => p.key === "infants")?.defaultValue ?? 0 }, (_, i) => ({ id: `infant_${i}_${Date.now()}`, age: config.find((p) => p.key === "infants")?.defaultAge ?? 0, }) ), }; }; // --- PROPS INTERFACE --- export interface FlightSummaryProps { passengersData?: PassengerConfig[]; flightTypesData?: { value: string; name: string }[]; flightClassesData?: { value: string; name: string }[]; flightTypeLabel?: string; flightClassLabel?: string; selection?: FlightSelection; onSelectionChange?: (details: FlightSelection) => void; formatPassengerAgeLabel?: (passengerType: string, index: number) => string; containerStyle?: StyleProp; buttonGroupContainerStyle?: StyleProp; buttonStyle?: StyleProp; selectedButtonStyle?: StyleProp; buttonTextStyle?: StyleProp; selectedButtonTextStyle?: StyleProp; passengerSectionStyle?: StyleProp; passengerRowStyle?: StyleProp; passengerLabelContainerStyle?: StyleProp; passengerNameStyle?: StyleProp; passengerDescriptionStyle?: StyleProp; labelStyle?: StyleProp; agePickerRowStyle?: StyleProp; agePickerLabelStyle?: StyleProp; stepperContainerStyle?: NumericStepperProps["style"]; stepperButtonStyle?: NumericStepperProps["buttonStyle"]; stepperButtonTextStyle?: NumericStepperProps["buttonTextStyle"]; stepperValueTextStyle?: NumericStepperProps["valueTextStyle"]; overrideTheme?: ThemeType; } // --- COMPONENT IMPLEMENTATION --- const FlightSummary: React.FC = ({ passengersData: passengersProp = DEFAULT_PASSENGERS, flightTypesData: flightTypesProp = DEFAULT_FLIGHT_TYPES, flightClassesData: flightClassesProp = DEFAULT_FLIGHT_CLASSES, flightClassLabel = "Class", flightTypeLabel = "Type", selection, onSelectionChange, formatPassengerAgeLabel = (passengerType, index) => `${passengerType} ${index} Age`, containerStyle, buttonGroupContainerStyle, buttonStyle, selectedButtonStyle, buttonTextStyle, selectedButtonTextStyle, passengerSectionStyle, passengerRowStyle, passengerLabelContainerStyle, passengerNameStyle, passengerDescriptionStyle, labelStyle, agePickerRowStyle, agePickerLabelStyle, stepperContainerStyle, stepperButtonStyle, stepperButtonTextStyle, stepperValueTextStyle, overrideTheme }) => { const { theme: defaultTheme, isRTL } = useTheme(); let theme = overrideTheme || defaultTheme; const styles = useMemo(() => themedStyles(theme, isRTL), [theme, isRTL]); const passengersConfig = useMemo(() => passengersProp, [passengersProp]); const flightTypes = useMemo(() => flightTypesProp, [flightTypesProp]); const flightClasses = useMemo(() => flightClassesProp, [flightClassesProp]); const [selectedFlightType, setSelectedFlightType] = useState( selection?.flightType ?? flightTypes[0].value ); const [selectedFlightClass, setSelectedFlightClass] = useState( selection?.flightClass ?? flightClasses[0].value ); const [passengers, setPassengers] = useState( selection?.passengers ?? getInitialPassengerState(passengersConfig) ); // Memoize the state to be sent upwards const currentSelection = useMemo( () => ({ flightType: selectedFlightType, flightClass: selectedFlightClass, passengers, }), [selectedFlightType, selectedFlightClass, passengers] ); // Effect to call onSelectionChange when the memoized state changes useEffect(() => { onSelectionChange?.(currentSelection); }, [currentSelection, onSelectionChange]); const handlePassengerCountChange = useCallback( ( key: "adults" | "children" | "infants", action: "increment" | "decrement" ) => { setPassengers((prev) => { const config = passengersConfig.find((p) => p.key === key); if (!config) return prev; let newPassengersState = { ...prev }; if (key === "adults") { const currentVal = prev.adults; const min = config.minValue; const max = config.maxValue ?? 99; let newValue = currentVal; if (action === "increment") newValue = Math.min(max, currentVal + 1); else newValue = Math.max(min, currentVal - 1); newPassengersState.adults = newValue; } else { // Handle children and infants (arrays) const currentArray = prev[key]; if (action === "increment") { if (currentArray.length < (config.maxValue ?? 99)) { const newPassenger: IndividualPassenger = { id: `${key}_${Date.now()}`, age: config.defaultAge ?? 0, }; newPassengersState[key] = [...currentArray, newPassenger]; } } else { if (currentArray.length > config.minValue) { newPassengersState[key] = currentArray.slice(0, -1); } } } return newPassengersState; }); }, [passengersConfig] ); const handleIndividualAgeChange = useCallback( (key: "children" | "infants", id: string, newAge: number) => { // Prevent update if newAge is null/undefined (from picker placeholder) if (newAge === null || newAge === undefined) return; setPassengers((prev) => { const newArray = prev[key].map((p) => p.id === id ? { ...p, age: newAge } : p ); return { ...prev, [key]: newArray }; }); }, [] ); const renderButtonGroup = ( items: any[], selectedValue: string, onSelect: (item: any) => void, label?: string ) => ( {label} {items.map((item) => { const isSelected = item.value === selectedValue; return ( onSelect(item)} activeOpacity={0.7} > {item.name} ); })} ); // Helper to generate age items for the picker const getAgePickerItems = (config: PassengerConfig | undefined) => { if (!config?.ageRange) return []; const { min, max } = config.ageRange; return Array.from({ length: max - min + 1 }, (_, i) => ({ label: `${min + i}`, value: min + i, })); }; return ( {renderButtonGroup( flightTypes, selectedFlightType, (item) => setSelectedFlightType(item.value), flightTypeLabel )} {passengersConfig.map((config) => ( {config.name} {config.description} handlePassengerCountChange(config.key, "increment") } onDecrement={() => handlePassengerCountChange(config.key, "decrement") } style={stepperContainerStyle} buttonStyle={stepperButtonStyle} buttonTextStyle={stepperButtonTextStyle} valueTextStyle={stepperValueTextStyle} /> {/* Render age pickers for each child/infant */} {(config.key === "children" || config.key === "infants") && passengers[config.key].map((passenger, index) => ( {" "} {formatPassengerAgeLabel(config.name, index + 1)} handleIndividualAgeChange( config.key as any, passenger.id, value ) } items={getAgePickerItems(config)} placeholder={{}} style={{ inputIOS: styles.pickerInput, inputAndroid: styles.pickerInput, iconContainer: styles.pickerIconContainer, }} textInputProps={{ pointerEvents: "none" }} useNativeAndroidPickerStyle={false} Icon={() => ( )} /> ))} ))} {renderButtonGroup( flightClasses, selectedFlightClass, (item) => setSelectedFlightClass(item.value), flightClassLabel )} ); }; const themedStyles = (theme: ThemeType, isRTL: boolean) => StyleSheet.create({ container: { backgroundColor: theme.surface, padding: 16, }, sectionContainer: { marginBottom: 20, }, sectionLabel: { color: theme.labelText, fontSize: 12, fontWeight: "600", marginBottom: 8, }, buttonGroupContainer: { flexDirection: "row", flexWrap: "wrap", gap: 8, }, button: { paddingVertical: 8, paddingHorizontal: 16, borderRadius: 16, borderWidth: 1, borderColor: theme.border, backgroundColor: theme.surface, }, selectedButton: { borderColor: theme.primary, backgroundColor: theme.primary, }, buttonText: { color: theme.onSurface, fontWeight: "500", fontSize: 14, }, selectedButtonText: { color: theme.onPrimary, fontWeight: "bold", }, passengerSection: { marginVertical: 15, }, passengerRow: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingVertical: 10, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: theme.divider, }, passengerLabelContainer: { flexDirection: "column", alignItems: "flex-start", flexShrink: 1, }, passengerName: { fontSize: 16, fontWeight: "bold", color: theme.onSurface, }, passengerDescription: { fontSize: 12, color: theme.helper, opacity: 0.8, }, agePickerRow: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingVertical: 8, paddingHorizontal: 16, backgroundColor: theme.background, borderBottomWidth: StyleSheet.hairlineWidth, borderColor: theme.divider, }, agePickerLabel: { fontSize: 14, color: theme.onBackground, }, pickerContainer: { width: 120, height: 40, borderWidth: 1, borderColor: theme.border, borderRadius: 8, justifyContent: "center", }, pickerInput: { fontSize: 14, paddingHorizontal: 10, paddingVertical: 8, color: theme.onSurface, }, pickerIconContainer: { top: 10, right: 10, }, }); export default FlightSummary;