feat: update app configuration and enhance UI components

This commit is contained in:
2026-02-06 18:06:48 +01:00
parent 7c8ef45e5a
commit 882cfc281d
19 changed files with 339 additions and 266 deletions

View File

@@ -1,13 +1,13 @@
import { useAlert } from '@/components/AlertComponent';
import React, { useEffect, useState } from 'react';
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';
import LoadingScreen from '@/components/LoadingScreen';
import NfcScanModal from '@/components/NfcScanModal';
import QrScanModal from '@/components/QrScanModal';
import { AttendanceRecord } from '@/types/types';
import api from '@/utils/api';
import { formatDate, formatTime, parseSecondsToTime } from '@/utils/dateTime';
import { AttendanceRecord } from '@/types/types';
import { CheckCircle2, Nfc, QrCode } from 'lucide-react-native';
import React, { useEffect, useState } from 'react';
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
import NfcManager from 'react-native-nfc-manager';
import { SafeAreaView } from 'react-native-safe-area-context';
@@ -120,7 +120,7 @@ export default function AttendanceScreen() {
<View className="flex-1 bg-gray-50">
{/* Header */}
<View className="bg-white px-6 pb-6 shadow-sm border-b border-gray-100">
<SafeAreaView edges={['top']} className='pt-4'>
<SafeAreaView edges={['top']} className='pt-5'>
<Text className="text-3xl font-bold text-gray-800 mb-1">Gestione Presenze</Text>
<Text className="text-base text-gray-500">Registra i tuoi movimenti</Text>
</SafeAreaView>

View File

@@ -6,6 +6,7 @@ import { useLocalSearchParams, useRouter } from 'expo-router';
import { ChevronLeft, ServerOff, WifiOff } from 'lucide-react-native';
import React, { useCallback, useEffect, useState } from 'react';
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
export default function AutomationDetailScreen() {
const router = useRouter();
@@ -95,14 +96,18 @@ export default function AutomationDetailScreen() {
return (
<View className="flex-1 bg-gray-50">
<View className="bg-white p-6 pt-16 shadow-sm flex-row justify-between items-center border-b border-gray-100">
<View className='flex-row items-center gap-4'>
<TouchableOpacity onPress={() => router.back()} className='rounded-full active:bg-gray-100'>
<ChevronLeft size={28} color="#4b5563" />
</TouchableOpacity>
<Text className="text-2xl font-bold text-gray-800">{area.name}</Text>
</View>
<View className={`ms-auto w-3.5 h-3.5 rounded-full border-2 border-white shadow-sm bg-green-500`} />
<View className="bg-white px-4 pb-6 shadow-sm border-b border-gray-100">
<SafeAreaView edges={['top']} className='pt-5'>
<View className="flex-row justify-between items-center">
<View className='flex-row items-center gap-4'>
<TouchableOpacity onPress={() => router.back()} className='rounded-full active:bg-gray-100'>
<ChevronLeft size={28} color="#4b5563" />
</TouchableOpacity>
<Text className="text-2xl font-bold text-gray-800">{area.name}</Text>
</View>
<View className={`ms-auto w-3.5 h-3.5 rounded-full border-2 border-white shadow-sm bg-green-500`} />
</View>
</SafeAreaView>
</View>
<ScrollView

View File

@@ -1,10 +1,11 @@
import LoadingScreen from '@/components/LoadingScreen';
import type { HaArea } from '@/types/types';
import { getHaAreas, testHaConnection } from '@/utils/haApi';
import { useRouter } from 'expo-router';
import { Archive, Briefcase, DoorOpen, Grid2X2, Lightbulb, Plus, Users, WifiOff } from 'lucide-react-native';
import { Archive, Briefcase, DoorOpen, Grid2X2, Lightbulb, Users, WifiOff } from 'lucide-react-native';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
import LoadingScreen from '@/components/LoadingScreen';
import { SafeAreaView } from 'react-native-safe-area-context';
const AREA_STYLES = [
{ icon: Users, bgColor: 'bg-indigo-100' },
@@ -70,11 +71,48 @@ export default function AutomationScreen() {
return <LoadingScreen />;
}
// TODO: Remove duplicated code
if (!connectionStatus.success) {
return (
<View className="flex-1 bg-gray-50">
<View className="bg-white pt-16 shadow-sm border-b border-gray-100">
<View className="px-6 pb-4 flex-row justify-between items-center">
<View className="bg-white shadow-sm border-b border-gray-100">
<SafeAreaView edges={['top']} className='pt-5'>
<View className="flex-row justify-between items-center px-6 pb-6">
<View>
<Text className="text-3xl font-bold text-gray-800 mb-1">Domotica</Text>
<Text className="text-base text-gray-500">Controlla gli ambienti</Text>
</View>
<View className={`px-4 py-2 rounded-xl border ${connectionStatus.success ? 'bg-green-100 border-green-200' : 'bg-red-100 border-red-200'}`}>
<Text className={`text-xs font-bold tracking-wide ${connectionStatus.success ? 'text-green-700' : 'text-red-700'}`}>
{connectionStatus.success ? 'ONLINE' : 'OFFLINE'}
</Text>
</View>
</View>
</SafeAreaView>
</View>
<ScrollView
contentContainerClassName="flex-grow items-center justify-center pb-[100px]"
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} colors={['#099499']} tintColor={'#099499'} />}
>
<WifiOff size={48} color="#9ca3af" />
<Text className="text-lg text-gray-500 font-medium mt-4">Errore di connessione</Text>
<Text className="text-center text-gray-400 mt-2 px-10">
{connectionStatus.message}
</Text>
<TouchableOpacity onPress={onRefresh} className="mt-6 bg-[#099499] px-6 py-3 rounded-lg active:scale-95">
<Text className="text-white font-bold">Riprova</Text>
</TouchableOpacity>
</ScrollView>
</View>
);
}
return (
<View className="flex-1 bg-gray-50">
<View className="bg-white shadow-sm border-b border-gray-100">
<SafeAreaView edges={['top']} className='pt-5'>
<View className="flex-row justify-between items-center px-6 pb-6">
<View>
<Text className="text-3xl font-bold text-gray-800 mb-1">Domotica</Text>
<Text className="text-base text-gray-500">Controlla gli ambienti</Text>
@@ -98,51 +136,7 @@ export default function AutomationScreen() {
))}
</ScrollView>
)}
</View>
<ScrollView
contentContainerClassName="flex-grow items-center justify-center pb-[100px]"
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} colors={['#099499']} tintColor={'#099499'} />}
>
<WifiOff size={48} color="#9ca3af" />
<Text className="text-lg text-gray-500 font-medium mt-4">Errore di connessione</Text>
<Text className="text-center text-gray-400 mt-2 px-10">
{connectionStatus.message}
</Text>
<TouchableOpacity onPress={onRefresh} className="mt-6 bg-[#099499] px-6 py-3 rounded-lg active:scale-95">
<Text className="text-white font-bold">Riprova</Text>
</TouchableOpacity>
</ScrollView>
</View>
);
}
return (
<View className="flex-1 bg-gray-50">
<View className="bg-white pt-16 shadow-sm border-b border-gray-100">
<View className="px-6 pb-4 flex-row justify-between items-center">
<View>
<Text className="text-3xl font-bold text-gray-800 mb-1">Domotica</Text>
<Text className="text-base text-gray-500">Controlla gli ambienti</Text>
</View>
<View className={`px-4 py-2 rounded-xl border ${connectionStatus.success ? 'bg-green-100 border-green-200' : 'bg-red-100 border-red-200'}`}>
<Text className={`text-xs font-bold tracking-wide ${connectionStatus.success ? 'text-green-700' : 'text-red-700'}`}>
{connectionStatus.success ? 'ONLINE' : 'OFFLINE'}
</Text>
</View>
</View>
{connectionStatus.success && (
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerClassName="px-5 pb-4 gap-3">
{floors.map(floor => (
<TouchableOpacity
key={floor}
onPress={() => setSelectedFloor(floor)}
className={`px-5 py-2.5 rounded-xl ${selectedFloor === floor ? 'bg-[#099499]' : 'bg-gray-100 active:bg-gray-200'}`}
>
<Text className={`font-bold ${selectedFloor === floor ? 'text-white' : 'text-gray-600'}`}>{floor}</Text>
</TouchableOpacity>
))}
</ScrollView>
)}
</SafeAreaView>
</View>
<ScrollView

View File

@@ -1,11 +1,11 @@
import { useRouter } from 'expo-router';
import { AlertTriangle, CalendarDays, CheckCircle2, FileText, QrCode, User, CalendarClock, LayoutDashboard } from 'lucide-react-native';
import React, { useState, useContext, useEffect } from 'react';
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
import { AuthContext } from '@/utils/authContext';
import LoadingScreen from '@/components/LoadingScreen';
import { ActivityItem } from '@/types/types';
import api from '@/utils/api';
import LoadingScreen from '@/components/LoadingScreen';
import { AuthContext } from '@/utils/authContext';
import { useRouter } from 'expo-router';
import { AlertTriangle, CalendarClock, CalendarDays, CheckCircle2, FileText, LayoutDashboard, QrCode, User } from 'lucide-react-native';
import React, { useContext, useEffect, useState } from 'react';
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
export default function HomeScreen() {
@@ -64,7 +64,7 @@ export default function HomeScreen() {
return (
<View className="flex-1 bg-[#099499]">
<SafeAreaView edges={['top']} className='pt-4'>
<SafeAreaView edges={['top']} className='pt-5'>
{/* Custom Banner */}
<View className="pb-6 px-6 shadow-sm z-10">
<View className="flex-row justify-between items-start">

View File

@@ -1,13 +1,13 @@
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 { 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';
import LoadingScreen from '@/components/LoadingScreen';
import RequestPermitModal from '@/components/RequestPermitModal';
import { TimeOffRequest, TimeOffRequestType } from '@/types/types';
import api from '@/utils/api';
import { formatDate, formatTime } from '@/utils/dateTime';
import { Calendar as CalendarIcon, CalendarX, Clock, Plus, Thermometer } from 'lucide-react-native';
import React, { JSX, useEffect, useMemo, useState } from 'react';
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
// Icon Mapping
@@ -93,7 +93,7 @@ export default function PermitsScreen() {
{/* Header */}
<View className="bg-white px-6 pb-6 shadow-sm border-b border-gray-100 flex-row justify-between items-center">
<SafeAreaView edges={['top']} className='pt-4'>
<SafeAreaView edges={['top']} className='pt-5'>
<View>
<Text className="text-3xl font-bold text-gray-800 mb-1">Ferie e Permessi</Text>
<Text className="text-base text-gray-500">Gestisci le tue assenze</Text>

View File

@@ -1,16 +1,16 @@
import AddDocumentModal from '@/components/AddDocumentModal';
import { useAlert } from '@/components/AlertComponent';
import { Download, FileText, MapPin, Plus, Search, CalendarIcon, ChevronLeft } from 'lucide-react-native';
import React, { useEffect, useState } from 'react';
import { useRouter } from 'expo-router';
import LoadingScreen from '@/components/LoadingScreen';
import { RangePickerModal } from '@/components/RangePickerModal';
import { RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { DocumentItem } from '@/types/types';
import api from '@/utils/api';
import dayjs from 'dayjs';
import LoadingScreen from '@/components/LoadingScreen';
import { formatTimestamp, parseTimestamp } from '@/utils/dateTime';
import AddDocumentModal from '@/components/AddDocumentModal';
import { downloadAndShareDocument, uploadDocument } from '@/utils/documentUtils';
import dayjs from 'dayjs';
import { useRouter } from 'expo-router';
import { CalendarIcon, ChevronLeft, Download, FileText, MapPin, Plus, Search } from 'lucide-react-native';
import React, { useEffect, useState } from 'react';
import { RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
export default function DocumentsScreen() {
@@ -107,7 +107,7 @@ export default function DocumentsScreen() {
<View className="flex-1 bg-gray-50">
{/* Header */}
<View className="bg-white px-4 pb-6 shadow-sm border-b border-gray-100">
<SafeAreaView edges={['top']} className='pt-4'>
<SafeAreaView edges={['top']} className='pt-5'>
<View className='flex-row items-center gap-4'>
<TouchableOpacity onPress={() => router.back()} className="p-2 rounded-full active:bg-gray-100">
<ChevronLeft size={28} color="#4b5563" />

View File

@@ -1,8 +1,8 @@
import { AuthContext } from '@/utils/authContext';
import { useRouter } from 'expo-router';
import { ChevronLeft, FileText, LogOut, Mail, Settings, User } from 'lucide-react-native';
import { ChevronLeft, FileText, LogOut, Mail, User } from 'lucide-react-native';
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() {
@@ -15,7 +15,7 @@ export default function ProfileScreen() {
return (
<View className="flex-1 bg-[#099499]">
<SafeAreaView edges={['top']} className='pt-4'>
<SafeAreaView edges={['top']} className='pt-5'>
{/* Header Section */}
<View className="pb-6 px-4">
<View className="flex-row justify-start items-center gap-4">

View File

@@ -1,16 +1,16 @@
import AddDocumentModal from '@/components/AddDocumentModal';
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 { 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';
import { RangePickerModal } from '@/components/RangePickerModal';
import { ConstructionSite, DocumentItem } from '@/types/types';
import api from '@/utils/api';
import dayjs from 'dayjs';
import { formatTimestamp, parseTimestamp } from '@/utils/dateTime';
import { downloadAndShareDocument, uploadDocument } from '@/utils/documentUtils';
import AddDocumentModal from '@/components/AddDocumentModal';
import dayjs from 'dayjs';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { Calendar as CalendarIcon, ChevronLeft, Download, FileText, Plus, Search } from 'lucide-react-native';
import React, { useCallback, useEffect, useState } from 'react';
import { RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
export default function SiteDocumentsScreen() {
@@ -132,7 +132,7 @@ export default function SiteDocumentsScreen() {
<View className="flex-1 bg-gray-50">
{/* Header */}
<View className="bg-white px-4 pb-6 shadow-sm border-b border-gray-100">
<SafeAreaView edges={['top']} className='pt-4'>
<SafeAreaView edges={['top']} className='pt-5'>
<View className='flex-row items-center gap-4'>
<TouchableOpacity onPress={() => router.back()} className="p-2 -ml-2 rounded-full active:bg-gray-100">
<ChevronLeft size={28} color="#4b5563" />

View File

@@ -1,11 +1,11 @@
import { useAlert } from '@/components/AlertComponent';
import LoadingScreen from '@/components/LoadingScreen';
import { ConstructionSite } from '@/types/types';
import api from '@/utils/api';
import { useRouter } from 'expo-router';
import { Building2, ChevronRight, MapPin, Search } from 'lucide-react-native';
import React, { useEffect, useState } from 'react';
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';
import { SafeAreaView } from 'react-native-safe-area-context';
export default function SitesScreen() {
@@ -56,7 +56,7 @@ export default function SitesScreen() {
<View className="flex-1 bg-gray-50">
{/* Header */}
<View className="bg-white px-6 pb-6 shadow-sm border-b border-gray-100">
<SafeAreaView edges={['top']} className='pt-4'>
<SafeAreaView edges={['top']} className='pt-5'>
<Text className="text-3xl font-bold text-gray-800 mb-1">Cantieri</Text>
<Text className="text-base text-gray-500">Seleziona un cantiere per vedere i documenti</Text>
</SafeAreaView>

View File

@@ -4,20 +4,23 @@ import { Stack } from 'expo-router';
import { AlertProvider } from '@/components/AlertComponent';
import { NetworkProvider } from '@/utils/networkProvider';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { KeyboardProvider } from "react-native-keyboard-controller";
export default function AppLayout() {
return (
<SafeAreaProvider>
<NetworkProvider>
<AuthProvider>
<AlertProvider>
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="(protected)" />
<Stack.Screen name="login" />
</Stack>
</AlertProvider>
</AuthProvider>
</NetworkProvider>
<KeyboardProvider>
<NetworkProvider>
<AuthProvider>
<AlertProvider>
<Stack screenOptions={{ headerShown: false, animation: 'flip' }}>
<Stack.Screen name="(protected)" />
<Stack.Screen name="login" />
</Stack>
</AlertProvider>
</AuthProvider>
</NetworkProvider>
</KeyboardProvider>
</SafeAreaProvider>
);
}

View File

@@ -3,7 +3,8 @@ 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';
import { Image, Platform, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { KeyboardAvoidingView } from 'react-native-keyboard-controller';
export default function LoginScreen() {
const alert = useAlert();
@@ -15,7 +16,7 @@ export default function LoginScreen() {
// Login Handler function
const handleLogin = async () => {
if (!username || !password) {
if (!username || !password) {
alert.showAlert('error', 'Attenzione', 'Inserisci username e password');
return;
}
@@ -41,7 +42,7 @@ export default function LoginScreen() {
} catch (error: any) {
console.error("Login Error:", error);
let message = "Si è verificato un errore durante l'accesso.";
if (error.response) {
// Server error (e.g., 401 Invalid credentials)
if (error.response.status === 401) {
@@ -74,8 +75,9 @@ export default function LoginScreen() {
{/* Form Container */}
<View className="flex-1 bg-white rounded-t-[2.5rem] px-8 pt-10 shadow-xl w-full">
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
className="flex-1"
behavior={"padding"}
keyboardVerticalOffset={100}
className='flex-1 mh-600'
>
<ScrollView showsVerticalScrollIndicator={false} className="h-full">
<View className="gap-6 flex flex-col" style={{ gap: '1.5rem' }}>