Files
mariani_mobile/app/(protected)/permits/index.tsx

162 lines
7.9 KiB
TypeScript

import { useAlert } from '@/components/AlertComponent';
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
const typeIcons: Record<string, (color: string) => JSX.Element> = {
Ferie: (color) => <CalendarIcon size={24} color={color} pointerEvents="none" />,
Permesso: (color) => <Clock size={24} color={color} pointerEvents="none" />,
Malattia: (color) => <Thermometer size={24} color={color} pointerEvents="none" />,
Assenza: (color) => <CalendarX size={24} color={color} pointerEvents="none" />,
};
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());
const [isLoading, setIsLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const fetchPermits = async () => {
try {
if (!refreshing) setIsLoading(true);
// 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);
} catch (error) {
console.error('Errore nel recupero dei permessi:', error);
alert.showAlert('error', 'Errore', 'Impossibile recuperare i permessi. Riprova più tardi.');
} finally {
setIsLoading(false);
setRefreshing(false);
}
};
const filteredPermits = useMemo(() => {
if (!permits.length) return [];
// Calculate start and end of the current month
const year = currentMonthDate.getFullYear();
const month = currentMonthDate.getMonth();
const startOfMonth = new Date(year, month, 1);
// 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() ?? '');
// 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() ?? '');
// 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]);
useEffect(() => {
fetchPermits();
}, []);
const onRefresh = () => {
setRefreshing(true);
fetchPermits();
};
if (isLoading && !refreshing) {
return <LoadingScreen />;
}
return (
<View className="flex-1 bg-gray-50">
<RequestPermitModal
visible={showModal}
types={types}
onClose={() => setShowModal(false)}
onSubmit={(data) => { console.log('Richiesta:', data); fetchPermits(); }}
/>
{/* 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-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>
</SafeAreaView>
</View>
<ScrollView
contentContainerStyle={{ padding: 20, paddingBottom: 100, gap: 24 }}
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} colors={['#099499']} />
}
>
{/* Calendar Widget */}
<CalendarWidget events={permits} types={types} onMonthChange={(date) => setCurrentMonthDate(date)} />
{/* Recent Requests List */}
<View>
{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>
{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' }}>
{typeIcons[item.timeOffRequestType.name]?.(item.timeOffRequestType.color)}
</View>
<View>
<Text className="font-bold text-gray-800 text-lg">{item.timeOffRequestType.name}</Text>
<Text className="text-base text-gray-500 mt-0.5">
{formatDate(item.start_date?.toLocaleString())} {item.end_date ? `- ${formatDate(item.end_date.toLocaleString())}` : ''}
</Text>
{item.timeOffRequestType.name === 'Permesso' && (
<Text className="text-sm text-orange-600 font-bold mt-1">
{formatTime(item.start_time)} - {formatTime(item.end_time)}
</Text>
)}
</View>
</View>
{/* TODO: Add functionality to edit/remove the request */}
<View className={`px-3 py-1.5 rounded-lg ${item.status ? 'bg-green-100' : 'bg-yellow-100'}`}>
<Text className={`text-xs font-bold uppercase tracking-wide ${item.status ? 'text-green-700' : 'text-yellow-700'}`}>
{item.status ? 'Approvato' : 'In Attesa'}
</Text>
</View>
</View>
))}
</View>
)}
</View>
</ScrollView>
{/* FAB */}
<TouchableOpacity
onPress={() => setShowModal(true)}
className="absolute bottom-8 right-6 w-16 h-16 bg-[#099499] rounded-full shadow-lg items-center justify-center active:scale-90"
>
<Plus size={32} color="white" pointerEvents="none" />
</TouchableOpacity>
</View>
);
}