import {
Alert,
Button,
Col,
Form,
Image,
Input,
InputNumber,
Modal,
Popover,
Result,
Row,
Space,
Steps,
Tag,
notification,
} from "antd";
import { useContext, useMemo, useRef, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import {
Constants,
FormatDatetime,
WebSocketContext,
SentMessagesCommands,
GetDuration,
MyAvatar,
getUserId,
GroupTasksStepsLockedAndUserUpdateInputValueRememberId,
hasXYPermission,
} from "../../../utils";
import {
CheckOutlined,
FileImageOutlined,
InfoCircleOutlined,
LockOutlined,
RetweetOutlined,
UndoOutlined,
} from "@ant-design/icons";
import { StlViewer } from "react-stl-viewer";
import TextArea from "antd/es/input/TextArea";
import { useTranslation } from "react-i18next";
export default function GroupTasksViewModal({ isOpen }) {
const webSocketContext = useContext(WebSocketContext);
const navigate = useNavigate();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
let { paramGroupTaskId } = useParams();
const { t } = useTranslation();
const handleCancel = () => navigate(Constants.ROUTE_PATHS.GROUP_TASKS);
let currentGroupTask;
webSocketContext.GroupTasks.forEach((groupTask) => {
if (groupTask.Id === paramGroupTaskId) {
currentGroupTask = groupTask;
}
});
// invalid group task id in url specified or no permissions
if (
!currentGroupTask ||
!hasXYPermission(
webSocketContext.User.Permissions,
Constants.PERMISSIONS.GROUP_TASKS.OVERVIEW.XYView,
currentGroupTask.Category
)
) {
return (
{t("common.button.close")}
}
>
);
}
const getAlertType = (status) => {
switch (status) {
case Constants.GROUP_TASKS_STATUS.FINISHED:
return "success";
case Constants.GROUP_TASKS_STATUS.FAILED:
return "error";
case Constants.GROUP_TASKS_STATUS.CANCELED:
case Constants.GROUP_TASKS_STATUS.PAUSED:
case Constants.GROUP_TASKS_STATUS.UNDO_ENDED:
return "warning";
case Constants.GROUP_TASKS_STATUS.INPUT_REQUIRED:
case Constants.GROUP_TASKS_STATUS.RUNNING:
default:
return "info";
}
};
const getAlertMessage = (status) => {
switch (status) {
case Constants.GROUP_TASKS_STATUS.FINISHED:
return t("groupTasks.groupTasksViewModal.alertMessage.successful");
case Constants.GROUP_TASKS_STATUS.RUNNING:
return t("groupTasks.groupTasksViewModal.alertMessage.taskIsRunning");
case Constants.GROUP_TASKS_STATUS.CANCELED:
return t("groupTasks.groupTasksViewModal.alertMessage.taskCanceled");
case Constants.GROUP_TASKS_STATUS.FAILED:
return t("groupTasks.groupTasksViewModal.alertMessage.taskFailed");
case Constants.GROUP_TASKS_STATUS.INPUT_REQUIRED:
return t(
"groupTasks.groupTasksViewModal.alertMessage.taskInputRequired"
);
case Constants.GROUP_TASKS_STATUS.PAUSED:
return t("groupTasks.groupTasksViewModal.alertMessage.paused");
case Constants.GROUP_TASKS_STATUS.UNDO_ENDED:
return t("groupTasks.groupTasksViewModal.alertMessage.undoEnded");
default:
return "Alert message not found";
}
};
const getStepItemStatus = (status) => {
switch (status) {
case Constants.GROUP_TASKS_STATUS.FINISHED:
return "finish";
case Constants.GROUP_TASKS_STATUS.RUNNING:
return "process";
case Constants.GROUP_TASKS_STATUS.CANCELED:
case Constants.GROUP_TASKS_STATUS.FAILED:
return "error";
case Constants.GROUP_TASKS_STATUS.INPUT_REQUIRED:
case Constants.GROUP_TASKS_STATUS.PAUSED:
case Constants.GROUP_TASKS_STATUS.UNDO_ENDED:
default:
return "wait";
}
};
const handleTaskFailedTryAgainRunTaskStep = (taskStepId, step) => {
webSocketContext.SendSocketMessage(
SentMessagesCommands.TaskFailedTryAgainRunTaskStep,
{
groupTaskId: currentGroupTask.Id,
category: currentGroupTask.Category,
groupId: currentGroupTask.GroupId,
step: step,
taskStepId: taskStepId,
}
);
};
const handleTaskContinueTaskStep = (taskStepId, step) => {
const groupTasksViewModalRequiredInputsForm = document.getElementById(
"groupTasksViewModalRequiredInputsForm"
);
let canTaskContinued = true;
let taskInputs = [];
if (groupTasksViewModalRequiredInputsForm !== null) {
const specifiedTaskInputs =
groupTasksViewModalRequiredInputsForm.getElementsByTagName("input");
if (specifiedTaskInputs.length > 0) {
for (let i = 0; i < specifiedTaskInputs.length; i++) {
if (specifiedTaskInputs[i].value === "") {
canTaskContinued = false;
break;
}
taskInputs.push({
parameterName:
specifiedTaskInputs[i].id.split(
"-"
)[6] /* Format: UUID-STEP-PARAMETER_NAME */,
value: specifiedTaskInputs[i].value,
});
}
}
const specifiedTaskTextareas =
groupTasksViewModalRequiredInputsForm.getElementsByTagName("textarea");
if (specifiedTaskTextareas.length > 0) {
for (let i = 0; i < specifiedTaskTextareas.length; i++) {
if (specifiedTaskTextareas[i].value === "") {
canTaskContinued = false;
break;
}
taskInputs.push({
parameterName:
specifiedTaskTextareas[i].id.split(
"-"
)[6] /* Format: UUID-STEP-PARAMETER_NAME */,
value: specifiedTaskTextareas[i].value,
});
}
}
}
if (!canTaskContinued) {
notificationApi["error"]({
message: t(
"groupTasks.groupTasksViewModal.notification.inputsCannotBeEmpty.message"
),
description: t(
"groupTasks.groupTasksViewModal.notification.inputsCannotBeEmpty.description"
),
});
return;
}
webSocketContext.SendSocketMessage(
SentMessagesCommands.TaskContinueTaskStep,
{
groupTaskId: currentGroupTask.Id,
category: currentGroupTask.Category,
groupId: currentGroupTask.GroupId,
step: step,
taskStepId: taskStepId,
taskInputs: taskInputs,
}
);
};
const handleUserActionTaskStep = (action, taskStepId, step) => {
webSocketContext.SendSocketMessage(
SentMessagesCommands.HandleUserActionTaskStep,
{
action: action,
groupTaskId: currentGroupTask.Id,
category: currentGroupTask.Category,
groupId: currentGroupTask.GroupId,
step: step,
taskStepId: taskStepId,
}
);
};
const ActionHandler = ({ status, taskStepId, index, taskLocked }) => {
const currentStepTask = webSocketContext.CategoryGroups.find(
(category) => category.category === currentGroupTask.Category
).groups.find((group) => group.id === currentGroupTask.GroupId).tasks[
index
];
switch (status) {
case Constants.GROUP_TASKS_STATUS.FAILED:
return (
);
case Constants.GROUP_TASKS_STATUS.INPUT_REQUIRED:
return (
);
case Constants.GROUP_TASKS_STATUS.PAUSED:
return (
{currentStepTask.repeatPossible && (
}
size="small"
disabled={taskLocked}
onClick={() =>
handleUserActionTaskStep(2, taskStepId, index + 1)
}
>
{t("common.button.repeat")}
)}
{currentStepTask.undoPossible && (
}
size="small"
disabled={taskLocked}
onClick={() =>
handleUserActionTaskStep(1, taskStepId, index + 1)
}
>
{t("common.button.undo")}
)}
}
size="small"
type={
currentStepTask.repeatPossible && currentStepTask.undoPossible
? "primary"
: "default"
}
disabled={taskLocked}
onClick={() => handleUserActionTaskStep(0, taskStepId, index + 1)}
>
{t("common.button.resume")}
);
case Constants.GROUP_TASKS_STATUS.UNDO_ENDED:
return (
);
default:
return <>>;
}
};
const stepsItemHandler = () => {
let groupTaskSteps = [];
let groupTasks = [];
webSocketContext.GroupTasksSteps.forEach((step) => {
if (step.GroupTasksId === paramGroupTaskId) {
groupTaskSteps.push(step);
}
});
groupTaskSteps.sort((a, b) => a.Step - b.Step);
webSocketContext.CategoryGroups.forEach((categoryGroup) => {
if (categoryGroup.category === currentGroupTask.Category) {
categoryGroup.groups.forEach((group) => {
if (currentGroupTask.GroupId === group.id) {
groupTasks = group.tasks;
}
});
}
});
const getStepItem = (groupTask, index) => {
return {
key: index,
title:
groupTaskSteps[index] !== undefined &&
groupTaskSteps[index].Inputs !== "" &&
groupTaskSteps[index].Status !==
Constants.GROUP_TASKS_STATUS.INPUT_REQUIRED &&
groupTaskSteps[index].Status !==
Constants.GROUP_TASKS_STATUS.UNDO_ENDED ? (
<>
{groupTask.name}{" "}
{t(
"groupTasks.groupTasksViewModal.popover.specifiedTaskInputs"
)}
}
content={
<>
{groupTask.parameters.length > 0 &&
groupTask.parameters.map((task) => {
return (
{task.displayName}:{" "}
{
JSON.parse(groupTaskSteps[index].Inputs).find(
(input) =>
input.parameterName === task.parameterName
)?.value
}
);
})}
>
}
>
{" "}
{groupTaskSteps[index]?.CreatorUserId !== undefined && (
)}
>
) : (
<>
{groupTask.name}{" "}
{groupTaskSteps[index]?.CreatorUserId !== undefined && (
)}
>
),
status:
groupTaskSteps[index] !== undefined
? getStepItemStatus(groupTaskSteps[index].Status)
: "wait",
description:
groupTaskSteps[index] !== undefined ? (
<>
ID: {groupTaskSteps[index].Id}
{t("groupTasks.groupTasksViewModal.startedAt")}:{" "}
{FormatDatetime(groupTaskSteps[index].StartedAt)}
{t("groupTasks.groupTasksViewModal.endedAt")}:{" "}
{groupTaskSteps[index].EndedAt !== "0001-01-01T00:00:00Z"
? FormatDatetime(groupTaskSteps[index].EndedAt)
: Constants.TEXT_EMPTY_PLACEHOLDER}
{t("groupTasks.groupTasksViewModal.duration")}:{" "}
{GetDuration(
groupTaskSteps[index].StartedAt,
groupTaskSteps[index].EndedAt
)}
{getAlertMessage(groupTaskSteps[index].Status)}{" "}
{groupTaskSteps[index].LockedByUserId !== "" && (
<>
{" "}
>
)}
}
description={
groupTaskSteps[index].Status ===
Constants.GROUP_TASKS_STATUS.INPUT_REQUIRED ? (
) : (
groupTaskSteps[index].Log.length > 0 && (
)
)
}
type={getAlertType(groupTaskSteps[index].Status)}
showIcon
/>
>
) : (
""
),
};
};
let stepItems = [];
groupTasks.forEach((groupTask, index) =>
stepItems.push(getStepItem(groupTask, index))
);
// occurs when tasks were taken from the group task config, but at a previous time the tasks existed
if (currentGroupTask.NumberOfSteps > groupTasks.length) {
for (let i = groupTasks.length; i < currentGroupTask.NumberOfSteps; i++) {
let stepParams = [];
if (groupTaskSteps[i].Inputs !== "") {
let params = JSON.parse(groupTaskSteps[i].Inputs);
for (let i2 = 0; i2 < params.length; i2++) {
params[i2].displayName = params[i2].parameterName;
}
stepParams = params;
}
stepItems.push(getStepItem({ name: "???", parameters: stepParams }, i));
}
}
return stepItems;
};
return (
{t("common.button.close")}
}
>
{notificationContextHolder}
{webSocketContext.GroupTasks.map((groupTask) => {
if (groupTask.Id === paramGroupTaskId) {
let currentGroupTask = groupTask;
return (
{currentGroupTask.GroupName}{" "}
{t("groupTasks.groupTasksViewModal.popover.details")}
}
content={
<>
ID:{" "}
{paramGroupTaskId}
{t("groupTasks.groupTasksViewModal.category")}:
{" "}
{currentGroupTask.Category}
{t("groupTasks.groupTasksViewModal.startedAt")}:
{" "}
{FormatDatetime(currentGroupTask.StartedAt)}
{t("groupTasks.groupTasksViewModal.endedAt")}:
{" "}
{FormatDatetime(currentGroupTask.EndedAt)}
{t("groupTasks.groupTasksViewModal.duration")}:
{" "}
{GetDuration(
currentGroupTask.StartedAt,
currentGroupTask.EndedAt
)}
{currentGroupTask.GlobalInputs !== "[]" ? (
<>
{t(
"groupTasks.groupTasksViewModal.popover.specifiedGlobalInputs"
)}
{JSON.parse(currentGroupTask.GlobalInputs).map(
(globalInput) => {
return (
{
webSocketContext.CategoryGroups.find(
(categoryGroup) =>
categoryGroup.category ===
currentGroupTask.Category
)
.groups.find(
(group) =>
group.id ===
currentGroupTask.GroupId
)
.globalInputs.find(
(gI) =>
gI.parameterName ===
globalInput.parameterName
).displayName
}
:{" "}
{globalInput.value !== ""
? globalInput.value
: Constants.TEXT_EMPTY_PLACEHOLDER}
);
}
)}
>
) : null}
>
}
>
{" "}
{currentGroupTask.Description}
);
}
return "";
})}
);
}
function InputRequiredHandler({
webSocketContext,
currentGroupTask,
groupTaskParameters,
groupTaskStepInputs,
notificationApi,
step,
taskLockedByUserId,
}) {
const { t } = useTranslation();
const [inputFields, setInputFields] = useState({});
const globalInputs = JSON.parse(currentGroupTask.GlobalInputs);
const stepInputs = JSON.parse(groupTaskStepInputs);
const getDefaultValue = (groupTaskParameter) => {
if (stepInputs !== false) {
const stepInput = stepInputs.find(
(stepInput) =>
stepInput.parameterName === groupTaskParameter.parameterName
)?.value;
if (stepInput) {
return stepInput;
}
}
if (globalInputs === undefined || !groupTaskParameter.global) return null;
const globalInputValue = globalInputs.find(
(globalInput) =>
globalInput.parameterName === groupTaskParameter.parameterName
);
return globalInputValue !== undefined ? globalInputValue.value : "";
};
const getLabel = (displayName, groupTaskParameter) => {
return groupTaskParameter.global ? (
<>
{displayName}
{t("groupTasks.tag.global")}
>
) : (
displayName
);
};
let lastChange = useRef();
let typingTimer = useRef();
const sendTypingMessage = (
currentGroupTaskId,
groupTaskParameterName,
inputValue
) => {
webSocketContext.SendSocketMessage(SentMessagesCommands.TaskLocking, {
element: `${currentGroupTaskId}-${step}-${groupTaskParameterName}`,
lockedByUserId: getUserId(),
groupTaskId: currentGroupTaskId,
parameterName: groupTaskParameterName,
value: inputValue,
step: step,
rememberId: GroupTasksStepsLockedAndUserUpdateInputValueRememberId,
});
};
const onInputChange = (
inputValue,
currentGroupTaskId,
groupTaskParameterName
) => {
setInputFields((fields) => {
return ({ ...fields }[groupTaskParameterName] =
inputValue === null ? "" : inputValue);
});
if (taskLockedByUserId !== "") return;
if (Date.now() >= lastChange.current || lastChange.current === undefined) {
lastChange.current = Date.now() + 1000;
sendTypingMessage(currentGroupTaskId, groupTaskParameterName, inputValue);
} else {
clearTimeout(typingTimer.current);
}
typingTimer.current = setTimeout(
() =>
sendTypingMessage(
currentGroupTaskId,
groupTaskParameterName,
inputValue
),
1000
);
};
return (
onInputChange(
e.target.value,
currentGroupTask.Id,
groupTaskParameter.parameterName
)
}
value={inputFields[groupTaskParameter.parameterName]}
/>
);
case "number":
return (
onInputChange(
e,
currentGroupTask.Id,
groupTaskParameter.parameterName
)
}
value={inputFields[groupTaskParameter.parameterName]}
/>
);
case "textarea":
return (
);
default:
notificationApi["error"]({
message: t(
"groupTasks.groupTasksViewModal.notification.groupTaskParameterNotImplemented.message",
{ groupTaskParameterType: groupTaskParameter.type }
),
description: t(
"groupTasks.groupTasksViewModal.notification.groupTaskParameterNotImplemented.description",
{
groupTaskParameterDisplayName: groupTaskParameter.displayName,
}
),
});
return (
Type {groupTaskParameter.type} not implemented. Was
specified in: {groupTaskParameter.displayName}
);
}
})}
);
}
const MyStlViewer = ({ url }) => {
const memoizedCode = useMemo(() => {
return (
console.error(err)}
onFinishLoading={(ev) => console.log(ev)}
/>
);
}, [url]);
return memoizedCode;
};
function GroupTaskStepLogHandler({ currentGroupTaskId, log, files }) {
const getDownloadUrl = (file) => {
return `${Constants.STATIC_CONTENT_ADDRESS}grouptasks/${currentGroupTaskId}/${file.SystemFileName}`;
};
const getTag = (file) => {
return (
}
color="processing"
style={{ marginTop: 6 }}
>
{file.OriginalFileName}
);
};
const fileContent = (files) => {
const nonImageFiles = [];
const imageFiles = [];
for (const file of files) {
const fileExtension = file.SystemFileName.split(".")[1];
if (
fileExtension === "png" ||
fileExtension === "jpeg" ||
fileExtension === "jpg" ||
fileExtension === "webp"
) {
imageFiles.push(file);
} else {
nonImageFiles.push(file);
}
}
const elements = [];
for (const nonImageFile of nonImageFiles) {
const fileExtension = nonImageFile.SystemFileName.split(".")[1];
if (fileExtension === "stl") {
elements.push(
);
} else {
elements.push(getTag(nonImageFile));
}
}
const imageFileElements = [];
for (const imageFile of imageFiles) {
imageFileElements.push(
{getTag(imageFile)}
);
}
if (imageFileElements.length > 0) {
elements.push(
{imageFileElements}
);
}
return elements;
};
return (
{log}
{files !== "" && files !== " " && fileContent(JSON.parse(files))}
);
}