import React, { useState, useEffect, useMemo, useRef } from 'react'; import { View, Text, ScrollView, TouchableOpacity, ActivityIndicator, Dimensions } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { StatusBar } from 'expo-status-bar'; import { useRouter } from 'expo-router'; import { ChevronDown, ChevronLeft, ChevronUp, LayoutDashboard } from 'lucide-react-native'; import api from '@/utils/api'; import EchartWrapper from '@/components/EchartWrapper'; import { CHART_ENDPOINTS, CHART_DATA_KEY, formatEuro, buildPieOption, buildAperteChiuseOption } from '@/utils/dashboardCharts'; const TOOLTIP_BASE = { renderMode: 'richText', confine: true, textStyle: { fontSize: 10 } }; const screenWidth = Dimensions.get("window").width; export default function DashboardScreen() { const router = useRouter(); const [chartData, setChartData] = useState({ costiRicavi: null, fatturatoCliente: null, aperteChiuseCliente: null, aperteChiuseFornitore: null, partiteCliente: null, }); const [loadingCharts, setLoadingCharts] = useState>({}); const [expandedCards, setExpandedCards] = useState>({ costiRicavi: false, aperteChiuseCliente: false, aperteChiuseFornitore: false, fatturatoCliente: false, apertoCliente: false, chiusoCliente: false, }); const chartRichiesti = useRef(new Set()); const isLoading = (key: string) => !!loadingCharts[CHART_DATA_KEY[key] || key]; const fetchChart = (key: keyof typeof CHART_ENDPOINTS) => { const dataKey = CHART_DATA_KEY[key] || key; const endpoint = CHART_ENDPOINTS[key]; if (!endpoint || chartRichiesti.current.has(dataKey)) { return Promise.resolve(); } chartRichiesti.current.add(dataKey); setLoadingCharts(prev => ({ ...prev, [dataKey]: true })); return api.get(endpoint).then(res => { if (res.data && res.data.success) { setChartData((prev: any) => ({ ...prev, [dataKey]: res.data.result })); } }).catch(err => { chartRichiesti.current.delete(dataKey); console.log("Errore caricamento dashboard:", err); }).finally(() => { setLoadingCharts(prev => ({ ...prev, [dataKey]: false })); }); }; // Sequential Prefetching useEffect(() => { let cancelled = false; const prefetch = async () => { const richieste: string[] = []; const visti = new Set(); Object.keys(CHART_ENDPOINTS).forEach(key => { const dataKey = CHART_DATA_KEY[key] || key; if (visti.has(dataKey)) { return; } visti.add(dataKey); richieste.push(key); }); for (const key of richieste) { if (cancelled) { return; } await fetchChart(key as keyof typeof CHART_ENDPOINTS); } }; prefetch(); return () => { cancelled = true; }; }, []); const toggleCard = (key: string) => { const isCurrentlyExpanded = expandedCards[key]; setExpandedCards(prev => ({ ...prev, [key]: !prev[key] })); const dataKey = CHART_DATA_KEY[key] || key; if (!isCurrentlyExpanded && !chartData[dataKey] && !isLoading(key)) { fetchChart(key as keyof typeof CHART_ENDPOINTS); } }; // --- Chart Options --- const costiRicaviOption = useMemo(() => !chartData.costiRicavi ? null : { tooltip: { ...TOOLTIP_BASE, trigger: 'axis', valueFormatter: formatEuro }, legend: { bottom: 0, itemGap: 6, itemWidth: 14, itemHeight: 10, textStyle: { fontSize: 12 }, data: [ `Costi Materiali(${chartData.costiRicavi.current_year})`, `Costi Manodopera(${chartData.costiRicavi.current_year})`, `Ricavi(${chartData.costiRicavi.current_year})`, `Utile(${chartData.costiRicavi.current_year})`, `Costi Materiali(${chartData.costiRicavi.prev_year})`, `Costi Manodopera(${chartData.costiRicavi.prev_year})`, `Ricavi(${chartData.costiRicavi.prev_year})`, `Utile(${chartData.costiRicavi.prev_year})` ], selected: { [`Utile(${chartData.costiRicavi.prev_year})`]: false, [`Ricavi(${chartData.costiRicavi.prev_year})`]: false, [`Costi Materiali(${chartData.costiRicavi.prev_year})`]: false, [`Costi Manodopera(${chartData.costiRicavi.prev_year})`]: false }, }, grid: { left: '3%', right: '4%', top: '8%', bottom: 95, containLabel: true }, xAxis: { type: 'category', data: chartData.costiRicavi.mesi }, yAxis: { type: 'value' }, series: [ { name: `Costi Materiali(${chartData.costiRicavi.current_year})`, type: 'bar', data: chartData.costiRicavi.costi }, { name: `Costi Manodopera(${chartData.costiRicavi.current_year})`, type: 'bar', data: chartData.costiRicavi.costi_mdo }, { name: `Ricavi(${chartData.costiRicavi.current_year})`, type: 'bar', data: chartData.costiRicavi.ricavi }, { name: `Utile(${chartData.costiRicavi.current_year})`, type: 'bar', lineStyle: { width: 2.5, type: 'dashed' }, data: chartData.costiRicavi.utile }, { name: `Costi Materiali(${chartData.costiRicavi.prev_year})`, type: 'bar', itemStyle: { color: '#b3b3b3' }, data: chartData.costiRicavi.costi_prev }, { name: `Costi Manodopera(${chartData.costiRicavi.prev_year})`, type: 'bar', itemStyle: { color: '#b3b3b3' }, data: chartData.costiRicavi.costi_mdo_prev }, { name: `Ricavi(${chartData.costiRicavi.prev_year})`, type: 'bar', itemStyle: { color: '#b3b3b3' }, data: chartData.costiRicavi.ricavi_prev }, { name: `Utile(${chartData.costiRicavi.prev_year})`, type: 'bar', lineStyle: { width: 1.5, type: 'dashed', color: '#b3b3b3' }, data: chartData.costiRicavi.utile_prev } ] }, [chartData.costiRicavi]); const aperteChiuseClienteOption = useMemo(() => chartData.aperteChiuseCliente ? buildAperteChiuseOption(chartData.aperteChiuseCliente) : null, [chartData.aperteChiuseCliente]); const aperteChiuseFornitoreOption = useMemo(() => chartData.aperteChiuseFornitore ? buildAperteChiuseOption(chartData.aperteChiuseFornitore) : null, [chartData.aperteChiuseFornitore]); const fatturatoClienteOption = useMemo(() => chartData.fatturatoCliente ? buildPieOption(chartData.fatturatoCliente) : null, [chartData.fatturatoCliente]); const apertoClienteOption = useMemo(() => chartData.partiteCliente ? buildPieOption(chartData.partiteCliente.aperto) : null, [chartData.partiteCliente]); const chiusoClienteOption = useMemo(() => chartData.partiteCliente ? buildPieOption(chartData.partiteCliente.chiuso) : null, [chartData.partiteCliente]); const numItems = chartData.fatturatoCliente?.length || 0; const dynamicPieHeight = Math.max(260, 200 + (Math.ceil(numItems / 2) * 26)); const pieHeightFor = (arr: any) => Math.max(260, 200 + (Math.ceil((arr?.length || 0) / 2) * 26)); const dynamicApertoHeight = pieHeightFor(chartData.partiteCliente?.aperto); const dynamicChiusoHeight = pieHeightFor(chartData.partiteCliente?.chiuso); // Render Card Helper const renderCard = (key: string, title: string, option: any, height: number) => { const isExpanded = expandedCards[key]; const loading = isLoading(key); return ( toggleCard(key)} > {title} {isExpanded ? : } {isExpanded && ( {loading ? ( ) : option ? ( ) : ( Nessun dato disponibile )} )} ); }; return ( router.navigate('/')} className="p-2 -ml-2 rounded-full active:bg-white/20 w-12 items-center justify-center" > Grafici e Statistiche Dashboard {renderCard('costiRicavi', 'Costi / Ricavi', costiRicaviOption, 390)} {renderCard('aperteChiuseCliente', 'Aperto / Chiuso Clienti', aperteChiuseClienteOption, 330)} {renderCard('aperteChiuseFornitore', 'Aperto / Chiuso Fornitori', aperteChiuseFornitoreOption, 330)} {renderCard('fatturatoCliente', 'Fatturato per Cliente', fatturatoClienteOption, dynamicPieHeight)} {renderCard('apertoCliente', 'Aperto per Cliente', apertoClienteOption, dynamicApertoHeight)} {renderCard('chiusoCliente', 'Chiuso per Cliente', chiusoClienteOption, dynamicChiusoHeight)} ); }