Skip to content

Update Admin > Statistics views data fetching and error handling#9416

Open
hotzenklotz wants to merge 2 commits intoupdate-admin-page-designfrom
admin-views-use-query
Open

Update Admin > Statistics views data fetching and error handling#9416
hotzenklotz wants to merge 2 commits intoupdate-admin-page-designfrom
admin-views-use-query

Conversation

@hotzenklotz
Copy link
Copy Markdown
Member

I updated the Admin > Statistics views slightly:

  • unified data fetching with useQuery
  • unified error handling for fetching with a new hook useQueryWithErrorHandling
  • fixed several code smells:
time_tracking_overview.tsx:
  • Bug: filteredTimeEntries?.length === null is always false (array length is never null)
    → fixed to filteredTimeEntries.length === 0
  • Typo: setEndeDate → setEndDate
  • Race condition: both useFetch calls shared the same isFetching state — one could clear
    it while the other was still loading. Now each query has its own isLoading/isFetching
    flag, combined as const isFetching = isTeamsLoading || isEntriesFetching
time_tracking_detail_view.tsx:
  • Missing key prop: project header in taskRows had no key → added key={project}
  • Missing key on fragments: the <> {row} </> wrapper in the map had no key →
    replaced with
  • Destructured props at component signature instead of accessing via props.x
project_and_annotation_type_dropdown.tsx:
  • let allOptions = [...] → const allOptions (it's only mutated via .push(), never
    reassigned)
  • useState(Array) → useState<string[]>([]) (the former uses Array as a lazy
    initializer function — valid but highly unusual and non-obvious)

URL of deployed dev instance (used for testing):

  • https://___.webknossos.xyz

Steps to test:

  • abc

Issues:

  • fixes #

(Please delete unneeded items, merge only when none are left open)

  • Added changelog entry (create a $PR_NUMBER.md file in unreleased_changes or use ./tools/create-changelog-entry.py)
  • Added migration guide entry if applicable (edit the same file as for the changelog)
  • Updated documentation if applicable
  • Adapted wk-libs python client if relevant API parts change
  • Removed dev-only changes like prints and application.conf edits
  • Considered common edge cases
  • Needs datastore update after deployment

@hotzenklotz hotzenklotz self-assigned this Mar 24, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 24, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 547ce0b4-cc74-4d6b-b4f4-988c06e2ea0f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduced a new custom hook useQueryWithErrorHandling to centralize data fetching and error handling, then refactored five admin statistics components to use this hook instead of manual state management with useFetch or useEffect, improving consistency and maintainability across the reporting UI.

Changes

Cohort / File(s) Summary
New Query Hook
frontend/javascripts/libs/react_hooks.ts
Added generic useQueryWithErrorHandling hook that wraps TanStack useQuery with automatic error handling via handleGenericError, accepting optional fallbackMessage parameter.
Report & View Components
frontend/javascripts/admin/statistic/available_tasks_report_view.tsx, project_progress_report_view.tsx, time_tracking_detail_view.tsx, time_tracking_overview.tsx
Replaced manual state management and useEffect-based fetching with useQueryWithErrorHandling, updated lifecycle handling (auto-refresh, last updated timestamps), removed explicit error toasts and branching control flow, corrected state setter typo in overview component.
Dropdown Component
frontend/javascripts/admin/statistic/project_and_annotation_type_dropdown.tsx
Switched data fetching from useFetch to useQueryWithErrorHandling with conditional enabled logic, tightened type annotations for selectedFilters and allOptions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • knollengewaechs
  • philippotto
  • MichaelBuessemeyer

Poem

🐰 A hook so neat, a pattern so grand,
Five components now hand-in-hand,
No more tangled fetches of yore—
useQueryWithErrorHandling at the door! 🎉
Errors handled, states aligned,
Better data flows we shall find! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: updating Admin Statistics views to use a unified data fetching and error handling approach.
Description check ✅ Passed The description provides detailed context about the changes, including the new useQueryWithErrorHandling hook, specific bug fixes, and code quality improvements across multiple files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch admin-views-use-query

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hotzenklotz
Copy link
Copy Markdown
Member Author

@Reviewer: Make sure to hide whitespace:
Screenshot 2026-03-24 at 14 59 05

