feat: Implement alert system and network connectivity handling, refactor error handling across screens

This commit is contained in:
2026-01-30 12:38:25 +01:00
parent 9bb8279631
commit 7a6a7f5d35
14 changed files with 277 additions and 40 deletions

View File

@ -1,5 +1,6 @@
import { useAlert } from '@/components/AlertComponent';
import React, { useEffect, useState } from 'react';
import { View, Text, TouchableOpacity, ScrollView, Alert, RefreshControl } from 'react-native';
import { View, Text, TouchableOpacity, ScrollView, RefreshControl } from 'react-native';
import { QrCode, CheckCircle2, Nfc } from 'lucide-react-native';
import QrScanModal from '@/components/QrScanModal';
import NfcScanModal from '@/components/NfcScanModal';
@ -11,6 +12,7 @@ import NfcManager from 'react-native-nfc-manager';
export default function AttendanceScreen() {
const [scannerType, setScannerType] = useState<'qr' | 'nfc'>('qr');
const alert = useAlert();
const [showScanner, setShowScanner] = useState(false);
const [lastScan, setLastScan] = useState<{ type: string; time: string; site: string } | null>(null);
const [attendances, setAttendances] = useState<AttendanceRecord[]>([]);
@ -36,7 +38,7 @@ export default function AttendanceScreen() {
setAttendances(response.data);
} catch (error) {
console.error('Errore nel recupero delle presenze:', error);
Alert.alert('Errore', 'Impossibile recuperare le presenze. Riprova più tardi.');
alert.showAlert('error', 'Errore', 'Impossibile recuperare le presenze. Riprova più tardi.');
} finally {
setIsLoading(false);
setRefreshing(false);
@ -67,23 +69,20 @@ export default function AttendanceScreen() {
try {
const supported = await NfcManager.isSupported();
if (!supported) {
Alert.alert('NFC non supportato', 'Il tuo dispositivo non supporta la scansione NFC.');
alert.showAlert('warning', 'NFC non supportato', 'Il tuo dispositivo non supporta la scansione NFC.');
return;
}
const enabled = await NfcManager.isEnabled();
if (!enabled) {
Alert.alert('NFC disattivato', 'Per favore attiva l\'NFC nelle impostazioni del dispositivo per continuare.', [
{ text: 'OK' },
{ text: 'Vai alle impostazioni', onPress: () => NfcManager.goToNfcSetting() }
]);
alert.showAlert('warning', 'NFC disattivato', 'Per favore attiva l\'NFC nelle impostazioni del dispositivo per continuare.');
return;
}
setShowScanner(true);
} catch (err) {
console.warn(err);
Alert.alert('Errore', 'Impossibile verificare lo stato dell\'NFC.');
alert.showAlert('error', 'Errore', 'Impossibile verificare lo stato dell\'NFC.');
}
}
};
@ -106,7 +105,7 @@ export default function AttendanceScreen() {
}
} catch (error) {
console.error('Errore nell\'invio dei dati di scansione:', error);
Alert.alert('Errore', 'Impossibile registrare la presenza. Riprova più tardi.');
alert.showAlert('error', 'Errore', 'Impossibile registrare la presenza. Riprova più tardi.');
return;
}
};

View File

