Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/pages/audit-report/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down
96 changes: 65 additions & 31 deletions src/pages/views/components/SingleView.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -19,32 +26,43 @@ const SingleView: React.FC<SingleViewProps> = ({ 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 (
<ViewLayout
title="View"
title="View Error"
icon="workflow"
onRefresh={handleForceRefresh}
centered
>
<div className="text-center">
<div className="mx-auto mb-4 h-12 w-12 animate-spin rounded-full border-b-2 border-blue-600"></div>
<p className="text-gray-600">Loading view results...</p>
</div>
<ErrorViewer error={error} className="mx-auto max-w-3xl" />
</ViewLayout>
);
}

if (error) {
if (isLoading && !viewResult) {
return (
<ViewLayout
title="View Error"
title="View"
icon="workflow"
onRefresh={handleForceRefresh}
centered
>
<ErrorViewer error={error} className="mx-auto max-w-3xl" />
<div className="text-center">
<div className="mx-auto mb-4 h-12 w-12 animate-spin rounded-full border-b-2 border-blue-600"></div>
<p className="text-gray-600">Loading view results...</p>
</div>
</ViewLayout>
);
}
Expand All @@ -68,27 +86,43 @@ const SingleView: React.FC<SingleViewProps> = ({ id }) => {
const { icon, title, name } = viewResult;

return (
<ViewLayout
title={title || name}
icon={icon || "workflow"}
onRefresh={handleForceRefresh}
loading={isFetching}
extra={
viewResult.lastRefreshedAt && (
<p className="text-sm text-gray-500">
Last refreshed:{" "}
<Age from={viewResult.lastRefreshedAt} format="full" />
</p>
)
}
>
<ViewWithSections
className="flex h-full w-full flex-1 flex-col overflow-y-auto px-6"
viewResult={viewResult}
aggregatedVariables={aggregatedVariables}
currentVariables={currentVariables}
/>
</ViewLayout>
<>
<Dialog open={refreshErrorOpen} onOpenChange={setRefreshErrorOpen}>
<DialogContent className="max-w-xl">
<DialogHeader>
<DialogTitle>View refresh failed</DialogTitle>
<DialogDescription>
The view failed to refresh. You are seeing cached data from the
last successful refresh.
</DialogDescription>
</DialogHeader>
{refreshError ? (
<ErrorViewer error={refreshError} className="mt-4" />
) : null}
</DialogContent>
</Dialog>
<ViewLayout
title={title || name}
icon={icon || "workflow"}
onRefresh={handleForceRefresh}
loading={isFetching}
extra={
viewResult.lastRefreshedAt && (
<p className="text-sm text-gray-500">
Last refreshed:{" "}
<Age from={viewResult.lastRefreshedAt} format="full" />
</p>
)
}
>
<ViewWithSections
className="flex h-full w-full flex-1 flex-col overflow-y-auto px-6"
viewResult={viewResult}
aggregatedVariables={aggregatedVariables}
currentVariables={currentVariables}
/>
</ViewLayout>
</>
);
};

Expand Down
29 changes: 18 additions & 11 deletions src/pages/views/hooks/useViewData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,18 @@ export function useViewData({

const variables = isDisplayPluginMode ? displayPluginVariables : undefined;

const viewQueryKey = isDisplayPluginMode
? ["viewDataById", viewId, configId, variables]
: ["view-result", viewId];

const {
data: viewResult,
isLoading: isLoadingViewResult,
isFetching: isFetchingViewResult,
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" }
Expand Down Expand Up @@ -120,21 +122,25 @@ 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({
queryKey: ["viewDisplayPluginVariables", viewId, configId]
});
}

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]
}),
Expand All @@ -147,12 +153,13 @@ export function useViewData({
])
);
}, [
viewResult?.namespace,
viewResult?.name,
allSectionRefs,
configId,
isDisplayPluginMode,
queryClient,
refetch,
variables,
viewId
]);

Expand Down
Loading