Rom89823974978 commited on
Commit
e259887
·
1 Parent(s): 932823b
frontend/src/components/Dashboard.tsx CHANGED
@@ -72,33 +72,28 @@ const Dashboard: React.FC<DashboardProps> = ({
72
  const [orgInput, setOrgInput] = useState("");
73
  const [statsData, setStatsData] = useState<Stats>(initialStats);
74
  const [loadingStats, setLoadingStats] = useState(false);
75
- const fetchTimer = useRef<number | null>(null);
76
 
77
  // Debounced stats & filters fetch
78
  useEffect(() => {
79
- if (fetchTimer.current) clearTimeout(fetchTimer.current);
80
- fetchTimer.current = window.setTimeout(() => {
81
- const qs = new URLSearchParams();
82
- Object.entries(filters).forEach(([key, val]) => {
83
- if (val) qs.set(key, val);
84
- });
85
-
86
- setLoadingStats(true);
87
- // Fetch stats
88
- fetch(`/api/stats?${qs.toString()}`)
89
- .then(res => res.json())
90
- .then((data: Stats) => setStatsData(data))
91
- .catch(console.error)
92
- .finally(() => setLoadingStats(false));
93
-
94
- // Fetch available filters
95
- fetch(`/api/filters?${qs.toString()}`)
96
- .then(res => res.json())
97
- .then((data: AvailableFilters) => setFilters(prev => ({ ...prev, ...{} } as any)))
98
- .catch(console.error);
99
- }, 300);
100
- return () => { if (fetchTimer.current) clearTimeout(fetchTimer.current); };
101
- }, [filters, setFilters]);
102
 
103
  const updateFilter = (key: keyof FilterState) =>
104
  (opt: { value: string } | null) =>
 
72
  const [orgInput, setOrgInput] = useState("");
73
  const [statsData, setStatsData] = useState<Stats>(initialStats);
74
  const [loadingStats, setLoadingStats] = useState(false);
 
75
 
76
  // Debounced stats & filters fetch
77
  useEffect(() => {
78
+ const qs = new URLSearchParams();
79
+ Object.entries(filters).forEach(([key, val]) => {
80
+ if (val) qs.set(key, val);
81
+ });
82
+
83
+ setLoadingStats(true);
84
+ // Fetch stats
85
+ fetch(`/api/stats?${qs.toString()}`)
86
+ .then(res => res.json())
87
+ .then((data: Stats) => setStatsData(data))
88
+ .catch(console.error)
89
+ .finally(() => setLoadingStats(false));
90
+
91
+ // Fetch available filters
92
+ fetch(`/api/filters?${qs.toString()}`)
93
+ .then(res => res.json())
94
+ .then((data: AvailableFilters) => setFilters(prev => ({ ...prev, ...{} } as any)))
95
+ .catch(console.error);
96
+ }, [filters]);
 
 
 
 
97
 
98
  const updateFilter = (key: keyof FilterState) =>
99
  (opt: { value: string } | null) =>
frontend/src/components/ProjectExplorer.tsx CHANGED
@@ -77,7 +77,7 @@ const ProjectExplorer: React.FC<ProjectExplorerProps> = ({
77
  return (
78
  <Flex direction={{ base: "column", md: "row" }} gap={6}>
79
  {/* Left Pane: Projects & Filters */}
80
- <Box flex={{ base: "none", md: "0 0 600px" }} width={{ base: "100%", md: "600px" }}>
81
  <Heading size="sm" mb={2}>Projects</Heading>
82
  <Flex gap={4} mb={4} flexWrap="wrap">
83
  <Input
@@ -91,7 +91,7 @@ const ProjectExplorer: React.FC<ProjectExplorerProps> = ({
91
  value={statusFilter}
92
  onChange={(e) => { setStatusFilter(e.target.value); setPage(0); }}
93
  isDisabled={loadingFilters}
94
- width="150px"
95
  >
96
  {filterOpts.statuses.map((s) => <option key={s} value={s}>{s}</option>)}
97
  </ChakraSelect>
@@ -100,7 +100,7 @@ const ProjectExplorer: React.FC<ProjectExplorerProps> = ({
100
  value={legalFilter}
101
  onChange={(e) => { setLegalFilter(e.target.value); setPage(0); }}
102
  isDisabled={loadingFilters}
103
- width="150px"
104
  >
105
  {filterOpts.legalBases.map((lb) => <option key={lb} value={lb}>{lb}</option>)}
106
  </ChakraSelect>
@@ -109,7 +109,7 @@ const ProjectExplorer: React.FC<ProjectExplorerProps> = ({
109
  value={orgFilter}
110
  onChange={(e) => { setOrgFilter(e.target.value); setPage(0); }}
111
  isDisabled={loadingFilters}
112
- width="180px"
113
  >
114
  {filterOpts.organizations.map((o) => <option key={o} value={o}>{o}</option>)}
115
  </ChakraSelect>
@@ -118,7 +118,7 @@ const ProjectExplorer: React.FC<ProjectExplorerProps> = ({
118
  value={countryFilter}
119
  onChange={(e) => { setCountryFilter(e.target.value); setPage(0); }}
120
  isDisabled={loadingFilters}
121
- width="150px"
122
  >
123
  {filterOpts.countries.map((c) => <option key={c} value={c}>{c}</option>)}
124
  </ChakraSelect>
 
77
  return (
78
  <Flex direction={{ base: "column", md: "row" }} gap={6}>
79
  {/* Left Pane: Projects & Filters */}
80
+ <Box flex={{ base: "none", md: "0 0 600px" }} width={{ base: "100%", md: "900px" }}>
81
  <Heading size="sm" mb={2}>Projects</Heading>
82
  <Flex gap={4} mb={4} flexWrap="wrap">
83
  <Input
 
91
  value={statusFilter}
92
  onChange={(e) => { setStatusFilter(e.target.value); setPage(0); }}
93
  isDisabled={loadingFilters}
94
+ width="120px"
95
  >
96
  {filterOpts.statuses.map((s) => <option key={s} value={s}>{s}</option>)}
97
  </ChakraSelect>
 
100
  value={legalFilter}
101
  onChange={(e) => { setLegalFilter(e.target.value); setPage(0); }}
102
  isDisabled={loadingFilters}
103
+ width="120px"
104
  >
105
  {filterOpts.legalBases.map((lb) => <option key={lb} value={lb}>{lb}</option>)}
106
  </ChakraSelect>
 
109
  value={orgFilter}
110
  onChange={(e) => { setOrgFilter(e.target.value); setPage(0); }}
111
  isDisabled={loadingFilters}
112
+ width="150px"
113
  >
114
  {filterOpts.organizations.map((o) => <option key={o} value={o}>{o}</option>)}
115
  </ChakraSelect>
 
118
  value={countryFilter}
119
  onChange={(e) => { setCountryFilter(e.target.value); setPage(0); }}
120
  isDisabled={loadingFilters}
121
+ width="120px"
122
  >
123
  {filterOpts.countries.map((c) => <option key={c} value={c}>{c}</option>)}
124
  </ChakraSelect>
frontend/src/hooks/useAppState.ts CHANGED
@@ -49,13 +49,13 @@ export const useAppState = () => {
49
  .catch(console.error);
50
  };
51
 
52
- const fetchStats = debounce((filters: FilterState) => {
53
  const params = new URLSearchParams(filters);
54
  fetch(`/api/stats?${params.toString()}`)
55
  .then(res => res.json())
56
  .then((data: Stats) => setStats(data))
57
  .catch(console.error);
58
- }, 500);
59
 
60
  const fetchAvailableFilters = (filters: FilterState) => {
61
  const params = new URLSearchParams(filters);
 
49
  .catch(console.error);
50
  };
51
 
52
+ const fetchStats = (filters: FilterState) => {
53
  const params = new URLSearchParams(filters);
54
  fetch(`/api/stats?${params.toString()}`)
55
  .then(res => res.json())
56
  .then((data: Stats) => setStats(data))
57
  .catch(console.error);
58
+ };
59
 
60
  const fetchAvailableFilters = (filters: FilterState) => {
61
  const params = new URLSearchParams(filters);