Skip to content

Commit 82e67fe

Browse files
Update Admin > Statistics views data fetching and error handling (#9416)
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 <Row> in taskRows had no key → added key={project} - Missing key on fragments: the <> {row} <Divider/> </> wrapper in the map had no key → replaced with <Fragment key={index}> - 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<string>) → 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](../blob/master/docs) if applicable - [ ] Adapted [wk-libs python client](https://github.com/scalableminds/webknossos-libs/tree/master/webknossos/webknossos/client) if relevant API parts change - [ ] Removed dev-only changes like prints and application.conf edits - [ ] Considered [common edge cases](../blob/master/.github/common_edge_cases.md) - [ ] Needs datastore update after deployment --------- Co-authored-by: Philipp Otto <philippotto@users.noreply.github.com>
1 parent 6a7f8f1 commit 82e67fe

File tree

6 files changed

+295
-297
lines changed

6 files changed

+295
-297
lines changed

frontend/javascripts/admin/statistic/available_tasks_report_view.tsx

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,26 @@ import AdminPage from "admin/admin_page";
33
import { getAvailableTasksReport } from "admin/rest_api";
44
import { Spin, Table, Tag, Tooltip } from "antd";
55
import TeamSelectionComponent from "dashboard/dataset/team_selection_component";
6-
import { handleGenericError } from "libs/error_handling";
6+
import { useQueryWithErrorHandling } from "libs/react_hooks";
77
import { compareBy, localeCompareBy } from "libs/utils";
88
import { useState } from "react";
99
import type { APIAvailableTasksReport } from "types/api_types";
1010

1111
const { Column } = Table;
1212

1313
/*
14-
* Note that the phrasing available tasks is chosen here over pending to
14+
* Note that the phrasing "available" tasks is chosen here over "pending" to
1515
* emphasize that tasks are still available for individual users.
1616
* From the project viewpoint they are tasks with pending instances.
1717
*/
1818
function AvailableTasksReportView() {
19-
const [data, setData] = useState<APIAvailableTasksReport[]>([]);
20-
const [isLoading, setIsLoading] = useState(false);
19+
const [selectedTeamId, setSelectedTeamId] = useState<string | null>(null);
2120

22-
async function fetchData(teamId: string | null | undefined) {
23-
if (teamId == null) {
24-
setData([]);
25-
} else {
26-
try {
27-
setIsLoading(true);
28-
const progressData = await getAvailableTasksReport(teamId);
29-
setData(progressData);
30-
} catch (error) {
31-
handleGenericError(error as Error);
32-
} finally {
33-
setIsLoading(false);
34-
}
35-
}
36-
}
21+
const { data = [], isLoading } = useQueryWithErrorHandling({
22+
queryKey: ["availableTasksReport", selectedTeamId],
23+
enabled: selectedTeamId != null,
24+
queryFn: () => getAvailableTasksReport(selectedTeamId!),
25+
});
3726

3827
return (
3928
<AdminPage
@@ -47,7 +36,7 @@ function AvailableTasksReportView() {
4736
<TeamSelectionComponent
4837
onChange={(selectedTeam) => {
4938
if (!Array.isArray(selectedTeam) && selectedTeam != null) {
50-
fetchData(selectedTeam.id);
39+
setSelectedTeamId(selectedTeam.id);
5140
}
5241
}}
5342
prefix={<FilterOutlined />}

frontend/javascripts/admin/statistic/project_and_annotation_type_dropdown.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { FilterOutlined } from "@ant-design/icons";
22
import { getProjects } from "admin/rest_api";
33
import { Select } from "antd";
4-
import { useFetch } from "libs/react_helpers";
5-
import { useWkSelector } from "libs/react_hooks";
4+
import { useQueryWithErrorHandling, useWkSelector } from "libs/react_hooks";
65
import { isUserAdminOrTeamManager } from "libs/utils";
76
import type React from "react";
87
import { useEffect, useState } from "react";
@@ -55,17 +54,15 @@ function ProjectAndAnnotationTypeDropdown({
5554
}: ProjectAndTypeDropdownProps) {
5655
// This state property is derived from selectedProjectIds and selectedAnnotationType.
5756
// It is mainly used to determine the selected items in the multiselect form item.
58-
const [selectedFilters, setSelectedFilters] = useState(Array<string>);
57+
const [selectedFilters, setSelectedFilters] = useState<string[]>([]);
5958
const [filterOptions, setFilterOptions] = useState<Array<NestedSelectOptions>>([]);
6059
const activeUser = useWkSelector((state) => state.activeUser);
61-
const allProjects = useFetch(
62-
async () => {
63-
if (activeUser == null || !isUserAdminOrTeamManager(activeUser)) return [];
64-
return await getProjects();
65-
},
66-
[],
67-
[],
68-
);
60+
61+
const { data: allProjects = [] } = useQueryWithErrorHandling({
62+
queryKey: ["projects"],
63+
enabled: activeUser != null && isUserAdminOrTeamManager(activeUser),
64+
queryFn: getProjects,
65+
});
6966

7067
useEffect(() => {
7168
const selectedKeys =
@@ -84,7 +81,10 @@ function ProjectAndAnnotationTypeDropdown({
8481
value: project.id,
8582
};
8683
});
87-
let allOptions = [ANNOTATION_TYPE_FILTERS, ANNOTATION_STATE_FILTERS];
84+
const allOptions: Array<NestedSelectOptions> = [
85+
ANNOTATION_TYPE_FILTERS,
86+
ANNOTATION_STATE_FILTERS,
87+
];
8888
if (projectOptions.length > 0) {
8989
allOptions.push({ label: "Filter projects (only tasks)", options: projectOptions });
9090
}

0 commit comments

Comments
 (0)