First commit of the new app

This commit is contained in:
2026-05-26 09:18:37 +01:00
parent 295d1bda21
commit b427fb0f85
110 changed files with 6483 additions and 833 deletions

View File

@@ -0,0 +1,126 @@
import { colors } from '@/assets/styles/colors';
import { Passageiro } from '@/assets/types';
import styles from '@/styles/screens/reserva/detail.styles';
import { FontAwesome } from '@expo/vector-icons';
import { useEffect, useState } from 'react';
import { Pressable, Text, View } from 'react-native';
import Animated, {
Easing,
useAnimatedStyle,
useSharedValue,
withTiming,
} from 'react-native-reanimated';
import { DashedDivider } from './DashedDivider';
import { FieldBox } from './FieldBox';
type Props = {
passageiro: Passageiro;
index: number;
isOpen: boolean;
isFirst: boolean;
onToggle: () => void;
};
const ANIM_DURATION = 280;
export function PassageiroCard({ passageiro, index, isOpen, onToggle }: Props) {
const progress = useSharedValue(isOpen ? 1 : 0);
const contentHeight = useSharedValue(0);
const [measuredHeight, setMeasuredHeight] = useState(0);
useEffect(() => {
if (measuredHeight > 0) {
contentHeight.value = measuredHeight;
}
}, [measuredHeight, contentHeight]);
useEffect(() => {
progress.value = withTiming(isOpen ? 1 : 0, {
duration: ANIM_DURATION,
easing: Easing.out(Easing.cubic),
});
}, [isOpen, progress]);
const bodyAnimatedStyle = useAnimatedStyle(() => ({
height: contentHeight.value * progress.value,
opacity: progress.value,
}));
const chevronAnimatedStyle = useAnimatedStyle(() => ({
transform: [{ rotate: `${progress.value * 180}deg` }],
}));
return (
<View>
<View style={styles.passageiroContainer}>
<Pressable onPress={onToggle} style={styles.passageiroHeaderRow}>
<View>
<Text style={styles.passageiroSubtitle}>Passageiro {index + 1}</Text>
<Text style={styles.passageiroName}>
{passageiro.nome} {passageiro.sobrenome}
</Text>
</View>
<Animated.View style={chevronAnimatedStyle}>
<FontAwesome name="chevron-down" size={14} color={colors.vermelho} />
</Animated.View>
</Pressable>
<Animated.View
style={[styles.passageiroBodyWrap, bodyAnimatedStyle]}
pointerEvents={isOpen ? 'auto' : 'none'}>
<View
style={styles.passageiroBodyMeasure}
onLayout={(e) => {
const h = Math.ceil(e.nativeEvent.layout.height);
if (h > 0) setMeasuredHeight(h);
}}>
<View style={styles.passageiroBody}>
<View style={styles.twoCol}>
<View style={styles.twoColItem}>
<FieldBox label="Nome" value={passageiro.nome} />
</View>
<View style={styles.twoColItem}>
<FieldBox label="Apelido" value={passageiro.sobrenome} />
</View>
</View>
<View style={styles.twoCol}>
<View style={styles.twoColItem}>
<FieldBox label="Data de Nasc." value={passageiro.dataNascimento} />
</View>
<View style={styles.twoColItem}>
<FieldBox label="Género" value={passageiro.genero} />
</View>
</View>
<View style={styles.twoCol}>
<View style={styles.twoColItem}>
<FieldBox label="Nacionalidade" value={passageiro.nacionalidade} />
</View>
<View style={styles.twoColItem}>
<FieldBox label="Telemóvel" value={passageiro.telemovel} />
</View>
</View>
<FieldBox label="Morada" value={passageiro.morada} />
<FieldBox label="Nacionalidade Doc." value={passageiro.paisEmissao} />
<FieldBox label="Nr Doc Identificação" value={passageiro.numeroDocumento} />
<View style={styles.twoCol}>
<View style={styles.twoColItem}>
<FieldBox label="Data Emissão" value={passageiro.dataDeEmissao} />
</View>
<View style={styles.twoColItem}>
<FieldBox label="Data Val./Exp." value={passageiro.dataDeValidade} />
</View>
</View>
<FieldBox label="Email" value={passageiro.email} />
</View>
</View>
</Animated.View>
</View>
<DashedDivider width={1} borderStyle="dashed" />
</View>
);
}