import { createContext, useContext, useCallback, type ReactNode } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { moduleService } from "../services/moduleService"; import type { ModuleDto, EnableModuleRequest, SubscriptionDto, } from "../types/module"; // ==================== TYPES ==================== export interface ModuleContextValue { /** Lista di tutti i moduli disponibili */ modules: ModuleDto[]; /** Lista dei moduli attualmente attivi */ activeModules: ModuleDto[]; /** Codici dei moduli attivi (per filtro veloce) */ activeModuleCodes: string[]; /** Stato di caricamento */ isLoading: boolean; /** Errore di caricamento */ error: Error | null; /** Verifica se un modulo è abilitato */ isModuleEnabled: (code: string) => boolean; /** Ottiene un modulo per codice */ getModule: (code: string) => ModuleDto | undefined; /** Attiva un modulo */ enableModule: ( code: string, request: EnableModuleRequest, ) => Promise; /** Disattiva un modulo */ disableModule: (code: string) => Promise; /** Ricarica i dati dei moduli */ refreshModules: () => Promise; } const ModuleContext = createContext(null); // ==================== PROVIDER ==================== interface ModuleProviderProps { children: ReactNode; } export function ModuleProvider({ children }: ModuleProviderProps) { const queryClient = useQueryClient(); // Query per tutti i moduli const { data: modules = [], isLoading: isLoadingModules, error: modulesError, } = useQuery({ queryKey: ["modules"], queryFn: moduleService.getAll, staleTime: 5 * 60 * 1000, // 5 minuti refetchOnWindowFocus: false, }); // Query per moduli attivi const { data: activeModules = [], isLoading: isLoadingActive, error: activeError, } = useQuery({ queryKey: ["modules", "active"], queryFn: moduleService.getActive, staleTime: 5 * 60 * 1000, refetchOnWindowFocus: false, }); // Calcola i codici dei moduli attivi const activeModuleCodes = activeModules.map((m) => m.code); // Verifica se un modulo è abilitato const isModuleEnabled = useCallback( (code: string): boolean => { return activeModuleCodes.includes(code); }, [activeModuleCodes], ); // Ottiene un modulo per codice const getModule = useCallback( (code: string): ModuleDto | undefined => { return modules.find((m) => m.code === code); }, [modules], ); // Attiva un modulo const enableModule = useCallback( async ( code: string, request: EnableModuleRequest, ): Promise => { const subscription = await moduleService.enable(code, request); // Invalida le query per ricaricare i dati await queryClient.invalidateQueries({ queryKey: ["modules"] }); return subscription; }, [queryClient], ); // Disattiva un modulo const disableModule = useCallback( async (code: string): Promise => { await moduleService.disable(code); // Invalida le query per ricaricare i dati await queryClient.invalidateQueries({ queryKey: ["modules"] }); }, [queryClient], ); // Ricarica i dati dei moduli const refreshModules = useCallback(async (): Promise => { await queryClient.invalidateQueries({ queryKey: ["modules"] }); }, [queryClient]); const value: ModuleContextValue = { modules, activeModules, activeModuleCodes, isLoading: isLoadingModules || isLoadingActive, error: modulesError || activeError, isModuleEnabled, getModule, enableModule, disableModule, refreshModules, }; return ( {children} ); } // ==================== HOOKS ==================== /** * Hook per accedere al context dei moduli */ export function useModules(): ModuleContextValue { const context = useContext(ModuleContext); if (!context) { throw new Error("useModules must be used within a ModuleProvider"); } return context; } /** * Hook per verificare se un singolo modulo è abilitato */ export function useModuleEnabled(code: string): boolean { const { isModuleEnabled } = useModules(); return isModuleEnabled(code); } /** * Hook per ottenere solo i moduli attivi */ export function useActiveModules(): ModuleDto[] { const { activeModules } = useModules(); return activeModules; } /** * Hook per ottenere i codici dei moduli attivi */ export function useActiveModuleCodes(): string[] { const { activeModuleCodes } = useModules(); return activeModuleCodes; } /** * Hook per ottenere un modulo specifico */ export function useModule(code: string): ModuleDto | undefined { const { getModule } = useModules(); return getModule(code); }