@@ -13,7 +13,6 @@ import { logger } from '@/utils/logger'
1313import { ModernButton } from './Simulator/components/ModernButton'
1414import { useIsDemo } from '@/hooks/useIsDemo'
1515import { usePdlStore } from '@/stores/pdlStore'
16- import { useDataFetchStore } from '@/stores/dataFetchStore'
1716
1817// Helper function to check if a date is weekend (Saturday or Sunday)
1918function isWeekend ( dateString : string ) : boolean {
@@ -180,8 +179,9 @@ export default function Simulator() {
180179 const [ isInitializing , setIsInitializing ] = useState ( true )
181180 // const [isClearingCache, setIsClearingCache] = useState(false) // Unused for now
182181
183- // Register fetch function in store for PageHeader button
184- const { setFetchDataFunction, setIsLoading } = useDataFetchStore ( )
182+ // Note: We DO NOT register handleSimulation in the global store
183+ // because PageHeader already handles the unified fetch function.
184+ // The Simulator uses its own button to trigger simulation.
185185
186186 // Filters and sorting state
187187 const [ filterType , setFilterType ] = useState < string > ( 'all' )
@@ -361,17 +361,6 @@ export default function Simulator() {
361361 }
362362 } , [ selectedPdl , pdlsData , offersData , providersData , queryClient ] )
363363
364- // Register fetch function in store for PageHeader button
365- useEffect ( ( ) => {
366- setFetchDataFunction ( handleSimulation )
367- return ( ) => setFetchDataFunction ( null )
368- } , [ handleSimulation , setFetchDataFunction ] )
369-
370- // Sync loading state with store
371- useEffect ( ( ) => {
372- setIsLoading ( isSimulating )
373- } , [ isSimulating , setIsLoading ] )
374-
375364 const calculateSimulationsForAllOffers = ( consumptionData : any [ ] , offers : EnergyOffer [ ] , providers : EnergyProvider [ ] , tempoColors : TempoDay [ ] , pdl ?: PDL ) => {
376365 // Create a map of date -> TEMPO color for fast lookup
377366 const tempoColorMap = new Map < string , 'BLUE' | 'WHITE' | 'RED' > ( )
@@ -773,6 +762,128 @@ export default function Simulator() {
773762 return results
774763 }
775764
765+ // Auto-launch simulation if cache data exists
766+ // IMPORTANT: This hook must be before any early returns to respect React's rules of hooks
767+ useEffect ( ( ) => {
768+ logger . log ( '[Auto-launch] useEffect triggered' , {
769+ selectedPdl,
770+ isSimulating,
771+ hasSimulationResult : ! ! simulationResult ,
772+ hasAutoLaunched,
773+ isDemo,
774+ pdlsDataLoaded : ! ! pdlsData ,
775+ offersDataLoaded : ! ! offersData ,
776+ providersDataLoaded : ! ! providersData ,
777+ } )
778+
779+ // Don't auto-launch if already launched, simulating, or have results
780+ if ( ! selectedPdl || isSimulating || simulationResult || hasAutoLaunched ) {
781+ logger . log ( '[Auto-launch] Skipping auto-launch due to conditions' )
782+ return
783+ }
784+
785+ // Don't auto-launch if PDL data, offers, or providers are not loaded yet
786+ if ( ! pdlsData || ! Array . isArray ( offersData ) || offersData . length === 0 || ! providersData ) {
787+ logger . log ( '[Auto-launch] Skipping auto-launch - data not loaded yet' , {
788+ pdlsData : ! ! pdlsData ,
789+ offersData : Array . isArray ( offersData ) ? offersData . length : 'not array' ,
790+ providersData : ! ! providersData
791+ } )
792+ return
793+ }
794+
795+ // Check if we have cached data for this PDL (uses new single cache key format)
796+ const cachedData = queryClient . getQueryData ( [ 'consumptionDetail' , selectedPdl ] ) as any
797+
798+ if ( ! cachedData ?. data ?. meter_reading ?. interval_reading ?. length ) {
799+ logger . log ( '[Auto-launch] ❌ No cached data found for PDL:' , selectedPdl )
800+ return
801+ }
802+
803+ const readings = cachedData . data . meter_reading . interval_reading
804+ const totalPoints = readings . length
805+
806+ // Check if we have enough data (at least 30 days worth = ~1440 points at 30min intervals)
807+ const minRequiredPoints = 30 * 48 // 30 days * 48 half-hours
808+ const hasEnoughData = totalPoints >= minRequiredPoints
809+
810+ logger . log ( `[Auto-launch] Cache check: ${ totalPoints } points (need ${ minRequiredPoints } minimum)` )
811+
812+ if ( hasEnoughData ) {
813+ logger . log ( `✅ Auto-launching simulation with ${ totalPoints } cached points` )
814+ setHasAutoLaunched ( true )
815+ // Show loading overlay while preparing simulation
816+ setIsInitialLoadingFromCache ( true )
817+
818+ // Use setTimeout to avoid calling during render
819+ setTimeout ( ( ) => {
820+ handleSimulation ( )
821+ } , 100 )
822+ } else {
823+ logger . log ( `❌ Not enough cached data (${ totalPoints } points), skipping auto-launch` )
824+ }
825+ } , [ selectedPdl , isSimulating , simulationResult , hasAutoLaunched , isDemo , pdlsData , offersData , providersData , queryClient , handleSimulation ] )
826+
827+ // Filter and sort simulation results
828+ // IMPORTANT: This hook must be before any early returns to respect React's rules of hooks
829+ const filteredAndSortedResults = useMemo ( ( ) => {
830+ if ( ! simulationResult || ! Array . isArray ( simulationResult ) ) return [ ]
831+
832+ let filtered = [ ...simulationResult ]
833+
834+ // Filter by type
835+ if ( filterType !== 'all' ) {
836+ filtered = filtered . filter ( ( result ) => result . offerType === filterType )
837+ }
838+
839+ // Filter by provider
840+ if ( filterProvider !== 'all' ) {
841+ filtered = filtered . filter ( ( result ) => result . providerName === filterProvider )
842+ }
843+
844+ // Filter by recency (tariffs < 6 months old)
845+ if ( showOnlyRecent ) {
846+ filtered = filtered . filter ( ( result ) => ! isOldTariff ( result . validFrom ) )
847+ }
848+
849+ // Sort by selected criteria
850+ filtered . sort ( ( a , b ) => {
851+ let comparison = 0
852+ switch ( sortBy ) {
853+ case 'subscription' :
854+ comparison = a . subscriptionCost - b . subscriptionCost
855+ break
856+ case 'energy' :
857+ comparison = a . energyCost - b . energyCost
858+ break
859+ case 'total' :
860+ default :
861+ comparison = a . totalCost - b . totalCost
862+ break
863+ }
864+ return sortOrder === 'asc' ? comparison : - comparison
865+ } )
866+
867+ return filtered
868+ } , [ simulationResult , filterType , filterProvider , showOnlyRecent , sortBy , sortOrder ] )
869+
870+ // Get unique providers and types for filter options
871+ // IMPORTANT: These hooks must be before any early returns to respect React's rules of hooks
872+ const availableProviders = useMemo ( ( ) => {
873+ if ( ! simulationResult || ! Array . isArray ( simulationResult ) ) return [ ]
874+ const providers = new Set ( simulationResult . map ( ( r ) => r . providerName ) )
875+ return Array . from ( providers ) . sort ( )
876+ } , [ simulationResult ] )
877+
878+ const availableTypes = useMemo ( ( ) => {
879+ if ( ! simulationResult || ! Array . isArray ( simulationResult ) ) return [ ]
880+ const types = new Set ( simulationResult . map ( ( r ) => r . offerType ) )
881+ return Array . from ( types ) . sort ( )
882+ } , [ simulationResult ] )
883+
884+ // ==================== EARLY RETURNS (after all hooks) ====================
885+ // These must come AFTER all hooks to respect React's rules of hooks
886+
776887 if ( pdlsLoading ) {
777888 return (
778889 < div className = "flex items-center justify-center py-12" >
@@ -814,6 +925,8 @@ export default function Simulator() {
814925 )
815926 }
816927
928+ // ==================== HELPER FUNCTIONS (after early returns) ====================
929+
817930 const getTypeColor = ( type : string ) => {
818931 switch ( type ) {
819932 case 'BASE' :
@@ -862,67 +975,6 @@ export default function Simulator() {
862975 }
863976 }
864977
865- // Auto-launch simulation if cache data exists
866- useEffect ( ( ) => {
867- logger . log ( '[Auto-launch] useEffect triggered' , {
868- selectedPdl,
869- isSimulating,
870- hasSimulationResult : ! ! simulationResult ,
871- hasAutoLaunched,
872- isDemo,
873- pdlsDataLoaded : ! ! pdlsData ,
874- offersDataLoaded : ! ! offersData ,
875- providersDataLoaded : ! ! providersData ,
876- } )
877-
878- // Don't auto-launch if already launched, simulating, or have results
879- if ( ! selectedPdl || isSimulating || simulationResult || hasAutoLaunched ) {
880- logger . log ( '[Auto-launch] Skipping auto-launch due to conditions' )
881- return
882- }
883-
884- // Don't auto-launch if PDL data, offers, or providers are not loaded yet
885- if ( ! pdlsData || ! Array . isArray ( offersData ) || offersData . length === 0 || ! providersData ) {
886- logger . log ( '[Auto-launch] Skipping auto-launch - data not loaded yet' , {
887- pdlsData : ! ! pdlsData ,
888- offersData : Array . isArray ( offersData ) ? offersData . length : 'not array' ,
889- providersData : ! ! providersData
890- } )
891- return
892- }
893-
894- // Check if we have cached data for this PDL (uses new single cache key format)
895- const cachedData = queryClient . getQueryData ( [ 'consumptionDetail' , selectedPdl ] ) as any
896-
897- if ( ! cachedData ?. data ?. meter_reading ?. interval_reading ?. length ) {
898- logger . log ( '[Auto-launch] ❌ No cached data found for PDL:' , selectedPdl )
899- return
900- }
901-
902- const readings = cachedData . data . meter_reading . interval_reading
903- const totalPoints = readings . length
904-
905- // Check if we have enough data (at least 30 days worth = ~1440 points at 30min intervals)
906- const minRequiredPoints = 30 * 48 // 30 days * 48 half-hours
907- const hasEnoughData = totalPoints >= minRequiredPoints
908-
909- logger . log ( `[Auto-launch] Cache check: ${ totalPoints } points (need ${ minRequiredPoints } minimum)` )
910-
911- if ( hasEnoughData ) {
912- logger . log ( `✅ Auto-launching simulation with ${ totalPoints } cached points` )
913- setHasAutoLaunched ( true )
914- // Show loading overlay while preparing simulation
915- setIsInitialLoadingFromCache ( true )
916-
917- // Use setTimeout to avoid calling during render
918- setTimeout ( ( ) => {
919- handleSimulation ( )
920- } , 100 )
921- } else {
922- logger . log ( `❌ Not enough cached data (${ totalPoints } points), skipping auto-launch` )
923- }
924- } , [ selectedPdl , isSimulating , simulationResult , hasAutoLaunched , isDemo , pdlsData , offersData , providersData , queryClient , handleSimulation ] )
925-
926978 const toggleRowExpansion = ( offerId : string ) => {
927979 setExpandedRows ( ( prev ) => {
928980 const newSet = new Set ( prev )
@@ -953,61 +1005,6 @@ export default function Simulator() {
9531005 return sortOrder === 'asc' ? < ArrowUp size = { 14 } /> : < ArrowDown size = { 14 } />
9541006 }
9551007
956- // Filter and sort simulation results
957- const filteredAndSortedResults = useMemo ( ( ) => {
958- if ( ! simulationResult || ! Array . isArray ( simulationResult ) ) return [ ]
959-
960- let filtered = [ ...simulationResult ]
961-
962- // Filter by type
963- if ( filterType !== 'all' ) {
964- filtered = filtered . filter ( ( result ) => result . offerType === filterType )
965- }
966-
967- // Filter by provider
968- if ( filterProvider !== 'all' ) {
969- filtered = filtered . filter ( ( result ) => result . providerName === filterProvider )
970- }
971-
972- // Filter by recency (tariffs < 6 months old)
973- if ( showOnlyRecent ) {
974- filtered = filtered . filter ( ( result ) => ! isOldTariff ( result . validFrom ) )
975- }
976-
977- // Sort by selected criteria
978- filtered . sort ( ( a , b ) => {
979- let comparison = 0
980- switch ( sortBy ) {
981- case 'subscription' :
982- comparison = a . subscriptionCost - b . subscriptionCost
983- break
984- case 'energy' :
985- comparison = a . energyCost - b . energyCost
986- break
987- case 'total' :
988- default :
989- comparison = a . totalCost - b . totalCost
990- break
991- }
992- return sortOrder === 'asc' ? comparison : - comparison
993- } )
994-
995- return filtered
996- } , [ simulationResult , filterType , filterProvider , showOnlyRecent , sortBy , sortOrder ] )
997-
998- // Get unique providers and types for filter options
999- const availableProviders = useMemo ( ( ) => {
1000- if ( ! simulationResult || ! Array . isArray ( simulationResult ) ) return [ ]
1001- const providers = new Set ( simulationResult . map ( ( r ) => r . providerName ) )
1002- return Array . from ( providers ) . sort ( )
1003- } , [ simulationResult ] )
1004-
1005- const availableTypes = useMemo ( ( ) => {
1006- if ( ! simulationResult || ! Array . isArray ( simulationResult ) ) return [ ]
1007- const types = new Set ( simulationResult . map ( ( r ) => r . offerType ) )
1008- return Array . from ( types ) . sort ( )
1009- } , [ simulationResult ] )
1010-
10111008 // Clear cache function (admin only) - Unused for now as cache clearing is in the sidebar
10121009 /*
10131010 const confirmClearCache = async () => {
0 commit comments