first version of the cruise lovers app, build 1.0.1, ready to be published on the app store
This commit is contained in:
@@ -1,20 +1,20 @@
|
||||
import { useAuth } from '@/assets/contexts/useAuth';
|
||||
import styles from '@/styles/screens/auth/login.styles';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { router, type Href } from 'expo-router';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Image,
|
||||
ImageBackground,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
Pressable,
|
||||
ScrollView,
|
||||
Text,
|
||||
TextInput,
|
||||
View,
|
||||
StatusBar,
|
||||
Image,
|
||||
ImageBackground,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
Pressable,
|
||||
ScrollView,
|
||||
StatusBar,
|
||||
Text,
|
||||
TextInput,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import styles from '@/styles/screens/auth/login.styles';
|
||||
import { useAuth } from '@/assets/contexts/useAuth';
|
||||
import { router, type Href } from 'expo-router';
|
||||
|
||||
export default function Login() {
|
||||
const [email, setEmail] = useState('');
|
||||
@@ -69,7 +69,7 @@ export default function Login() {
|
||||
<TextInput
|
||||
value={email}
|
||||
onChangeText={setEmail}
|
||||
placeholder="hello@domain.pt"
|
||||
placeholder="login@cruiselovers.pt"
|
||||
placeholderTextColor="#9AA0A6"
|
||||
autoCapitalize="none"
|
||||
keyboardType="email-address"
|
||||
|
||||
@@ -53,7 +53,7 @@ export default function RecoverEmailScreen() {
|
||||
<TextInput
|
||||
value={email}
|
||||
onChangeText={setEmail}
|
||||
placeholder="hello@domain.pt"
|
||||
placeholder="recuperar@cruiselovers.pt"
|
||||
placeholderTextColor="#9AA0A6"
|
||||
autoCapitalize="none"
|
||||
keyboardType="email-address"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ContactMapPreview } from '@/assets/components/ContactMapPreview';
|
||||
import { LoadingSpinner } from '@/assets/components/LoadingSpinner';
|
||||
import { API_ENDPOINTS, buildApiUrl } from '@/assets/config/api';
|
||||
import { useAuth } from '@/assets/contexts/useAuth';
|
||||
import { cacheContacts, getCachedContacts } from '@/assets/services/offlineStorage';
|
||||
@@ -7,16 +9,15 @@ import styles from '@/styles/screens/tabs/contactos.styles';
|
||||
import { FontAwesome } from '@expo/vector-icons';
|
||||
import * as Clipboard from 'expo-clipboard';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { LoadingSpinner } from '@/assets/components/LoadingSpinner';
|
||||
import {
|
||||
Alert,
|
||||
Image,
|
||||
Linking,
|
||||
Pressable,
|
||||
RefreshControl,
|
||||
ScrollView,
|
||||
Text,
|
||||
View,
|
||||
Alert,
|
||||
Image,
|
||||
Linking,
|
||||
Pressable,
|
||||
RefreshControl,
|
||||
ScrollView,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
// ─── helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -77,7 +78,6 @@ const parseCoords = (raw?: string | null): { lat: number; lng: number } | null =
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
const parseHorarios = (raw: string): { dia: string; horas: string }[] => {
|
||||
return raw
|
||||
.split('\n')
|
||||
@@ -114,9 +114,6 @@ export default function Contactos() {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [mapLoading, setMapLoading] = useState(true);
|
||||
const [mapError, setMapError] = useState(false);
|
||||
|
||||
const fetchContacts = async () => {
|
||||
// 1. Mostrar cache imediatamente enquanto atualiza em fundo
|
||||
const cached = await getCachedContacts();
|
||||
@@ -138,8 +135,6 @@ export default function Contactos() {
|
||||
|
||||
const data: ContactResponse = await response.json();
|
||||
|
||||
console.log('data', data);
|
||||
|
||||
if (data.status === 200 && data.contact) {
|
||||
const validSocials = (data.socials || []).filter((s) => s.value?.trim());
|
||||
setContactData(data.contact);
|
||||
@@ -186,9 +181,6 @@ export default function Contactos() {
|
||||
|
||||
const horarios = contactData?.horarios ? parseHorarios(contactData.horarios) : [];
|
||||
const coords = parseCoords(contactData?.coordenadas);
|
||||
const mapUrl = coords
|
||||
? `https://staticmap.openstreetmap.de/staticmap.php?center=${coords.lat},${coords.lng}&zoom=15&size=600x300&markers=${coords.lat},${coords.lng},red-pushpin`
|
||||
: null;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
@@ -249,30 +241,13 @@ export default function Contactos() {
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.cardTitle}>Localização</Text>
|
||||
|
||||
{!!mapUrl && (
|
||||
<Pressable
|
||||
onPress={() => openMaps(contactData.address, coords?.lat, coords?.lng)}
|
||||
style={styles.mapImageWrap}>
|
||||
<Image
|
||||
source={{ uri: mapUrl }}
|
||||
style={styles.mapImage}
|
||||
resizeMode="cover"
|
||||
onLoadStart={() => { setMapLoading(true); setMapError(false); }}
|
||||
onLoad={() => setMapLoading(false)}
|
||||
onError={() => { setMapLoading(false); setMapError(true); }}
|
||||
/>
|
||||
{mapLoading && !mapError && (
|
||||
<View style={styles.mapOverlay}>
|
||||
<LoadingSpinner size="small" />
|
||||
</View>
|
||||
)}
|
||||
{mapError && (
|
||||
<View style={styles.mapOverlay}>
|
||||
<FontAwesome name="map-marker" size={28} color={colors.vermelho} />
|
||||
<Text style={styles.mapErrorText}>Mapa indisponível</Text>
|
||||
</View>
|
||||
)}
|
||||
</Pressable>
|
||||
{coords && (
|
||||
<ContactMapPreview
|
||||
lat={coords.lat}
|
||||
lng={coords.lng}
|
||||
title={contactData.address}
|
||||
onPress={() => openMaps(contactData.address, coords.lat, coords.lng)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<View style={styles.addressRow}>
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
import { colors } from '@/assets/styles/colors';
|
||||
import { useAuth } from '@/assets/contexts/useAuth';
|
||||
import { API_ENDPOINTS, buildApiUrl } from '@/assets/config/api';
|
||||
import { cacheReservas, getCachedReservas } from '@/assets/services/offlineStorage';
|
||||
import { downloadDocumentos, getDocumentosChecksums, verificarDocumentosParaDownload } from '@/assets/services/documentSync';
|
||||
import { Reserva, ReservasResponse } from '@/assets/types';
|
||||
import { Href, router } from 'expo-router';
|
||||
import styles from '@/styles/screens/tabs/home.styles';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { LoadingSpinner } from '@/assets/components/LoadingSpinner';
|
||||
import {
|
||||
Alert,
|
||||
Image,
|
||||
Pressable,
|
||||
RefreshControl,
|
||||
ScrollView,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
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) {
|
||||
@@ -40,9 +44,19 @@ const getStatusPriority = (statusCode: string): number => {
|
||||
}
|
||||
};
|
||||
|
||||
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<Reserva[]>([]);
|
||||
const [activeFilter, setActiveFilter] = useState<HomeFilter>('total');
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -95,19 +109,14 @@ export default function Home() {
|
||||
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 = [...(data.reservas || [])].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();
|
||||
});
|
||||
const orderedReservas = sortReservas(data.reservas || []);
|
||||
|
||||
setReservas(orderedReservas);
|
||||
setIsFromCache(false);
|
||||
@@ -144,8 +153,10 @@ export default function Home() {
|
||||
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').length;
|
||||
const proxima = proximas[0];
|
||||
const confirmadas = reservas.filter(
|
||||
(reserva) => reserva.statusCode === '1' || reserva.statusCode === '5'
|
||||
).length;
|
||||
const proxima = sortReservas(proximas)[0];
|
||||
|
||||
let dias = '--';
|
||||
if (proxima) {
|
||||
@@ -161,9 +172,55 @@ export default function Home() {
|
||||
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';
|
||||
@@ -217,7 +274,9 @@ export default function Home() {
|
||||
);
|
||||
}
|
||||
|
||||
console.log(reservas);
|
||||
const isProximaActive = activeFilter === 'proxima';
|
||||
const isTotalActive = activeFilter === 'total';
|
||||
const isConfirmadasActive = activeFilter === 'confirmadas';
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
@@ -227,52 +286,78 @@ export default function Home() {
|
||||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}>
|
||||
|
||||
<View style={styles.statsRow}>
|
||||
<View style={[styles.statCard, styles.statCardPrimary]}>
|
||||
<Pressable
|
||||
style={[styles.statCard, isProximaActive && styles.statCardPrimary]}
|
||||
onPress={() => setActiveFilter('proxima')}>
|
||||
{stats.emViagem ? (
|
||||
<View style={styles.statIconEmViagemWrap}>
|
||||
<FontAwesome name="ship" size={20} color={colors.azul} />
|
||||
</View>
|
||||
) : (
|
||||
) : isProximaActive ? (
|
||||
<Image source={require('@/assets/icons/mala.png')} style={styles.statIcon} />
|
||||
) : (
|
||||
<MalaIcon width={38} height={38} style={styles.statIconSvg} />
|
||||
)}
|
||||
<Text style={styles.statLabelPrimary}>
|
||||
<Text style={isProximaActive ? styles.statLabelPrimary : styles.statLabel}>
|
||||
{stats.emViagem ? 'Em viagem' : 'Proxima Viagem'}
|
||||
</Text>
|
||||
<Text style={styles.statValuePrimary} numberOfLines={stats.emViagem ? 2 : 1}>
|
||||
<Text
|
||||
style={isProximaActive ? styles.statValuePrimary : styles.statValue}
|
||||
numberOfLines={stats.emViagem ? 2 : 1}>
|
||||
{stats.emViagem ? stats.destinoEmViagem : `${stats.dias} dias`}
|
||||
</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
|
||||
<View style={styles.statCard}>
|
||||
<Image source={require('@/assets/icons/calendario.png')} style={styles.statIcon} />
|
||||
<Text style={styles.statLabel}>Total Reservas</Text>
|
||||
<Text style={styles.statValue}>{stats.total}</Text>
|
||||
</View>
|
||||
<Pressable
|
||||
style={[styles.statCard, isTotalActive && styles.statCardPrimary]}
|
||||
onPress={() => setActiveFilter('total')}>
|
||||
{isTotalActive ? (
|
||||
<TotalReservasIcon width={38} height={38} style={styles.statIconSvg} />
|
||||
) : (
|
||||
<Image source={require('@/assets/icons/calendario.png')} style={styles.statIcon} />
|
||||
)}
|
||||
<Text style={isTotalActive ? styles.statLabelPrimary : styles.statLabel}>
|
||||
Total Reservas
|
||||
</Text>
|
||||
<Text style={isTotalActive ? styles.statValuePrimary : styles.statValue}>
|
||||
{stats.total}
|
||||
</Text>
|
||||
</Pressable>
|
||||
|
||||
<View style={styles.statCard}>
|
||||
<Image source={require('@/assets/icons/confirmadas.png')} style={styles.statIcon} />
|
||||
<Text style={styles.statLabel}>Confirmadas</Text>
|
||||
<Text style={styles.statValue}>{stats.confirmadas}</Text>
|
||||
</View>
|
||||
<Pressable
|
||||
style={[styles.statCard, isConfirmadasActive && styles.statCardPrimary]}
|
||||
onPress={() => setActiveFilter('confirmadas')}>
|
||||
{isConfirmadasActive ? (
|
||||
<ConfirmadasIcon width={38} height={38} style={styles.statIconSvg} />
|
||||
) : (
|
||||
<Image source={require('@/assets/icons/confirmadas.png')} style={styles.statIcon} />
|
||||
)}
|
||||
<Text style={isConfirmadasActive ? styles.statLabelPrimary : styles.statLabel}>
|
||||
Confirmadas
|
||||
</Text>
|
||||
<Text style={isConfirmadasActive ? styles.statValuePrimary : styles.statValue}>
|
||||
{stats.confirmadas}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
{isFromCache && (
|
||||
<View style={styles.offlineBanner}>
|
||||
<FontAwesome name="wifi" size={13} color={colors.branco} />
|
||||
<WifiIcon width={13} height={13} fill={colors.branco} />
|
||||
<Text style={styles.offlineBannerText}>Sem ligação — a mostrar dados guardados</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<Text style={styles.sectionTitle}>As minhas Reservas</Text>
|
||||
<Text style={styles.sectionTitle}>{sectionTitle}</Text>
|
||||
|
||||
{!!error && <Text style={styles.errorText}>{error}</Text>}
|
||||
|
||||
{!error && reservas.length === 0 && (
|
||||
<Text style={styles.emptyText}>Sem reservas para apresentar.</Text>
|
||||
{!error && filteredReservas.length === 0 && (
|
||||
<Text style={styles.emptyText}>{emptyMessage}</Text>
|
||||
)}
|
||||
|
||||
{!error &&
|
||||
reservas.map((reserva, index) => {
|
||||
filteredReservas.map((reserva, index) => {
|
||||
const statusType = getStatusType(reserva.statusCode);
|
||||
const statusConfig = getStatusConfig(reserva.statusCode);
|
||||
const statusColor = getStatusColor(statusType);
|
||||
@@ -305,6 +390,8 @@ export default function Home() {
|
||||
<View style={styles.statusBadgeContent}>
|
||||
<FontAwesome name={statusConfig.icon} size={12} color={statusColor} />
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
style={[
|
||||
styles.statusText,
|
||||
statusType === 'verde' && styles.statusTextSuccess,
|
||||
@@ -318,7 +405,9 @@ export default function Home() {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<Text style={styles.destination}>{reserva.destino}</Text>
|
||||
<Text style={styles.destination} numberOfLines={1} ellipsizeMode="tail">
|
||||
{reserva.destino}
|
||||
</Text>
|
||||
<Text style={styles.meta}><FontAwesome name="map" size={12} color="#4A6592" /> {reserva.pais}</Text>
|
||||
<View style={styles.dateRow}>
|
||||
<FontAwesome name="calendar" size={12} color="#4A6592" />
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { LoadingSpinner } from '@/assets/components/LoadingSpinner';
|
||||
import { API_ENDPOINTS, buildApiUrl } from '@/assets/config/api';
|
||||
import { useAuth } from '@/assets/contexts/useAuth';
|
||||
import ChevronRightIcon from '@/assets/icons/chevron-right.svg';
|
||||
import UserDeleteIcon from '@/assets/icons/user-xmark-solid-full.svg';
|
||||
import LogoutIcon from '@/assets/icons/right-from-bracket-solid-full.svg';
|
||||
import UserIcon from '@/assets/icons/user-solid-full.svg';
|
||||
import UserDeleteIcon from '@/assets/icons/user-xmark-solid-full.svg';
|
||||
import { colors } from '@/assets/styles/colors';
|
||||
import { UserInfoResponse } from '@/assets/types';
|
||||
import styles from '@/styles/screens/tabs/perfil.styles';
|
||||
import { type Href, router } from 'expo-router';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { LoadingSpinner } from '@/assets/components/LoadingSpinner';
|
||||
import {
|
||||
Alert,
|
||||
Image,
|
||||
@@ -24,10 +24,10 @@ import {
|
||||
View,
|
||||
} from 'react-native';
|
||||
import Animated, {
|
||||
Easing,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
Easing,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
} from 'react-native-reanimated';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ import { ValorReservaCard } from '@/assets/components/reserva/ValorReservaCard';
|
||||
import { VooCard } from '@/assets/components/reserva/VooCard';
|
||||
import { API_ENDPOINTS, buildApiUrl } from '@/assets/config/api';
|
||||
import { useAuth } from '@/assets/contexts/useAuth';
|
||||
import WifiIcon from '@/assets/icons/wifi-solid.svg';
|
||||
import { colors } from '@/assets/styles/colors';
|
||||
import { cacheReservaFull, getCachedReservaFull } from '@/assets/services/offlineStorage';
|
||||
import {
|
||||
Documento,
|
||||
@@ -27,7 +29,6 @@ import {
|
||||
VooSegment,
|
||||
} from '@/assets/types';
|
||||
import styles from '@/styles/screens/reserva/detail.styles';
|
||||
import { FontAwesome } from '@expo/vector-icons';
|
||||
import { Stack, useLocalSearchParams } from 'expo-router';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
@@ -118,6 +119,21 @@ export default function ReservaDetalheScreen() {
|
||||
}
|
||||
};
|
||||
|
||||
function metodosPagamento($metodo: string) {
|
||||
switch ($metodo) {
|
||||
case 'mb':
|
||||
return 'Multibanco';
|
||||
case 'mbway':
|
||||
return 'MB Way';
|
||||
case 'cc':
|
||||
return 'Cartão de Crédito';
|
||||
case 'tb':
|
||||
return 'Transferência Bancária';
|
||||
default:
|
||||
return $metodo;
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchReserva();
|
||||
}, [referencia, token]);
|
||||
@@ -196,7 +212,7 @@ export default function ReservaDetalheScreen() {
|
||||
<>
|
||||
{isFromCache && (
|
||||
<View style={styles.offlineBanner}>
|
||||
<FontAwesome name="wifi" size={13} color="#fff" />
|
||||
<WifiIcon width={13} height={13} fill={colors.branco} />
|
||||
<Text style={styles.offlineBannerText}>Sem ligação — a mostrar dados guardados</Text>
|
||||
</View>
|
||||
)}
|
||||
@@ -350,7 +366,7 @@ export default function ReservaDetalheScreen() {
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.tableCellRight}>
|
||||
<Text style={styles.tableValueText}>{pagamento.metodoPagamento}</Text>
|
||||
<Text style={styles.tableValueText}>{metodosPagamento(pagamento.metodoPagamento)}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<DashedDivider width={1} borderStyle="dashed" />
|
||||
|
||||
Reference in New Issue
Block a user