Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions docs/user/monitor_jobs.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
# Managing your jobs
# Job Monitor documentation

## Basics
## The search bar

###
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.
A resarch is automatically performed when all the equations in the search bar are valid.

### Create a filter
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.
The search bar only suggests attributes, operators, and values that are available in your current set of jobs.

## Advanced
### Edit a filter
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.

### Remove a filter
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.


## Use the table
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.

### Actions on the table
You can click on the eye icon to select more columns to display in the table.
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.
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.

### Actions on a job
You can do a right-click on a job to open the `Job History`.
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:
- **Get IDs**: This button will copy the IDs of the selected jobs to the clipboard.
- **Rechedule**: This button will reschedule the selected jobs.
- **Kill**: This button will kill the selected jobs.
- **Delete**: This button will delete the selected jobs.

4 changes: 2 additions & 2 deletions packages/diracx-web-components/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ const config: StorybookConfig = {
"../../hooks/metadata": require.resolve(
"../stories/mocks/metadata.mock.tsx",
),
"./JobDataService": require.resolve(
"../stories/mocks/JobDataService.mock.tsx",
"./jobDataService": require.resolve(
"../stories/mocks/jobDataService.mock.ts",
),
};
return config;
Expand Down
9 changes: 9 additions & 0 deletions packages/diracx-web-components/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ export default [
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"react/destructuring-assignment": ["error", "always"],
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
ignoreRestSiblings: true,
},
],
"no-restricted-properties": [
"error",
{
Expand Down
2 changes: 1 addition & 1 deletion packages/diracx-web-components/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const config = {
moduleNameMapper: {
"^@axa-fr/react-oidc$": "<rootDir>/stories/mocks/react-oidc.mock.tsx",
"^../../hooks/metadata$": "<rootDir>/stories/mocks/metadata.mock.tsx",
"^./JobDataService$": "<rootDir>/stories/mocks/JobDataService.mock.tsx",
"^./jobDataService$": "<rootDir>/stories/mocks/jobDataService.mock.ts",
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
refreshJobs,
rescheduleJobs,
useJobs,
} from "./JobDataService";
} from "./jobDataService";

/**
* Job Data Table props
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ import {
RowSelectionState,
VisibilityState,
PaginationState,
ColumnDef,
} from "@tanstack/react-table";

import { useApplicationId } from "../../hooks/application";
import { FilterToolbar } from "../shared/FilterToolbar";
import { InternalFilter } from "../../types/Filter";
import { Job, SearchBody } from "../../types";
import { Filter } from "../../types/Filter";
import { Job, SearchBody, CategoryType } from "../../types";
import { JobDataTable } from "./JobDataTable";
import { JobSearchBar } from "./JobSearchBar";

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

// State for filters
const [filters, setFilters] = useState<InternalFilter[]>(
const [filters, setFilters] = useState<Filter[]>(
parsedInitialState ? parsedInitialState.filters : [],
);

// State for search body
const [searchBody, setSearchBody] = useState<SearchBody>({
search: parsedInitialState
? parsedInitialState.filters.map((filter: InternalFilter) => ({
? parsedInitialState.filters.map((filter: Filter) => ({
parameter: filter.parameter,
operator: filter.operator,
value: filter.value,
Expand Down Expand Up @@ -98,7 +99,7 @@ export default function JobMonitor() {
// Save the state of the app in local storage
useEffect(() => {
const state = {
filters: [...filters.filter((filter) => filter.isApplied)],
filters: [...filters],
columnVisibility: { ...columnVisibility },
columnPinning: {
left: [...(columnPinning.left || [])],
Expand All @@ -123,15 +124,10 @@ export default function JobMonitor() {

// Handle the application of filters
const handleApplyFilters = () => {
// Switch the applied state of the filters
setFilters((filters) =>
filters.map((filter) => ({ ...filter, isApplied: true })),
);

setSearchBody((prev) => ({
...prev,
search: filters.map(({ parameter, operator, value, values }) => ({
parameter,
parameter: fromHumanReadableText(parameter, columns),
operator,
value,
values,
Expand All @@ -143,18 +139,6 @@ export default function JobMonitor() {
}));
};

const handleRemoveAllFilters = useCallback(() => {
setSearchBody((prevState) => ({
...prevState,
search: [],
}));
setPagination((prevState) => ({
...prevState,
pageIndex: 0,
}));
setFilters([]);
}, [setFilters]);

// Status colors
const statusColors: Record<string, string> = useMemo(
() => ({
Expand Down Expand Up @@ -213,13 +197,17 @@ export default function JobMonitor() {
columnHelper.accessor("JobID", {
id: "JobID",
header: "ID",
meta: { type: "number" },
meta: { type: CategoryType.NUMBER, hideSuggestion: true },
}),
columnHelper.accessor("Status", {
id: "Status",
header: "Status",
cell: (info) => renderStatusCell(info.getValue()),
meta: { type: "category", values: Object.keys(statusColors).sort() },
meta: {
type: CategoryType.STRING,
values: Object.keys(statusColors).sort(),
hideSuggestion: false,
},
}),
columnHelper.accessor("MinorStatus", {
id: "MinorStatus",
Expand Down Expand Up @@ -248,17 +236,17 @@ export default function JobMonitor() {
columnHelper.accessor("LastUpdateTime", {
id: "LastUpdateTime",
header: "Last Update Time",
meta: { type: "date" },
meta: { type: CategoryType.DATE, hideSuggestion: true },
}),
columnHelper.accessor("HeartBeatTime", {
id: "HeartBeatTime",
header: "Last Sign of Life",
meta: { type: "date" },
meta: { type: CategoryType.DATE, hideSuggestion: true },
}),
columnHelper.accessor("SubmissionTime", {
id: "SubmissionTime",
header: "Submission Time",
meta: { type: "date" },
meta: { type: CategoryType.DATE, hideSuggestion: true },
}),
columnHelper.accessor("Owner", {
id: "Owner",
Expand All @@ -275,17 +263,22 @@ export default function JobMonitor() {
columnHelper.accessor("StartExecTime", {
id: "StartExecTime",
header: "Start Execution Time",
meta: { type: "date" },
meta: { type: CategoryType.DATE, hideSuggestion: true },
}),
columnHelper.accessor("EndExecTime", {
id: "EndExecTime",
header: "End Execution Time",
meta: { type: "date" },
meta: { type: CategoryType.DATE, hideSuggestion: true },
}),
columnHelper.accessor("UserPriority", {
id: "UserPriority",
header: "User Priority",
meta: { type: "number" },
meta: { type: CategoryType.NUMBER },
}),
columnHelper.accessor("RescheduleCounter", {
id: "RescheduleCounter",
header: "Reschedule Counter",
meta: { type: CategoryType.NUMBER },
}),
],
[columnHelper, renderStatusCell, statusColors],
Expand All @@ -300,12 +293,12 @@ export default function JobMonitor() {
overflow: "hidden",
}}
>
<FilterToolbar
columns={columns}
<JobSearchBar
filters={filters}
setFilters={setFilters}
searchBody={searchBody}
handleApplyFilters={handleApplyFilters}
handleClearFilters={handleRemoveAllFilters}
columns={columns}
/>
<JobDataTable
searchBody={searchBody}
Expand Down Expand Up @@ -370,3 +363,21 @@ export function validateAndConvertState(state: string): [string, boolean] {

return [JSON.stringify(newState), true];
}

/**
* Converts a human-readable job attribute name to its internal name.
* @param name - The human-readable name of the job attribute
* @param columns - The array of column definitions
* @returns The corresponding internal name of the job attribute
*/
export function fromHumanReadableText(
name: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
columns: ColumnDef<Job, any>[],
): string {
const index = columns.findIndex((column) => column.header === name);
if (index !== -1) {
return columns[index].id || name; // Return the id if it exists, otherwise
}
return name;
}
Loading