import { Alert, Button, Form, Input, InputNumber, Popover, Space, Steps, Tag, notification, } from "antd"; import { useEffect, useRef, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { Constants, FormatDatetime, GetDuration, getUserId, hasXYPermission, myFetch, } from "../../../utils"; import { CheckOutlined, InfoCircleOutlined, LockOutlined, RetweetOutlined, UndoOutlined, } from "@ant-design/icons"; import TextArea from "antd/es/input/TextArea"; import { useTranslation } from "react-i18next"; import { MyAvatar } from "../../../Components/MyAvatar"; import MyModal, { MyNotFoundModalContent } from "../../../Components/MyModal"; import MyAttachments from "../../../Components/MyAttachments"; import { useWebSocketContext } from "../../../Contexts/WebSocketContext"; import { useGroupTasksContext } from "../../../Contexts/GroupTasksContext"; import { useAppContext } from "../../../Contexts/AppContext"; import { GroupTasksStepsLockedAndUserUpdateInputValueRememberId, SentMessagesCommands, } from "../../../Handlers/WebSocketMessageHandler"; export default function GroupTasksViewModal({ isOpen }) { const webSocketContext = useWebSocketContext(); const appContext = useAppContext(); const groupTasksContext = useGroupTasksContext(); const navigate = useNavigate(); const [notificationApi, notificationContextHolder] = notification.useNotification(); let { paramCategory, paramGroupTaskId } = useParams(); const { t } = useTranslation(); const currentGroupTask = useRef(null); const handleCancel = () => { currentGroupTask.current = null; navigate(`${Constants.ROUTE_PATHS.GROUP_TASKS}${paramCategory}`); }; useEffect(() => { if (!isOpen) return; myFetch( `/grouptasks/${paramCategory}/steps/${paramGroupTaskId}`, "GET" ).then((data) => { currentGroupTask.current = data.GroupTask; groupTasksContext.setGroupTasksSteps(data.GroupTaskSteps); }); }, [isOpen, paramCategory]); if (!isOpen) return <>; 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.current.Id, category: currentGroupTask.current.Category, groupId: currentGroupTask.current.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.current.Id, category: currentGroupTask.current.Category, groupId: currentGroupTask.current.GroupId, step: step, taskStepId: taskStepId, taskInputs: taskInputs, } ); }; const handleUserActionTaskStep = (action, taskStepId, step) => { webSocketContext.SendSocketMessage( SentMessagesCommands.HandleUserActionTaskStep, { action: action, groupTaskId: currentGroupTask.current.Id, category: currentGroupTask.current.Category, groupId: currentGroupTask.current.GroupId, step: step, taskStepId: taskStepId, } ); }; const ActionHandler = ({ status, taskStepId, index, taskLocked }) => { const currentStepTask = groupTasksContext.categoryGroup.groups.find( (group) => group.id === currentGroupTask.current.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 && ( )} {currentStepTask.undoPossible && ( )} ); case Constants.GROUP_TASKS_STATUS.UNDO_ENDED: return ( ); default: return <>; } }; const stepsItemHandler = () => { let groupTaskSteps = []; let groupTasks = []; groupTasksContext.groupTasksSteps.forEach((step) => { if (step.GroupTasksId === paramGroupTaskId) { groupTaskSteps.push(step); } }); groupTaskSteps.sort((a, b) => a.Step - b.Step); groupTasksContext.categoryGroup.groups.forEach((group) => { if (currentGroupTask.current.GroupId === group.id) { groupTasks = group.tasks; return; } }); 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.current.NumberOfSteps > groupTasks.length) { for ( let i = groupTasks.length; i < currentGroupTask.current.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 ( {notificationContextHolder} {!currentGroupTask.current || !hasXYPermission( appContext.userPermissions, Constants.PERMISSIONS.GROUP_TASKS.OVERVIEW.XYView, currentGroupTask.current.Category ) ? ( ) : (

{currentGroupTask.current.GroupName}{" "} {t("groupTasks.groupTasksViewModal.popover.details")}

} content={ <>

ID:{" "} {paramGroupTaskId}
{t("groupTasks.groupTasksViewModal.category")}: {" "} {currentGroupTask.current.Category}
{t("groupTasks.groupTasksViewModal.startedAt")}: {" "} {FormatDatetime(currentGroupTask.current.StartedAt)}
{t("groupTasks.groupTasksViewModal.endedAt")}: {" "} {FormatDatetime(currentGroupTask.current.EndedAt)}
{t("groupTasks.groupTasksViewModal.duration")}: {" "} {GetDuration( currentGroupTask.current.StartedAt, currentGroupTask.current.EndedAt )}

{currentGroupTask.current.GlobalInputs !== "[]" ? ( <>

{t( "groupTasks.groupTasksViewModal.popover.specifiedGlobalInputs" )}

{JSON.parse(currentGroupTask.current.GlobalInputs).map( (globalInput) => { return ( { groupTasksContext.categoryGroup.groups .find( (group) => group.id === currentGroupTask.current.GroupId ) .globalInputs.find( (gI) => gI.parameterName === globalInput.parameterName ).displayName } :{" "} {globalInput.value !== "" ? globalInput.value : Constants.TEXT_EMPTY_PLACEHOLDER}
); } )}
) : null} } > {" "}
{currentGroupTask.current.Description}
)}
); } 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 (
{groupTaskParameters.map((groupTaskParameter) => { switch (groupTaskParameter.type) { case "text": 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 (