Skip to content

Commit db1d498

Browse files
committed
feat: Search Bar for the Job Monitor
1 parent e4e2b00 commit db1d498

47 files changed

Lines changed: 2463 additions & 1116 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/user/monitor_jobs.md

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,34 @@
1-
# Managing your jobs
1+
# Job Monitor documentation
22

3-
## Basics
3+
## The search bar
44

5-
###
5+
The search bar allows you to filter jobs based on various criteria. The filters are represented as equations in the search bar, where each equation consists of a job attribute, an operator, and a value.
6+
A resarch is automatically performed when all the equations in the search bar are valid.
67

8+
### Create a filter
9+
To create a filter, click on the search bar and start typing. Suggestions will appear based on the available job attributes. You can select a suggestion to choose the criterion. After that, you can either type or select an operator and a value to filter by.
10+
The search bar only suggests attributes, operators, and values that are available in your current set of jobs.
711

8-
## Advanced
12+
### Edit a filter
13+
To edit a filter, click on the filter in the search bar. You can change the operator or value by clicking on them. You can also use the arrow keys to navigate through the equations and edit them.
14+
15+
### Remove a filter
16+
To remove a filter, you can either press the `Backspace` key to remove the last token or right-click on the equation to remove the entire equation.
17+
18+
19+
## Use the table
20+
The table displays the jobs that match the criteria specified in the search bar. Each row represents a job, and the columns show various attributes of the job, such as its ID, status, type, and submission date.
21+
22+
### Actions on the table
23+
You can click on the eye icon to select more columns to display in the table.
24+
You can sort the table by clicking on the column headers. Clicking on a column header will sort the table in ascending order, and clicking again will sort it in descending order.
25+
You can set the page size by clicking on the `Row per page` dropdown at the bottom of the table. This allows you to control how many jobs are displayed per page.
26+
27+
### Actions on a job
28+
You can do a right-click on a job to open the `Job History`.
29+
You can select one or more jobs by clicking on the checkboxes next to each job. Once you have selected jobs, you can perform actions on them using the buttons at the top of the table. The available actions include:
30+
- **Get IDs**: This button will copy the IDs of the selected jobs to the clipboard.
31+
- **Rechedule**: This button will reschedule the selected jobs.
32+
- **Kill**: This button will kill the selected jobs.
33+
- **Delete**: This button will delete the selected jobs.
934

