feat: Add document download and upload. Add NFC support and enhance attendance and permits views

- Improved error message handling in LoginScreen for invalid credentials.
- Added new images: mariani-icon.png and mariani-splash.png.
- Updated AddDocumentModal to handle file extensions and improve UI.
- Enhanced CalendarWidget to support month change callbacks.
- Introduced NfcScanModal for NFC tag scanning with animations.
- Revamped QrScanModal to utilize camera for QR code scanning.
- Removed mock data from data.ts and streamlined Office data.
- Updated package dependencies for expo-camera and react-native-nfc-manager.
- Added utility function to parse seconds to time format.
- Refactored document upload logic to use FormData for server uploads.
This commit is contained in:
2026-01-19 18:10:31 +01:00
parent 325bfbe19f
commit 44d021891f
20 changed files with 882 additions and 299 deletions

View File

@ -1,4 +1,4 @@
import React, { JSX, useEffect, useState } from 'react';
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 { TimeOffRequest, TimeOffRequestType } from '@/types/types';
@ -20,6 +20,7 @@ export default function PermitsScreen() {
const [showModal, setShowModal] = useState(false);
const [permits, setPermits] = useState<TimeOffRequest[]>([]);
const [types, setTypes] = useState<TimeOffRequestType[]>([]);
const [currentMonthDate, setCurrentMonthDate] = useState(new Date());
const [isLoading, setIsLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
@ -43,6 +44,29 @@ export default function PermitsScreen() {
}
};
const filteredPermits = useMemo(() => {
if (!permits.length) return [];
// Calcoliamo inizio e fine del mese visualizzato
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);
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)
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.
return itemStart <= endOfMonth && itemEnd >= startOfMonth;
});
}, [permits, currentMonthDate]);
useEffect(() => {
fetchPermits();
}, []);
@ -82,17 +106,16 @@ export default function PermitsScreen() {
>
{/* Calendar Widget */}
<CalendarWidget events={permits} types={types} />
<CalendarWidget events={permits} types={types} onMonthChange={(date) => setCurrentMonthDate(date)} />
{/* Lista Richieste Recenti */}
<View>
{permits.length === 0 ? (
<Text className="text-center text-gray-500 mt-8">Nessuna richiesta di permesso trovata.</Text>
{filteredPermits.length === 0 ? (
<Text className="text-center text-gray-500 mt-8">Nessuna richiesta di permesso questo mese</Text>
) : (
<View className="gap-4">
<Text className="text-xl font-bold text-gray-800 px-1">Le tue richieste</Text>
{/* TODO: Aggiungere una paginazione con delle freccette affianco? - Limite backend? */}
{permits.map((item) => (
{filteredPermits.map((item) => (
<View key={item.id} className="bg-white p-5 rounded-3xl shadow-sm border border-gray-100 flex-row justify-between items-center">
<View className="flex-row items-center gap-4">
<View className={`p-4 rounded-2xl`} style={{ backgroundColor: item.timeOffRequestType.color ? `${item.timeOffRequestType.color}25` : '#E5E7EB' }}>