import { ActivityIndicator, Pressable, StyleSheet, Text, View } from 'react-native';
import type { Quote } from '@/lib/dispatch/protocol';
import { formatDistance, formatDuration, formatPrice, RIDE_TYPES } from '@/lib/rideTypes';
import type { Route } from '@/lib/types';
type Props = {
route: Route | null;
quote: Quote | null;
loading: boolean;
selectedId: string;
onSelect: (id: string) => void;
onConfirm: () => void;
};
export function RideOptions({ route, quote, loading, selectedId, onSelect, onConfirm }: Props) {
if (loading || !route || !quote) {
return (
<View style={[styles.sheet, styles.loading]}>
<ActivityIndicator color="#111" />
<Text style={styles.muted}>{loading || !route ? 'Finding the best routeβ¦' : 'Getting pricesβ¦'}</Text>
</View>
);
}
const available = RIDE_TYPES.filter((ride) => quote.fares[ride.id] !== undefined);
const selected = available.find((r) => r.id === selectedId) ?? available[0];
return (
<View style={styles.sheet}>
<Text style={styles.tripMeta}>
{formatDistance(route.distanceMeters)} Β· {formatDuration(route.durationSeconds)}
{route.approximate ? ' Β· estimate' : ''}
</Text>
{quote.surge > 1 && (
<Text style={styles.surge} testID="surge-notice">
Busy area Β· prices are {quote.surge}Γ higher right now
</Text>
)}
{available.map((ride) => {
const isSelected = ride.id === selected.id;
return (
<Pressable
key={ride.id}
onPress={() => onSelect(ride.id)}
style={[styles.option, isSelected && styles.optionSelected]}
accessibilityRole="radio"
accessibilityState={{ selected: isSelected }}
>
<Text style={styles.icon}>{ride.icon}</Text>
<View style={styles.optionText}>
<Text style={styles.optionName}>
{ride.name} <Text style={styles.muted}>Β· {ride.seats} seats</Text>
</Text>
<Text style={styles.muted}>{ride.description}</Text>
</View>
<Text style={styles.price} testID={`price-${ride.id}`}>
{formatPrice(quote.fares[ride.id])}
</Text>
</Pressable>
);
})}
<Pressable style={styles.confirm} onPress={onConfirm} accessibilityRole="button" testID="confirm-ride">
<Text style={styles.confirmText}>Choose {selected.name}</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
sheet: {
backgroundColor: '#fff',
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
paddingHorizontal: 16,
paddingTop: 16,
paddingBottom: 32,
shadowColor: '#000',
shadowOpacity: 0.15,
shadowRadius: 12,
elevation: 12,
},
loading: { alignItems: 'center', gap: 8, paddingVertical: 32 },
surge: { backgroundColor: '#eaf1ff', color: '#1d4ed8', padding: 8, borderRadius: 8, overflow: 'hidden', textAlign: 'center', marginBottom: 6, fontWeight: '500' },
tripMeta: { textAlign: 'center', color: '#555', marginBottom: 8, fontWeight: '500' },
option: {
flexDirection: 'row',
alignItems: 'center',
padding: 12,
borderRadius: 12,
borderWidth: 2,
borderColor: 'transparent',
},
optionSelected: { borderColor: '#111', backgroundColor: '#f6f6f6' },
icon: { fontSize: 32, marginRight: 12 },
optionText: { flex: 1 },
optionName: { fontSize: 17, fontWeight: '600', color: '#111' },
muted: { color: '#6b6b6b', fontWeight: '400', fontSize: 14 },
price: { fontSize: 17, fontWeight: '600', color: '#111' },
confirm: { marginTop: 12, backgroundColor: '#111', borderRadius: 10, paddingVertical: 16, alignItems: 'center' },
confirmText: { color: '#fff', fontSize: 17, fontWeight: '600' },
});