209 lines
10 KiB
TypeScript
209 lines
10 KiB
TypeScript
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 React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
import { RefreshControl, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
|
import LoadingScreen from '@/components/LoadingScreen';
|
|
|
|
const AREA_STYLES = [
|
|
{ icon: Users, bgColor: 'bg-indigo-100' },
|
|
{ icon: Briefcase, bgColor: 'bg-slate-100' },
|
|
{ icon: Lightbulb, bgColor: 'bg-yellow-100' },
|
|
{ icon: DoorOpen, bgColor: 'bg-green-100' },
|
|
{ icon: Archive, bgColor: 'bg-orange-100' },
|
|
{ icon: Grid2X2, bgColor: 'bg-sky-100' },
|
|
];
|
|
|
|
export default function AutomationScreen() {
|
|
const router = useRouter();
|
|
const [allAreas, setAllAreas] = useState<HaArea[]>([]);
|
|
const [connectionStatus, setConnectionStatus] = useState<{ success: boolean; message: string }>({ success: false, message: 'Connecting...' });
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [refreshing, setRefreshing] = useState(false);
|
|
|
|
const [floors, setFloors] = useState<string[]>(['Tutti']);
|
|
const [selectedFloor, setSelectedFloor] = useState<string>('Tutti');
|
|
|
|
const initialize = useCallback(async () => {
|
|
const connResult = await testHaConnection();
|
|
setConnectionStatus(connResult);
|
|
|
|
if (connResult.success) {
|
|
const areasResult = await getHaAreas();
|
|
const areas = areasResult.map(area => ({
|
|
...area,
|
|
floor_name: area.floor_name || 'Non assegnato',
|
|
device_count: area.device_count || 0,
|
|
}));
|
|
setAllAreas(areas);
|
|
|
|
const uniqueFloors = Array.from(new Set(areas.map(area => area.floor_name || 'Non assegnato')));
|
|
setFloors(['Tutti', ...uniqueFloors]);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const load = async () => {
|
|
setIsLoading(true);
|
|
await initialize();
|
|
setIsLoading(false);
|
|
};
|
|
|
|
load();
|
|
}, [initialize]);
|
|
|
|
const onRefresh = useCallback(async () => {
|
|
setRefreshing(true);
|
|
await initialize();
|
|
setRefreshing(false);
|
|
}, [initialize]);
|
|
|
|
const filteredAreas = useMemo(() => {
|
|
if (selectedFloor === 'Tutti') {
|
|
return allAreas;
|
|
}
|
|
return allAreas.filter(area => area.floor_name === selectedFloor);
|
|
}, [allAreas, selectedFloor]);
|
|
|
|
if (isLoading) {
|
|
return <LoadingScreen />;
|
|
}
|
|
|
|
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>
|
|
<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>
|
|
)}
|
|
</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>
|
|
)}
|
|
</View>
|
|
|
|
<ScrollView
|
|
contentContainerClassName="p-5"
|
|
showsVerticalScrollIndicator={false}
|
|
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} colors={['#099499']} tintColor={'#099499'} />}
|
|
>
|
|
{filteredAreas.length > 0 ? (
|
|
<View className="flex-row flex-wrap justify-between">
|
|
{filteredAreas.map((area, index) => {
|
|
const { icon: IconComponent, bgColor } = AREA_STYLES[index % AREA_STYLES.length];
|
|
return (
|
|
<TouchableOpacity
|
|
key={area.id}
|
|
onPress={() => router.push({
|
|
pathname: '/(protected)/automation/[id]',
|
|
params: {
|
|
id: area.name,
|
|
areaData: JSON.stringify(area),
|
|
}
|
|
})}
|
|
className="bg-white rounded-3xl p-5 shadow-sm border border-gray-100 active:scale-[0.98] w-[48%] mb-4 aspect-square"
|
|
>
|
|
<View className="flex-1 justify-between">
|
|
<View className={`${bgColor} w-14 h-14 items-center justify-center rounded-2xl`}>
|
|
<IconComponent size={32} />
|
|
</View>
|
|
<View>
|
|
<Text
|
|
className="text-xl font-bold text-gray-800 leading-tight"
|
|
numberOfLines={2}
|
|
>
|
|
{area.name}
|
|
</Text>
|
|
<Text className="text-base text-gray-400 font-medium mt-1">
|
|
{area.device_count} dispositivi
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</TouchableOpacity>
|
|
)
|
|
})}
|
|
</View>
|
|
) : (
|
|
<View className="mt-20 items-center justify-center">
|
|
<WifiOff size={48} color="#9ca3af" />
|
|
<Text className="text-lg text-gray-500 font-medium mt-4">Nessuna area trovata</Text>
|
|
<Text className="text-center text-gray-400 mt-2">
|
|
{`Nessuna area per il piano "${selectedFloor}"`}
|
|
</Text>
|
|
</View>
|
|
)}
|
|
<View className="h-20" />
|
|
</ScrollView>
|
|
|
|
{/* <TouchableOpacity
|
|
onPress={() => 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"
|
|
>
|
|
<Plus size={32} color="white" />
|
|
</TouchableOpacity> */}
|
|
</View>
|
|
);
|
|
} |