@ -1,6 +1,7 @@
import { useAlert } from '@/components/AlertComponent';
import React, { JSX, useEffect, useMemo, useState } from 'react';
import { Calendar as CalendarIcon, CalendarX, Clock, Plus, Thermometer } from 'lucide-react-native';
import { Alert, ScrollView, Text, TouchableOpacity, View, ActivityIndicator, RefreshControl } from 'react-native';
import { ScrollView, Text, TouchableOpacity, View, ActivityIndicator, RefreshControl } from 'react-native';
import { TimeOffRequest, TimeOffRequestType } from '@/types/types';
import RequestPermitModal from '@/components/RequestPermitModal';
import CalendarWidget from '@/components/CalendarWidget';
@ -18,6 +19,7 @@ const typeIcons: Record<string, (color: string) => JSX.Element> = {
export default function PermitsScreen() {
const [showModal, setShowModal] = useState(false);
const alert = useAlert();
const [permits, setPermits] = useState<TimeOffRequest[]>([]);
const [types, setTypes] = useState<TimeOffRequestType[]>([]);
const [currentMonthDate, setCurrentMonthDate] = useState(new Date());
@ -37,7 +39,7 @@ export default function PermitsScreen() {
setTypes(typesResponse.data);
} catch (error) {
console.error('Errore nel recupero dei permessi:', error);
Alert.alert('Errore', 'Impossibile recuperare i permessi. Riprova più tardi.');
alert.showAlert('error', 'Errore', 'Impossibile recuperare i permessi. Riprova più tardi.');
} finally {
setIsLoading(false);
setRefreshing(false);

View File

@ -1,8 +1,9 @@
import { useAlert } from '@/components/AlertComponent';
import { ArrowLeft, Download, FileText, MapPin, Plus, Search, CalendarIcon } from 'lucide-react-native';
import React, { useEffect, useState } from 'react';
import { useRouter } from 'expo-router';
import { RangePickerModal } from '@/components/RangePickerModal';
import { Alert, RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { DocumentItem } from '@/types/types';
import api from '@/utils/api';
import dayjs from 'dayjs';
@ -13,6 +14,7 @@ import { downloadAndShareDocument, uploadDocument } from '@/utils/documentUtils'
export default function DocumentsScreen() {
const router = useRouter();
const alert = useAlert();
const [documents, setDocuments] = useState<DocumentItem[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
@ -35,7 +37,7 @@ export default function DocumentsScreen() {
setDocuments(response.data);
} catch (error) {
console.error('Errore nel recupero dei documenti utente:', error);
Alert.alert('Errore', 'Impossibile recuperare i documenti. Riprova più tardi.');
alert.showAlert('error', 'Errore', 'Impossibile recuperare i documenti. Riprova più tardi.');
} finally {
setIsLoading(false);
setRefreshing(false);
@ -83,12 +85,12 @@ export default function DocumentsScreen() {
setIsUploading(true);
try {
await uploadDocument(file, null, customTitle);
Alert.alert('Successo', 'Documento caricato con successo!');
alert.showAlert('success', 'Successo', 'Documento caricato con successo!');
setShowUploadModal(false);
fetchUserDocuments();
} catch (error) {
console.error('Errore nel caricamento del documento:', error);
Alert.alert('Errore', 'Impossibile caricare il documento. Riprova più tardi.');
alert.showAlert('error', 'Errore', 'Impossibile caricare il documento. Riprova più tardi.');
} finally {
setIsUploading(false);
}

View File

@ -1,7 +1,8 @@
import { useAlert } from '@/components/AlertComponent';
import { Download, FileText, Plus, Search, Calendar as CalendarIcon, ChevronLeft } from 'lucide-react-native';
import React, { useCallback, useEffect, useState } from 'react';
import { RangePickerModal } from '@/components/RangePickerModal';
import { Alert, RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { ConstructionSite, DocumentItem } from '@/types/types';
import LoadingScreen from '@/components/LoadingScreen';
@ -13,6 +14,7 @@ import AddDocumentModal from '@/components/AddDocumentModal';
export default function SiteDocumentsScreen() {
const router = useRouter();
const alert = useAlert();
const params = useLocalSearchParams();
const [site, setSite] = useState<ConstructionSite | null>(null);
const [documents, setDocuments] = useState<DocumentItem[]>([]);
@ -34,7 +36,7 @@ export default function SiteDocumentsScreen() {
setDocuments(response.data);
} catch (error) {
console.error('Errore nel recupero dei documenti del cantiere:', error);
Alert.alert('Errore', 'Impossibile recuperare i documenti del cantiere. Riprova più tardi.');
alert.showAlert('error', 'Errore', 'Impossibile recuperare i documenti del cantiere. Riprova più tardi.');
} finally {
setIsLoading(false);
setRefreshing(false);
@ -99,7 +101,7 @@ export default function SiteDocumentsScreen() {
await downloadAndShareDocument(mimetype, fileName, fileUrl);
} catch (error) {
console.error('Errore nel download/condivisione del documento:', error);
Alert.alert('Errore', 'Impossibile scaricare il documento. Riprova più tardi.');
alert.showAlert('error', 'Errore', 'Impossibile scaricare il documento. Riprova più tardi.');
}
};
@ -108,12 +110,12 @@ export default function SiteDocumentsScreen() {
setIsUploading(true);
try {
await uploadDocument(file, Number(params.id), customTitle);
Alert.alert('Successo', 'Documento caricato con successo!');
alert.showAlert('success', 'Successo', 'Documento caricato con successo!');
setShowUploadModal(false);
fetchSiteDocuments(Number(params.id), true);
} catch (error) {
console.error('Errore nel caricamento del documento:', error);
Alert.alert('Errore', 'Impossibile caricare il documento. Riprova più tardi.');
alert.showAlert('error', 'Errore', 'Impossibile caricare il documento. Riprova più tardi.');
} finally {
setIsUploading(false);
}

View File

@ -1,10 +1,11 @@
import { Building2, ChevronRight, MapPin, Search } from 'lucide-react-native';
import React, { useEffect, useState } from 'react';
import { Alert, RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { useRouter } from 'expo-router';
import api from '@/utils/api';
import LoadingScreen from '@/components/LoadingScreen';
import { ConstructionSite } from '@/types/types';
import { useAlert } from '@/components/AlertComponent';
export default function SitesScreen() {
const [searchTerm, setSearchTerm] = useState('');
@ -12,6 +13,7 @@ export default function SitesScreen() {
const [isLoading, setIsLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const router = useRouter();
const alert = useAlert();
// Fetch cantieri e documenti
const fetchConstructionSites = async () => {
@ -23,7 +25,7 @@ export default function SitesScreen() {
setConstructionSites(response.data);
} catch (error) {
console.error('Errore nel recupero dei cantieri:', error);
Alert.alert('Errore', 'Impossibile recuperare i cantieri. Riprova più tardi.');
alert.showAlert('error', 'Errore', 'Impossibile recuperare i cantieri. Riprova più tardi.');
} finally {
setIsLoading(false);
setRefreshing(false);

View File

@ -1,14 +1,20 @@
import '../global.css';
import { AuthProvider } from '@/utils/authContext';
import { Stack } from 'expo-router';
import { AlertProvider } from '@/components/AlertComponent';
import { NetworkProvider } from '@/utils/networkProvider';
export default function AppLayout() {
return (
<AuthProvider>
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="(protected)" />
<Stack.Screen name="login" />
</Stack>
</AuthProvider>
<NetworkProvider>
<AuthProvider>
<AlertProvider>
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="(protected)" />
<Stack.Screen name="login" />
</Stack>
</AlertProvider>
</AuthProvider>
</NetworkProvider>
);
}

View File

@ -1,10 +1,12 @@
import React, { useState, useContext } from 'react';
import { Eye, EyeOff, Lock, LogIn, Mail } from 'lucide-react-native';
import { KeyboardAvoidingView, Platform, ScrollView, Text, TextInput, TouchableOpacity, View, Image, Alert } from 'react-native';
import { AuthContext } from '@/utils/authContext';
import { useAlert } from '@/components/AlertComponent';
import api from '@/utils/api';
import { AuthContext } from '@/utils/authContext';
import { Eye, EyeOff, Lock, LogIn, Mail } from 'lucide-react-native';
import React, { useContext, useState } from 'react';
import { Image, KeyboardAvoidingView, Platform, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
export default function LoginScreen() {
const alert = useAlert();
const authContext = useContext(AuthContext);
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
@ -14,8 +16,8 @@ export default function LoginScreen() {
// TODO: Riscrivere funzione per migliorare leggibilità e gestione errori
const handleLogin = async () => {
// TODO: Implementa toast o messaggio di errore più user-friendly
if (!username || !password) {
Alert.alert("Attenzione", "Inserisci username e password");
if (!username || !password) {
alert.showAlert('error', 'Attenzione', 'Inserisci username e password');
return;
}
@ -55,7 +57,7 @@ export default function LoginScreen() {
message = "Impossibile contattare il server. Controlla la connessione.";
}
Alert.alert("Login Fallito", message);
alert.showAlert('error', "Login Fallito", message);
} finally {
setIsLoading(false);
}