@hotzenklotz hotzenklotz changed the title add useQuery to admin statistics views Update Admin > Statistics views data fetching and error handling Mar 24, 2026
@hotzenklotz
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 24, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/javascripts/admin/statistic/available_tasks_report_view.tsx`:
- Around line 37-40: The onChange handler for the team selector currently
ignores the null case and leaves stale state; update the handler that calls
setSelectedTeamId so that when selectedTeam is null (filter cleared) you call
setSelectedTeamId(null) (or an appropriate empty value) instead of doing
nothing, otherwise keep setting setSelectedTeamId(selectedTeam.id) when a single
team object is provided; adjust the existing Array.isArray(selectedTeam) check
to branch: array -> ignore/handle arrays, null -> reset selectedTeamId, else ->
set selectedTeam id.

In `@frontend/javascripts/admin/statistic/project_progress_report_view.tsx`:
- Around line 76-79: The onChange handler for the team selector currently
ignores the null branch so clearing the filter doesn't update state; update the
handler in project_progress_report_view.tsx so that when selectedTeam is null
you explicitly call handleTeamChange(null) (while keeping the Array.isArray
guard for multi-select values) to clear the team filter and stop the previous
report from continuing to poll/render.
- Around line 21-35: The query is created with queryFn: () =>
getProjectProgressReport(team!.id) and enabled: team != null, but calling
refetch() (e.g., from the Refresh button handler) still invokes queryFn even
when team is null, causing a crash; update the Refresh handler(s) that call
refetch (and any other places using refetch with the same
useQueryWithErrorHandling pattern) to first check team != null before calling
refetch, or guard by using if (!team) return; so queryFn
(getProjectProgressReport) is never invoked with team! when no team is selected.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fecbdb04-3393-4d03-b68f-7711ede1e5ac

📥 Commits

Reviewing files that changed from the base of the PR and between 22263cf and 443e9db.

📒 Files selected for processing (6)
  • frontend/javascripts/admin/statistic/available_tasks_report_view.tsx
  • frontend/javascripts/admin/statistic/project_and_annotation_type_dropdown.tsx
  • frontend/javascripts/admin/statistic/project_progress_report_view.tsx
  • frontend/javascripts/admin/statistic/time_tracking_detail_view.tsx
  • frontend/javascripts/admin/statistic/time_tracking_overview.tsx
  • frontend/javascripts/libs/react_hooks.ts

Comment on lines 37 to 40
onChange={(selectedTeam) => {
if (!Array.isArray(selectedTeam) && selectedTeam != null) {
fetchData(selectedTeam.id);
setSelectedTeamId(selectedTeam.id);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Reset selectedTeamId when the filter is cleared.

This handler ignores null, so clearing the selector leaves the previous team id in state and the table keeps showing stale report data.

💡 Proposed fix
           <TeamSelectionComponent
             onChange={(selectedTeam) => {
-              if (!Array.isArray(selectedTeam) && selectedTeam != null) {
-                setSelectedTeamId(selectedTeam.id);
-              }
+              if (Array.isArray(selectedTeam) || selectedTeam == null) {
+                setSelectedTeamId(null);
+                return;
+              }
+              setSelectedTeamId(selectedTeam.id);
             }}
             prefix={<FilterOutlined />}
           />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onChange={(selectedTeam) => {
if (!Array.isArray(selectedTeam) && selectedTeam != null) {
fetchData(selectedTeam.id);
setSelectedTeamId(selectedTeam.id);
}
onChange={(selectedTeam) => {
if (Array.isArray(selectedTeam) || selectedTeam == null) {
setSelectedTeamId(null);
return;
}
setSelectedTeamId(selectedTeam.id);
}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/admin/statistic/available_tasks_report_view.tsx` around
lines 37 - 40, The onChange handler for the team selector currently ignores the
null case and leaves stale state; update the handler that calls
setSelectedTeamId so that when selectedTeam is null (filter cleared) you call
setSelectedTeamId(null) (or an appropriate empty value) instead of doing
nothing, otherwise keep setting setSelectedTeamId(selectedTeam.id) when a single
team object is provided; adjust the existing Array.isArray(selectedTeam) check
to branch: array -> ignore/handle arrays, null -> reset selectedTeamId, else ->
set selectedTeam id.

Comment on lines +21 to +35
const {
data = [],
isLoading,
isFetching,
refetch,
dataUpdatedAt,
} = useQueryWithErrorHandling(
{
queryKey: ["projectProgressReport", team?.id],
enabled: team != null,
queryFn: () => getProjectProgressReport(team!.id),
refetchInterval: RELOAD_INTERVAL,
},
messages["project.report.failed_to_refresh"],
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

In TanStack React Query v5, does calling refetch() on a useQuery with enabled: false still execute the queryFn?

💡 Result:

Yes, calling refetch on a useQuery with enabled: false in TanStack React Query v5 still executes the queryFn.

Citations:


Disable Refresh until a team is selected.

Calling refetch() executes the queryFn even when the query is disabled. This will crash when dereferencing team!.id before a team has been selected.

Proposed fix
           <Button
             icon={<ReloadOutlined spin={isFetching} />}
             variant="outlined"
             onClick={() => refetch()}
+            disabled={team == null}
           >
             Refresh
           </Button>

Also applies to: 62-66

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/admin/statistic/project_progress_report_view.tsx` around
lines 21 - 35, The query is created with queryFn: () =>
getProjectProgressReport(team!.id) and enabled: team != null, but calling
refetch() (e.g., from the Refresh button handler) still invokes queryFn even
when team is null, causing a crash; update the Refresh handler(s) that call
refetch (and any other places using refetch with the same
useQueryWithErrorHandling pattern) to first check team != null before calling
refetch, or guard by using if (!team) return; so queryFn
(getProjectProgressReport) is never invoked with team! when no team is selected.

Comment on lines +76 to +79
onChange={(selectedTeam) => {
if (!Array.isArray(selectedTeam) && selectedTeam != null) {
handleTeamChange(selectedTeam);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle clearing the team filter.

This handler drops the null branch as well, so reopening the filter and clearing it keeps the previous team selected and the old report continues to poll/render.

💡 Proposed fix
               onChange={(selectedTeam) => {
-                if (!Array.isArray(selectedTeam) && selectedTeam != null) {
-                  handleTeamChange(selectedTeam);
-                }
+                if (Array.isArray(selectedTeam) || selectedTeam == null) {
+                  setTeam(undefined);
+                  return;
+                }
+                handleTeamChange(selectedTeam);
               }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onChange={(selectedTeam) => {
if (!Array.isArray(selectedTeam) && selectedTeam != null) {
handleTeamChange(selectedTeam);
}
onChange={(selectedTeam) => {
if (Array.isArray(selectedTeam) || selectedTeam == null) {
setTeam(undefined);
return;
}
handleTeamChange(selectedTeam);
}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/admin/statistic/project_progress_report_view.tsx` around
lines 76 - 79, The onChange handler for the team selector currently ignores the
null branch so clearing the filter doesn't update state; update the handler in
project_progress_report_view.tsx so that when selectedTeam is null you
explicitly call handleTeamChange(null) (while keeping the Array.isArray guard
for multi-select values) to clear the team filter and stop the previous report
from continuing to poll/render.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant