-
Notifications
You must be signed in to change notification settings - Fork 0
Implement Analytics Dashboard feature (service, store, hook, UI, docs, tests) #109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,63 @@ | ||||||||||||
| /** | ||||||||||||
| * @fileoverview Analytics dashboard data hook | ||||||||||||
| */ | ||||||||||||
|
|
||||||||||||
| import { useCallback, useEffect } from 'react'; | ||||||||||||
| import { useAuthentication } from '@/hooks/useAuthentication'; | ||||||||||||
| import { | ||||||||||||
| analyticsSelectors, | ||||||||||||
| useAnalyticsDashboardStore, | ||||||||||||
| useAnalyticsDashboardSelectors | ||||||||||||
| } from '../stores/AnalyticsDashboardStore'; | ||||||||||||
| import type { AnalyticsDashboardConfig, AnalyticsFilters } from '../types/feature.types'; | ||||||||||||
|
|
||||||||||||
| export function useAnalyticsDashboard(config?: Partial<AnalyticsDashboardConfig>) { | ||||||||||||
| const { user } = useAuthentication(); | ||||||||||||
| const store = useAnalyticsDashboardStore(); | ||||||||||||
| const selectors = useAnalyticsDashboardSelectors({ | ||||||||||||
| isLoading: analyticsSelectors.isLoading, | ||||||||||||
| hasData: analyticsSelectors.hasData, | ||||||||||||
| filteredTools: analyticsSelectors.filteredTools | ||||||||||||
| }); | ||||||||||||
|
|
||||||||||||
| const { | ||||||||||||
| status, | ||||||||||||
| filters, | ||||||||||||
| initialize, | ||||||||||||
| loadAnalytics, | ||||||||||||
| updateFilters: setFilters, | ||||||||||||
| refresh | ||||||||||||
| } = store; | ||||||||||||
|
|
||||||||||||
| useEffect(() => { | ||||||||||||
| initialize(config); | ||||||||||||
| }, [initialize, config]); | ||||||||||||
|
Comment on lines
+32
to
+34
|
||||||||||||
|
|
||||||||||||
| useEffect(() => { | ||||||||||||
| if (status === 'idle') { | ||||||||||||
| loadAnalytics(user?.id); | ||||||||||||
| } | ||||||||||||
| }, [status, loadAnalytics, user?.id]); | ||||||||||||
|
|
||||||||||||
| useEffect(() => { | ||||||||||||
| if (status === 'ready') { | ||||||||||||
| loadAnalytics(user?.id); | ||||||||||||
| } | ||||||||||||
|
Comment on lines
+42
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The Useful? React with 👍 / 👎. |
||||||||||||
| }, [filters.timeRange, status, loadAnalytics, user?.id]); | ||||||||||||
|
Comment on lines
+36
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The data fetching logic in these The data loading logic should be refactored to separate the initial data load from subsequent loads triggered by filter changes. const isMounted = React.useRef(false);
useEffect(() => {
initialize(config);
loadAnalytics(user?.id);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [initialize, config, user?.id]);
useEffect(() => {
if (isMounted.current) {
loadAnalytics(user?.id);
} else {
isMounted.current = true;
}
}, [filters.timeRange]);
Comment on lines
+42
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cat -n src/features/analytics-dashboard/hooks/useAnalyticsDashboard.tsRepository: Krosebrook/Flashfusionwebsite Length of output: 2046 🏁 Script executed: find . -name "AnalyticsDashboardStore*" -type fRepository: Krosebrook/Flashfusionwebsite Length of output: 141 🏁 Script executed: cat -n ./src/features/analytics-dashboard/stores/AnalyticsDashboardStore.tsRepository: Krosebrook/Flashfusionwebsite Length of output: 6761 🏁 Script executed: python3 << 'EOF'
# Trace the execution flow
print("Initial state:")
print(" status: 'idle'")
print(" filters.timeRange: [initial]")
print("")
print("Step 1: Component mounts")
print(" Effect 1 runs (status === 'idle')")
print(" Calls: loadAnalytics(user?.id)")
print("")
print("Step 2: Inside loadAnalytics")
print(" Checks: if (status === 'loading') return;")
print(" status is 'idle', so continues")
print(" Sets: status = 'loading'")
print(" Starts async fetch")
print("")
print("Step 3: After async fetch completes")
print(" Sets: status = 'ready'")
print(" Component re-renders with new status")
print("")
print("Step 4: Effect 2 dependency check")
print(" Dependencies: [filters.timeRange, status, loadAnalytics, user?.id]")
print(" status changed from 'idle'/'loading' to 'ready'")
print(" Effect 2 runs (status === 'ready')")
print(" Calls: loadAnalytics(user?.id)")
print("")
print("Step 5: Inside loadAnalytics (second call)")
print(" Checks: if (status === 'loading') return;")
print(" status is currently 'ready', so continues")
print(" Sets: status = 'loading'")
print(" Starts async fetch AGAIN")
print("")
print("Step 6: After second async fetch completes")
print(" Sets: status = 'ready'")
print(" Component re-renders")
print("")
print("Step 7: Effect 2 runs AGAIN")
print(" status changed from 'loading' to 'ready'")
print(" Condition: if (status === 'ready') is TRUE")
print(" Calls: loadAnalytics(user?.id) AGAIN")
print("")
print("Conclusion: Loop confirmed!")
print("Each time loadAnalytics completes and sets status='ready',")
print("Effect 2 sees the status change and re-triggers,")
print("calling loadAnalytics again, which sets status='ready',")
print("which triggers Effect 2 again, etc.")
EOFRepository: Krosebrook/Flashfusionwebsite Length of output: 159 Effect re-fetches on every The effect depends on 💡 Suggested fix-import { useCallback, useEffect } from 'react';
+import { useCallback, useEffect, useRef } from 'react';
...
- const {
- status,
- filters,
- initialize,
- loadAnalytics,
- updateFilters: setFilters,
- refresh
- } = store;
+ const {
+ data,
+ status,
+ filters,
+ initialize,
+ loadAnalytics,
+ updateFilters: setFilters,
+ refresh
+ } = store;
+
+ const previousTimeRange = useRef(filters.timeRange);
...
- useEffect(() => {
- if (status === 'ready') {
- loadAnalytics(user?.id);
- }
- }, [filters.timeRange, status, loadAnalytics, user?.id]);
+ useEffect(() => {
+ if (status !== 'ready') return;
+ const timeRangeChanged = previousTimeRange.current !== filters.timeRange;
+ if (!data || timeRangeChanged) {
+ previousTimeRange.current = filters.timeRange;
+ loadAnalytics(user?.id);
+ }
+ }, [data, filters.timeRange, status, loadAnalytics, user?.id]);🤖 Prompt for AI Agents |
||||||||||||
|
|
||||||||||||
|
Comment on lines
+42
to
+47
|
||||||||||||
| useEffect(() => { | |
| if (status === 'ready') { | |
| loadAnalytics(user?.id); | |
| } | |
| }, [filters.timeRange, status, loadAnalytics, user?.id]); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,24 @@ | ||
| /** | ||
| * @fileoverview Analytics Dashboard Feature - Public API | ||
| * @version 1.0.0 | ||
| * | ||
| * Entry point for the Analytics Dashboard feature | ||
| * @version 2.0.0 | ||
| */ | ||
|
|
||
| // Export main component | ||
| export { AnalyticsDashboard, default } from './components/AnalyticsDashboard'; | ||
| export { useAnalyticsDashboard } from './hooks/useAnalyticsDashboard'; | ||
|
|
||
| // Export store | ||
| export { useFeatureStore } from './stores/FeatureStore'; | ||
| export { | ||
| useAnalyticsDashboardStore, | ||
| analyticsSelectors, | ||
| useAnalyticsDashboardSelector, | ||
| useAnalyticsDashboardSelectors | ||
| } from './stores/AnalyticsDashboardStore'; | ||
|
|
||
| // Export service | ||
| export { FeatureService } from './services/FeatureService'; | ||
| export { AnalyticsDashboardService } from './services/AnalyticsDashboardService'; | ||
|
|
||
| // Export types | ||
| export type { | ||
| FeatureData, | ||
| FeatureConfig, | ||
| FeatureResult, | ||
| FeatureError, | ||
| FeatureStatus | ||
| AnalyticsDashboardData, | ||
| AnalyticsDashboardConfig, | ||
| AnalyticsFilters, | ||
| AnalyticsStatus, | ||
| AnalyticsError | ||
| } from './types/feature.types'; |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: Ready-state effect causes infinite reload: status toggles to 'ready' inside loadAnalytics, which is in the effect dependency list, so every successful fetch re-triggers another fetch.
Prompt for AI agents