1
- import { useState , useEffect } from "react" ;
1
+ import { useState , useEffect , useCallback } from "react" ;
2
2
import { useQuery , useQueryClient } from "@tanstack/react-query" ;
3
3
import { getViewDataById } from "../../../api/services/views" ;
4
+ import { ViewVariable } from "../../audit-report/types" ;
4
5
import View from "../../audit-report/components/View/View" ;
5
6
import { Head } from "../../../ui/Head" ;
6
7
import { Icon } from "../../../ui/Icons/Icon" ;
@@ -14,26 +15,27 @@ interface SingleViewProps {
14
15
15
16
const SingleView : React . FC < SingleViewProps > = ( { id } ) => {
16
17
const [ error , setError ] = useState < string > ( ) ;
17
- const [ currentGlobalFilters , setCurrentGlobalFilters ] = useState <
18
+ const [ currentViewVariables , setCurrentViewVariables ] = useState <
18
19
Record < string , string >
19
20
> ( { } ) ;
21
+ const [ hasFiltersInitialized , setHasFiltersInitialized ] = useState ( false ) ;
20
22
const queryClient = useQueryClient ( ) ;
21
23
22
24
// Fetch all the view metadata, panel results and the column definitions
23
25
// NOTE: This doesn't fetch the table rows.
24
- // Use currentGlobalFilters in the query key so it updates when filters change
25
26
const {
26
27
data : viewResult ,
27
28
isLoading,
29
+ isFetching,
28
30
error : viewDataError
29
31
} = useQuery ( {
30
- queryKey : [ "view-result" , id , currentGlobalFilters ] ,
32
+ queryKey : [ "view-result" , id , currentViewVariables ] ,
31
33
queryFn : ( ) => {
32
- console . log ( "useQuery running with filters:" , currentGlobalFilters ) ;
33
- return getViewDataById ( id , currentGlobalFilters ) ;
34
+ return getViewDataById ( id , currentViewVariables ) ;
34
35
} ,
35
36
enabled : ! ! id ,
36
- staleTime : 5 * 60 * 1000
37
+ staleTime : 5 * 60 * 1000 ,
38
+ placeholderData : ( previousData : any ) => previousData
37
39
} ) ;
38
40
39
41
useEffect ( ( ) => {
@@ -48,7 +50,36 @@ const SingleView: React.FC<SingleViewProps> = ({ id }) => {
48
50
setError ( undefined ) ;
49
51
} , [ viewDataError ] ) ;
50
52
51
- if ( isLoading ) {
53
+ // Initialize filters when view data loads, but preserve user selections
54
+ useEffect ( ( ) => {
55
+ if ( viewResult ?. variables && viewResult . variables . length > 0 ) {
56
+ if ( ! hasFiltersInitialized ) {
57
+ // First time - initialize with defaults
58
+ const initial : Record < string , string > = { } ;
59
+ viewResult . variables . forEach ( ( filter : ViewVariable ) => {
60
+ const defaultValue =
61
+ filter . default ||
62
+ ( filter . options . length > 0 ? filter . options [ 0 ] : "" ) ;
63
+ if ( defaultValue ) {
64
+ initial [ filter . key ] = defaultValue ;
65
+ }
66
+ } ) ;
67
+ setCurrentViewVariables ( initial ) ;
68
+ setHasFiltersInitialized ( true ) ;
69
+ }
70
+ }
71
+ } , [ viewResult ?. variables , hasFiltersInitialized ] ) ;
72
+
73
+ // Handle global filter changes with useCallback to stabilize reference
74
+ const handleGlobalFilterChange = useCallback (
75
+ ( newFilters : Record < string , string > ) => {
76
+ setCurrentViewVariables ( newFilters ) ;
77
+ } ,
78
+ [ ]
79
+ ) ;
80
+
81
+ // Only show full loading screen for initial load, not for filter refetches
82
+ if ( isLoading && ! viewResult ) {
52
83
return (
53
84
< div className = "flex min-h-screen items-center justify-center" >
54
85
< div className = "text-center" >
@@ -60,8 +91,9 @@ const SingleView: React.FC<SingleViewProps> = ({ id }) => {
60
91
}
61
92
62
93
if ( ! viewResult ) {
63
- // FIXME: No view result does not mean the view is not found.
64
- // we need to display the error in here.
94
+ // TODO: Better error handling.
95
+ // viewResult = undefined does not mean the view is not found.
96
+ // There could be errors other than 404.
65
97
return (
66
98
< div className = "flex min-h-screen items-center justify-center" >
67
99
< div className = "text-center" >
@@ -89,11 +121,11 @@ const SingleView: React.FC<SingleViewProps> = ({ id }) => {
89
121
90
122
const handleForceRefresh = async ( ) => {
91
123
if ( namespace && name ) {
92
- const freshData = await getViewDataById ( id , currentGlobalFilters , {
124
+ const freshData = await getViewDataById ( id , currentViewVariables , {
93
125
"cache-control" : "max-age=1"
94
126
} ) ;
95
127
queryClient . setQueryData (
96
- [ "view-result" , id , currentGlobalFilters ] ,
128
+ [ "view-result" , id , currentViewVariables ] ,
97
129
freshData
98
130
) ;
99
131
// Invalidate the table query that will be handled by the View component
@@ -119,7 +151,7 @@ const SingleView: React.FC<SingleViewProps> = ({ id }) => {
119
151
}
120
152
onRefresh = { handleForceRefresh }
121
153
contentClass = "p-0 h-full"
122
- loading = { isLoading }
154
+ loading = { isFetching }
123
155
extra = {
124
156
viewResult ?. lastRefreshedAt && (
125
157
< p className = "text-sm text-gray-500" >
@@ -138,10 +170,9 @@ const SingleView: React.FC<SingleViewProps> = ({ id }) => {
138
170
columnOptions = { viewResult ?. columnOptions }
139
171
panels = { viewResult ?. panels }
140
172
variables = { viewResult ?. variables }
141
- viewId = { id }
142
- onGlobalFilterStateChange = { setCurrentGlobalFilters }
173
+ onVariableStateChange = { handleGlobalFilterChange }
143
174
viewResult = { viewResult }
144
- currentGlobalFilters = { currentGlobalFilters }
175
+ currentVariables = { currentViewVariables }
145
176
/>
146
177
</ div >
147
178
</ SearchLayout >
0 commit comments