packages/diracx-web-components/.storybook/main.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ const config: StorybookConfig = {
4242
"../../hooks/metadata": require.resolve(
4343
"../stories/mocks/metadata.mock.tsx",
4444
),
45-
"./JobDataService": require.resolve(
46-
"../stories/mocks/JobDataService.mock.tsx",
45+
"./jobDataService": require.resolve(
46+
"../stories/mocks/jobDataService.mock.ts",
4747
),
4848
};
4949
return config;

packages/diracx-web-components/eslint.config.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ export default [
6464
"react/prop-types": "off",
6565
"react/react-in-jsx-scope": "off",
6666
"react/destructuring-assignment": ["error", "always"],
67+
"@typescript-eslint/no-unused-vars": [
68+
"error",
69+
{
70+
argsIgnorePattern: "^_",
71+
varsIgnorePattern: "^_",
72+
caughtErrorsIgnorePattern: "^_",
73+
ignoreRestSiblings: true,
74+
},
75+
],
6776
"no-restricted-properties": [
6877
"error",
6978
{

packages/diracx-web-components/jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const config = {
1313
moduleNameMapper: {
1414
"^@axa-fr/react-oidc$": "<rootDir>/stories/mocks/react-oidc.mock.tsx",
1515
"^../../hooks/metadata$": "<rootDir>/stories/mocks/metadata.mock.tsx",
16-
"^./JobDataService$": "<rootDir>/stories/mocks/JobDataService.mock.tsx",
16+
"^./jobDataService$": "<rootDir>/stories/mocks/jobDataService.mock.ts",
1717
},
1818
};
1919

packages/diracx-web-components/src/components/JobMonitor/JobDataTable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import {
3535
refreshJobs,
3636
rescheduleJobs,
3737
useJobs,
38-
} from "./JobDataService";
38+
} from "./jobDataService";
3939

4040
/**
4141
* Job Data Table props

packages/diracx-web-components/src/components/JobMonitor/JobMonitor.tsx

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ import {
2020
RowSelectionState,
2121
VisibilityState,
2222
PaginationState,
23+
ColumnDef,
2324
} from "@tanstack/react-table";
2425

2526
import { useApplicationId } from "../../hooks/application";
26-
import { FilterToolbar } from "../shared/FilterToolbar";
27-
import { InternalFilter } from "../../types/Filter";
28-
import { Job, SearchBody } from "../../types";
27+
import { Filter } from "../../types/Filter";
28+
import { Job, SearchBody, CategoryType } from "../../types";
2929
import { JobDataTable } from "./JobDataTable";
30+
import { JobSearchBar } from "./JobSearchBar";
3031

3132
/**
3233
* Build the Job Monitor application
@@ -44,14 +45,14 @@ export default function JobMonitor() {
4445
typeof initialState === "string" ? JSON.parse(initialState) : null;
4546

4647
// State for filters
47-
const [filters, setFilters] = useState<InternalFilter[]>(
48+
const [filters, setFilters] = useState<Filter[]>(
4849
parsedInitialState ? parsedInitialState.filters : [],
4950
);
5051

5152
// State for search body
5253
const [searchBody, setSearchBody] = useState<SearchBody>({
5354
search: parsedInitialState
54-
? parsedInitialState.filters.map((filter: InternalFilter) => ({
55+
? parsedInitialState.filters.map((filter: Filter) => ({
5556
parameter: filter.parameter,
5657
operator: filter.operator,
5758
value: filter.value,
@@ -98,7 +99,7 @@ export default function JobMonitor() {
9899
// Save the state of the app in local storage
99100
useEffect(() => {
100101
const state = {
101-
filters: [...filters.filter((filter) => filter.isApplied)],
102+
filters: [...filters],
102103
columnVisibility: { ...columnVisibility },
103104
columnPinning: {
104105
left: [...(columnPinning.left || [])],
@@ -123,15 +124,10 @@ export default function JobMonitor() {
123124

124125
// Handle the application of filters
125126
const handleApplyFilters = () => {
126-
// Switch the applied state of the filters
127-
setFilters((filters) =>
128-
filters.map((filter) => ({ ...filter, isApplied: true })),
129-
);
130-
131127
setSearchBody((prev) => ({
132128
...prev,
133129
search: filters.map(({ parameter, operator, value, values }) => ({
134-
parameter,
130+
parameter: fromHumanReadableText(parameter, columns),
135131
operator,
136132
value,
137133
values,
@@ -143,18 +139,6 @@ export default function JobMonitor() {
143139
}));
144140
};
145141

146-
const handleRemoveAllFilters = useCallback(() => {
147-
setSearchBody((prevState) => ({
148-
...prevState,
149-
search: [],
150-
}));
151-
setPagination((prevState) => ({
152-
...prevState,
153-
pageIndex: 0,
154-
}));
155-
setFilters([]);
156-
}, [setFilters]);
157-
158142
// Status colors
159143
const statusColors: Record<string, string> = useMemo(
160144
() => ({
@@ -213,13 +197,17 @@ export default function JobMonitor() {
213197
columnHelper.accessor("JobID", {
214198
id: "JobID",
215199
header: "ID",
216-
meta: { type: "number" },
200+
meta: { type: CategoryType.NUMBER, hideSuggestion: true },
217201
}),
218202
columnHelper.accessor("Status", {
219203
id: "Status",
220204
header: "Status",
221205
cell: (info) => renderStatusCell(info.getValue()),
222-
meta: { type: "category", values: Object.keys(statusColors).sort() },
206+
meta: {
207+
type: CategoryType.STRING,
208+
values: Object.keys(statusColors).sort(),
209+
hideSuggestion: false,
210+
},
223211
}),
224212
columnHelper.accessor("MinorStatus", {
225213
id: "MinorStatus",
@@ -248,17 +236,17 @@ export default function JobMonitor() {
248236
columnHelper.accessor("LastUpdateTime", {
249237
id: "LastUpdateTime",
250238
header: "Last Update Time",
251-
meta: { type: "date" },
239+
meta: { type: CategoryType.DATE, hideSuggestion: true },
252240
}),
253241
columnHelper.accessor("HeartBeatTime", {
254242
id: "HeartBeatTime",
255243
header: "Last Sign of Life",
256-
meta: { type: "date" },
244+
meta: { type: CategoryType.DATE, hideSuggestion: true },
257245
}),
258246
columnHelper.accessor("SubmissionTime", {
259247
id: "SubmissionTime",
260248
header: "Submission Time",
261-
meta: { type: "date" },
249+
meta: { type: CategoryType.DATE, hideSuggestion: true },
262250
}),
263251
columnHelper.accessor("Owner", {
264252
id: "Owner",
@@ -275,17 +263,22 @@ export default function JobMonitor() {
275263
columnHelper.accessor("StartExecTime", {
276264
id: "StartExecTime",
277265
header: "Start Execution Time",
278-
meta: { type: "date" },
266+
meta: { type: CategoryType.DATE, hideSuggestion: true },
279267
}),
280268
columnHelper.accessor("EndExecTime", {
281269
id: "EndExecTime",
282270
header: "End Execution Time",
283-
meta: { type: "date" },
271+
meta: { type: CategoryType.DATE, hideSuggestion: true },
284272
}),
285273
columnHelper.accessor("UserPriority", {
286274
id: "UserPriority",
287275
header: "User Priority",
288-
meta: { type: "number" },
276+
meta: { type: CategoryType.NUMBER },
277+
}),
278+
columnHelper.accessor("RescheduleCounter", {
279+
id: "RescheduleCounter",
280+
header: "Reschedule Counter",
281+
meta: { type: CategoryType.NUMBER },
289282
}),
290283
],
291284
[columnHelper, renderStatusCell, statusColors],
@@ -300,12 +293,12 @@ export default function JobMonitor() {
300293
overflow: "hidden",
301294
}}
302295
>
303-
<FilterToolbar
304-
columns={columns}
296+
<JobSearchBar
305297
filters={filters}
306298
setFilters={setFilters}
299+
searchBody={searchBody}
307300
handleApplyFilters={handleApplyFilters}
308-
handleClearFilters={handleRemoveAllFilters}
301+
columns={columns}
309302
/>
310303
<JobDataTable
311304
searchBody={searchBody}
@@ -370,3 +363,21 @@ export function validateAndConvertState(state: string): [string, boolean] {
370363

371364
return [JSON.stringify(newState), true];
372365
}
366+
367+
/**
368+
* Converts a human-readable job attribute name to its internal name.
369+
* @param name - The human-readable name of the job attribute
370+
* @param columns - The array of column definitions
371+
* @returns The corresponding internal name of the job attribute
372+
*/
373+
export function fromHumanReadableText(
374+
name: string,
375+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
376+
columns: ColumnDef<Job, any>[],
377+
): string {
378+
const index = columns.findIndex((column) => column.header === name);
379+
if (index !== -1) {
380+
return columns[index].id || name; // Return the id if it exists, otherwise
381+
}
382+
return name;
383+
}

0 commit comments

Comments
 (0)