Skip to content

Commit 028b289

Browse files
committed
Merge branch 'r/19.x' into develop
2 parents ec94c9b + 6bc01bd commit 028b289

9 files changed

Lines changed: 71 additions & 69 deletions

File tree

src/components/shared/DropDown.tsx

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
dropDownStyle,
66
} from "../../utils/componentStyles";
77
import { GroupBase, MenuListProps, Props, SelectInstance } from "react-select";
8-
import { isJson } from "../../utils/utils";
98
import { ParseKeys } from "i18next";
109
import { List, RowComponentProps } from "react-window";
1110
import AsyncSelect from "react-select/async";
@@ -102,29 +101,23 @@ const DropDown = <T, >({
102101
unformattedOptions.push({
103102
value: "",
104103
label: `-- ${t("SELECT_NO_OPTION_SELECTED")} --`,
104+
order: 0,
105105
});
106106
}
107107

108108
// Sort
109109
/**
110-
* This is used to determine whether any entry of the passed `unformattedOptions`
110+
* This is used to determine whether every entry of the passed `unformattedOptions`
111111
* contains an `order` field, indicating that a custom ordering for that list
112112
* exists and the list therefore should not be ordered alphabetically.
113113
*/
114114
const hasCustomOrder = unformattedOptions.every(item => {
115-
if (!isJson(item.label)) {
116-
return false;
117-
}
118-
// TODO: Handle JSON parsing errors
119-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
120-
const parsed = JSON.parse(item.label);
121-
return parsed && typeof parsed === "object" && "order" in parsed;
115+
return item.order !== undefined;
122116
});
123117

124118
if (hasCustomOrder) {
125119
// Apply custom ordering.
126-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
127-
unformattedOptions.sort((a, b) => JSON.parse(a.label).order - JSON.parse(b.label).order);
120+
unformattedOptions.sort((a, b) => a.order! - b.order!);
128121
} else {
129122
// Apply alphabetical ordering.
130123
unformattedOptions.sort((a, b) => a.label.localeCompare(b.label));

src/components/shared/Table.tsx

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
selectRowIds,
2424
selectRowById,
2525
rowsSelectors,
26+
resetTableProperties,
2627
} from "../../slices/tableSlice";
2728
import {
2829
changeAllSelected,
@@ -35,12 +36,14 @@ import cn from "classnames";
3536
import EditTableViewModal from "../shared/EditTableViewModal";
3637

3738
import Notifications from "./Notifications";
38-
import { useAppDispatch, useAppSelector } from "../../store";
39+
import { AppThunk, useAppDispatch, useAppSelector } from "../../store";
3940
import { TableColumn } from "../../configs/tableConfigs/aclsTableConfig";
4041
import ButtonLikeAnchor from "./ButtonLikeAnchor";
4142
import { ModalHandle } from "./modals/Modal";
4243
import { ParseKeys } from "i18next";
4344
import { LuChevronDown, LuChevronLeft, LuChevronRight, LuChevronUp } from "react-icons/lu";
45+
import { AsyncThunk } from "@reduxjs/toolkit";
46+
import { useLocation } from "react-router";
4447

4548
const containerPageSize = React.createRef<HTMLDivElement>();
4649

@@ -53,15 +56,49 @@ export type TemplateMap = {
5356
*/
5457
const Table = ({
5558
templateMap,
59+
fetchResource,
60+
loadResourceIntoTable,
5661
}: {
5762
templateMap: TemplateMap
63+
fetchResource: AsyncThunk<any, void, any>,
64+
loadResourceIntoTable: () => AppThunk,
5865
}) => {
5966
const { t } = useTranslation();
6067
const dispatch = useAppDispatch();
68+
const location = useLocation();
6169

6270
const editTableViewModalRef = useRef<ModalHandle>(null);
6371
const selectAllCheckboxRef = useRef<HTMLInputElement>(null);
6472

73+
useEffect(() => {
74+
// State variable for interrupting the load function
75+
let allowLoadIntoTable = true;
76+
77+
// Clear table of previous data
78+
dispatch(resetTableProperties());
79+
80+
// Load resource on mount
81+
const loadResource = async () => {
82+
// Fetching resources from server
83+
await dispatch(fetchResource());
84+
85+
// Load resources into table
86+
if (allowLoadIntoTable) {
87+
dispatch(loadResourceIntoTable());
88+
}
89+
};
90+
loadResource();
91+
92+
// Fetch resources every minute
93+
const fetchResourceInterval = setInterval(loadResource, 5000);
94+
95+
return () => {
96+
allowLoadIntoTable = false;
97+
clearInterval(fetchResourceInterval);
98+
};
99+
// eslint-disable-next-line react-hooks/exhaustive-deps
100+
}, [location.hash]);
101+
65102
const forceDeselectAll = () => {
66103
dispatch(changeAllSelected(false));
67104
if (selectAllCheckboxRef.current?.checked) {
@@ -187,6 +224,8 @@ const TableHeadRows = ({ forceDeselectAll }: { forceDeselectAll: () => unknown }
187224
let direction: ReverseOptions = "ASC";
188225
if (reverse && reverse === "ASC") {
189226
direction = "DESC";
227+
} else if (reverse && reverse === "DESC") {
228+
direction = "NONE";
190229
}
191230
dispatch(reverseTable(direction));
192231
dispatch(updatePages());
@@ -199,7 +238,7 @@ const TableHeadRows = ({ forceDeselectAll }: { forceDeselectAll: () => unknown }
199238
<th
200239
key={key}
201240
className={cn({
202-
"col-sort": !!sortBy && column.name === sortBy,
241+
"col-sort": reverse !== "NONE" && !!sortBy && column.name === sortBy,
203242
sortable: true,
204243
})}
205244
onClick={() => sortByColumn(column.name)}

src/components/shared/TableFilters.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
FilterData,
1010
editFilterValue,
1111
editTextFilter,
12+
fetchFilters,
1213
removeTextFilter,
1314
resetFilterValues,
1415
} from "../../slices/tableFilterSlice";
@@ -30,6 +31,7 @@ import SearchContainer from "./SearchContainer";
3031
import { Resource } from "../../slices/tableSlice";
3132
import { HiFunnel } from "react-icons/hi2";
3233
import { LuSettings, LuX } from "react-icons/lu";
34+
import { useLocation } from "react-router";
3335

3436
/**
3537
* This component renders the table filters in the upper right corner of the table
@@ -45,6 +47,7 @@ const TableFilters = ({
4547
}) => {
4648
const { t } = useTranslation();
4749
const dispatch = useAppDispatch();
50+
const location = useLocation();
4851

4952
const filterMap = useAppSelector(state => getFilters(state, resource));
5053
const [selectedFilter, setSelectedFilter] = useState("");
@@ -63,6 +66,11 @@ const TableFilters = ({
6366

6467
const filter = filterMap.find(({ name }) => name === selectedFilter);
6568

69+
useEffect(() => {
70+
dispatch(fetchFilters(resource));
71+
// eslint-disable-next-line react-hooks/exhaustive-deps
72+
}, [location.hash]);
73+
6674
// Remove all selected filters, no filter should be "active" anymore
6775
const removeFilters = async () => {
6876
// Clear state
@@ -140,6 +148,7 @@ const TableFilters = ({
140148
// simply by going to first page and then load resources.
141149
// This helps increase performance by reducing the number of calls to load resources.
142150
const applyFilterChangesDebounced = async () => {
151+
console.log("Applying filter changes with value: " + itemValue);
143152
// No matter what, we go to page one.
144153
dispatch(goToPage(0));
145154
// Reload of resource
@@ -149,8 +158,8 @@ const TableFilters = ({
149158

150159
useEffect(() => {
151160
if (itemValue) {
152-
// Call to apply filter changes with 500MS debounce!
153-
const applyFilterChangesDebouncedTimeoutId = setTimeout(applyFilterChangesDebounced, 500);
161+
// Call to apply filter changes with 600MS debounce!
162+
const applyFilterChangesDebouncedTimeoutId = setTimeout(applyFilterChangesDebounced, 600);
154163

155164
return () => clearTimeout(applyFilterChangesDebouncedTimeoutId);
156165
}

src/components/shared/TablePage.tsx

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import { ReactNode, useEffect } from "react";
1+
import { ReactNode } from "react";
22
import { useTranslation } from "react-i18next";
33
import TableFilters from "../shared/TableFilters";
44
import Table, { TemplateMap } from "../shared/Table";
55
import Notifications from "../shared/Notifications";
6-
import { fetchFilters } from "../../slices/tableFilterSlice";
76
import { CreateType, NavBarLink } from "../NavBar";
8-
import { AppThunk, RootState, useAppDispatch, useAppSelector } from "../../store";
9-
import { resetTableProperties, Resource } from "../../slices/tableSlice";
7+
import { AppThunk, RootState, useAppSelector } from "../../store";
8+
import { Resource } from "../../slices/tableSlice";
109
import { AsyncThunk } from "@reduxjs/toolkit";
1110
import { ParseKeys } from "i18next";
12-
import { useLocation } from "react-router";
1311
import MainPage from "./MainPage";
1412

1513
/**
@@ -39,43 +37,9 @@ const TablePage = ({
3937
children?: ReactNode
4038
}) => {
4139
const { t } = useTranslation();
42-
const dispatch = useAppDispatch();
43-
44-
const location = useLocation();
4540

4641
const numberOfRows = useAppSelector(state => getTotalResources(state));
4742

48-
useEffect(() => {
49-
// State variable for interrupting the load function
50-
let allowLoadIntoTable = true;
51-
52-
// Clear table of previous data
53-
dispatch(resetTableProperties());
54-
55-
dispatch(fetchFilters(resource));
56-
57-
// Load resource on mount
58-
const loadResource = async () => {
59-
// Fetching resources from server
60-
await dispatch(fetchResource());
61-
62-
// Load resources into table
63-
if (allowLoadIntoTable) {
64-
dispatch(loadResourceIntoTable());
65-
}
66-
};
67-
loadResource();
68-
69-
// Fetch resources every minute
70-
const fetchResourceInterval = setInterval(loadResource, 5000);
71-
72-
return () => {
73-
allowLoadIntoTable = false;
74-
clearInterval(fetchResourceInterval);
75-
};
76-
// eslint-disable-next-line react-hooks/exhaustive-deps
77-
}, [location.hash]);
78-
7943
return (
8044
<MainPage
8145
navBarLinks={navBarLinks}
@@ -100,7 +64,11 @@ const TablePage = ({
10064
<h4>{t("TABLE_SUMMARY", { numberOfRows })}</h4>
10165
</div>
10266
{/* Include table component */}
103-
<Table templateMap={templateMap} />
67+
<Table
68+
templateMap={templateMap}
69+
fetchResource={fetchResource}
70+
loadResourceIntoTable={loadResourceIntoTable}
71+
/>
10472
</MainPage>
10573
);
10674
};
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useEffect } from "react";
2-
import { useAppDispatch, useAppSelector } from "../store";
2+
import store, { useAppDispatch } from "../store";
33
import { resetCorruptedState } from "../slices/tableFilterSlice";
44

55
/**
@@ -9,10 +9,11 @@ import { resetCorruptedState } from "../slices/tableFilterSlice";
99
*/
1010
export const useTableFilterStateValidation = () => {
1111
const dispatch = useAppDispatch();
12-
const tableFilters = useAppSelector(state => state.tableFilters);
1312

1413
useEffect(() => {
1514
// Check for corrupted state and dispatch reset action if needed
15+
const tableFilters = store.getState().tableFilters;
16+
1617
const hasCorruption =
1718
!Array.isArray(tableFilters.data) ||
1819
!Array.isArray(tableFilters.textFilter) ||
@@ -22,6 +23,5 @@ export const useTableFilterStateValidation = () => {
2223
console.warn("Detected corrupted table filter state, resetting to defaults");
2324
dispatch(resetCorruptedState());
2425
}
25-
// eslint-disable-next-line react-hooks/exhaustive-deps
26-
}, []);
26+
}, [dispatch]);
2727
};

src/i18n/org/opencastproject/adminui/languages/lang-en_US.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1064,7 +1064,7 @@
10641064
"DESCRIPTION": "Description",
10651065
"EXECUTION_HOST": "Execution Host",
10661066
"JOB": "Job",
1067-
"TIME_IN_QUEUE": "Time in Queue",
1067+
"TIME_IN_QUEUE": "Total subjobs time in queue",
10681068
"STARTED": "Started",
10691069
"FINISHED": "Finished",
10701070
"RETRY_STRATEGY": "Retry Strategy",

src/slices/eventSlice.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,6 @@ export const fetchEvents = createAppAsyncThunk("events/fetchEvents", async (_, {
253253
const state = getState();
254254
let params: ReturnType<typeof getURLParams> & { getComments?: boolean } = getURLParams(state, "events");
255255

256-
// Add a secondary filter to enforce order of events
257-
// (Elasticsearch does not guarantee ordering)
258-
params = {
259-
...params,
260-
sort: params.sort ? params.sort + ",uid:asc" : "uid:asc",
261-
};
262-
263256
// Only if the notes column is enabled, fetch comment information for events
264257
if (state.events.columns.find(column => column.label === "EVENTS.EVENTS.TABLE.ADMINUI_NOTES" && !column.deactivated)) {
265258
params = {

src/slices/tableSlice.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export type SubmitRow = {
8989

9090
export type Resource = "events" | "series" | "recordings" | "jobs" | "servers" | "services" | "users" | "groups" | "acls" | "themes"
9191

92-
export type ReverseOptions = "ASC" | "DESC"
92+
export type ReverseOptions = "ASC" | "DESC" | "NONE"
9393

9494
export type TableState = {
9595
status: "uninitialized" | "loading" | "succeeded" | "failed",

src/utils/resourceUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export const getURLParams = (
7171
};
7272
}
7373

74-
if (getTableSortingForResource(state, resource)) {
74+
if (getTableDirectionForResource(state, resource) !== "NONE") {
7575
params = {
7676
...params,
7777
sort: getTableSortingForResource(state, resource)

0 commit comments

Comments
 (0)