import { LoadingSpinner } from '@/assets/components/LoadingSpinner'; import { API_ENDPOINTS, buildApiUrl } from '@/assets/config/api'; import { useAuth } from '@/assets/contexts/useAuth'; import ConfirmadasIcon from '@/assets/icons/icon-reservas-confirmadas.svg'; import MalaIcon from '@/assets/icons/icon-mala.svg'; import TotalReservasIcon from '@/assets/icons/icon-total-reservas.svg'; import WifiIcon from '@/assets/icons/wifi-solid.svg'; import { downloadDocumentos, getDocumentosChecksums, verificarDocumentosParaDownload } from '@/assets/services/documentSync'; import { cacheReservas, getCachedReservas } from '@/assets/services/offlineStorage'; import { colors } from '@/assets/styles/colors'; import { Reserva, ReservasResponse } from '@/assets/types'; import styles from '@/styles/screens/tabs/home.styles'; import { FontAwesome } from '@expo/vector-icons'; import { Href, router } from 'expo-router'; import { useEffect, useMemo, useState } from 'react'; import { Alert, Image, Pressable, RefreshControl, ScrollView, Text, View, } from 'react-native'; const getStatusPriority = (statusCode: string): number => { switch (statusCode) { case '10': return 1; // Em viagem case '1': case '5': return 2; // Confirmada case '-1': return 3; // Em pagamento case '-2': case '-3': return 4; // Pendente case '99': return 5; // Viagem realizada case '-5': return 7; // Cancelada — sempre no fim default: return 6; } }; type HomeFilter = 'proxima' | 'total' | 'confirmadas'; const sortReservas = (items: Reserva[]) => [...items].sort((a, b) => { const priorityDiff = getStatusPriority(a.statusCode) - getStatusPriority(b.statusCode); if (priorityDiff !== 0) return priorityDiff; return new Date(a.startDate).getTime() - new Date(b.startDate).getTime(); }); export default function Home() { const { token } = useAuth(); const [reservas, setReservas] = useState([]); const [activeFilter, setActiveFilter] = useState('total'); const [isLoading, setIsLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const [isFromCache, setIsFromCache] = useState(false); const syncDocumentos = async () => { if (!token) return; try { const checksumsData = await getDocumentosChecksums(token); if (!checksumsData || checksumsData.status !== 200 || !checksumsData.documentos?.length) return; const documentosParaDownload = await verificarDocumentosParaDownload(checksumsData.documentos); if (!documentosParaDownload.length) return; Alert.alert( 'Documentos disponíveis', `Existem ${documentosParaDownload.length} ficheiro${documentosParaDownload.length === 1 ? '' : 's'} novo${documentosParaDownload.length === 1 ? '' : 's'} para descarregar/atualizar. Pretende descarregá-los agora?`, [ { text: 'Não', style: 'cancel' }, { text: 'Sim', onPress: () => downloadDocumentos(documentosParaDownload), }, ], ); } catch { // sync de documentos é silencioso — não bloqueia o utilizador } }; const fetchReservas = async () => { try { setError(null); if (!token) { setReservas([]); setIsFromCache(false); return; } const url = buildApiUrl(API_ENDPOINTS.USER_RESERVAS); const formData = new FormData(); formData.append('token', token); const response = await fetch(url, { method: 'POST', headers: { Accept: 'application/json', }, body: formData, }); const data: ReservasResponse = await response.json(); if (!response.ok || (data.status !== 200 && data.status !== '200')) { throw new Error(data.message || 'Erro ao carregar reservas'); } const orderedReservas = sortReservas(data.reservas || []); setReservas(orderedReservas); setIsFromCache(false); await cacheReservas(orderedReservas); syncDocumentos(); } catch { const cached = await getCachedReservas(); if (cached && cached.length > 0) { setReservas(cached); setIsFromCache(true); setError(null); } else { setError('Sem ligação à internet e sem dados guardados.'); } } finally { setIsLoading(false); } }; useEffect(() => { setReservas([]); setIsFromCache(false); setIsLoading(true); fetchReservas(); }, [token]); const onRefresh = async () => { setRefreshing(true); await fetchReservas(); setRefreshing(false); }; const stats = useMemo(() => { const now = new Date(); const proximas = reservas.filter((reserva) => new Date(reserva.startDate) >= now); const total = reservas.length; const confirmadas = reservas.filter( (reserva) => reserva.statusCode === '1' || reserva.statusCode === '5' ).length; const proxima = sortReservas(proximas)[0]; let dias = '--'; if (proxima) { const diff = new Date(proxima.startDate).getTime() - now.getTime(); dias = `${Math.max(0, Math.ceil(diff / (1000 * 60 * 60 * 24)))}`; } const reservaEmViagem = reservas.find((reserva) => reserva.statusCode === '10'); return { dias, total, confirmadas, emViagem: Boolean(reservaEmViagem), destinoEmViagem: reservaEmViagem?.destino?.trim() || '—', reservaEmViagem, proximaReserva: proxima ?? null, }; }, [reservas]); const filteredReservas = useMemo(() => { const now = new Date(); if (activeFilter === 'total') { return sortReservas(reservas); } if (activeFilter === 'confirmadas') { return sortReservas( reservas.filter( (reserva) => reserva.statusCode === '1' || reserva.statusCode === '5' ) ); } if (stats.reservaEmViagem) { return [stats.reservaEmViagem]; } const proxima = reservas .filter((reserva) => new Date(reserva.startDate) >= now) .sort((a, b) => new Date(a.startDate).getTime() - new Date(b.startDate).getTime())[0]; return proxima ? [proxima] : []; }, [activeFilter, reservas, stats.reservaEmViagem]); const sectionTitle = useMemo(() => { if (activeFilter === 'confirmadas') return 'Reservas Confirmadas'; if (activeFilter === 'proxima') { return stats.emViagem ? 'Em Viagem' : 'Próxima Viagem'; } return 'As minhas Reservas'; }, [activeFilter, stats.emViagem]); const emptyMessage = useMemo(() => { if (activeFilter === 'confirmadas') return 'Sem reservas confirmadas.'; if (activeFilter === 'proxima') { return stats.emViagem ? 'Sem viagem em curso.' : 'Sem viagens futuras para apresentar.'; } return 'Sem reservas para apresentar.'; }, [activeFilter, stats.emViagem]); const getStatusType = (statusCode: string) => { if (statusCode === '1' || statusCode === '5') return 'verde'; if (statusCode === '-5') return 'vermelho'; if (statusCode === '10' || statusCode === '99') return 'roxo'; if (statusCode === '-2' || statusCode === '-1' || statusCode === '-3') return 'amarelo'; return 'neutral'; }; const getStatusColor = (statusType: string) => { switch (statusType) { case 'verde': return '#13AE45'; case 'vermelho': return '#E6463B'; case 'roxo': return '#B138E6'; case 'amarelo': return '#C99700'; default: return '#4A6592'; } }; const getStatusConfig = (statusCode: string) => { switch (statusCode) { case '1': return { label: 'Confirmada', icon: 'check-circle' as const }; case '5': return { label: 'Confirmada*', icon: 'check-circle' as const }; case '-5': return { label: 'Cancelada', icon: 'times-circle' as const }; case '10': return { label: 'Em Viagem', icon: 'ship' as const }; case '99': return { label: 'Viagem Realizada', icon: 'ship' as const }; case '-2': case '-3': return { label: 'Pendente', icon: 'clock-o' as const }; case '-1': return { label: 'Em pagamento', icon: 'clock-o' as const }; default: return { label: 'Não definido', icon: 'question-circle' as const }; } }; if (isLoading) { return ( ); } const isProximaActive = activeFilter === 'proxima'; const isTotalActive = activeFilter === 'total'; const isConfirmadasActive = activeFilter === 'confirmadas'; return ( }> setActiveFilter('proxima')}> {stats.emViagem ? ( ) : isProximaActive ? ( ) : ( )} {stats.emViagem ? 'Em viagem' : 'Proxima Viagem'} {stats.emViagem ? stats.destinoEmViagem : `${stats.dias} dias`} setActiveFilter('total')}> {isTotalActive ? ( ) : ( )} Total Reservas {stats.total} setActiveFilter('confirmadas')}> {isConfirmadasActive ? ( ) : ( )} Confirmadas {stats.confirmadas} {isFromCache && ( Sem ligação — a mostrar dados guardados )} {sectionTitle} {!!error && {error}} {!error && filteredReservas.length === 0 && ( {emptyMessage} )} {!error && filteredReservas.map((reserva, index) => { const statusType = getStatusType(reserva.statusCode); const statusConfig = getStatusConfig(reserva.statusCode); const statusColor = getStatusColor(statusType); return ( router.push(`/reserva/${encodeURIComponent(reserva.referenciaViagem || '')}` as Href) }> {reserva.referenciaViagem || '---'} {statusConfig.label} {reserva.destino} {reserva.pais} {reserva.startDate} ); })} ); }