Files
ipcostruzioni_app/app/(protected)/machine/index.tsx

228 lines
10 KiB
TypeScript

import { useAlert } from '@/components/AlertComponent';
import MachineCard from '@/components/MachineCard';
import FilterModal from '@/components/FilterModal';
import LoadingScreen from '@/components/LoadingScreen';
import QrScanModal from '@/components/QrScanModal';
import { StatusBar } from 'expo-status-bar';
import api from '@/utils/api';
import { formatTime } from '@/utils/dateTime';
import { CarFront, CheckCircle2, Filter, QrCode } from 'lucide-react-native';
import React, { useEffect, useState } from 'react';
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { MachineAttendanceItem } from '@/types/types';
export default function MachineScreen() {
const alert = useAlert();
const [showScanner, setShowScanner] = useState(false);
const [lastScan, setLastScan] = useState<{ type: string; time: string; site: string } | null>(null);
const [attendances, setAttendances] = useState<MachineAttendanceItem[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
// Filters state
const [isFilterVisible, setIsFilterVisible] = useState(false);
const [filterRange, setFilterRange] = useState<{ startDate: string | null; endDate: string | null }>({ startDate: null, endDate: null });
const [filterMachine, setFilterMachine] = useState<any>(null);
const activeFiltersCount = (filterRange.startDate ? 1 : 0) + (filterMachine ? 1 : 0);
const fetchAttendances = async (currentRange = filterRange, currentMachine = filterMachine) => {
try {
if (!refreshing) setIsLoading(true);
const rangeParam = currentRange.startDate ? currentRange : null;
const params = { range: rangeParam, machine: currentMachine };
const response = await api.post('/machine-attendance/list', { params });
if (response.data?.success) {
setAttendances(response.data.data || []);
}
} catch (error) {
console.error('Errore nel recupero degli utilizzi macchine:', error);
alert.showAlert('error', 'Errore', 'Impossibile recuperare i dati. Riprova più tardi.');
} finally {
setIsLoading(false);
setRefreshing(false);
}
};
useEffect(() => {
fetchAttendances();
setLastScan(null);
}, []);
const onRefresh = () => {
setRefreshing(true);
fetchAttendances();
setLastScan(null);
};
const handleStartScan = () => {
setShowScanner(true);
};
const onScan = async (data: string) => {
console.log('Scanned data:', data);
try {
const response = await api.post('/machine-attendance/in', { uuid: data });
if (response.data?.success) {
console.log('Scan data sent successfully:', response.data);
fetchAttendances();
setLastScan({
type: response.data.type,
time: formatTime(response.data.time),
site: response.data.site
});
} else {
alert.showAlert('error', 'Errore', response.data?.message || 'Impossibile registrare operazione.');
}
} catch (error) {
console.error('Errore nell\'invio dei dati di scansione:', error);
alert.showAlert('error', 'Errore', 'Impossibile registrare operazione. Riprova più tardi.');
return;
}
};
if (isLoading && !refreshing) {
return <LoadingScreen />;
}
return (
<View className="flex-1 bg-gray-50">
<StatusBar style="light" />
{/* Header */}
<SafeAreaView edges={['top']} className='pt-5 bg-primary-dark'>
<View className="pb-6 px-6 z-10 flex-row justify-between items-center">
<View>
<Text className="text-white text-md font-semibold uppercase tracking-wider mb-1">Elenco degli utilizzi dei mezzi</Text>
<Text className="text-yellow-400 text-3xl font-bold leading-tight">Macchine</Text>
</View>
<View className="bg-white/10 p-4 rounded-full">
<CarFront size={32} color="white" pointerEvents="none" />
</View>
</View>
</SafeAreaView>
<View className="flex-1 bg-gray-50 rounded-t-[2.5rem] overflow-hidden">
<ScrollView
contentContainerStyle={{ paddingBottom: 100 }}
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} colors={['#1071C2']} />
}
>
<View className="flex-1 p-5 items-center">
{/* Feedback Card */}
{lastScan ? (
<View className="w-full bg-green-50 border border-green-200 rounded-3xl p-5 mb-8 flex-row items-center gap-4 shadow-sm">
<View className="bg-green-500 rounded-full p-3 shadow-lg shadow-green-500/40 flex-shrink-0">
<CheckCircle2 size={32} color="white" pointerEvents="none" />
</View>
<View className="flex-1">
<Text
className="font-bold text-green-800 text-xl leading-tight"
numberOfLines={1}
ellipsizeMode="tail"
>
{lastScan.type} Registrata
</Text>
<Text
className="text-base text-green-700 font-medium mt-0.5 leading-snug"
numberOfLines={2}
ellipsizeMode="tail"
>
{lastScan.site} alle {lastScan.time}
</Text>
</View>
</View>
) : null}
{/* Scanner Section */}
<View className="w-full mb-6">
<View className="bg-white rounded-3xl p-8 shadow-sm border border-gray-100">
<Text className="text-2xl font-bold text-gray-800 mb-6 text-center">Scansione QR Code</Text>
<TouchableOpacity
onPress={handleStartScan}
className="bg-[#1071C2] rounded-2xl py-6 flex-row items-center justify-center active:bg-blue-700 shadow-lg shadow-blue-900/20 active:scale-[0.98]"
>
<QrCode color="white" size={32} pointerEvents="none" />
<Text className="text-white text-xl font-bold ml-3 uppercase">Scansiona Codice</Text>
</TouchableOpacity>
<Text className="text-gray-500 text-center mt-6 text-base px-2 leading-relaxed">
Posiziona il codice QR davanti alla fotocamera per registrare l'ingresso o l'uscita dalla macchina
</Text>
</View>
</View>
{/* History using MachineCard component */}
<View className="w-full mt-4">
<Text className="text-gray-500 font-bold text-base mb-4 uppercase tracking-wider px-2">Ultimi Utilizzi</Text>
{attendances.length === 0 ? (
<View className="bg-white p-6 rounded-3xl border border-gray-100 items-center justify-center border-dashed">
<Text className="text-gray-400 font-medium">Nessun utilizzo registrato</Text>
</View>
) : (
<View>
{attendances.map((item, index) => (
<MachineCard key={index} item={item} />
))}
</View>
)}
</View>
</View>
</ScrollView>
{/* FAB Filter */}
<TouchableOpacity
onPress={() => setIsFilterVisible(true)}
className="absolute bottom-8 right-6 w-16 h-16 bg-[#1071C2] rounded-full shadow-lg items-center justify-center active:scale-90"
>
<Filter size={28} color="white" pointerEvents="none" />
{activeFiltersCount > 0 && (
<View className="absolute top-0 right-0 bg-red-500 w-6 h-6 rounded-full items-center justify-center border-2 border-white">
<Text className="text-white text-xs font-bold">{activeFiltersCount}</Text>
</View>
)}
</TouchableOpacity>
{/* Filter Modal */}
<FilterModal
visible={isFilterVisible}
showPlace={false}
showMachine={true}
currentRange={filterRange}
currentMachine={filterMachine}
onClose={() => setIsFilterVisible(false)}
onApply={(range, place, supplier, machine) => {
setFilterRange(range);
setFilterMachine(machine);
setIsFilterVisible(false);
fetchAttendances(range, machine);
}}
onReset={() => {
const emptyRange = { startDate: null, endDate: null };
setFilterRange(emptyRange);
setFilterMachine(null);
setIsFilterVisible(false);
fetchAttendances(emptyRange, null);
}}
/>
{/* Scanner Modal */}
<QrScanModal
visible={showScanner}
onClose={() => setShowScanner(false)}
onScan={onScan}
/>
</View>
</View>
);
}