+ | {name} |
+ {editing.value === name ? (
+ <>
+
+
+ (edit_form.value = {
+ ...edit_form.peek(),
+ type: e.currentTarget.value,
+ })
+ }
+ required
+ />
+ |
+
+
+ (edit_form.value = {
+ ...edit_form.peek(),
+ value: e.currentTarget.value,
+ })
+ }
+ />
+ |
+
+
+ |
+ >
+ ) : (
+ <>
+ {variable.type} |
+ {format_variable_value(variable.value)} |
+
+
+
+
+
+ |
+ >
+ )}
+
+ );
+ },
+ );
+
+ const history_rows = () =>
+ state.api.process.instance.variables.value.data.map(
+ ({ name, type, value }) => (
+
+ {!history_mode ? (
+ <>
+
+
+
+
+
+ {creating.value ? (
+
+ ) : null}
+ >
+ ) : null}
| {t("common.name")} |
{t("common.type")} |
{t("common.value")} |
- {t("common.actions")} |
+ {!history_mode ? {t("common.actions")} | : null}
{selection_exists
- ? !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} |
-
- ),
- )
- : state.api.process.instance.variables.value.data.map(
- // eslint-disable-next-line react/jsx-key
- ({ name, type, value }) => (
-
- | {name} |
- {type} |
- {value} |
-
- ),
- )
+ ? !history_mode ? live_rows() : history_rows()
: t("common.loading")}
@@ -1464,13 +1689,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..079659e 100644
--- a/src/pages/Processes.test.jsx
+++ b/src/pages/Processes.test.jsx
@@ -405,6 +405,106 @@ describe("ProcessesPage — instance details", () => {
expect(getByText("42")).toBeTruthy();
});
+ it("adds a live variable", async () => {
+ mockParams = {
+ definition_id: "proc:1",
+ panel: "instances",
+ selection_id: "inst-9999",
+ sub_panel: "vars",
+ };
+ signal_response(state.api.process.instance.variables, {});
+ const { container, getByLabelText, getByText } = renderPage(state);
+
+ fireEvent.click(getByText("processes.variables.add"));
+ fireEvent.input(getByLabelText("processes.variables.name"), {
+ target: { value: "priority" },
+ });
+ fireEvent.input(getByLabelText("processes.variables.type"), {
+ target: { value: "Integer" },
+ });
+ fireEvent.input(getByLabelText("processes.variables.value"), {
+ target: { value: "100" },
+ });
+ fireEvent.submit(container.querySelector("form"));
+ await Promise.resolve();
+ await Promise.resolve();
+
+ expect(engine_rest.process_instance.update_variable).toHaveBeenCalled();
+ expect(engine_rest.process_instance.update_variable.mock.lastCall[0]).toBe(
+ state,
+ );
+ expect(engine_rest.process_instance.update_variable.mock.lastCall[1]).toBe(
+ "inst-9999",
+ );
+ expect(engine_rest.process_instance.update_variable.mock.lastCall[2]).toBe(
+ "priority",
+ );
+ expect(engine_rest.process_instance.update_variable.mock.lastCall[3]).toEqual(
+ { type: "Integer", value: 100 },
+ );
+ });
+
+ it("updates a live variable", async () => {
+ mockParams = {
+ definition_id: "proc:1",
+ panel: "instances",
+ selection_id: "inst-9999",
+ sub_panel: "vars",
+ };
+ signal_response(state.api.process.instance.variables, {
+ amount: { type: "Integer", value: 42 },
+ });
+ const { container, getByLabelText, getByText } = renderPage(state);
+
+ fireEvent.click(getByText("common.edit"));
+ fireEvent.input(getByLabelText("processes.variables.value"), {
+ target: { value: "99" },
+ });
+ fireEvent.submit(container.querySelector("form"));
+ await Promise.resolve();
+ await Promise.resolve();
+
+ expect(engine_rest.process_instance.update_variable).toHaveBeenCalled();
+ expect(engine_rest.process_instance.update_variable.mock.lastCall[1]).toBe(
+ "inst-9999",
+ );
+ expect(engine_rest.process_instance.update_variable.mock.lastCall[2]).toBe(
+ "amount",
+ );
+ expect(engine_rest.process_instance.update_variable.mock.lastCall[3]).toEqual(
+ { type: "Integer", value: 99 },
+ );
+ });
+
+ it("deletes a live variable after confirmation", async () => {
+ mockParams = {
+ definition_id: "proc:1",
+ panel: "instances",
+ selection_id: "inst-9999",
+ sub_panel: "vars",
+ };
+ signal_response(state.api.process.instance.variables, {
+ amount: { type: "Integer", value: 42 },
+ });
+ const prev = window.confirm;
+ window.confirm = vi.fn(() => true);
+ const { getByText } = renderPage(state);
+
+ fireEvent.click(getByText("common.delete"));
+ await Promise.resolve();
+ await Promise.resolve();
+
+ expect(window.confirm).toHaveBeenCalled();
+ expect(engine_rest.process_instance.delete_variable).toHaveBeenCalled();
+ expect(engine_rest.process_instance.delete_variable.mock.lastCall[1]).toBe(
+ "inst-9999",
+ );
+ expect(engine_rest.process_instance.delete_variable.mock.lastCall[2]).toBe(
+ "amount",
+ );
+ window.confirm = prev;
+ });
+
it("variables sub-panel fetches history variables in history mode", () => {
mockParams = {
definition_id: "proc:1",
@@ -423,6 +523,23 @@ describe("ProcessesPage — instance details", () => {
expect(getByText("amount")).toBeTruthy();
});
+ it("keeps history variables read-only", () => {
+ mockParams = {
+ definition_id: "proc:1",
+ panel: "instances",
+ selection_id: "inst-9999",
+ sub_panel: "vars",
+ };
+ mockQuery = { history: "true" };
+ signal_response(state.api.process.instance.variables, [
+ { name: "amount", type: "Integer", value: 7 },
+ ]);
+ const { queryByText } = renderPage(state);
+
+ expect(queryByText("processes.variables.add")).toBeNull();
+ expect(queryByText("common.actions")).toBeNull();
+ });
+
it("instance-incidents sub-panel fetches and renders incidents", () => {
mockParams = {
definition_id: "proc:1",
diff --git a/src/state.js b/src/state.js
index f63ad2e..a3b86d5 100644
--- a/src/state.js
+++ b/src/state.js
@@ -119,6 +119,8 @@ const createAppState = () => {
list: signal(null),
count: signal(null),
variables: signal(null),
+ variable_update: signal(null),
+ variable_delete: signal(null),
by_defintion_id: signal(null),
activity_instances: signal(null),
modification: signal(null),