import React, { createContext, useContext, useEffect, useMemo, useState } from "react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ZinoClient } from "./client"; import { AuthService } from "./auth"; import { WorkflowService } from "./workflow"; import { ViewService } from "./views"; import type { User } from "./types"; // --------------------------------------------------------------------------- // Context // --------------------------------------------------------------------------- export interface ZinoContextValue { client: ZinoClient; auth: AuthService; workflows: WorkflowService; views: ViewService; user: User | null; setUser: (user: User | null) => void; } const ZinoContext = createContext(null); const MOCK_SESSION_KEY = "zino_mock_session"; // Shared QueryClient — sensible defaults for generated apps. const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 30_000, // 30s before refetch retry: 1, refetchOnWindowFocus: false, }, }, }); // --------------------------------------------------------------------------- // Provider // --------------------------------------------------------------------------- export interface ZinoProviderProps { baseUrl: string; children: React.ReactNode; } /** * Wrap your app with `` to get access to Zino services * and TanStack Query throughout the component tree. * * ```tsx * * * * ``` */ export function ZinoProvider({ baseUrl, children }: ZinoProviderProps) { const [user, setUserState] = useState(null); const viteMock = import.meta.env.VITE_ZINO_MOCK === "true"; const resolvedUrl = baseUrl === "mock" || viteMock ? "mock" : baseUrl; const isMock = resolvedUrl === "mock"; const setUser = useMemo(() => (u: User | null) => { setUserState(u); if (isMock) { if (u) { sessionStorage.setItem(MOCK_SESSION_KEY, "1"); } else { sessionStorage.removeItem(MOCK_SESSION_KEY); } } }, [isMock]); // Restore session on mount — from localStorage token or mock sessionStorage. useEffect(() => { if (isMock && sessionStorage.getItem(MOCK_SESSION_KEY)) { const authSvc = new AuthService(new ZinoClient({ baseUrl: "mock" })); const mockUser = authSvc.getCurrentUser(); if (mockUser) setUserState(mockUser); } else { // Real mode — check if a token exists in localStorage const client = new ZinoClient({ baseUrl: resolvedUrl }); if (client.getToken()) { const authSvc = new AuthService(client); const restored = authSvc.getCurrentUser(); if (restored) setUserState(restored); } } }, []); const value = useMemo(() => { const client = new ZinoClient({ baseUrl: resolvedUrl, onAuthError: () => setUser(null), }); return { client, auth: new AuthService(client), workflows: new WorkflowService(client), views: new ViewService(client), user, setUser, }; }, [resolvedUrl, setUser]); const ctx = useMemo( () => ({ ...value, user, setUser }), [value, user, setUser], ); return ( {children} ); } // --------------------------------------------------------------------------- // Hook // --------------------------------------------------------------------------- /** * Access Zino services. Use with TanStack Query: * * ```tsx * const { views } = useZino(); * const { data } = useQuery({ * queryKey: ['recordview', rvId], * queryFn: () => views.getTabularView(rvId, { page: 1 }), * }); * ``` */ export function useZino(): ZinoContextValue { const ctx = useContext(ZinoContext); if (!ctx) { throw new Error("useZino must be used within a "); } return ctx; } /** Re-export queryClient for direct invalidation in mutations. */ export { queryClient };