diff --git a/app.json b/app.json
index f4a298a..8bd8a15 100644
--- a/app.json
+++ b/app.json
@@ -1,7 +1,7 @@
{
"expo": {
- "name": "mariani_app",
- "slug": "mariani_app",
+ "name": "Mariani",
+ "slug": "mariani-app",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/mariani-icon.png",
@@ -10,7 +10,7 @@
"newArchEnabled": true,
"ios": {
"supportsTablet": true,
- "bundleIdentifier": "com.anonymous.mariani-app"
+ "bundleIdentifier": "com.pcrt.mariani-app"
},
"android": {
"adaptiveIcon": {
@@ -25,7 +25,7 @@
"android.permission.CAMERA",
"android.permission.RECORD_AUDIO"
],
- "package": "com.anonymous.mariani_app"
+ "package": "com.pcrt.mariani-app"
},
"web": {
"output": "static",
diff --git a/app/(protected)/_layout.tsx b/app/(protected)/_layout.tsx
index 87666aa..c185500 100644
--- a/app/(protected)/_layout.tsx
+++ b/app/(protected)/_layout.tsx
@@ -14,7 +14,6 @@ export default function ProtectedLayout() {
return ;
}
- // TODO: Aggiungere padding per i dispositivi con notch/bottom bar
return (
,
}}
/>
- {/* // TODO: Rimuovere all'utente e mostrare solo a admin */}
+ {/* // TODO: Probably needs to be restricted to admin */}
,
}}
/>
- {/* TODO: Dovrebbe essere rimosso, va rivisto layout */}
+ {/* TODO: Should be removed - move tabs inside (tabs) and refactor _layout */}
('qr');
@@ -20,6 +21,7 @@ export default function AttendanceScreen() {
const [refreshing, setRefreshing] = useState(false);
const checkNfcAvailability = async () => {
+ // TODO: add env variable to disable NFC checks in development or if not needed
// if (!ENABLE_NFC) return;
try {
const isSupported = await NfcManager.isSupported();
@@ -58,13 +60,13 @@ export default function AttendanceScreen() {
};
const handleStartScan = async () => {
- // Modalità QR Code
+ // Qr Code mode
if (scannerType === 'qr') {
setShowScanner(true);
return;
}
- // Modalità NFC
+ // NFC mode
if (scannerType === 'nfc') {
try {
const supported = await NfcManager.isSupported();
@@ -117,9 +119,11 @@ export default function AttendanceScreen() {
return (
{/* Header */}
-
- Gestione Presenze
- Registra i tuoi movimenti
+
+
+ Gestione Presenze
+ Registra i tuoi movimenti
+
- {/* TODO: item.time può essere null -> calcolare tempo da in e out? */}
+ {/* TODO: item.time can be null - calculate time from in and out? */}
{item.time && (
diff --git a/app/(protected)/automation/index.tsx b/app/(protected)/automation/index.tsx
index 8ac4449..cd7baf2 100644
--- a/app/(protected)/automation/index.tsx
+++ b/app/(protected)/automation/index.tsx
@@ -197,13 +197,6 @@ export default function AutomationScreen() {
)}
-
- {/* alert('Aggiungi nuovo collegamento')}
- className="absolute bottom-8 right-6 w-16 h-16 bg-[#099499] rounded-full shadow-lg items-center justify-center active:scale-90"
- >
-
- */}
);
}
\ No newline at end of file
diff --git a/app/(protected)/index.tsx b/app/(protected)/index.tsx
index ba493c6..3d9eadb 100644
--- a/app/(protected)/index.tsx
+++ b/app/(protected)/index.tsx
@@ -6,6 +6,7 @@ import { AuthContext } from '@/utils/authContext';
import { ActivityItem } from '@/types/types';
import api from '@/utils/api';
import LoadingScreen from '@/components/LoadingScreen';
+import { SafeAreaView } from 'react-native-safe-area-context';
export default function HomeScreen() {
const router = useRouter();
@@ -63,27 +64,29 @@ export default function HomeScreen() {
return (
- {/* Banner Custom */}
-
-
-
-
- Benvenuto
-
- {user?.name} {user?.surname}
-
- {user?.role}
+
+ {/* Custom Banner */}
+
+
+
+
+ Benvenuto
+
+ {user?.name} {user?.surname}
+
+ {user?.role}
+
+
+
+ router.push('/profile')}>
+
+
-
- router.push('/profile')}>
-
-
-
-
+
- {/* Contenuto Scrollabile */}
+ {/* Scrollable Content */}
- {/* Warning Card */}
+ {/* Warning Card - Incomplete Attendance */}
{incompleteAttendance && (
-
-
+ router.push('/attendance')}
+ >
+
@@ -105,10 +111,7 @@ export default function HomeScreen() {
{incompleteAttendance}
- router.push('/attendance')} className="bg-orange-50 px-5 py-3 rounded-xl ml-2 active:bg-orange-100">
- Risolvi
-
-
+
)}
{/* Quick Actions */}
@@ -141,6 +144,7 @@ export default function HomeScreen() {
Ultime Attività
+ {/* TODO: Could be expanded */}
{/*
Vedi tutto
*/}
@@ -153,12 +157,12 @@ export default function HomeScreen() {
- {/* Icona */}
+ {/* Icon */}
- {/* Titolo e Sottotitolo */}
+ {/* Title and Subtitle */}
- {/* Data */}
+ {/* Date */}
{item.date_display}
diff --git a/app/(protected)/permits/index.tsx b/app/(protected)/permits/index.tsx
index af07baf..201a789 100644
--- a/app/(protected)/permits/index.tsx
+++ b/app/(protected)/permits/index.tsx
@@ -8,13 +8,14 @@ import CalendarWidget from '@/components/CalendarWidget';
import LoadingScreen from '@/components/LoadingScreen';
import api from '@/utils/api';
import { formatDate, formatTime } from '@/utils/dateTime';
+import { SafeAreaView } from 'react-native-safe-area-context';
// Icon Mapping
const typeIcons: Record JSX.Element> = {
- Ferie: (color) => ,
- Permesso: (color) => ,
- Malattia: (color) => ,
- Assenza: (color) => ,
+ Ferie: (color) => ,
+ Permesso: (color) => ,
+ Malattia: (color) => ,
+ Assenza: (color) => ,
};
export default function PermitsScreen() {
@@ -33,7 +34,7 @@ export default function PermitsScreen() {
// Fetch Permits
const response = await api.get('/time-off-request/list');
setPermits(response.data);
-
+
// Fetch Types
const typesResponse = await api.get('/time-off-request/get-types');
setTypes(typesResponse.data);
@@ -49,22 +50,21 @@ export default function PermitsScreen() {
const filteredPermits = useMemo(() => {
if (!permits.length) return [];
- // Calcoliamo inizio e fine del mese visualizzato
+ // Calculate start and end of the current month
const year = currentMonthDate.getFullYear();
const month = currentMonthDate.getMonth();
-
+
const startOfMonth = new Date(year, month, 1);
- // Trucco JS: giorno 0 del mese successivo = ultimo giorno del mese corrente
- const endOfMonth = new Date(year, month + 1, 0, 23, 59, 59);
+ // Day 0 of the next month = last day of the current month
+ const endOfMonth = new Date(year, month + 1, 0, 23, 59, 59);
return permits.filter(item => {
const itemStart = new Date(item.start_date?.toString() ?? '');
- // Se non c'è end_date, assumiamo sia un giorno singolo (quindi end = start)
+ // If there's no end_date, assume it's a single day (so end = start)
const itemEnd = item.end_date ? new Date(item.end_date?.toString() ?? '') : new Date(item.start_date?.toString() ?? '');
- // FORMULA OVERLAP:
- // Il permesso è visibile se inizia prima della fine del mese
- // E finisce dopo l'inizio del mese.
+ // The permit is visible if it starts before the end of the month
+ // And ends after the start of the month.
return itemStart <= endOfMonth && itemEnd >= startOfMonth;
});
}, [permits, currentMonthDate]);
@@ -92,11 +92,13 @@ export default function PermitsScreen() {
/>
{/* Header */}
-
-
- Ferie e Permessi
- Gestisci le tue assenze
-
+
+
+
+ Ferie e Permessi
+ Gestisci le tue assenze
+
+
setCurrentMonthDate(date)} />
- {/* Lista Richieste Recenti */}
+ {/* Recent Requests List */}
{filteredPermits.length === 0 ? (
Nessuna richiesta di permesso questo mese
@@ -135,7 +137,7 @@ export default function PermitsScreen() {
)}
- {/* TODO: Aggiungere funzionalità per modificare/eliminare la richiesta? */}
+ {/* TODO: Add functionality to edit/remove the request */}
{item.status ? 'Approvato' : 'In Attesa'}
diff --git a/app/(protected)/profile/documents.tsx b/app/(protected)/profile/documents.tsx
index f59420a..42a0e0d 100644
--- a/app/(protected)/profile/documents.tsx
+++ b/app/(protected)/profile/documents.tsx
@@ -1,5 +1,5 @@
import { useAlert } from '@/components/AlertComponent';
-import { ArrowLeft, Download, FileText, MapPin, Plus, Search, CalendarIcon } from 'lucide-react-native';
+import { Download, FileText, MapPin, Plus, Search, CalendarIcon, ChevronLeft } from 'lucide-react-native';
import React, { useEffect, useState } from 'react';
import { useRouter } from 'expo-router';
import { RangePickerModal } from '@/components/RangePickerModal';
@@ -11,6 +11,7 @@ import LoadingScreen from '@/components/LoadingScreen';
import { formatTimestamp, parseTimestamp } from '@/utils/dateTime';
import AddDocumentModal from '@/components/AddDocumentModal';
import { downloadAndShareDocument, uploadDocument } from '@/utils/documentUtils';
+import { SafeAreaView } from 'react-native-safe-area-context';
export default function DocumentsScreen() {
const router = useRouter();
@@ -32,7 +33,7 @@ export default function DocumentsScreen() {
try {
if (!refreshing) setIsLoading(true);
- // Fetch Documenti Utente
+ // Fetch User Documents
const response = await api.get(`/attachment/get-user-attachments`);
setDocuments(response.data);
} catch (error) {
@@ -53,24 +54,24 @@ export default function DocumentsScreen() {
fetchUserDocuments();
};
- // Filtra Documenti in base a searchTerm e range
+ // Filter Documents based on searchTerm and range
const filteredDocs = documents.filter(doc => {
- // Filtro Testuale
+ // Text Filter
const matchesSearch = doc.title.toLowerCase().includes(searchTerm.toLowerCase());
if (!matchesSearch) return false;
- // Filtro Date Range
+ // Date Range Filter
if (range.startDate || range.endDate) {
const docDate = parseTimestamp(doc.updated_at); // doc.date è "DD/MM/YYYY"
- // Controllo Data Inizio
+ // Start Date Check
if (range.startDate) {
- // dayjs(range.startDate).toDate() converte in oggetto Date JS standard
+ // dayjs(range.startDate).toDate() converts to standard JS Date object
const start = dayjs(range.startDate).startOf('day').toDate();
if (docDate < start) return false;
}
- // Controllo Data Fine
+ // End Date Check
if (range.endDate) {
const end = dayjs(range.endDate).endOf('day').toDate();
if (docDate > end) return false;
@@ -80,7 +81,7 @@ export default function DocumentsScreen() {
return true;
});
- // Gestione Caricamento Documento
+ // Document Upload Handling
const handleUploadDocument = async (file: any, customTitle?: string) => {
setIsUploading(true);
try {
@@ -105,14 +106,18 @@ export default function DocumentsScreen() {
return (
{/* Header */}
-
- router.back()} className="p-2 -ml-2 rounded-full active:bg-gray-100">
-
-
-
- Documenti
- Gestisci i tuoi documenti
-
+
+
+
+ router.back()} className="p-2 rounded-full active:bg-gray-100">
+
+
+
+ Documenti
+ Gestisci i tuoi documenti
+
+
+
@@ -141,7 +146,7 @@ export default function DocumentsScreen() {
- {/* Modale Unico per il Range */}
+ {/* Range Picker Modal */}
setShowRangePicker(false)}
@@ -149,7 +154,7 @@ export default function DocumentsScreen() {
onApply={setRange}
/>
- {/* List */}
+ {/* Documents List */}
downloadAndShareDocument(doc.mimetype, doc.title, doc.url)}
+ onPress={() => downloadAndShareDocument(doc.mimetype, doc.title, doc.url)}
className="p-4 bg-gray-50 rounded-2xl active:bg-gray-100">
@@ -193,7 +198,7 @@ export default function DocumentsScreen() {
- {/* Modale Caricamento Documento */}
+ {/* Document Upload Modal */}
setShowUploadModal(false)}
diff --git a/app/(protected)/profile/index.tsx b/app/(protected)/profile/index.tsx
index 781f25e..5756967 100644
--- a/app/(protected)/profile/index.tsx
+++ b/app/(protected)/profile/index.tsx
@@ -3,55 +3,56 @@ import { ChevronLeft, FileText, LogOut, Mail, Settings, User } from 'lucide-reac
import React, { useContext } from 'react';
import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
import { AuthContext } from '@/utils/authContext';
+import { SafeAreaView } from 'react-native-safe-area-context';
export default function ProfileScreen() {
const authContext = useContext(AuthContext);
const { user } = authContext;
const router = useRouter();
- // Genera le iniziali dell'utente
+ // Generate user initials
const initials = `${user?.name?.[0] ?? ''}${user?.surname?.[0] ?? ''}`.toUpperCase();
return (
- {/* --- SEZIONE HEADER (INVARIATA) --- */}
-
-
- router.back()}
- >
-
-
-
-
- {initials}
-
-
- Profilo
- {user?.name} {user?.surname}
+
+ {/* Header Section */}
+
+
+ router.back()}
+ >
+
+
+
+
+ {initials}
+
+
+ Profilo
+ {user?.name} {user?.surname}
+
-
+
- {/* Card info - Testi ingranditi */}
+ {/* Info Card - Enlarged Texts */}
- {/* Titolo sezione ingrandito */}
+ {/* Section title */}
Informazioni
- {/* Icona leggermente più grande e container adattato */}
- {/* Label e valore ingranditi */}
Email
{user?.email}
@@ -69,7 +70,7 @@ export default function ProfileScreen() {
- {/* Actions - Testi e Pulsanti ingranditi */}
+ {/* Actions */}
Azioni
@@ -86,7 +87,8 @@ export default function ProfileScreen() {
Apri
- console.log('Apri impostazioni')} className="bg-white p-4 rounded-3xl shadow-sm flex-row items-center justify-between border border-gray-100 mb-4">
+ {/* TODO: Settings not implemented at the moment */}
+ {/* console.log('Apri impostazioni')} className="bg-white p-4 rounded-3xl shadow-sm flex-row items-center justify-between border border-gray-100 mb-4">
@@ -97,7 +99,7 @@ export default function ProfileScreen() {
Apri
-
+ */}
diff --git a/app/(protected)/sites/[id].tsx b/app/(protected)/sites/[id].tsx
index c951235..5ba46d8 100644
--- a/app/(protected)/sites/[id].tsx
+++ b/app/(protected)/sites/[id].tsx
@@ -11,6 +11,7 @@ import dayjs from 'dayjs';
import { formatTimestamp, parseTimestamp } from '@/utils/dateTime';
import { downloadAndShareDocument, uploadDocument } from '@/utils/documentUtils';
import AddDocumentModal from '@/components/AddDocumentModal';
+import { SafeAreaView } from 'react-native-safe-area-context';
export default function SiteDocumentsScreen() {
const router = useRouter();
@@ -24,12 +25,12 @@ export default function SiteDocumentsScreen() {
const [showUploadModal, setShowUploadModal] = useState(false);
const [isUploading, setIsUploading] = useState(false);
- // Fetch dei documenti del cantiere
+ // Fetch site documents
const fetchSiteDocuments = useCallback(async (siteId: number, isRefreshing = false) => {
try {
if (!isRefreshing) setIsLoading(true);
- // Fetch Documenti
+ // Fetch Documents
const response = await api.get(`/attachment/get-site-attachments`, {
params: { siteId }
});
@@ -74,13 +75,13 @@ export default function SiteDocumentsScreen() {
});
const [showRangePicker, setShowRangePicker] = useState(false);
- // Filtraggio Documenti
+ // Filter documents based on search term and date range
const filteredDocs = documents.filter(doc => {
- // Filtro Testuale (su nome documento)
+ // Textual Filter (on document name)
const matchesSearch = doc.title.toLowerCase().includes(searchTerm.toLowerCase());
if (!matchesSearch) return false;
- // Filtro Date Range
+ // Date Range Filter
if (range.startDate || range.endDate) {
const docDate = parseTimestamp(doc.updated_at);
if (range.startDate) {
@@ -95,7 +96,7 @@ export default function SiteDocumentsScreen() {
return true;
});
- // Gestione Download e Condivisione Documento
+ // Handle Document Download and Share
const handleDownloadAndShare = async (mimetype: string, fileName: string, fileUrl: string) => {
try {
await downloadAndShareDocument(mimetype, fileName, fileUrl);
@@ -105,7 +106,7 @@ export default function SiteDocumentsScreen() {
}
};
- // Gestione Caricamento Documento
+ // Handle Document Upload
const handleUploadDocument = async (file: any, customTitle?: string) => {
setIsUploading(true);
try {
@@ -130,36 +131,40 @@ export default function SiteDocumentsScreen() {
return (
{/* Header */}
-
- router.back()} className="p-2 -ml-2 rounded-full active:bg-gray-100">
-
-
+
+
+
+ router.back()} className="p-2 -ml-2 rounded-full active:bg-gray-100">
+
+
-
- {/* Badge Codice Cantiere */}
- {site.code && (
-
-
- {site.code}
+
+ {/* Site Code Badge */}
+ {site.code && (
+
+
+ {site.code}
+
+
+ )}
+
+ {/* Site Name with Truncate */}
+
+ {site.name}
+
+ Archivio documenti
- )}
-
- {/* Nome Cantiere con Truncate funzionante */}
-
- {site.name}
-
-
- Archivio documenti
-
+
+
- {/* Search + Date Row (Spostato qui) */}
+ {/* Search + Date Row */}
@@ -189,7 +194,7 @@ export default function SiteDocumentsScreen() {
onApply={setRange}
/>
- {/* Lista Documenti */}
+ {/* Documents List */}
- {/* FAB (Spostato qui) */}
+ {/* FAB */}
setShowUploadModal(true)}
className="absolute bottom-8 right-6 w-16 h-16 bg-[#099499] rounded-full shadow-lg items-center justify-center active:scale-90"
@@ -230,13 +235,13 @@ export default function SiteDocumentsScreen() {
- {/* Modale Caricamento Documento */}
+ {/* Upload Document Modal */}
setShowUploadModal(false)}
onUpload={handleUploadDocument}
isUploading={isUploading}
/>
-
+
);
}
\ No newline at end of file
diff --git a/app/(protected)/sites/index.tsx b/app/(protected)/sites/index.tsx
index 41e4684..e32545a 100644
--- a/app/(protected)/sites/index.tsx
+++ b/app/(protected)/sites/index.tsx
@@ -6,6 +6,7 @@ import api from '@/utils/api';
import LoadingScreen from '@/components/LoadingScreen';
import { ConstructionSite } from '@/types/types';
import { useAlert } from '@/components/AlertComponent';
+import { SafeAreaView } from 'react-native-safe-area-context';
export default function SitesScreen() {
const [searchTerm, setSearchTerm] = useState('');
@@ -15,12 +16,12 @@ export default function SitesScreen() {
const router = useRouter();
const alert = useAlert();
- // Fetch cantieri e documenti
+ // Fetch sites and documents
const fetchConstructionSites = async () => {
try {
if (!refreshing) setIsLoading(true);
- // Fetch Cantieri
+ // Fetch Sites and their attachments count
const response = await api.get('/construction-site/sites-attachments');
setConstructionSites(response.data);
} catch (error) {
@@ -41,7 +42,7 @@ export default function SitesScreen() {
fetchConstructionSites();
};
- // Filtriamo i cantieri in base alla ricerca
+ // Filter sites based on search term
const filteredSites = constructionSites.filter(site =>
site.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
site.code.toLowerCase().includes(searchTerm.toLowerCase())
@@ -54,9 +55,11 @@ export default function SitesScreen() {
return (
{/* Header */}
-
- Cantieri
- Seleziona un cantiere per vedere i documenti
+
+
+ Cantieri
+ Seleziona un cantiere per vedere i documenti
+
@@ -74,11 +77,10 @@ export default function SitesScreen() {
/>
- {/* Lista Cantieri */}
- {/* TODO: Rimuovere lo ScrollIndicator? */}
+ {/* Sites List */}
}
@@ -88,8 +90,8 @@ export default function SitesScreen() {
key={index}
onPress={() => router.push({
pathname: '/(protected)/sites/[id]',
- params: {
- id: site.id ,
+ params: {
+ id: site.id,
siteData: JSON.stringify(site),
}
})}
@@ -111,7 +113,6 @@ export default function SitesScreen() {
- {/* Freccia al posto del download */}
))}
diff --git a/app/_layout.tsx b/app/_layout.tsx
index edb19b8..8f02740 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -3,18 +3,21 @@ import { AuthProvider } from '@/utils/authContext';
import { Stack } from 'expo-router';
import { AlertProvider } from '@/components/AlertComponent';
import { NetworkProvider } from '@/utils/networkProvider';
+import { SafeAreaProvider } from 'react-native-safe-area-context';
export default function AppLayout() {
return (
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
);
}
\ No newline at end of file
diff --git a/app/login.tsx b/app/login.tsx
index b093933..79a2bb4 100644
--- a/app/login.tsx
+++ b/app/login.tsx
@@ -13,21 +13,19 @@ export default function LoginScreen() {
const [showPassword, setShowPassword] = useState(false);
const [isLoading, setIsLoading] = useState(false);
- // TODO: Riscrivere funzione per migliorare leggibilità e gestione errori
+ // Login Handler function
const handleLogin = async () => {
- // TODO: Implementa toast o messaggio di errore più user-friendly
if (!username || !password) {
alert.showAlert('error', 'Attenzione', 'Inserisci username e password');
return;
}
-
setIsLoading(true);
try {
username.trim();
password.trim();
- // Esegui richiesta di login
+ // Execute login request
const response = await api.post("/user/login", {
username: username,
password: password
@@ -35,25 +33,24 @@ export default function LoginScreen() {
const { token, user } = response.data;
- console.log("Login successo, token ricevuto.");
+ console.log("Login riuscito. Token:", token);
console.log("Dati utente:", user);
- // Passiamo token e dati utente al context che gestirà salvataggio e redirect
+ // Pass token and user data to the context which will handle saving and redirect
authContext.logIn(token, user);
} catch (error: any) {
console.error("Login Error:", error);
let message = "Si è verificato un errore durante l'accesso.";
if (error.response) {
- // Errore dal server (es. 401 Credenziali errate)
+ // Server error (e.g., 401 Invalid credentials)
if (error.response.status === 401) {
- // TODO: Alert o Toast specifico per credenziali errate
message = "Credenziali non valide."
} else {
message = `Errore Server: ${error.response.data.message || error.response.status}`;
}
} else if (error.request) {
- // Server non raggiungibile
+ // Server not reachable
message = "Impossibile contattare il server. Controlla la connessione.";
}
@@ -65,7 +62,7 @@ export default function LoginScreen() {
return (
- {/* Header con Logo/Titolo */}
+ {/* Header with Logo/Title */}
+ {/* TODO: Implement password recovery functionality */}
Password dimenticata?
- {/* Tasto Login */}
+ {/* Login Button */}
{
if (!visible) {
setSelectedFile(null);
@@ -24,11 +24,11 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
}
}, [visible]);
- // TODO: Considerare selezione multipla?
+ // TODO: Need to handle multiple file selection?
const pickDocument = async () => {
try {
const result = await DocumentPicker.getDocumentAsync({
- type: '*/*', // Puoi limitare a 'application/pdf', 'image/*', ecc.
+ type: '*/*', // You can limit to 'application/pdf', 'image/*', etc.
copyToCacheDirectory: true,
});
@@ -55,7 +55,7 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
if (!selectedFile) return;
const fullTitle = customTitle ? `${customTitle}${fileExtension}` : selectedFile.name;
- // Se il titolo custom è vuoto, usiamo il nome originale
+ // If customTitle is empty, we use the original file name
onUpload(selectedFile, fullTitle);
};
@@ -65,8 +65,7 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
setFileExtension('');
};
- // Formatta dimensione file
- // TODO: Spostare in utils?
+ // Format file size in a human-readable format
const formatSize = (size?: number) => {
if (!size) return '0 B';
const k = 1024;
@@ -93,10 +92,8 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
- {/* Body */}
-
- {/* Area Selezione File */}
+ {/* File Selection Area */}
{!selectedFile ? (
PDF, Immagini, Word
) : (
- // Visualizzazione File Selezionato
+ // Selected File View
@@ -128,7 +125,7 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
)}
- {/* Campo Rinomina (Visibile solo se c'è un file) */}
+ {/* Rename field (Visible only if a file is selected) */}
{selectedFile && (
Rinomina File
diff --git a/components/AlertComponent.tsx b/components/AlertComponent.tsx
index e6dea51..6420efd 100644
--- a/components/AlertComponent.tsx
+++ b/components/AlertComponent.tsx
@@ -11,6 +11,7 @@ interface AlertContextData {
const AlertContext = createContext({} as AlertContextData);
+// TODO: Move this config to a separate file
const ALERT_CONFIG = {
success: {
icon: CheckCircle,
@@ -57,33 +58,33 @@ export const AlertProvider = ({ children }: { children: ReactNode }) => {
const { icon: Icon, color, bgColor, btnColor } = ALERT_CONFIG[type];
+ // TODO: Need to refactor component styles
return (
{children}
- {/* IL COMPONENTE ALERT MODALE */}
- {/* Backdrop scuro */}
+ {/* Dark Backdrop */}
- {/* Contenitore Alert */}
+ {/* Alert Container */}
- {/* Icona Cerchiata */}
+ {/* Icon Circle */}
- {/* Testi */}
+ {/* Texts */}
{title}
@@ -92,10 +93,10 @@ export const AlertProvider = ({ children }: { children: ReactNode }) => {
{message}
- {/* Pulsante OK */}
+ {/* OK Button */}
Ok, ho capito
diff --git a/components/CalendarWidget.tsx b/components/CalendarWidget.tsx
index 9c7684a..400f4a5 100644
--- a/components/CalendarWidget.tsx
+++ b/components/CalendarWidget.tsx
@@ -12,7 +12,7 @@ interface CalendarWidgetProps {
export default function CalendarWidget({ events, types, onMonthChange }: CalendarWidgetProps) {
const [currentDate, setCurrentDate] = useState(new Date());
- // Helpers per il calendario
+ // Calendar helpers
const daysInMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0).getDate();
const firstDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1).getDay(); // 0 = Sun
const adjustedFirstDay = firstDayOfMonth === 0 ? 6 : firstDayOfMonth - 1; // 0 = Mon
@@ -40,8 +40,8 @@ export default function CalendarWidget({ events, types, onMonthChange }: Calenda
const dateStr = `${year}-${month}-${dayStr}`;
return events.find(event => {
- // Logica semplice: controlla se la data cade nel range
- // Nota: per una logica perfetta sui range lunghi, servirebbe un controllo più approfondito
+ // Simple logic: check if the date falls within the range
+ // Note: for perfect logic on long ranges, a more thorough check would be needed
if (!event.start_date) return false;
if (event.timeOffRequestType.name === 'Permesso') return event.start_date === dateStr;
const end = event.end_date || event.start_date;
@@ -51,7 +51,7 @@ export default function CalendarWidget({ events, types, onMonthChange }: Calenda
return (
- {/* Header Mese */}
+ {/* Month Header */}
changeMonth(-1)}
@@ -106,7 +106,7 @@ export default function CalendarWidget({ events, types, onMonthChange }: Calenda
})}
- {/* Legenda */}
+ {/* Legend */}
{types.map((type) => (
diff --git a/components/DeviceCard.tsx b/components/DeviceCard.tsx
index a3e53fe..49d5fff 100644
--- a/components/DeviceCard.tsx
+++ b/components/DeviceCard.tsx
@@ -4,6 +4,7 @@ import React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
const DeviceCard = ({ device, onToggle }: { device: HaEntity; onToggle: (entityId: string) => void; }) => {
+ // Icon mapping based on entity domain
const getIcon = () => {
const domain = device.entity_id.split('.')[0];
@@ -30,6 +31,7 @@ const DeviceCard = ({ device, onToggle }: { device: HaEntity; onToggle: (entityI
const isToggleable = ['light', 'switch', 'fan', 'input_boolean'].includes(device.entity_id.split('.')[0]);
const isOn = device.state === 'on';
+ // State mapping
const getDeviceState = (state: string) => {
switch (state) {
case 'on':
diff --git a/components/NfcScanModal.tsx b/components/NfcScanModal.tsx
index cddced9..6fef9e1 100644
--- a/components/NfcScanModal.tsx
+++ b/components/NfcScanModal.tsx
@@ -9,8 +9,9 @@ interface NfcScanModalProps {
onScan: (data: string) => void;
}
+// TODO: Needs to be tested on a real device
export default function NfcScanModal({ visible, onClose, onScan }: NfcScanModalProps) {
- // Animazione per l'effetto "pulsante" (Breathing)
+ // Breathing animation effect
const scaleAnim = useRef(new Animated.Value(1)).current;
const opacityAnim = useRef(new Animated.Value(0.3)).current;
@@ -44,7 +45,7 @@ export default function NfcScanModal({ visible, onClose, onScan }: NfcScanModalP
}
};
- // Loop infinito di espansione e contrazione
+ // Animation loop for the pulsing effect
const animationLoop = () => {
Animated.loop(
Animated.parallel([
@@ -97,11 +98,9 @@ export default function NfcScanModal({ visible, onClose, onScan }: NfcScanModalP
statusBarTranslucent
>
-
- {/* Card Principale */}
- {/* Bottone Chiudi (Alto Destra) */}
+ {/* Close Button (Top Right) */}
- {/* Area Animata NFC */}
+ {/* NFC Animated Area */}
- {/* Cerchio Pulsante (Sfondo) */}
+ {/* Pulsing Circle (Background) */}
- {/* Cerchio Fisso (Primo piano) */}
+ {/* Fixed Circle (Foreground) */}
- {/* Testi */}
+ {/* Texts */}
Pronto alla scansione
@@ -139,7 +138,7 @@ export default function NfcScanModal({ visible, onClose, onScan }: NfcScanModalP
Avvicina il retro del tuo smartphone al Tag NFC per registrare la presenza.
- {/* Indicatore Visivo (Simulazione onde) */}
+ {/* Indicator (Wave Simulation) */}
diff --git a/components/OfflineScreen.tsx b/components/OfflineScreen.tsx
index 4ac288e..b59b02f 100644
--- a/components/OfflineScreen.tsx
+++ b/components/OfflineScreen.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
-import { WifiOff, RefreshCw } from 'lucide-react-native';
+import { WifiOff } from 'lucide-react-native';
interface OfflineScreenProps {
onRetry: () => void;
@@ -12,7 +12,7 @@ export default function OfflineScreen({ onRetry, isRetrying = false }: OfflineSc
return (
- {/* Icona con cerchio di sfondo */}
+ {/* Icon */}
@@ -25,7 +25,7 @@ export default function OfflineScreen({ onRetry, isRetrying = false }: OfflineSc
Sembra che non ci sia connessione a internet.{'\n'}Controlla il Wi-Fi o i dati mobili e riprova.
- {/* Pulsante Riprova */}
+ {/* Retry Button */}
-
{isRetrying ? 'Controllo...' : 'Riprova'}
diff --git a/components/QrScanModal.tsx b/components/QrScanModal.tsx
index 9da78ef..28a06b5 100644
--- a/components/QrScanModal.tsx
+++ b/components/QrScanModal.tsx
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react';
-import { View, Text, Modal, TouchableOpacity, Vibration, StyleSheet } from 'react-native';
+import { View, Text, Modal, TouchableOpacity, Vibration, StyleSheet, Dimensions } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { CameraView, useCameraPermissions } from 'expo-camera';
import { X, ScanLine } from 'lucide-react-native';
@@ -13,8 +13,10 @@ interface QrScanModalProps {
export default function QrScanModal({ visible, onClose, onScan }: QrScanModalProps) {
const [permission, requestPermission] = useCameraPermissions();
const [scanned, setScanned] = useState(false);
+ const { width, height } = Dimensions.get('window');
+ const squareSize = Math.min(width * 0.8, height * 0.8, 400);
- // Gestione Permessi e Reset Stato
+ // Permission Handling and Reset Scanned State on Modal Open
useEffect(() => {
if (visible) {
setScanned(false);
@@ -59,26 +61,25 @@ export default function QrScanModal({ visible, onClose, onScan }: QrScanModalPro
}}
/>
- {/* Overlay Oscuro con "buco" trasparente (Simulato visivamente con bordi o opacity) */}
-
-
+ {/* Dark Overlay with Transparent "Hole" (Visually Simulated with Borders or Opacity) */}
+
{/* Header Overlay */}
-
+
Scansiona QR Code
Inquadra il codice nel riquadro
-
+
- {/* Area Centrale (Trasparente per la camera) */}
-
+ {/* Central Area (Transparent for the camera) */}
+
-
- {/* Angoli decorativi */}
+
+ {/* Decorative Corners */}
- {/* Linea scansione animata o icona */}
+ {/* Animated Scan Line or Icon */}
{!scanned && }
@@ -94,7 +95,7 @@ export default function QrScanModal({ visible, onClose, onScan }: QrScanModalPro
Chiudi
-
+
);
diff --git a/components/RangePickerModal.tsx b/components/RangePickerModal.tsx
index 3561c60..cc54be6 100644
--- a/components/RangePickerModal.tsx
+++ b/components/RangePickerModal.tsx
@@ -4,7 +4,7 @@ import DateTimePicker, { DateType, useDefaultStyles } from 'react-native-ui-date
import { Check, X } from 'lucide-react-native';
export const RangePickerModal = ({ visible, onClose, onApply }: any) => {
- const defaultStyles = useDefaultStyles();
+ const defaultStyles = useDefaultStyles('light');
const [range, setRange] = useState<{
startDate: DateType;
endDate: DateType;
diff --git a/components/RequestPermitModal.tsx b/components/RequestPermitModal.tsx
index 09905fc..d412fed 100644
--- a/components/RequestPermitModal.tsx
+++ b/components/RequestPermitModal.tsx
@@ -16,7 +16,7 @@ interface RequestPermitModalProps {
}
export default function RequestPermitModal({ visible, types, onClose, onSubmit }: RequestPermitModalProps) {
- const defaultStyles = useDefaultStyles();
+ const defaultStyles = useDefaultStyles('light');
const alert = useAlert();
const [type, setType] = useState(types[0]); // Default to first type
const [date, setDate] = useState();
@@ -31,7 +31,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
const [endTime, setEndTime] = useState('');
const [message, setMessage] = useState('');
- // Funzione per resettare le selezioni di data
+ // Clean up function to reset all fields
const clearCalendar = () => {
setDate(null);
setRange({ startDate: null, endDate: null });
@@ -39,7 +39,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
setType(types[0]);
};
- // Funzione per validare la richiesta
+ // Function to validate the request
function validateRequest(type: TimeOffRequestType, date: string | null | undefined, range: { startDate: string | null; endDate: string | null }, startTime: string, endTime: string, message: string): string | null {
if (!type) return "Seleziona una tipologia di assenza.";
@@ -57,7 +57,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
return null;
}
- // Funzione per inviare la richiesta alla API
+ // Function to send the request to the API
const saveRequest = async (requestData: any) => {
try {
const response = await api.post('/time-off-request/save-request', requestData);
@@ -73,7 +73,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
}
};
- // Funzione per inviare la richiesta
+ // Function to submit the request
const handleSubmit = async () => {
const error = validateRequest(type, date, range, startTime, endTime, message);
if (error) {
@@ -92,7 +92,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
try {
await saveRequest(requestData);
- onSubmit(requestData); // TODO: Gestire risposta e controllare fetch in index?
+ onSubmit(requestData);
onClose();
} catch (e) {
alert.showAlert("error", "Errore", "Impossibile inviare la richiesta.");
@@ -108,7 +108,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
>
- {/* Header Modale */}
+ {/* Modal Header */}
Nuova Richiesta
@@ -118,7 +118,7 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
- {/* Tipologia */}
+ {/* Permit Type */}
Tipologia Assenza
) : (
@@ -218,7 +218,8 @@ export default function RequestPermitModal({ visible, types, onClose, onSubmit }
/>
)}
- {/* TODO: Trasformare message in una select? - Predefinito per alcuni tipi */}
+
+ {/* Reason field */}
Motivo
- {/* Azioni */}
+ {/* Actions */}
{
diff --git a/components/TimePickerModal.tsx b/components/TimePickerModal.tsx
index 445e7c7..053c7fd 100644
--- a/components/TimePickerModal.tsx
+++ b/components/TimePickerModal.tsx
@@ -13,7 +13,7 @@ interface TimePickerModalProps {
}
export const TimePickerModal = ({ visible, initialDate, title, onConfirm, onClose }: TimePickerModalProps) => {
- const defaultStyles = useDefaultStyles();
+ const defaultStyles = useDefaultStyles('light');
const [selectedDate, setSelectedDate] = useState(initialDate || new Date());
const formatTime = (date?: DateType | null) => {
@@ -36,7 +36,7 @@ export const TimePickerModal = ({ visible, initialDate, title, onConfirm, onClos
- {/* Header con chiusura */}
+ {/* Header */}
{title}
@@ -56,7 +56,7 @@ export const TimePickerModal = ({ visible, initialDate, title, onConfirm, onClos
onChange={(d) => setSelectedDate(d.date || new Date())}
/>
- {/* Bottone conferma */}
+ {/* Confirm Button */}
=18.17"
diff --git a/utils/api.ts b/utils/api.ts
index e19bc99..f4ec75a 100644
--- a/utils/api.ts
+++ b/utils/api.ts
@@ -4,17 +4,17 @@ import * as SecureStore from 'expo-secure-store';
const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL;
export const KEY_TOKEN = 'auth_key';
-// Crea un'istanza di axios
+// Create an Axios instance with default configuration
const api = axios.create({
baseURL: API_BASE_URL,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
- timeout: 10000, // 10 secondi timeout
+ timeout: 10000, // 10 seconds timeout
});
-// Interceptor: Aggiunge il token a OGNI richiesta se esiste
+// Interceptor: Adds the token to EVERY request if it exists
api.interceptors.request.use(
async (config) => {
const token = await SecureStore.getItemAsync(KEY_TOKEN);
@@ -29,7 +29,7 @@ api.interceptors.request.use(
}
);
-// Interceptor: Gestione errori globale (es. token scaduto)
+// Interceptor: Global error handling (e.g., expired token)
api.interceptors.response.use(
(response) => response,
async (error) => {
@@ -38,9 +38,9 @@ api.interceptors.response.use(
if (error.response) {
console.error('[API ERROR]', error.response.status, error.response.data);
- // Se riceviamo 401 (Unauthorized), potremmo voler fare il logout forzato
+ // If we receive 401 (Unauthorized), we might want to force logout
if (error.response.status === 401) {
- // TODO: Qui potresti emettere un evento per disconnettere l'utente
+ // TODO: Here you can add logic to redirect to login screen if needed
await SecureStore.deleteItemAsync(KEY_TOKEN);
}
} else {
diff --git a/utils/authContext.tsx b/utils/authContext.tsx
index 9313b99..16d732c 100644
--- a/utils/authContext.tsx
+++ b/utils/authContext.tsx
@@ -58,15 +58,15 @@ export function AuthProvider({ children }: PropsWithChildren) {
useEffect(() => {
const initApp = async () => {
try {
- // 1. Recupero Token salvato
+ // Get saved Token from SecureStore
const savedToken = await SecureStore.getItemAsync(KEY_TOKEN);
if (savedToken) {
console.log("Token trovato: ", savedToken);
- // 2. Chiamata al backend per verificare il token e scaricare i dati utente
- // Nota: api.ts aggiunge già l'header Authorization grazie all'interceptor (se configurato per leggere da SecureStore)
- // Se il tuo api.ts legge da AsyncStorage, assicurati che siano allineati, altrimenti passalo a mano qui:
+ // Call backend to verify token and fetch user data
+ // Note: api.ts already adds the Authorization header thanks to the interceptor (if configured to read from SecureStore)
+ // If your api.ts reads from AsyncStorage, make sure they are aligned, otherwise pass it manually here:
const response = await api.get("/user/info", {
headers: { Authorization: `Bearer ${savedToken}` }
});
@@ -75,13 +75,13 @@ export function AuthProvider({ children }: PropsWithChildren) {
const userData = result.user;
console.log("Sessione valida, dati utente caricati:", userData);
- // 3. Mappatura dati (Backend -> Frontend)
- // Il backend actionMe ritorna: { id, username, role }
+ // Data mapping (Backend -> Frontend)
+ // The backend actionMe returns: { id, username, role }
const loadedUser: UserData = {
id: userData.id,
username: userData.username,
role: userData.role,
- // Gestiamo i campi opzionali se il backend non li manda ancora
+ // Handle optional fields if the backend doesn't send them yet
name: userData.name,
surname: userData.surname || '',
email: userData.email || '',
@@ -96,7 +96,7 @@ export function AuthProvider({ children }: PropsWithChildren) {
} catch (error: any) {
console.error('Errore inizializzazione (Token scaduto o Server down):', error.message);
- // Se il token non è valido, puliamo tutto
+ // If the token is not valid, clear everything
await SecureStore.deleteItemAsync(KEY_TOKEN);
setIsAuthenticated(false);
setUser(null);
@@ -109,7 +109,7 @@ export function AuthProvider({ children }: PropsWithChildren) {
initApp();
}, []);
- // Protezione rotte (opzionale, ma consigliata qui o nel Layout)
+ // Route protection (optional, but recommended here or in the Layout)
useEffect(() => {
if (!isReady) return;
diff --git a/utils/dateTime.ts b/utils/dateTime.ts
index 728ad08..9c17afb 100644
--- a/utils/dateTime.ts
+++ b/utils/dateTime.ts
@@ -1,9 +1,9 @@
import { DateType } from "react-native-ui-datepicker";
/**
- * Trasforma una data da "YYYY-MM-DD" a "DD/MM/YYYY"
- * @param dateStr stringa data in formato ISO "YYYY-MM-DD"
- * @returns stringa formattata "DD/MM/YYYY"
+ * Transforms "YYYY-MM-DD" to "DD/MM/YYYY"
+ * @param dateStr string in ISO date format "YYYY-MM-DD"
+ * @returns formatted string "DD/MM/YYYY"
*/
export const formatDate = (dateStr: string | null | undefined): string => {
if (!dateStr) return '';
@@ -12,9 +12,9 @@ export const formatDate = (dateStr: string | null | undefined): string => {
};
/**
- * Trasforma un'ora da "HH:MM:SS" a "HH:MM"
- * @param timeStr stringa ora in formato "HH:MM:SS"
- * @returns stringa formattata "HH:MM"
+ * Transforms time from "HH:MM:SS" to "HH:MM"
+ * @param timeStr string in time format "HH:MM:SS"
+ * @returns formatted string "HH:MM"
*/
export const formatTime = (timeStr: string | null | undefined): string => {
if (!timeStr) return '';
@@ -23,9 +23,9 @@ export const formatTime = (timeStr: string | null | undefined): string => {
};
/**
- * Formatta una data per l'uso con un date picker, normalizzandola a mezzanotte
- * @param d Data in formato DateType
- * @returns stringa data in formato "YYYY-MM-DD" o null se l'input è null/undefined
+ * Formats a date for use with a date picker, normalizing it to midnight
+ * @param d Date in DateType format
+ * @returns string in "YYYY-MM-DD" format or null if input is null/undefined
*/
export const formatPickerDate = (d: DateType | null | undefined) => {
if (!d) return null;
@@ -41,9 +41,9 @@ export const formatPickerDate = (d: DateType | null | undefined) => {
}
/**
- * Trasforma un timestamp in stringa "DD/MM/YYYY HH:mm:ss"
- * @param timestamp stringa o oggetto Date
- * @returns stringa formattata oppure vuota se input non valido
+ * Transforms a timestamp into a string "DD/MM/YYYY HH:mm:ss"
+ * @param timestamp string or Date object
+ * @returns formatted string or empty string if input is invalid
*/
export const formatTimestamp = (timestamp: string | Date | null | undefined): string => {
if (!timestamp) return '';
@@ -52,7 +52,7 @@ export const formatTimestamp = (timestamp: string | Date | null | undefined): st
if (isNaN(date.getTime())) return '';
const dd = String(date.getDate()).padStart(2, '0');
- const mm = String(date.getMonth() + 1).padStart(2, '0'); // mesi da 0 a 11
+ const mm = String(date.getMonth() + 1).padStart(2, '0'); // months from 0 to 11
const yyyy = date.getFullYear();
const hh = String(date.getHours()).padStart(2, '0');
@@ -63,9 +63,9 @@ export const formatTimestamp = (timestamp: string | Date | null | undefined): st
};
/**
- * Converte un timestamp ISO in oggetto Date
- * @param dateStr stringa data in formato ISO
- * @returns oggetto Date corrispondente
+ * Converts an ISO timestamp to a Date object
+ * @param dateStr string in ISO date format
+ * @returns corresponding Date object
*/
export const parseTimestamp = (dateStr: string | undefined | null): Date => {
if (!dateStr) return new Date();
diff --git a/utils/documentUtils.tsx b/utils/documentUtils.tsx
index 14b3d75..7cc2ecf 100644
--- a/utils/documentUtils.tsx
+++ b/utils/documentUtils.tsx
@@ -1,16 +1,12 @@
import api from '@/utils/api';
import { Directory, File, Paths } from 'expo-file-system';
-import * as FileSystem from 'expo-file-system/legacy';
-import { StorageAccessFramework } from 'expo-file-system/legacy';
import * as Sharing from 'expo-sharing';
-import * as Linking from 'expo-linking';
-import { Platform } from 'react-native';
/**
- * Gestisce l'upload di un documento verso il server usando FormData
- * @param file File da caricare (deve avere almeno la proprietà 'uri')
- * @param siteId ID del sito a cui associare il documento (null per registro generale)
- * @param customTitle Titolo personalizzato per il documento (opzionale)
+ * Handles upload of a document through the server using FormData
+ * @param file File to upload (must have at least the 'uri' property)
+ * @param siteId ID of the site to associate the document with (null for general register)
+ * @param customTitle Custom title for the document (optional)
*/
export const uploadDocument = async (
file: any,
@@ -64,10 +60,10 @@ export const uploadDocument = async (
};
/**
- * Scarica un documento e offre di aprirlo/condividerlo (expo-sharing)
- * @param attachmentId ID o URL relativo del documento
- * @param fileName Nome con cui salvare il file
- * @param fileUrl URL completo del file da scaricare
+ * Download and share a document (expo-sharing)
+ * @param attachmentId ID or relative URL of the document
+ * @param fileName Name to save the file as
+ * @param fileUrl Full URL of the file to download
*/
export const downloadAndShareDocument = async (
mimetype: string,
@@ -75,7 +71,7 @@ export const downloadAndShareDocument = async (
fileUrl: string
): Promise => {
try {
- // TODO: Gestire meglio il download (attualmente si basa su expo-sharing)
+ // TODO: Download based on expo-sharing - some mime types may not be supported
if (!fileUrl || !fileName) {
throw new Error("Parametri mancanti per il download del documento.");
}
diff --git a/utils/haApi.ts b/utils/haApi.ts
index 98735a6..8e8a21a 100644
--- a/utils/haApi.ts
+++ b/utils/haApi.ts
@@ -1,18 +1,18 @@
import axios, { AxiosError } from 'axios';
import { HaArea, HaEntity } from '@/types/types';
-// CONFIGURAZIONE
+// HOME ASSISTANT API CONFIGURATION
const HA_API_URL = process.env.EXPO_PUBLIC_HA_API_URL;
const HA_TOKEN = process.env.EXPO_PUBLIC_HA_TOKEN;
-// Crea un'istanza di axios per Home Assistant
+// Create an axios instance for Home Assistant
const haApi = axios.create({
baseURL: HA_API_URL,
headers: {
'Authorization': `Bearer ${HA_TOKEN}`,
'Content-Type': 'application/json',
},
- timeout: 5000, // 5 secondi di timeout
+ timeout: 5000, // 5 seconds timeout
});
haApi.interceptors.request.use((config) => {
@@ -24,7 +24,7 @@ haApi.interceptors.request.use((config) => {
* Connection test
*/
export const testHaConnection = async (): Promise<{ success: boolean; message: string }> => {
- // Controlla se le variabili d'ambiente sono caricate
+ // Check if environment variables are loaded
if (!HA_API_URL || !HA_TOKEN) {
console.error("Variabili d'ambiente per Home Assistant non trovate. Assicurati che EXPO_PUBLIC_HA_API_URL and EXPO_PUBLIC_HA_TOKEN siano definite nel file .env");
return { success: false, message: "Configurazione API per Home Assistant mancante." };
@@ -32,7 +32,7 @@ export const testHaConnection = async (): Promise<{ success: boolean; message: s
try {
const response = await haApi.get('/');
- // Se la risposta è OK, HA restituisce un JSON con 'message'
+ // If the response is OK, HA returns a JSON with 'message'
if (response.status === 200 && response.data.message) {
return { success: true, message: response.data.message };
}
@@ -46,7 +46,7 @@ export const testHaConnection = async (): Promise<{ success: boolean; message: s
}
if (axiosError.response) {
- // Errori con una risposta dal server (es. 401, 404)
+ // Errors with a server response (e.g., 401, 404)
if (axiosError.response.status === 401) {
return { success: false, message: "Errore 401: Token non autorizzato. Controlla il Long-Lived Token." };
}
@@ -55,10 +55,10 @@ export const testHaConnection = async (): Promise<{ success: boolean; message: s
}
return { success: false, message: `Errore server: Status ${axiosError.response.status}` };
} else if (axiosError.request) {
- // Errori di rete (la richiesta è partita ma non ha ricevuto risposta)
+ // Network errors (cannot receive a response)
return { success: false, message: `Errore di rete: Impossibile raggiungere ${HA_API_URL}` };
} else {
- // Errore generico
+ // Generic errors
return { success: false, message: `Errore sconosciuto: ${axiosError.message}` };
}
}
@@ -92,7 +92,7 @@ export const getHaAreas = async (): Promise => {
} catch (error) {
console.error("Errore recupero aree:", error);
- return []; // Restituisce un array vuoto in caso di errore
+ return []; // Return an empty array in case of error
}
};
@@ -120,7 +120,7 @@ export const getHaEntitiesByArea = async (areaId: string): Promise =
} catch (error) {
console.error(`Errore recupero entità per l'area ${areaId}:`, error);
- return []; // Restituisce un array vuoto in caso di errore
+ return []; // Return an empty array in case of error
}
};