task locking
parent
837e7ee23d
commit
079e7d526d
|
@ -1,6 +1,5 @@
|
|||
import {
|
||||
Alert,
|
||||
Avatar,
|
||||
Button,
|
||||
Form,
|
||||
Input,
|
||||
|
@ -10,10 +9,9 @@ import {
|
|||
Result,
|
||||
Steps,
|
||||
Tag,
|
||||
Tooltip,
|
||||
notification,
|
||||
} from "antd";
|
||||
import { useContext } from "react";
|
||||
import { useContext, useRef, useState } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import {
|
||||
Constants,
|
||||
|
@ -23,12 +21,9 @@ import {
|
|||
GetDuration,
|
||||
MyAvatar,
|
||||
getUserId,
|
||||
setNativeValue,
|
||||
} from "../../utils";
|
||||
import {
|
||||
AntDesignOutlined,
|
||||
InfoCircleOutlined,
|
||||
LockOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { InfoCircleOutlined, LockOutlined } from "@ant-design/icons";
|
||||
|
||||
export default function GroupTasksViewModal({ isOpen }) {
|
||||
const webSocketContext = useContext(WebSocketContext);
|
||||
|
@ -102,7 +97,6 @@ export default function GroupTasksViewModal({ isOpen }) {
|
|||
case Constants.GROUP_TASKS_STATUS.RUNNING:
|
||||
return "process";
|
||||
case Constants.GROUP_TASKS_STATUS.CANCELED:
|
||||
return "error";
|
||||
case Constants.GROUP_TASKS_STATUS.FAILED:
|
||||
return "error";
|
||||
case Constants.GROUP_TASKS_STATUS.INPUT_REQUIRED:
|
||||
|
@ -145,7 +139,10 @@ export default function GroupTasksViewModal({ isOpen }) {
|
|||
}
|
||||
|
||||
taskInputs.push({
|
||||
parameterName: specifiedTaskInputs[i].id,
|
||||
parameterName:
|
||||
specifiedTaskInputs[i].id.split(
|
||||
"-"
|
||||
)[6] /* Format: UUID-STEP-PARAMETER_NAME */,
|
||||
value: specifiedTaskInputs[i].value,
|
||||
});
|
||||
}
|
||||
|
@ -528,6 +525,8 @@ function InputRequiredHandler({
|
|||
step,
|
||||
taskLockedByUserId,
|
||||
}) {
|
||||
const [inputFields, setInputFields] = useState({});
|
||||
|
||||
const globalInputs = JSON.parse(currentGroupTask.GlobalInputs);
|
||||
|
||||
const getDefaultValue = (groupTaskParameter) => {
|
||||
|
@ -552,18 +551,29 @@ function InputRequiredHandler({
|
|||
);
|
||||
};
|
||||
|
||||
let lastChanges = [];
|
||||
let lastChanges = useRef([]);
|
||||
let typingTimer = useRef();
|
||||
|
||||
const onInputChange = (currentGroupTaskId) => {
|
||||
lastChanges = lastChanges.filter(
|
||||
const onInputChange = (
|
||||
inputValue,
|
||||
currentGroupTaskId,
|
||||
groupTaskParameterName
|
||||
) => {
|
||||
setInputFields((fields) => {
|
||||
return ({ ...fields }[groupTaskParameterName] = inputValue);
|
||||
});
|
||||
|
||||
if (taskLockedByUserId !== "") return;
|
||||
|
||||
lastChanges.current = lastChanges.current.filter(
|
||||
(lc) =>
|
||||
Date.now() - new Date(lc.changeTime) < Constants.GROUP_TASK_LOCKED_TIME
|
||||
);
|
||||
|
||||
const lastChange = lastChanges.find((lC) => lC.step === step);
|
||||
const lastChange = lastChanges.current.find((lC) => lC.step === step);
|
||||
|
||||
if (lastChange === undefined) {
|
||||
lastChanges.push({
|
||||
lastChanges.current.push({
|
||||
step: step,
|
||||
changeTime: Date.now(),
|
||||
});
|
||||
|
@ -574,6 +584,19 @@ function InputRequiredHandler({
|
|||
step: step,
|
||||
});
|
||||
}
|
||||
|
||||
// sync input value with other web clients
|
||||
clearTimeout(typingTimer.current);
|
||||
|
||||
typingTimer.current = setTimeout(() => {
|
||||
webSocketContext.SendSocketMessage(
|
||||
SentMessagesCommands.UpdateGroupTaskStepUserInputValue,
|
||||
{
|
||||
element: `${currentGroupTaskId}-${step}-${groupTaskParameterName}`,
|
||||
value: inputValue,
|
||||
}
|
||||
);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -592,15 +615,17 @@ function InputRequiredHandler({
|
|||
>
|
||||
<Input
|
||||
key={"input-" + groupTaskParameter.parameterName}
|
||||
id={groupTaskParameter.parameterName}
|
||||
id={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
|
||||
defaultValue={getDefaultValue(groupTaskParameter)}
|
||||
disabled={taskLockedByUserId !== ""}
|
||||
onChange={() =>
|
||||
onChange={(e) =>
|
||||
onInputChange(
|
||||
e.target.value,
|
||||
currentGroupTask.Id,
|
||||
groupTaskParameter.parameterName
|
||||
)
|
||||
}
|
||||
value={inputFields[groupTaskParameter.parameterName]}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
|
@ -616,12 +641,14 @@ function InputRequiredHandler({
|
|||
>
|
||||
<InputNumber
|
||||
key={"fitem-" + groupTaskParameter.parameterName}
|
||||
id={groupTaskParameter.parameterName}
|
||||
id={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
|
||||
style={{ width: "100%" }}
|
||||
defaultValue={getDefaultValue(groupTaskParameter)}
|
||||
disabled={taskLockedByUserId !== ""}
|
||||
onChange={() =>
|
||||
max={Number.MAX_SAFE_INTEGER}
|
||||
onChange={(e) =>
|
||||
onInputChange(
|
||||
e,
|
||||
currentGroupTask.Id,
|
||||
groupTaskParameter.parameterName
|
||||
)
|
||||
|
|
56
src/utils.js
56
src/utils.js
|
@ -1,6 +1,6 @@
|
|||
import { UserOutlined } from "@ant-design/icons";
|
||||
import { Avatar, Badge, Tooltip } from "antd";
|
||||
import { createContext, useEffect, useRef, useState } from "react";
|
||||
import { createContext, useContext, useEffect, useRef, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Buffer } from "buffer";
|
||||
|
||||
|
@ -111,6 +111,7 @@ const ReceivedMessagesCommands = {
|
|||
UpdateScannerLastUsed: 15,
|
||||
TaskLocked: 16,
|
||||
TaskUnlocked: 17,
|
||||
UpdateGroupTaskStepUserInputValue: 18,
|
||||
};
|
||||
|
||||
// commands sent to the backend server
|
||||
|
@ -120,6 +121,7 @@ export const SentMessagesCommands = {
|
|||
TaskContinueTaskStep: 3,
|
||||
ReloadGroupTasks: 4,
|
||||
TaskLocking: 5,
|
||||
UpdateGroupTaskStepUserInputValue: 6,
|
||||
};
|
||||
|
||||
export function WebSocketProvider({
|
||||
|
@ -146,21 +148,19 @@ export function WebSocketProvider({
|
|||
const navigate = useNavigate();
|
||||
const StartGroupTasksOpenModalRememberIdRef = useRef(null);
|
||||
|
||||
let socket = null;
|
||||
//let socket = null;
|
||||
const ws = useRef(null);
|
||||
|
||||
const connect = () => {
|
||||
socket = new WebSocket(Constants.WS_ADDRESS + "?auth=" + userSession);
|
||||
ws.current = new WebSocket(Constants.WS_ADDRESS + "?auth=" + userSession);
|
||||
|
||||
ws.current = socket;
|
||||
|
||||
socket.onopen = () => {
|
||||
ws.current.onopen = () => {
|
||||
console.log("connected");
|
||||
setConnectionBadgeStatus("success");
|
||||
setIsReady(true);
|
||||
};
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
ws.current.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log("received message", data);
|
||||
|
||||
|
@ -301,7 +301,6 @@ export function WebSocketProvider({
|
|||
|
||||
break;
|
||||
case ReceivedMessagesCommands.UpdateScannerLastUsed:
|
||||
console.log("Received", body);
|
||||
setScanners((arr) => {
|
||||
const newArr = [...arr];
|
||||
|
||||
|
@ -313,7 +312,6 @@ export function WebSocketProvider({
|
|||
});
|
||||
break;
|
||||
case ReceivedMessagesCommands.TaskLocked:
|
||||
console.log("taskLocked", body);
|
||||
setGroupTasksSteps((arr) => {
|
||||
const newArr = [...arr];
|
||||
|
||||
|
@ -343,13 +341,21 @@ export function WebSocketProvider({
|
|||
return newArr;
|
||||
});
|
||||
break;
|
||||
case ReceivedMessagesCommands.UpdateGroupTaskStepUserInputValue:
|
||||
const foundInput = document.getElementById(body.element);
|
||||
|
||||
if (foundInput) {
|
||||
setNativeValue(foundInput, body.value);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error("unknown command", cmd);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
socket.onclose = (event) => {
|
||||
ws.current.onclose = (event) => {
|
||||
setIsReady(false);
|
||||
setConnectionBadgeStatus("error");
|
||||
console.log("closed", event);
|
||||
|
@ -374,7 +380,8 @@ export function WebSocketProvider({
|
|||
useEffect(() => {
|
||||
connect();
|
||||
|
||||
return () => socket.close();
|
||||
//return () => socket.close();
|
||||
return () => ws.current.close();
|
||||
}, []);
|
||||
|
||||
const SendSocketMessage = (cmd, body) => {
|
||||
|
@ -406,6 +413,33 @@ export function WebSocketProvider({
|
|||
</WebSocketContext.Provider>
|
||||
);
|
||||
}
|
||||
/*
|
||||
function doEvent(obj, event) {
|
||||
var event = new Event(event, { target: obj, bubbles: true });
|
||||
event.simulated = true;
|
||||
|
||||
let tracker = obj._valueTracker;
|
||||
if (tracker) {
|
||||
tracker.setValue(lastValue);
|
||||
}
|
||||
|
||||
return obj ? obj.dispatchEvent(event) : false;
|
||||
} */
|
||||
|
||||
// https://stackoverflow.com/a/52486921
|
||||
function setNativeValue(element, value) {
|
||||
let lastValue = element.value;
|
||||
element.value = value;
|
||||
let event = new Event("input", { target: element, bubbles: true });
|
||||
// React 15
|
||||
event.simulated = true;
|
||||
// React 16
|
||||
let tracker = element._valueTracker;
|
||||
if (tracker) {
|
||||
tracker.setValue(lastValue);
|
||||
}
|
||||
element.dispatchEvent(event);
|
||||
}
|
||||
|
||||
export function FormatDatetime(datetime) {
|
||||
if (datetime === "0001-01-01T00:00:00Z") {
|
||||
|
|
Loading…
Reference in New Issue