diff --git a/src/pages/audit-report/types/index.ts b/src/pages/audit-report/types/index.ts index 9184e27e1..0d10e1894 100644 --- a/src/pages/audit-report/types/index.ts +++ b/src/pages/audit-report/types/index.ts @@ -433,6 +433,10 @@ export interface ViewResult { namespace?: string; name: string; + refreshStatus?: "cache" | "fresh" | "error"; + refreshError?: string; + responseSource?: "cache" | "fresh"; + lastRefreshedAt?: string; columns?: ViewColumnDef[]; rows?: ViewRow[]; diff --git a/src/pages/views/components/SingleView.tsx b/src/pages/views/components/SingleView.tsx index e83d1ee48..a9a122655 100644 --- a/src/pages/views/components/SingleView.tsx +++ b/src/pages/views/components/SingleView.tsx @@ -1,9 +1,16 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import Age from "../../../ui/Age/Age"; import ViewLayout from "./ViewLayout"; import ViewWithSections from "./ViewWithSections"; import { useViewData } from "../hooks/useViewData"; import { ErrorViewer } from "@flanksource-ui/components/ErrorViewer"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle +} from "@flanksource-ui/components/ui/dialog"; interface SingleViewProps { id: string; @@ -19,32 +26,43 @@ const SingleView: React.FC = ({ id }) => { currentVariables, handleForceRefresh } = useViewData({ viewId: id }); + const [refreshErrorOpen, setRefreshErrorOpen] = useState(false); - if (isLoading && !viewResult) { + const refreshError = + viewResult?.refreshStatus === "error" ? viewResult.refreshError : undefined; + const isCachedResponse = viewResult?.responseSource === "cache"; + + useEffect(() => { + if (refreshError && isCachedResponse) { + setRefreshErrorOpen(true); + } + }, [refreshError, isCachedResponse, viewResult?.requestFingerprint]); + + if (error && !viewResult) { return ( -
-
-

Loading view results...

-
+
); } - if (error) { + if (isLoading && !viewResult) { return ( - +
+
+

Loading view results...

+
); } @@ -68,27 +86,43 @@ const SingleView: React.FC = ({ id }) => { const { icon, title, name } = viewResult; return ( - - Last refreshed:{" "} - -

- ) - } - > - -
+ <> + + + + View refresh failed + + The view failed to refresh. You are seeing cached data from the + last successful refresh. + + + {refreshError ? ( + + ) : null} + + + + Last refreshed:{" "} + +

+ ) + } + > + +
+ ); }; diff --git a/src/pages/views/hooks/useViewData.ts b/src/pages/views/hooks/useViewData.ts index d2fcff12f..422fd211b 100644 --- a/src/pages/views/hooks/useViewData.ts +++ b/src/pages/views/hooks/useViewData.ts @@ -49,6 +49,10 @@ export function useViewData({ const variables = isDisplayPluginMode ? displayPluginVariables : undefined; + const viewQueryKey = isDisplayPluginMode + ? ["viewDataById", viewId, configId, variables] + : ["view-result", viewId]; + const { data: viewResult, isLoading: isLoadingViewResult, @@ -56,9 +60,7 @@ export function useViewData({ error: viewResultError, refetch } = useQuery({ - queryKey: isDisplayPluginMode - ? ["viewDataById", viewId, configId, variables] - : ["view-result", viewId], + queryKey: viewQueryKey, queryFn: () => { const headers = forceRefreshRef.current ? { "cache-control": "max-age=1" } @@ -120,6 +122,16 @@ export function useViewData({ : result.data?.name ? [{ namespace: result.data.namespace ?? "", name: result.data.name }] : []; + const currentNamespace = result.data?.namespace ?? viewResult?.namespace; + const currentName = result.data?.name ?? viewResult?.name; + const refsToInvalidate = sectionsToRefresh.filter( + (section) => + !( + currentName && + section.name === currentName && + section.namespace === (currentNamespace ?? "") + ) + ); if (isDisplayPluginMode) { await queryClient.invalidateQueries({ @@ -127,14 +139,8 @@ export function useViewData({ }); } - await queryClient.invalidateQueries({ - queryKey: isDisplayPluginMode - ? ["viewDataById", viewId, configId, variables] - : ["view-result", viewId] - }); - await Promise.all( - sectionsToRefresh.flatMap((section) => [ + refsToInvalidate.flatMap((section) => [ queryClient.invalidateQueries({ queryKey: ["view-result", section.namespace, section.name] }), @@ -147,12 +153,13 @@ export function useViewData({ ]) ); }, [ + viewResult?.namespace, + viewResult?.name, allSectionRefs, configId, isDisplayPluginMode, queryClient, refetch, - variables, viewId ]);