diff --git a/public/locales/de-DE/translation.json b/public/locales/de-DE/translation.json
index 5f7d6a9..cb77887 100644
--- a/public/locales/de-DE/translation.json
+++ b/public/locales/de-DE/translation.json
@@ -345,6 +345,9 @@
"jobs": {
"overriding-job-priority": "Überschriebene Job-Priorität",
"suspend": "Anhalten",
+ "activate": "Aktivieren",
+ "include-jobs": "Bestehende Jobs einbeziehen",
+ "suspension-updated": "Status der Job-Definition aktualisiert.",
"change-priority": "Job-Priorität ändern"
},
"sort": {
diff --git a/public/locales/en-US/translation.json b/public/locales/en-US/translation.json
index ac20fbe..d521846 100644
--- a/public/locales/en-US/translation.json
+++ b/public/locales/en-US/translation.json
@@ -345,6 +345,9 @@
"jobs": {
"overriding-job-priority": "Overriding Job Priority",
"suspend": "Suspend",
+ "activate": "Activate",
+ "include-jobs": "Include existing jobs",
+ "suspension-updated": "Job definition state updated.",
"change-priority": "Change Overriding Job Priority"
},
"sort": {
diff --git a/src/api/resources/job_definition.js b/src/api/resources/job_definition.js
index 52c2e87..7822943 100644
--- a/src/api/resources/job_definition.js
+++ b/src/api/resources/job_definition.js
@@ -1,12 +1,21 @@
-import { GET } from '../helper.jsx'
+import { GET, PUT } from '../helper.jsx'
const get_job_definitions = (state, definition_id) =>
GET(`/job-definition?processDefinitionId=${definition_id}`, state, state.api.job_definition.all.by_process_definition)
+const set_job_definition_suspended = (state, id, suspended, includeJobs = false) =>
+ PUT(
+ `/job-definition/${id}/suspended`,
+ { suspended, includeJobs },
+ state,
+ state.api.job_definition.update,
+ )
+
const job_definition = {
all: {
by_process_definition: get_job_definitions
- }
+ },
+ set_suspended: set_job_definition_suspended,
}
-export default job_definition
\ No newline at end of file
+export default job_definition
diff --git a/src/api/resources/job_definition.test.js b/src/api/resources/job_definition.test.js
index dde29f3..32c1d3c 100644
--- a/src/api/resources/job_definition.test.js
+++ b/src/api/resources/job_definition.test.js
@@ -2,9 +2,10 @@ import { describe, it, vi, beforeEach } from "vitest";
vi.mock("../helper.jsx", () => ({
GET: vi.fn(),
+ PUT: vi.fn(),
}));
-import { GET } from "../helper.jsx";
+import { GET, PUT } from "../helper.jsx";
import { create_mock_state, expect_api_call } from "../../test/helpers.js";
import job_definition from "./job_definition.js";
@@ -22,4 +23,14 @@ describe("api/resources/job_definition", () => {
signal: state.api.job_definition.all.by_process_definition,
});
});
+
+ it("set_suspended() PUTs the suspended state for a job definition", () => {
+ job_definition.set_suspended(state, "job-def-1", true, true);
+ expect_api_call(PUT, {
+ url: "/job-definition/job-def-1/suspended",
+ body: { suspended: true, includeJobs: true },
+ state,
+ signal: state.api.job_definition.update,
+ });
+ });
});
diff --git a/src/css/components.css b/src/css/components.css
index 5bed3e4..31de438 100644
--- a/src/css/components.css
+++ b/src/css/components.css
@@ -869,6 +869,25 @@ div.accordion {
color: var(--text-2);
}
+/* process job definitions */
+
+.job-definition-actions {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-1);
+ margin-bottom: var(--spacing-1);
+}
+
+.job-definition-actions label {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-half);
+}
+
+.job-definition-actions input[type="checkbox"] {
+ width: auto;
+}
+
/* task detail cards */
.task-cards {
diff --git a/src/pages/Processes.jsx b/src/pages/Processes.jsx
index d6ac79e..c71fe8e 100644
--- a/src/pages/Processes.jsx
+++ b/src/pages/Processes.jsx
@@ -686,7 +686,7 @@ const DefinitionsEmpty = () => {
{t("processes.empty.how-to")}
@@ -928,8 +928,7 @@ const InstanceDetails = () => {
params: { selection_id, definition_id, panel },
query,
} = useRoute(),
- history_mode = query.history === "true",
- [t] = useTranslation();
+ history_mode = query.history === "true";
if (selection_id) {
if (
@@ -1043,20 +1042,16 @@ const InstanceVariables = () => {
? !history_mode
? Object.entries(
state.api.process.instance.variables.value.data,
- ).map(
- // eslint-disable-next-line react/jsx-key
- ([name, { type, value }]) => (
-
- | {name} |
- {type} |
- {value} |
-
- ),
- )
+ ).map(([name, { type, value }]) => (
+
+ | {name} |
+ {type} |
+ {value} |
+
+ ))
: state.api.process.instance.variables.value.data.map(
- // eslint-disable-next-line react/jsx-key
({ name, type, value }) => (
-
+
| {name} |
{type} |
{value} |
@@ -1407,7 +1402,9 @@ const JobDefinitions = () => {
const state = useContext(AppState),
{ definition_id, query } = useRoute(),
history_mode = query?.history === "true",
- [t] = useTranslation();
+ [t] = useTranslation(),
+ include_jobs = useSignal(false),
+ update = state.api.job_definition.update;
useEffect(() => {
void engine_rest.job_definition.all.by_process_definition(
@@ -1417,6 +1414,19 @@ const JobDefinitions = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [definition_id]);
+ const set_suspended = async (id, suspended) => {
+ await engine_rest.job_definition.set_suspended(
+ state,
+ id,
+ suspended,
+ include_jobs.value,
+ );
+ void engine_rest.job_definition.all.by_process_definition(
+ state,
+ definition_id,
+ );
+ };
+
/** @namespace state.api.job_definition.all.by_process_definition.value.data **/
/** @namespace definition.jobType **/
/** @namespace definition.jobConfiguration **/
@@ -1426,6 +1436,23 @@ const JobDefinitions = () => {
{history_mode && (
{t("processes.history-mode-na")}
)}
+
+
+
+ null}
+ on_success={() => (
+ {t("processes.jobs.suspension-updated")}
+ )}
+ />
@@ -1452,8 +1479,21 @@ const JobDefinitions = () => {
| {definition.jobConfiguration} |
{definition.overridingJobPriority ?? "-"} |
-
-
+ {definition.suspended ? (
+
+ ) : (
+
+ )}
|
),
@@ -1464,13 +1504,6 @@ const JobDefinitions = () => {
);
};
-const BackToListBtn = ({ url, title, className }) => (
-
-
-
-
-);
-
const DefinitionsManage = () => {
const state = useContext(AppState),
{ route } = useLocation(),
diff --git a/src/pages/Processes.test.jsx b/src/pages/Processes.test.jsx
index 60c4b48..b38368e 100644
--- a/src/pages/Processes.test.jsx
+++ b/src/pages/Processes.test.jsx
@@ -356,6 +356,59 @@ describe("ProcessesPage — definition tabs", () => {
expect(getByText("timer")).toBeTruthy();
expect(getByText("R/PT5M")).toBeTruthy();
});
+
+ it("jobs tab suspends an active job definition", async () => {
+ mockParams = { definition_id: "proc:1", panel: "jobs" };
+ signal_response(state.api.job_definition.all.by_process_definition, [
+ {
+ id: "jd1",
+ suspended: false,
+ jobType: "timer",
+ jobConfiguration: "R/PT5M",
+ },
+ ]);
+ engine_rest.job_definition.set_suspended.mockResolvedValue(undefined);
+ const { container, getByText } = renderPage(state);
+ fireEvent.click(
+ container.querySelector(
+ '.job-definition-actions input[type="checkbox"]',
+ ),
+ );
+ fireEvent.click(getByText("processes.jobs.suspend"));
+ await Promise.resolve();
+ await Promise.resolve();
+ expect(engine_rest.job_definition.set_suspended).toHaveBeenCalled();
+ expect(engine_rest.job_definition.set_suspended.mock.lastCall).toEqual([
+ state,
+ "jd1",
+ true,
+ true,
+ ]);
+ });
+
+ it("jobs tab activates a suspended job definition", async () => {
+ mockParams = { definition_id: "proc:1", panel: "jobs" };
+ signal_response(state.api.job_definition.all.by_process_definition, [
+ {
+ id: "jd1",
+ suspended: true,
+ jobType: "timer",
+ jobConfiguration: "R/PT5M",
+ },
+ ]);
+ engine_rest.job_definition.set_suspended.mockResolvedValue(undefined);
+ const { getByText } = renderPage(state);
+ fireEvent.click(getByText("processes.jobs.activate"));
+ await Promise.resolve();
+ await Promise.resolve();
+ expect(engine_rest.job_definition.set_suspended).toHaveBeenCalled();
+ expect(engine_rest.job_definition.set_suspended.mock.lastCall).toEqual([
+ state,
+ "jd1",
+ false,
+ false,
+ ]);
+ });
});
describe("ProcessesPage — instance details", () => {
diff --git a/src/state.js b/src/state.js
index f63ad2e..4a90f14 100644
--- a/src/state.js
+++ b/src/state.js
@@ -183,6 +183,7 @@ const createAppState = () => {
all: {
by_process_definition: signal(null),
},
+ update: signal(null),
},
};
diff --git a/src/state.test.js b/src/state.test.js
index c6b9968..86a2ac1 100644
--- a/src/state.test.js
+++ b/src/state.test.js
@@ -57,6 +57,7 @@ describe("state", () => {
expect(api.authorization.all.value).toBeNull();
expect(api.batch.list.value).toBeNull();
expect(api.batch.one.value).toBeNull();
+ expect(api.job_definition.update.value).toBeNull();
});
});
});