From a1e0287e6d083f95df378a788767b3324f2d1c1d Mon Sep 17 00:00:00 2001 From: Peter Meier Date: Sun, 15 Mar 2026 22:39:57 +0100 Subject: [PATCH] Instant env switch: invalidate all queries on environment change EnvironmentContext holds current env as React state. On change, QueryInvalidatorOnEnvChange calls queryClient.invalidateQueries() so all content/list pages refetch immediately without manual reload. Co-Authored-By: Claude Sonnet 4.6 --- admin-ui/src/app/providers.tsx | 20 ++++++- .../src/components/EnvironmentSwitcher.tsx | 60 +++++++++++++++++++ admin-ui/src/lib/EnvironmentContext.tsx | 37 ++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 admin-ui/src/components/EnvironmentSwitcher.tsx create mode 100644 admin-ui/src/lib/EnvironmentContext.tsx diff --git a/admin-ui/src/app/providers.tsx b/admin-ui/src/app/providers.tsx index f9a5528..361f1e7 100644 --- a/admin-ui/src/app/providers.tsx +++ b/admin-ui/src/app/providers.tsx @@ -3,6 +3,18 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { useState, useEffect } from "react"; import { syncStoredApiKey } from "@/lib/api"; +import { EnvironmentProvider, useEnvironment } from "@/lib/EnvironmentContext"; + +/** Invalidates all queries whenever the active environment changes. */ +function QueryInvalidatorOnEnvChange({ queryClient }: { queryClient: QueryClient }) { + const { environment } = useEnvironment(); + useEffect(() => { + if (environment != null) { + void queryClient.invalidateQueries(); + } + }, [environment, queryClient]); + return null; +} export function Providers({ children }: { children: React.ReactNode }) { useEffect(() => { @@ -17,7 +29,13 @@ export function Providers({ children }: { children: React.ReactNode }) { }, }) ); + return ( - {children} + + + + {children} + + ); } diff --git a/admin-ui/src/components/EnvironmentSwitcher.tsx b/admin-ui/src/components/EnvironmentSwitcher.tsx new file mode 100644 index 0000000..a42b030 --- /dev/null +++ b/admin-ui/src/components/EnvironmentSwitcher.tsx @@ -0,0 +1,60 @@ +"use client"; + +import { useEffect } from "react"; +import { useQuery } from "@tanstack/react-query"; +import { useTranslations } from "next-intl"; +import { fetchEnvironments } from "@/lib/api"; +import { useEnvironment } from "@/lib/EnvironmentContext"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +export function EnvironmentSwitcher() { + const t = useTranslations("EnvironmentSwitcher"); + const { environment, setEnvironment } = useEnvironment(); + const { data } = useQuery({ + queryKey: ["environments"], + queryFn: fetchEnvironments, + }); + + const environments = data?.environments ?? []; + const defaultEnv = data?.default ?? null; + + useEffect(() => { + if (environments.length > 0 && !environment && defaultEnv) { + setEnvironment(defaultEnv); + } + }, [environments.length, environment, defaultEnv, setEnvironment]); + + if (environments.length === 0) return null; + + const value = environment ?? defaultEnv ?? environments[0]; + + const handleChange = (next: string) => { + setEnvironment(next); + }; + + return ( +
+ + {t("label")}: + + +
+ ); +} diff --git a/admin-ui/src/lib/EnvironmentContext.tsx b/admin-ui/src/lib/EnvironmentContext.tsx new file mode 100644 index 0000000..586c3d9 --- /dev/null +++ b/admin-ui/src/lib/EnvironmentContext.tsx @@ -0,0 +1,37 @@ +"use client"; + +import { createContext, useContext, useState, useEffect, type ReactNode } from "react"; +import { getCurrentEnvironment, setCurrentEnvironment } from "@/lib/api"; + +type EnvironmentContextType = { + environment: string | null; + setEnvironment: (env: string) => void; +}; + +const EnvironmentContext = createContext({ + environment: null, + setEnvironment: () => {}, +}); + +export function EnvironmentProvider({ children }: { children: ReactNode }) { + const [environment, setEnv] = useState(null); + + useEffect(() => { + setEnv(getCurrentEnvironment()); + }, []); + + function setEnvironment(env: string) { + setCurrentEnvironment(env); + setEnv(env); + } + + return ( + + {children} + + ); +} + +export function useEnvironment() { + return useContext(EnvironmentContext); +}