From b929f828a2c2ce172a437035a2adec902d012f90 Mon Sep 17 00:00:00 2001 From: Prashant-Microsoft Date: Tue, 24 Dec 2024 13:15:04 +0530 Subject: [PATCH 01/24] fix: ui changes (#1) --- src/frontend/wwwroot/app.css | 219 ++-- src/frontend/wwwroot/assets/Send.svg | 10 + src/frontend/wwwroot/home/home.css | 352 ++++--- src/frontend/wwwroot/home/home.js | 295 ++++-- src/frontend/wwwroot/task/task.css | 270 ++--- src/frontend/wwwroot/task/task.js | 1421 ++++++++++++++------------ 6 files changed, 1435 insertions(+), 1132 deletions(-) create mode 100644 src/frontend/wwwroot/assets/Send.svg diff --git a/src/frontend/wwwroot/app.css b/src/frontend/wwwroot/app.css index 88c2976e6..6651c7760 100644 --- a/src/frontend/wwwroot/app.css +++ b/src/frontend/wwwroot/app.css @@ -4,191 +4,238 @@ /* App global */ -html { - overflow-y: auto; +html, +body { + overflow-x: hidden; + overflow-y: auto; + height: 100%; } body { - position: relative; - background: rgb(247, 249, 251); - min-height: 100vh; + position: relative; + background: rgb(247, 249, 251); + min-height: 100vh; } .border-right { - border-right: 1px solid hsl(221, 14%, calc(86% + 0%)); + border-right: 1px solid hsl(221, 14%, calc(86% + 0%)); } /* App template */ #app .columns { - min-height: 100vh; + min-height: 100vh; + height: 100%; +} +#app .modal, +#app .menu { + overflow: hidden; /* Prevent scrolling within modals and menus */ } - #app .asside { - background: rgba(231, 236, 243, 0.7); + background: rgba(231, 236, 243, 0.7); } @media (min-width: 1800px) { - #app .asside { - max-width: 400px; - } + #app .asside { + max-width: 400px; + } } #app .menu-logo { - font-size: 1.25rem; - font-weight: 700; - cursor: pointer; + font-size: 1.25rem; + font-weight: 700; + cursor: pointer; } #app .menu-logo img { - width: 30px; + width: 30px; } #app .asside .menu-list a { - background-color: transparent; + background-color: transparent; } #app .asside .menu-list a.is-active { - background-color: rgb(71, 80, 235); + background-color: rgb(71, 80, 235); } #app .asside .menu-list a.is-active i { - color: white !important; + color: white !important; } #app .asside .menu-list a.is-active:hover { - background-color: rgb(71, 80, 235); + background-color: rgb(71, 80, 235); } #app .asside .menu-list a.menu-task { - display: flex; - align-items: center; + display: flex; + align-items: center; } #app .asside .menu-list a.menu-task span { - flex: 1; + flex: 1; } #app .asside .menu-list a:hover { - background-color: rgba(0, 0, 0, 0.1); + background-color: rgba(0, 0, 0, 0.1); } #app .iframe { - width: 100%; - background-color: transparent; - overflow-y: auto; + width: 100%; + background-color: transparent; + /* overflow-y: auto; */ } #app .context-switch { - position: fixed; - bottom: 50px; - right: calc(50% - 220px); - z-index: 3; + position: fixed; + bottom: 50px; + right: calc(50% - 220px); + z-index: 3; } .is-avatar.is-rounded { - border-radius: var(--bulma-radius-rounded); + border-radius: var(--bulma-radius-rounded); } .is-avatar.is-agent { - display: flex; - /* background-color: rgba(231, 236, 243, 0.7); */ - background-color: rgba(70, 79, 235, 0.25); + display: flex; + /* background-color: rgba(231, 236, 243, 0.7); */ + background-color: rgba(70, 79, 235, 0.25); } .is-avatar.is-agent img { - width: 75%; - height: 75%; - margin: 13%; + width: 75%; + height: 75%; + margin: 13%; } - - @keyframes moveImage { - 0% { - transform: rotate(0deg); - } + 0% { + transform: rotate(0deg); + } - 50% { - transform: rotate(-3deg); - } + 50% { + transform: rotate(-3deg); + } - 100% { - transform: rotate(3deg); - } + 100% { + transform: rotate(3deg); + } } .is-avatar.is-agent img.manager { - background-color: rgba(220, 56, 72, .35); - box-shadow: 0 0 0 4px rgba(220, 56, 72, .35); - animation: moveImage .3s infinite alternate; + background-color: rgba(220, 56, 72, 0.35); + box-shadow: 0 0 0 4px rgba(220, 56, 72, 0.35); + animation: moveImage 0.3s infinite alternate; } .is-avatar.is-agent img.hr_agent { - background-color: rgba(0, 209, 178, .35); - box-shadow: 0 0 0 4px rgba(0, 209, 178, .35); - animation: moveImage .5s infinite alternate; + background-color: rgba(0, 209, 178, 0.35); + box-shadow: 0 0 0 4px rgba(0, 209, 178, 0.35); + animation: moveImage 0.5s infinite alternate; } .is-avatar.is-agent img.procurement_agent { - background-color: rgba(255, 183, 15, .35); - box-shadow: 0 0 0 4px rgba(255, 183, 15, .35); - animation: moveImage .1s infinite alternate; + background-color: rgba(255, 183, 15, 0.35); + box-shadow: 0 0 0 4px rgba(255, 183, 15, 0.35); + animation: moveImage 0.1s infinite alternate; } .is-avatar.is-agent img.tech_agent { - background-color: rgba(178, 222, 39, .35); - box-shadow: 0 0 0 4px rgba(178, 222, 39, .35); - animation: moveImage .7s infinite alternate; + background-color: rgba(178, 222, 39, 0.35); + box-shadow: 0 0 0 4px rgba(178, 222, 39, 0.35); + animation: moveImage 0.7s infinite alternate; } .is-avatar.is-agent img.unknown { - background-color: rgba(39, 57, 222, 0.35); - box-shadow: 0 0 0 4px rgba(39, 57, 222, 0.35); - animation: moveImage .7s infinite alternate; + background-color: rgba(39, 57, 222, 0.35); + box-shadow: 0 0 0 4px rgba(39, 57, 222, 0.35); + animation: moveImage 0.7s infinite alternate; } .is-avatar.has-status::after { - content: ""; - position: absolute; - bottom: 0; - right: 0; - width: 30%; - height: 30%; - border-radius: 50%; - background-color: rgb(255, 255, 255); - border: 2px solid rgb(255, 255, 255); + content: ""; + position: absolute; + bottom: 0; + right: 0; + width: 30%; + height: 30%; + border-radius: 50%; + background-color: rgb(255, 255, 255); + border: 2px solid rgb(255, 255, 255); } .is-avatar.has-status.has-status-active::after { - background-color: hsl(var(--bulma-success-h), var(--bulma-success-s), var(--bulma-success-l)); + background-color: hsl( + var(--bulma-success-h), + var(--bulma-success-s), + var(--bulma-success-l) + ); } .is-avatar.has-status.has-status-busy::after { - background-color: hsl(var(--bulma-danger-h), var(--bulma-danger-s), var(--bulma-danger-l)); + background-color: hsl( + var(--bulma-danger-h), + var(--bulma-danger-s), + var(--bulma-danger-l) + ); } .is-avatar.has-status.has-status-paused::after { - background-color: hsl(var(--bulma-dark-h), var(--bulma-dark-s), var(--bulma-dark-l)); + background-color: hsl( + var(--bulma-dark-h), + var(--bulma-dark-s), + var(--bulma-dark-l) + ); } .button.is-greyed-out { - background-color: #e0e0e0; - color: lightgrey; - cursor: not-allowed; + background-color: #e0e0e0; + color: lightgrey; + cursor: not-allowed; } .button.is-selected { - background-color: #d3d3d3; - color: #000; + background-color: #d3d3d3; + color: #000; } - .notyf__toast { - max-width: 100% !important; - border-radius: var(--bulma-control-radius) !important; + max-width: 100% !important; + border-radius: var(--bulma-control-radius) !important; } .notyf__wrapper { - padding: 0.75rem 0.5rem !important; -} \ No newline at end of file + padding: 0.75rem 0.5rem !important; +} +/* Menu list scroll style start*/ +#app .asside .menu-list { + max-height: 300px; + overflow-y: scroll; + padding-right: 2px; + transition: all 0.3s ease; + box-sizing: border-box; +} +/* Hide the scrollbar initially (before hover) */ +#app .asside .menu-list::-webkit-scrollbar { + width: 8px; + opacity: 0; + visibility: hidden; + transition: opacity 0.3s ease, visibility 0s 0.3s; +} +/* Style the scrollbar thumb (the draggable part) */ +#app .asside .menu-list::-webkit-scrollbar-thumb { + border-radius: 10px; + transition: background-color 0.3s ease; +} +/* Show the scrollbar and thumb when hovering */ +#app .asside .menu-list:hover::-webkit-scrollbar { + opacity: 1; + visibility: visible; + transition: opacity 0.3s ease, visibility 0s; +} +/* Style the thumb when hovering */ +#app .asside .menu-list:hover::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.2); +} +/* Menu list scroll style end*/ diff --git a/src/frontend/wwwroot/assets/Send.svg b/src/frontend/wwwroot/assets/Send.svg new file mode 100644 index 000000000..214d2ef72 --- /dev/null +++ b/src/frontend/wwwroot/assets/Send.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/frontend/wwwroot/home/home.css b/src/frontend/wwwroot/home/home.css index e1999d5f4..f7c1b6b06 100644 --- a/src/frontend/wwwroot/home/home.css +++ b/src/frontend/wwwroot/home/home.css @@ -1,194 +1,260 @@ @import "../app.css"; .container { - inset: 0; - min-height: 100vh; - overflow-y: auto; + inset: 0; + min-height: 100vh; + overflow-y: auto; } .section { - min-width: 800px; - z-index: 1; + min-width: 800px; + z-index: 1; } .app-logo { - width: 60px; - margin: 0 auto; + width: 60px; + margin: 0 auto; } .background { - inset: 0; - position: absolute; - opacity: 0.1; - overflow-y: hidden; + inset: 0; + position: absolute; + opacity: 0.1; + overflow-y: hidden; } .description { - color: Black; - text-align: center; + color: Black; + text-align: center; - /* Web/Body 1 */ - font-family: "Segoe UI"; - font-size: 14px; - font-style: normal; - font-weight: 400; - line-height: 20px; /* 142.857% */ - margin-bottom: 10px; + /* Web/Body 1 */ + font-family: "Segoe UI"; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; /* 142.857% */ + margin-bottom: 10px; } .title { - color: var(--Light-Mode-Foreground-Neutral-Primary, var(--Foreground-Neutral-Primary, #111)); - text-align: center; - font-family: "Segoe UI"; - font-size: 32px; - font-style: normal; - font-weight: 700; - line-height: 40px; /* 125% */ + color: var( + --Light-Mode-Foreground-Neutral-Primary, + var(--Foreground-Neutral-Primary, #111) + ); + text-align: center; + font-family: "Segoe UI"; + font-size: 32px; + font-style: normal; + font-weight: 700; + line-height: 40px; /* 125% */ } .assistants { - text-align: center; - font-family: "Segoe UI"; - font-size: 32px; - font-style: normal; - font-weight: 700; - line-height: 40px; /* 125% */ + text-align: center; + font-family: "Segoe UI"; + font-size: 32px; + font-style: normal; + font-weight: 700; + line-height: 40px; /* 125% */ - background: var(--Gradient-M365-Chat-Light-Accessible, linear-gradient(90deg, #464FEB 10.42%, #8330E9 100%)); - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; + background: var( + --Gradient-M365-Chat-Light-Accessible, + linear-gradient(90deg, #464feb 10.42%, #8330e9 100%) + ); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; } .orb { - width: 50%; - height: 50%; - border-radius: 50%; - filter: blur(50px); - background: radial-gradient(circle); - position: absolute; + width: 50%; + height: 50%; + border-radius: 50%; + filter: blur(50px); + background: radial-gradient(circle); + position: absolute; } .orb.one { - bottom: -40%; - left: 47.5%; - background-color: rgba(70, 79, 235, 1); + bottom: -40%; + left: 47.5%; + background-color: rgba(70, 79, 235, 1); } .orb.two { - bottom: -30%; - left: 25%; - background-color: rgb(18, 172, 149); - z-index: 1; + bottom: -30%; + left: 25%; + background-color: rgb(18, 172, 149); + z-index: 1; } .orb.three { - bottom: -40%; - left: 2.5%; - background-color: rgb(199, 20, 184); -} - -.new-task-control:has(>textarea:disabled)::before, -.new-task-control:has(>textarea:disabled)::after { - pointer-events: none; - content: ''; - position: absolute; - left: -2px; - top: -2px; - background: linear-gradient(45deg, #fb0094, #0000ff, #fb0094, #0000ff, #fb0094, #0000ff, #fb0094, #0000ff); - background-size: 400%; - width: calc(100% + 4px); - height: calc(100% + 4px); - border-radius: var(--bulma-input-radius); - opacity: 0.5; - z-index: -1; - animation: steam 20s linear infinite; + bottom: -40%; + left: 2.5%; + background-color: rgb(199, 20, 184); +} + +.new-task-control:has(> textarea:disabled)::before, +.new-task-control:has(> textarea:disabled)::after { + pointer-events: none; + content: ""; + position: absolute; + left: -2px; + top: -2px; + background: linear-gradient( + 45deg, + #fb0094, + #0000ff, + #fb0094, + #0000ff, + #fb0094, + #0000ff, + #fb0094, + #0000ff + ); + background-size: 400%; + width: calc(100% + 4px); + height: calc(100% + 4px); + border-radius: var(--bulma-input-radius); + opacity: 0.5; + z-index: -1; + animation: steam 20s linear infinite; } @keyframes steam { - 0% { - background-position: 0 0; - } + 0% { + background-position: 0 0; + } - 50% { - background-position: 400% 0; - } + 50% { + background-position: 400% 0; + } - 100% { - background-position: 0 0; - } + 100% { + background-position: 0 0; + } } .new-task-control::after { - filter: blur(25px); + filter: blur(25px); } .text-input-container { - width: 950px; - position: relative; - border: 1px solid #ccc; - border-radius: 8px; - background-color: white; - } + width: 950px; + position: relative; + border: 1px solid #ccc; + border-radius: 8px; + background-color: white; +} - textarea { - width: 98%; - padding: 0px; - border: none; - border-radius: 8px 8px 0 0; - font-size: 16px; - line-height: 1.5; - resize: none; - outline: none; - overflow: hidden; - margin: 0 10px; - align-items: center; - } +textarea { + width: 98%; + padding: 16px 0px 0px 0px; + border: none; + border-radius: 8px 8px 0 0; + font-size: 16px; + line-height: 1.5; + resize: none; + outline: none; + overflow: hidden; + margin: 0 10px; + align-items: center; + background-color: white; +} +textarea:disabled { + cursor: default; + background-color: white; +} - .middle-bar { - display: flex; - justify-content: space-between; - align-items: left; - padding: 0px 5px; - background-color: white; - } - - .bottom-bar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 3px 10px; - border-top: none; - border-bottom: 4px solid #0F6CBD; - background-color: white; +/*Spinner start*/ +#spinnerLoader { + display: flex; + flex-direction: column; + /* justify-content: center; */ + align-items: center; + position: absolute; + inset: 0; + color: black; + top: 30%; + left: 50%; + transform: translateX(-50%); + /* background-color: rgb(247, 249, 251);*/ + z-index: 9999; + font-weight: 500; +} + +#spinnerLoader span::before { + content: "Creating Tasks..."; + animation: spinLoaderAnimation infinite 3s linear; +} + +@keyframes spinLoaderAnimation { + 75% { + content: "Agents are on it..."; } +} + +#spinnerLoader i { + font-size: 3rem; +} + +#overlay { + position: fixed; + display: none; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, 0.5); + + z-index: 1; +} + +/*Spinner end*/ + +.middle-bar { + display: flex; + justify-content: space-between; + align-items: left; + padding: 0px 5px; + background-color: white; +} + +.bottom-bar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 3px 10px; + border-top: none; + border-bottom: 4px solid #0f6cbd; + background-color: white; +} - .icons { - display: flex; - align-items: center; - } - - .star-icon { - margin-right: 10px; - cursor: pointer; - } - - .char-count { - font-size: 14px; - color: #888; - } - - .send-button { - border: none; - background: none; - font-size: 18px; - cursor: pointer; - color: #007bff; - padding: 4px; - outline: none; - } - - .send-button:hover { - color: #0056b3; - } + display: flex; + align-items: center; +} + +.star-icon { + margin-right: 10px; + cursor: pointer; +} + +.char-count { + font-size: 14px; + color: #888; +} + +.send-button { + border: none; + background: none; + font-size: 18px; + cursor: pointer; + color: #007bff; + padding: 4px; + outline: none; +} + +.send-button:hover { + color: #0056b3; +} diff --git a/src/frontend/wwwroot/home/home.js b/src/frontend/wwwroot/home/home.js index 29d96fbff..842ad0fc6 100644 --- a/src/frontend/wwwroot/home/home.js +++ b/src/frontend/wwwroot/home/home.js @@ -1,98 +1,207 @@ (() => { - const notyf = new Notyf({ position: { x: 'right', y: 'top' }, ripple: false, duration: 3000 }); - const apiEndpoint = sessionStorage.getItem('apiEndpoint'); - const newTaskPrompt = document.getElementById("newTaskPrompt"); - const startTaskButton = document.getElementById("startTaskButton"); - newTaskPrompt.focus(); - - - const startTask = () => { - - startTaskButton.addEventListener('click', (event) => { - - const sessionId = 'sid_' + (new Date()).getTime() + '_' + Math.floor(Math.random() * 10000); - - newTaskPrompt.disabled = true; - startTaskButton.disabled = true; - startTaskButton.classList.add('is-loading'); - - window.headers - .then(headers =>{ - fetch(apiEndpoint + '/input_task', { - method: 'POST', - headers: headers, - body: JSON.stringify({ - session_id: sessionId, - description: newTaskPrompt.value - }) - }) - .then(response => response.json()) - .then(data => { - - if (data.status == 'Plan not created') { - notyf.error('Unable to create plan for this task.'); - newTaskPrompt.disabled = false; - startTaskButton.disabled = false; - return; - } - - console.log('startTaskButton', data); - - newTaskPrompt.disabled = false; - startTaskButton.disabled = false; - startTaskButton.classList.remove('is-loading'); - - window.parent.postMessage({ - action: 'taskStarted', - session_id: data.session_id, - task_id: data.plan_id, - task_name: newTaskPrompt.value - }, '*'); - - newTaskPrompt.value = ''; - - notyf.success('Task created successfully. AI agents are on it!'); - - }) - .catch(error => { - console.error('Error:', error); - newTaskPrompt.disabled = false; - startTaskButton.disabled = false; - startTaskButton.classList.remove('is-loading'); - }) - }); - }) - }; - - const quickTasks = () => { - - document.querySelectorAll('.quick-task').forEach(task => { - task.addEventListener('click', (event) => { - const quickTaskPrompt = task.querySelector('.quick-task-prompt').innerHTML; - newTaskPrompt.value = quickTaskPrompt.trim().replace(/\s+/g, ' '); - }); - }); - + const notyf = new Notyf({ + position: { x: "right", y: "top" }, + ripple: false, + duration: 3000, + }); + const apiEndpoint = sessionStorage.getItem("apiEndpoint"); + const newTaskPrompt = document.getElementById("newTaskPrompt"); + const startTaskButton = document.getElementById("startTaskButton"); + const startTaskButtonContainer = document.querySelector(".send-button"); + const startTaskButtonImg = startTaskButtonContainer + ? startTaskButtonContainer.querySelector("img") + : null; + + newTaskPrompt.focus(); + + // Create spinner element + const createSpinner = () => { + if (!document.getElementById("spinnerContainer")) { + const spinnerContainer = document.createElement("div"); + spinnerContainer.id = "spinnerContainer"; + spinnerContainer.innerHTML = ` +
+ + +
+ + `; + document.body.appendChild(spinnerContainer); } - const handleTextAreaTyping = () => { - const newTaskPrompt = document.getElementById('newTaskPrompt'); - newTaskPrompt.addEventListener('input', () => { - // whenever text is sent to the text area, we want to update the character count and dynamically resize the text area - const textInput = document.getElementById('newTaskPrompt'); - const charCount = document.getElementById('charCount'); - - // Update character count - charCount.textContent = textInput.value.length; - - // Dynamically adjust height - textInput.style.height = 'auto'; - textInput.style.height = textInput.scrollHeight + 'px'; - }); + }; + + // Function to create and add the overlay + const createOverlay = () => { + let overlay = document.getElementById("overlay"); + if (!overlay) { + overlay = document.createElement("div"); + overlay.id = "overlay"; + document.body.appendChild(overlay); + } + }; + const showOverlay = () => { + const overlay = document.getElementById("overlay"); + if (overlay) { + overlay.style.display = "block"; } - - startTask(); - quickTasks(); - handleTextAreaTyping(); + createSpinner(); + }; -})(); \ No newline at end of file + const hideOverlay = () => { + const overlay = document.getElementById("overlay"); + if (overlay) { + overlay.style.display = "none"; + } + removeSpinner(); + }; + + // Remove spinner element + const removeSpinner = () => { + const spinnerContainer = document.getElementById("spinnerContainer"); + if (spinnerContainer) { + spinnerContainer.remove(); + } + }; + + // Function to update button image based on textarea content + const updateButtonImage = () => { + if (startTaskButtonImg) { + if (newTaskPrompt.value.trim() === "") { + startTaskButtonImg.src = "../assets/images/air-button.svg"; + startTaskButton.disabled = true; + } else { + startTaskButtonImg.src = "/assets/Send.svg"; + startTaskButtonImg.style.width = "16px"; + startTaskButtonImg.style.height = "16px"; + startTaskButton.disabled = false; + } + } + }; + + const startTask = () => { + startTaskButton.addEventListener("click", (event) => { + if (startTaskButton.disabled) { + return; + } + const sessionId = + "sid_" + new Date().getTime() + "_" + Math.floor(Math.random() * 10000); + + newTaskPrompt.disabled = true; + startTaskButton.disabled = true; + startTaskButton.classList.add("is-loading"); + createOverlay(); + showOverlay(); + window.headers.then((headers) => { + fetch(apiEndpoint + "/input_task", { + method: "POST", + headers: headers, + body: JSON.stringify({ + session_id: sessionId, + description: newTaskPrompt.value, + }), + }) + .then((response) => response.json()) + .then((data) => { + if (data.status == "Plan not created") { + notyf.error("Unable to create plan for this task."); + newTaskPrompt.disabled = false; + startTaskButton.disabled = false; + return; + } + + console.log("startTaskButton", data); + + newTaskPrompt.disabled = false; + startTaskButton.disabled = false; + startTaskButton.classList.remove("is-loading"); + + window.parent.postMessage( + { + action: "taskStarted", + session_id: data.session_id, + task_id: data.plan_id, + task_name: newTaskPrompt.value, + }, + "*" + ); + + newTaskPrompt.value = ""; + + // Reset character count to 0 + const charCount = document.getElementById("charCount"); + if (charCount) { + charCount.textContent = "0"; + } + updateButtonImage(); + notyf.success("Task created successfully. AI agents are on it!"); + + // Remove spinner and hide overlay + removeSpinner(); + hideOverlay(); + }) + .catch((error) => { + console.error("Error:", error); + newTaskPrompt.disabled = false; + startTaskButton.disabled = false; + startTaskButton.classList.remove("is-loading"); + + // Remove spinner and hide overlay + removeSpinner(); + hideOverlay(); + }); + }); + }); + }; + + const quickTasks = () => { + document.querySelectorAll(".quick-task").forEach((task) => { + task.addEventListener("click", (event) => { + const quickTaskPrompt = + task.querySelector(".quick-task-prompt").innerHTML; + newTaskPrompt.value = quickTaskPrompt.trim().replace(/\s+/g, " "); + const charCount = document.getElementById("charCount"); + // Update character count + charCount.textContent = newTaskPrompt.value.length; + updateButtonImage(); + newTaskPrompt.focus(); + }); + }); + }; + const handleTextAreaTyping = () => { + const newTaskPrompt = document.getElementById("newTaskPrompt"); + newTaskPrompt.addEventListener("input", () => { + // const textInput = document.getElementById("newTaskPrompt"); + const charCount = document.getElementById("charCount"); + + // Update character count + charCount.textContent = newTaskPrompt.value.length; + + // Dynamically adjust height + newTaskPrompt.style.height = "auto"; + newTaskPrompt.style.height = newTaskPrompt.scrollHeight + "px"; + + updateButtonImage(); + }); + + newTaskPrompt.addEventListener("keydown", (event) => { + const textValue = newTaskPrompt.value.trim(); + // If Enter is pressed without Shift, and the textarea is empty, prevent default behavior + if (event.key === "Enter" && !event.shiftKey) { + if (textValue === "") { + event.preventDefault(); // Disable Enter when textarea is empty + } else { + // If there's content in the textarea, allow Enter to trigger the task button click + startTaskButton.click(); + } + } else if (event.key === "Enter" && event.shiftKey) { + return; + } + }); + }; + + updateButtonImage(); + startTask(); + quickTasks(); + handleTextAreaTyping(); +})(); diff --git a/src/frontend/wwwroot/task/task.css b/src/frontend/wwwroot/task/task.css index 8e5a683c3..ece2740ea 100644 --- a/src/frontend/wwwroot/task/task.css +++ b/src/frontend/wwwroot/task/task.css @@ -2,241 +2,241 @@ @import "../assets/bulma-switch.css"; .task-stats { - min-height: 70px; + min-height: 70px; } .section { - min-height: 100%; + min-height: 100%; } .task-asside { - min-height: 100vh; - max-width: 500px; + min-height: 100vh; + max-width: 500px; } .task-asside.is-audit { - max-width: 700px; + max-width: 700px; } .task-asside .task-menu { - margin: 3rem 1rem; + margin: 3rem 1rem; } .task-asside .task-menu .menu-label:first-of-type { - margin-top: 137px; + margin-top: 137px; } .task-asside .title { - font-size: 1.25rem; - height: 30px; - display: flex; - align-items: center; + font-size: 1.25rem; + height: 30px; + display: flex; + align-items: center; } .task-details { - max-width: 1280px; + max-width: 1280px; } .task-progress { - height: 40vh; - overflow-y: auto; - background-color: white; - border-radius: var(--bulma-radius); + height: 40vh; + overflow-y: auto; + background-color: white; + border-radius: var(--bulma-radius); } @media (min-height: 1200px) { - .task-progress { - height: 50vh; - } + .task-progress { + height: 50vh; + } } @media (min-height: 1400px) { - .task-progress { - height: 60vh; - } + .task-progress { + height: 60vh; + } } .task-progress .notification { - display: inline-block; - padding: 0.5rem 1rem; + display: inline-block; + padding: 0.5rem 1rem; } .menu-list .menu-item, .menu-list a, .menu-list button { - background-color: transparent; + background-color: transparent; } .menu-list ul.menu-stages { - border-inline-start: 3px solid var(--bulma-border); - padding-inline-start: 0; + border-inline-start: 3px solid var(--bulma-border); + padding-inline-start: 0; } .menu-list ul.menu-stages li { - margin-left: calc(-1.4rem - 5px); + margin-left: calc(-1.4rem - 5px); } .menu-list a.menu-stage { - display: flex; - align-items: center; - position: relative; - padding: 0.5em 0 0.5rem 0.75em; - width: calc(100% + 1.4rem - 5px); + display: flex; + align-items: center; + position: relative; + padding: 0.5em 0 0.5rem 0.75em; + width: calc(100% + 1.4rem - 5px); } -.menu-list a.menu-stage>i { - font-size: 1.4rem; - margin-top: 3px; - border-radius: 50%; - background-color: rgb(247, 249, 251); - padding: 5px; +.menu-list a.menu-stage > i { + font-size: 1.4rem; + margin-top: 3px; + border-radius: 50%; + background-color: rgb(247, 249, 251); + padding: 5px; } .menu-list a.menu-stage span { - flex: 1; + flex: 1; } .menu-list a.menu-stage.rejected span { - text-decoration: line-through; - opacity: 0.5; + text-decoration: line-through; + opacity: 0.5; } .menu-list a.menu-stage.action_requested span { - font-weight: 500; + font-weight: 500; } .menu-list a.menu-stage div { - display: flex; - align-items: center; + display: flex; + align-items: center; } .menu-stage-actions i { - font-size: 1.4rem; + font-size: 1.4rem; } .business-animation { - position: relative; - border-radius: var(--bulma-radius-large); + position: relative; + border-radius: var(--bulma-radius-large); } #taskLoader { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - position: absolute; - inset: 0; - color: black; - background-color: rgb(247, 249, 251); - z-index: 1000; - font-weight: 500; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: absolute; + inset: 0; + color: black; + background-color: rgb(247, 249, 251); + z-index: 1000; + font-weight: 500; } #taskLoader span::before { - content: "Getting task plan..."; - animation: taskLoaderAnimation infinite 3s linear; + content: "Getting task plan..."; + animation: taskLoaderAnimation infinite 3s linear; } #taskLoader.is-hidden { - display: none; + display: none; } @keyframes taskLoaderAnimation { + 0% { + content: "Getting task plan..."; + } - 0% { - content: "Getting task plan..."; - } - - 50% { - content: "Contacting agents..."; - } + 50% { + content: "Contacting agents..."; + } - 75% { - content: "Loading conversations..."; - } + 75% { + content: "Loading conversations..."; + } } #taskLoader i { - font-size: 3rem; + font-size: 3rem; } .task-stage-divider { - text-align: center; - margin: 1rem 0; - font-size: .85rem; - font-weight: 500; - border: 1px solid rgb(71, 80, 235); - border-left-width: 0; - border-right-width: 0; - border-bottom-width: 0; + text-align: center; + margin: 1rem 0; + font-size: 0.85rem; + font-weight: 500; + border: 1px solid rgb(71, 80, 235); + border-left-width: 0; + border-right-width: 0; + border-bottom-width: 0; } .task-stage-divider legend { - color: rgb(71, 80, 235); - -webkit-padding-start: 1rem; - -webkit-padding-end: 1rem; - background: transparent; + color: rgb(71, 80, 235); + -webkit-padding-start: 1rem; + -webkit-padding-end: 1rem; + background: transparent; } .text-input-container { - position: relative; - border: 1px solid #ccc; - border-radius: 8px; - background-color: white; + position: relative; + border: 1px solid #ccc; + border-radius: 8px; + background-color: white; } - + textarea { - width: 98%; - padding: 0px; - border: none; - border-radius: 8px 8px 0 0; - font-size: 16px; - line-height: 1.5; - resize: none; - outline: none; - overflow: hidden; - margin: 0 10px; - align-items: center; + width: 98%; + padding: 16px 0px 0px 0px; + border: none; + border-radius: 8px 8px 0 0; + font-size: 16px; + line-height: 1.5; + resize: none; + outline: none; + overflow: hidden; + margin: 0 10px; + align-items: center; + background-color: white; } .star-icon { - margin-right: 10px; - cursor: pointer; + margin-right: 10px; + cursor: pointer; } - - .char-count { - font-size: 14px; - color: #888; + +.char-count { + font-size: 14px; + color: #888; } .middle-bar { - display: flex; - justify-content: space-between; - align-items: left; - padding: 0px 5px; - background-color: white; - } - - .bottom-bar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 3px 10px; - border-top: none; - border-bottom: 4px solid #0F6CBD; - background-color: white; - } - - .send-button { - border: none; - background: none; - font-size: 18px; - cursor: pointer; - color: #007bff; - padding: 4px; - outline: none; -} - - .send-button:hover { - color: #0056b3; + display: flex; + justify-content: space-between; + align-items: left; + padding: 0px 5px; + background-color: white; +} + +.bottom-bar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 3px 10px; + border-top: none; + border-bottom: 4px solid #0f6cbd; + background-color: white; +} + +.send-button { + border: none; + background: none; + font-size: 18px; + cursor: pointer; + color: #007bff; + padding: 4px; + outline: none; +} + +.send-button:hover { + color: #0056b3; } diff --git a/src/frontend/wwwroot/task/task.js b/src/frontend/wwwroot/task/task.js index 666d6deec..b5c62b010 100644 --- a/src/frontend/wwwroot/task/task.js +++ b/src/frontend/wwwroot/task/task.js @@ -1,425 +1,458 @@ (() => { - const markdownConverter = new showdown.Converter(); - const apiEndpoint = sessionStorage.getItem('apiEndpoint'); - const taskStore = JSON.parse(sessionStorage.getItem('task')); - const taskName = document.getElementById("taskName"); - const taskStatusTag = document.getElementById("taskStatusTag"); - const taskStagesMenu = document.getElementById("taskStagesMenu"); - const taskPauseButton = document.getElementById("taskPauseButton"); - const taskAgentsButton = document.getElementById("taskAgentsButton"); - const taskWokFlowButton = document.getElementById("taskWokFlowButton"); - const taskMessageAddButton = document.getElementById("taskMessageAddButton"); - const taskMessages = document.getElementById("taskMessages"); - const taskDetailsAgents = document.getElementById("taskDetailsAgents"); - const taskProgress = document.getElementById("taskProgress"); - const taskProgressPercentage = document.getElementById("taskProgressPercentage"); - const taskProgressBar = document.getElementById("taskProgressBar"); - const taskLoader = document.getElementById("taskLoader"); - const taskAgentsHumans = document.getElementById("taskAgentsHumans"); - const taskStatusDetails = document.getElementById("taskStatusDetails"); - const taskCancelButton = document.getElementById("taskCancelButton"); - - const notyf = new Notyf({ - position: { x: 'right', y: 'top' }, - ripple: false, - duration: 3000, - types: [ - { - type: 'info', - background: 'rgb(71, 80, 235)', - icon: '' - } - ] - }); - - let taskSessionId = null; - let taskLastStageId = null; - let taskLastAction = null; - let taskAgents = []; - let taskAgentsVsHumans = []; - - const agentToIcon = (agentName) => { - let agentIcon = ''; - - switch (agentName) { - case 'MarketingAgent': - agentIcon = 'unknown'; - break; - case 'HrAgent': - agentIcon = 'hr_agent'; - break; - case 'ExpenseBillingAgent': - agentIcon = 'expense_billing_agent'; - break; - case 'InvoiceReconciliationAgent': - agentIcon = 'invoice_reconciliation_agent'; - break; - case 'TechSupportAgent': - agentIcon = 'tech_agent'; - break; - case 'ProcurementAgent': - agentIcon = 'procurement_agent'; - break; - case 'ProductAgent': - agentIcon = 'product_agent'; - break; - case 'GroupChatManager': - agentIcon = 'manager'; - break; - case 'GenericAgent': - agentIcon = 'manager'; - break; - case 'HumanAgent': - let userNumber = sessionStorage.getItem('userNumber'); - if (userNumber == null){ - // Generate a random number between 0 and 6 - userNumber = Math.floor(Math.random() * 6); - // Create the icon name by concatenating 'user' with the random number - sessionStorage.setItem('userNumber', userNumber); - } - let iconName = 'user' + userNumber; - agentIcon = iconName; - break; - case 'Done': - agentIcon = 'done'; - break; - default: - agentIcon = 'marketing_agent'; - } - - return ``; + const markdownConverter = new showdown.Converter(); + const apiEndpoint = sessionStorage.getItem("apiEndpoint"); + const taskStore = JSON.parse(sessionStorage.getItem("task")); + const taskName = document.getElementById("taskName"); + const taskStatusTag = document.getElementById("taskStatusTag"); + const taskStagesMenu = document.getElementById("taskStagesMenu"); + const taskPauseButton = document.getElementById("taskPauseButton"); + const taskAgentsButton = document.getElementById("taskAgentsButton"); + const taskWokFlowButton = document.getElementById("taskWokFlowButton"); + const taskMessageAddButton = document.getElementById("taskMessageAddButton"); + const taskMessages = document.getElementById("taskMessages"); + const taskDetailsAgents = document.getElementById("taskDetailsAgents"); + const taskProgress = document.getElementById("taskProgress"); + const taskProgressPercentage = document.getElementById( + "taskProgressPercentage" + ); + const taskProgressBar = document.getElementById("taskProgressBar"); + const taskLoader = document.getElementById("taskLoader"); + const taskAgentsHumans = document.getElementById("taskAgentsHumans"); + const taskStatusDetails = document.getElementById("taskStatusDetails"); + const taskCancelButton = document.getElementById("taskCancelButton"); + const startTaskButtonContainer = document.querySelector(".send-button"); + const startTaskButtonImg = startTaskButtonContainer + ? startTaskButtonContainer.querySelector("img") + : null; + + const notyf = new Notyf({ + position: { x: "right", y: "top" }, + ripple: false, + duration: 3000, + types: [ + { + type: "info", + background: "rgb(71, 80, 235)", + icon: '', + }, + ], + }); + + const updateButtonImage = () => { + if (startTaskButtonImg) { + const newTaskPrompt = document.getElementById("taskMessageTextarea"); + if (newTaskPrompt && newTaskPrompt.value.trim() === "") { + startTaskButtonImg.src = "../assets/images/air-button.svg"; + startTaskButton.disabled = true; + } else { + startTaskButtonImg.src = "/assets/Send.svg"; + startTaskButtonImg.style.width = "16px"; + startTaskButtonImg.style.height = "16px"; + startTaskButton.disabled = false; + } } - - - const toDateTime = (timestamp) => { - const date = new Date(timestamp * 1000); - const options = { month: 'short', day: 'numeric' }; - const timeOptions = { hour: 'numeric', minute: 'numeric', hour12: true }; - return `${date.toLocaleDateString('en-US', options)} at ${date.toLocaleTimeString('en-US', timeOptions)}`; - }; - - const removeClassesExcept = (element, classToKeep) => { - element.className = classToKeep; - } - - const taskHeaderActions = () => { - - if (taskPauseButton) { - taskPauseButton.addEventListener('click', (event) => { - const iconElement = taskPauseButton.querySelector('i'); - if (iconElement) { - iconElement.classList.toggle('fa-circle-pause'); - iconElement.classList.toggle('fa-circle-play'); - if (iconElement.classList.contains('fa-circle-play')) { - taskPauseButton.classList.add('has-text-success'); - removeClassesExcept(taskStatusTag, 'tag'); - taskStatusTag.classList.add('is-warning'); - taskStatusTag.textContent = 'Paused'; - } else { - taskPauseButton.classList.remove('has-text-success'); - removeClassesExcept(taskStatusTag, 'tag'); - taskStatusTag.classList.add('is-info'); - taskStatusTag.textContent = 'Restarting'; - taskDetails(); - } - } - }); + }; + + let taskSessionId = null; + let taskLastStageId = null; + let taskLastAction = null; + let taskAgents = []; + let taskAgentsVsHumans = []; + + const agentToIcon = (agentName) => { + let agentIcon = ""; + + switch (agentName) { + case "MarketingAgent": + agentIcon = "unknown"; + break; + case "HrAgent": + agentIcon = "hr_agent"; + break; + case "ExpenseBillingAgent": + agentIcon = "expense_billing_agent"; + break; + case "InvoiceReconciliationAgent": + agentIcon = "invoice_reconciliation_agent"; + break; + case "TechSupportAgent": + agentIcon = "tech_agent"; + break; + case "ProcurementAgent": + agentIcon = "procurement_agent"; + break; + case "ProductAgent": + agentIcon = "product_agent"; + break; + case "GroupChatManager": + agentIcon = "manager"; + break; + case "GenericAgent": + agentIcon = "manager"; + break; + case "HumanAgent": + let userNumber = sessionStorage.getItem("userNumber"); + if (userNumber == null) { + // Generate a random number between 0 and 6 + userNumber = Math.floor(Math.random() * 6); + // Create the icon name by concatenating 'user' with the random number + sessionStorage.setItem("userNumber", userNumber); } + let iconName = "user" + userNumber; + agentIcon = iconName; + break; + case "Done": + agentIcon = "done"; + break; + default: + agentIcon = "marketing_agent"; + } - if (taskCancelButton) { - taskCancelButton.addEventListener('click', (event) => { - - const apiTaskStore = JSON.parse(sessionStorage.getItem('apiTask')); - actionStages(apiTaskStore, false); - - }); + return ``; + }; + + const toDateTime = (timestamp) => { + const date = new Date(timestamp * 1000); + const options = { month: "short", day: "numeric" }; + const timeOptions = { hour: "numeric", minute: "numeric", hour12: true }; + return `${date.toLocaleDateString( + "en-US", + options + )} at ${date.toLocaleTimeString("en-US", timeOptions)}`; + }; + + const removeClassesExcept = (element, classToKeep) => { + element.className = classToKeep; + }; + + const taskHeaderActions = () => { + if (taskPauseButton) { + taskPauseButton.addEventListener("click", (event) => { + const iconElement = taskPauseButton.querySelector("i"); + if (iconElement) { + iconElement.classList.toggle("fa-circle-pause"); + iconElement.classList.toggle("fa-circle-play"); + if (iconElement.classList.contains("fa-circle-play")) { + taskPauseButton.classList.add("has-text-success"); + removeClassesExcept(taskStatusTag, "tag"); + taskStatusTag.classList.add("is-warning"); + taskStatusTag.textContent = "Paused"; + } else { + taskPauseButton.classList.remove("has-text-success"); + removeClassesExcept(taskStatusTag, "tag"); + taskStatusTag.classList.add("is-info"); + taskStatusTag.textContent = "Restarting"; + taskDetails(); + } } - + }); } - const updateTaskDetailsAgents = (agents) => { - - taskDetailsAgents.innerHTML = ''; - taskAgentsVsHumans = []; + if (taskCancelButton) { + taskCancelButton.addEventListener("click", (event) => { + const apiTaskStore = JSON.parse(sessionStorage.getItem("apiTask")); + actionStages(apiTaskStore, false); + }); + } + }; - agents.forEach(agent => { + const updateTaskDetailsAgents = (agents) => { + taskDetailsAgents.innerHTML = ""; + taskAgentsVsHumans = []; - const isAvatar = (agent === 'HumanAgent') ? 'is-human' : 'is-avatar' + agents.forEach((agent) => { + const isAvatar = agent === "HumanAgent" ? "is-human" : "is-avatar"; - taskDetailsAgents.innerHTML += ` + taskDetailsAgents.innerHTML += `
${agentToIcon(agent)}
`; - (agent === 'HumanAgent') ? taskAgentsVsHumans.push('Human') : taskAgentsVsHumans.push('Agent'); - - }) - - const humansInv = taskAgentsVsHumans.filter(agent => agent === 'Human'); - const humanInvText = humansInv.length > 1 ? 'humans' : 'human'; + agent === "HumanAgent" + ? taskAgentsVsHumans.push("Human") + : taskAgentsVsHumans.push("Agent"); + }); - const agentsInv = taskAgentsVsHumans.filter(agent => agent === 'Agent'); - const agentsInvText = agentsInv.length > 1 ? 'agents' : 'agent'; + const humansInv = taskAgentsVsHumans.filter((agent) => agent === "Human"); + const humanInvText = humansInv.length > 1 ? "humans" : "human"; + const agentsInv = taskAgentsVsHumans.filter((agent) => agent === "Agent"); + const agentsInvText = agentsInv.length > 1 ? "agents" : "agent"; - taskAgentsHumans.innerHTML = `Team selected: ${humansInv.length} ${humanInvText}, ${agentsInv.length} ${agentsInvText}`; - } + taskAgentsHumans.innerHTML = `Team selected: ${humansInv.length} ${humanInvText}, ${agentsInv.length} ${agentsInvText}`; + }; - const updateTaskStatusDetails = (task) => { + const updateTaskStatusDetails = (task) => { + taskStatusDetails.innerHTML = ""; - taskStatusDetails.innerHTML = ''; - - taskStatusDetails.innerHTML = ` + taskStatusDetails.innerHTML = `

Summary: ${task.summary}

Created: ${toDateTime(task.ts)}

`; + }; + + const fetchPlanDetails = async (session_id) => { + console.log("/plans?session_id:", window.headers); + + const headers = await window.headers; + + return fetch(apiEndpoint + "/plans?session_id=" + session_id, { + method: "GET", + headers: headers, + }) + .then((response) => response.json()) + .then((data) => { + console.log("fetchPlanDetails", data[0]); + + updateTaskStatusDetails(data[0]); + updateTaskProgress(data[0]); + fetchTaskStages(data[0]); + + sessionStorage.setItem("apiTask", JSON.stringify(data[0])); + }) + .catch((error) => { + console.error("Error:", error); + }); + }; + + const fetchTaskStages = (task) => { + window.headers.then((headers) => { + fetch(apiEndpoint + "/steps/" + task.id, { + method: "GET", + headers: headers, + }) + .then((response) => response.json()) + .then((data) => { + console.log("fetchTaskStages", data); + + if (taskStagesMenu) taskStagesMenu.innerHTML = ""; + let taskStageCount = 0; + let taskStageApprovalStatus = 0; + + if (data && data.length > 0) { + taskAgents = []; + + data.forEach((stage) => { + const stageItem = document.createElement("li"); + const stageBase64 = btoa( + encodeURIComponent(JSON.stringify(stage)) + ); + + let stageStatusIcon = ""; + let stageActions = ""; + let stageRejected = ""; + + switch (stage.status) { + case "planned": + stageStatusIcon = ``; + break; + case "awaiting_feedback": + stageStatusIcon = ``; + break; + case "approved": + stageStatusIcon = ``; + break; + case "rejected": + stageStatusIcon = ``; + break; + case "action_requested": + stageStatusIcon = ``; + break; + case "completed": + stageStatusIcon = ``; + break; + case "failed": + stageStatusIcon = ``; + break; + default: + stageStatusIcon = ``; + } + + if (stage.human_approval_status === "rejected") { + stageRejected = "rejected"; + stageStatusIcon = ``; + } + + if (stage.human_approval_status === "requested") + stageActions = ` + + `; - } + stageItem.innerHTML = ` + + ${stageStatusIcon} + ${taskStageCount + 1}. ${ + stage.action + } + ${stageActions} + + `; - const fetchPlanDetails = async (session_id) => { + if (taskStagesMenu) taskStagesMenu.appendChild(stageItem); - console.log("/plans?session_id:", window.headers); + taskSessionId = stage.session_id; + taskLastStageId = stage.id; + taskLastAction = stage.action; + taskAgents.push(stage.agent); - const headers = await window.headers + stageItem + .querySelectorAll(".menu-stage-action") + .forEach((action) => { + action.addEventListener("click", (event) => { + actionStage( + event.target.dataset.action, + event.target.dataset.stage + ); - return fetch(apiEndpoint + '/plans?session_id=' + session_id, { - method: 'GET', - headers: headers, - }) - .then(response => response.json()) - .then(data => { + action.parentElement.style.display = "none"; + }); + }); - console.log('fetchPlanDetails', data[0]); + if (stage.human_approval_status === "requested") + taskStageApprovalStatus++; - updateTaskStatusDetails(data[0]); - updateTaskProgress(data[0]); - fetchTaskStages(data[0]); + taskStageCount++; + }); - sessionStorage.setItem('apiTask', JSON.stringify(data[0])); + updateTaskDetailsAgents([...new Set(taskAgents)]); - }) - .catch(error => { - console.error('Error:', error); - }) - } + sessionStorage.setItem("showApproveAll", false); - const fetchTaskStages = (task) => { - - window.headers - .then(headers => { - fetch(apiEndpoint + '/steps/' + task.id, { - method: 'GET', - headers: headers, - }) - .then(response => response.json()) - .then(data => { - - console.log('fetchTaskStages', data); - - if (taskStagesMenu) taskStagesMenu.innerHTML = ''; - let taskStageCount = 0; - let taskStageApprovalStatus = 0; - - if (data && data.length > 0) { - - taskAgents = []; - - data.forEach(stage => { - const stageItem = document.createElement('li'); - const stageBase64 = btoa(encodeURIComponent(JSON.stringify(stage))); - - let stageStatusIcon = ''; - let stageActions = ''; - let stageRejected = ''; - - switch (stage.status) { - case 'planned': - stageStatusIcon = ``; - break; - case 'awaiting_feedback': - stageStatusIcon = ``; - break; - case 'approved': - stageStatusIcon = ``; - break; - case 'rejected': - stageStatusIcon = ``; - break; - case 'action_requested': - stageStatusIcon = ``; - break; - case 'completed': - stageStatusIcon = ``; - break; - case 'failed': - stageStatusIcon = ``; - break; - default: - stageStatusIcon = ``; - } - - if (stage.human_approval_status === 'rejected') { - stageRejected = 'rejected'; - stageStatusIcon = ``; - } - - if (stage.human_approval_status === 'requested') stageActions = ` - - `; - - stageItem.innerHTML = ` - - ${stageStatusIcon} - ${taskStageCount + 1}. ${stage.action} - ${stageActions} - - `; - - if (taskStagesMenu) taskStagesMenu.appendChild(stageItem); - - taskSessionId = stage.session_id; - taskLastStageId = stage.id; - taskLastAction = stage.action; - taskAgents.push(stage.agent); - - stageItem.querySelectorAll('.menu-stage-action').forEach(action => { - action.addEventListener('click', (event) => { - - actionStage(event.target.dataset.action, event.target.dataset.stage); - - action.parentElement.style.display = 'none'; - - }); - }); - - if (stage.human_approval_status === 'requested') taskStageApprovalStatus++; - - taskStageCount++; - - }) - - updateTaskDetailsAgents([...new Set(taskAgents)]); - - sessionStorage.setItem('showApproveAll', false); - - // Feature approve all removed for this version - // if (isHumanFeedbackPending()) { - // sessionStorage.setItem('showApproveAll', false); - // console.log('showApproveAll status', "showApproveAll is false"); - - // } else { - // sessionStorage.setItem('showApproveAll', taskStageApprovalStatus === taskStageCount); - // console.log('showApproveAll status', taskStageApprovalStatus === taskStageCount); - - // } - } - - fetchTaskMessages(task); - - window.parent.postMessage({ - action: 'taskStarted', - }, '*'); - - }) - .catch(error => { - console.error('Error:', error); - }) - }) - } + // Feature approve all removed for this version + // if (isHumanFeedbackPending()) { + // sessionStorage.setItem('showApproveAll', false); + // console.log('showApproveAll status', "showApproveAll is false"); + + // } else { + // sessionStorage.setItem('showApproveAll', taskStageApprovalStatus === taskStageCount); + // console.log('showApproveAll status', taskStageApprovalStatus === taskStageCount); + + // } + } + + fetchTaskMessages(task); + + window.parent.postMessage( + { + action: "taskStarted", + }, + "*" + ); + }) + .catch((error) => { + console.error("Error:", error); + }); + }); + }; + + const fetchTaskMessages = (task) => { + window.headers.then((headers) => { + fetch(apiEndpoint + "/agent_messages/" + task.session_id, { + method: "GET", + headers: headers, + }) + .then((response) => response.json()) + .then((data) => { + console.log("fetchTaskMessages", data); + + const toAgentName = (str) => { + return str.replace(/([a-z])([A-Z])/g, "$1 $2"); + }; + + const groupByStepId = (messages) => { + const groupedMessages = {}; + + messages.forEach((message) => { + const stepId = message.step_id || "planner"; + if (!groupedMessages[stepId]) { + groupedMessages[stepId] = []; + } + groupedMessages[stepId].push(message); + }); + + return groupedMessages; + }; + + const contextFilter = (messages) => { + const filteredMessages = []; + + messages.forEach((message) => { + if ( + message.source !== "PlannerAgent" && + message.source !== "GroupChatManager" + ) { + filteredMessages.push(message); + } + }); - const fetchTaskMessages = (task) => { - window.headers - .then(headers => { - fetch(apiEndpoint + '/agent_messages/' + task.session_id, { - method: 'GET', - headers: headers, - }) - .then(response => response.json()) - .then(data => { - - console.log('fetchTaskMessages', data); - - const toAgentName = (str) => { - return str.replace(/([a-z])([A-Z])/g, '$1 $2'); - }; - - const groupByStepId = (messages) => { - const groupedMessages = {}; - - messages.forEach(message => { - const stepId = message.step_id || 'planner'; - if (!groupedMessages[stepId]) { - groupedMessages[stepId] = []; - } - groupedMessages[stepId].push(message); - }); - - return groupedMessages; - } - - const contextFilter = (messages) => { - const filteredMessages = []; - - messages.forEach(message => { - if (message.source !== 'PlannerAgent' && message.source !== 'GroupChatManager') { - filteredMessages.push(message); - } - }); - - return filteredMessages; - } - - taskMessages.innerHTML = ''; - - // console.log(groupByStepId(data)); - - if (sessionStorage.getItem('context') && sessionStorage.getItem('context') === 'customer') { - - console.log('contextFilter', contextFilter(data)); - - data = contextFilter(data); - - } - - if (data) { - - let stageCount = 0; - let messageCount = 1; - const groupedData = groupByStepId(data); - - Object.keys(groupedData).forEach(stage => { - - const messages = groupedData[stage]; - const messageGroupItem = document.createElement('fieldset'); - - messageGroupItem.classList.add('task-stage-divider'); - messageGroupItem.classList.add('has-text-info'); - - messageGroupItem.innerHTML = (stageCount === 0) ? 'Planning' : `Stage ${stageCount}`; - taskMessages.appendChild(messageGroupItem); - - messages.forEach(message => { - const messageItem = document.createElement('div'); - const showApproveAll = sessionStorage.getItem('showApproveAll') === 'true' && data.length === messageCount; - - let approveAllStagesButton = ''; - - messageItem.classList.add('media'); - const isAvatar = (message.source === 'HumanAgent') ? 'is-human' : 'is-avatar' - const isActive = (message.source === 'PlannerAgent') ? 'has-status-busy' : 'has-status-active' - - if (sessionStorage.getItem('context') && sessionStorage.getItem('context') !== 'customer') { - if (showApproveAll) { - console.log('Creating approveAllStagesButton'); - approveAllStagesButton = `If you are happy with the plan, you can approve all stages.
`; - - } - } - const messageLeft = ` + return filteredMessages; + }; + + taskMessages.innerHTML = ""; + + // console.log(groupByStepId(data)); + + if ( + sessionStorage.getItem("context") && + sessionStorage.getItem("context") === "customer" + ) { + console.log("contextFilter", contextFilter(data)); + + data = contextFilter(data); + } + + if (data) { + let stageCount = 0; + let messageCount = 1; + const groupedData = groupByStepId(data); + + Object.keys(groupedData).forEach((stage) => { + const messages = groupedData[stage]; + const messageGroupItem = document.createElement("fieldset"); + + messageGroupItem.classList.add("task-stage-divider"); + messageGroupItem.classList.add("has-text-info"); + + messageGroupItem.innerHTML = + stageCount === 0 + ? "Planning" + : `Stage ${stageCount}`; + taskMessages.appendChild(messageGroupItem); + + messages.forEach((message) => { + const messageItem = document.createElement("div"); + const showApproveAll = + sessionStorage.getItem("showApproveAll") === "true" && + data.length === messageCount; + + let approveAllStagesButton = ""; + + messageItem.classList.add("media"); + const isAvatar = + message.source === "HumanAgent" ? "is-human" : "is-avatar"; + const isActive = + message.source === "PlannerAgent" + ? "has-status-busy" + : "has-status-active"; + + if ( + sessionStorage.getItem("context") && + sessionStorage.getItem("context") !== "customer" + ) { + if (showApproveAll) { + console.log("Creating approveAllStagesButton"); + approveAllStagesButton = `If you are happy with the plan, you can approve all stages.
`; + } + } + const messageLeft = `
@@ -429,22 +462,30 @@
- ${toAgentName(message.source)} • ${toDateTime(message.ts)} AI-generated content may be incorrect + ${toAgentName( + message.source + )} • ${toDateTime( + message.ts + )} AI-generated content may be incorrect
- ${markdownConverter.makeHtml(message.content)} ${approveAllStagesButton} + ${markdownConverter.makeHtml( + message.content + )} ${approveAllStagesButton}
- ` - const messageRight = ` + `; + const messageRight = `
You • ${toDateTime(message.ts)}
- ${markdownConverter.makeHtml(message.content)} + ${markdownConverter.makeHtml( + message.content + )}
@@ -454,318 +495,348 @@ ${agentToIcon(message.source)}
- ` - - const messageTemplate = (message.source === 'HumanAgent') ? messageRight : messageLeft; - messageItem.innerHTML = messageTemplate; - taskMessages.appendChild(messageItem); - - if (sessionStorage.getItem('context') && sessionStorage.getItem('context') !== 'customer') { - if (showApproveAll) { - - document.getElementById("approveAllStagesButton").addEventListener('click', (event) => actionStages(task, true)); - - }; - }; - - messageCount++; - - }); - - stageCount++; - - }); - - const mediaContents = document.querySelectorAll('.media-content'); - if (mediaContents.length > 0) { - mediaContents[mediaContents.length - 1].scrollIntoView({ behavior: 'smooth' }); - } - - if (sessionStorage.getItem('context') && sessionStorage.getItem('context') === 'customer' && !sessionStorage.getItem('actionStagesRun').includes(task.session_id)) { - - - actionStages(task, true); - - let actionStagesRun = JSON.parse(sessionStorage.getItem('actionStagesRun') || '[]'); - - actionStagesRun.push(task.session_id); - sessionStorage.setItem('actionStagesRun', JSON.stringify(actionStagesRun)); - } - - setTimeout(() => { taskLoader.classList.add('is-hidden'); }, 500); - - } - - }) - .catch(error => { - console.error('Error:', error); - }) + `; + + const messageTemplate = + message.source === "HumanAgent" ? messageRight : messageLeft; + messageItem.innerHTML = messageTemplate; + taskMessages.appendChild(messageItem); + + if ( + sessionStorage.getItem("context") && + sessionStorage.getItem("context") !== "customer" + ) { + if (showApproveAll) { + document + .getElementById("approveAllStagesButton") + .addEventListener("click", (event) => + actionStages(task, true) + ); + } + } + + messageCount++; + }); + + stageCount++; }); - - } + const mediaContents = document.querySelectorAll(".media-content"); + if (mediaContents.length > 0) { + mediaContents[mediaContents.length - 1].scrollIntoView({ + behavior: "smooth", + }); + } - const updateTaskProgress = (task) => { + if ( + sessionStorage.getItem("context") && + sessionStorage.getItem("context") === "customer" && + !sessionStorage + .getItem("actionStagesRun") + .includes(task.session_id) + ) { + actionStages(task, true); + + let actionStagesRun = JSON.parse( + sessionStorage.getItem("actionStagesRun") || "[]" + ); + + actionStagesRun.push(task.session_id); + sessionStorage.setItem( + "actionStagesRun", + JSON.stringify(actionStagesRun) + ); + } - const taskStatusToLabel = (str) => { - return str.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' '); - }; + setTimeout(() => { + taskLoader.classList.add("is-hidden"); + }, 500); + } + }) + .catch((error) => { + console.error("Error:", error); + }); + }); + }; + + const updateTaskProgress = (task) => { + const taskStatusToLabel = (str) => { + return str + .split("_") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); + }; - const totalSteps = task.total_steps; - const completedSteps = task.completed; - const approvalRequired = task.steps_requiring_approval; + const totalSteps = task.total_steps; + const completedSteps = task.completed; + const approvalRequired = task.steps_requiring_approval; - const percentage = (completedSteps / totalSteps) * 100; - const progressString = `Progress ${completedSteps}/${totalSteps}`; - const percentageString = `${percentage.toFixed(0)}%`; + const percentage = (completedSteps / totalSteps) * 100; + const progressString = `Progress ${completedSteps}/${totalSteps}`; + const percentageString = `${percentage.toFixed(0)}%`; - taskProgress.textContent = progressString; - taskProgressPercentage.textContent = percentageString; - taskProgressBar.value = parseFloat(percentageString); + taskProgress.textContent = progressString; + taskProgressPercentage.textContent = percentageString; + taskProgressBar.value = parseFloat(percentageString); - taskStatusTag.textContent = taskStatusToLabel(task.overall_status); + taskStatusTag.textContent = taskStatusToLabel(task.overall_status); - if (task.overall_status === 'completed') { - removeClassesExcept(taskStatusTag, 'tag'); - taskStatusTag.classList.add('is-success'); - } + if (task.overall_status === "completed") { + removeClassesExcept(taskStatusTag, "tag"); + taskStatusTag.classList.add("is-success"); + } + }; + + const isHumanFeedbackPending = () => { + const storedData = sessionStorage.getItem("apiTask"); + const planDetails = JSON.parse(storedData); + return ( + planDetails.human_clarification_request !== null && + planDetails.human_clarification_response === null + ); + }; + + const actionStage = (action, stage) => { + if (isHumanFeedbackPending()) { + notyf.error("You must first provide feedback to the planner."); + return; } - const isHumanFeedbackPending = () => { - const storedData = sessionStorage.getItem('apiTask'); - const planDetails = JSON.parse(storedData); - return planDetails.human_clarification_request !== null && planDetails.human_clarification_response === null; - }; - - const actionStage = (action, stage) => { - - if (isHumanFeedbackPending()) { - notyf.error('You must first provide feedback to the planner.'); - return; - } + const stageObj = JSON.parse(decodeURIComponent(atob(stage))); - const stageObj = JSON.parse(decodeURIComponent(atob(stage))); + console.log("actionStage", { + step_id: stageObj.id, + plan_id: stageObj.plan_id, + session_id: stageObj.session_id, + approved: action === "approved" ? true : false, + }); - console.log('actionStage', { - step_id: stageObj.id, - plan_id: stageObj.plan_id, - session_id: stageObj.session_id, - approved: action === 'approved' ? true : false, - }); + notyf.open({ + type: "info", + message: `Request sent for "${stageObj.action}"`, + }); - notyf.open({ - type: 'info', - message: `Request sent for "${stageObj.action}"` + window.headers.then((headers) => { + fetch(apiEndpoint + "/approve_step_or_steps", { + method: "POST", + headers: headers, + body: JSON.stringify({ + step_id: stageObj.id, + plan_id: stageObj.plan_id, + session_id: stageObj.session_id, + approved: action === "approved" ? true : false, + }), + }) + .then((response) => response.json()) + .then((data) => { + console.log("actionStage", data); + action === "approved" + ? notyf.success(`Stage "${stageObj.action}" approved.`) + : notyf.error(`Stage "${stageObj.action}" rejected.`); + + taskDetails(); + }) + .catch((error) => { + console.error("Error:", error); }); + }); + }; - window.headers - .then(headers =>{ - fetch(apiEndpoint + '/approve_step_or_steps', { - method: 'POST', - headers: headers, - body: JSON.stringify({ - step_id: stageObj.id, - plan_id: stageObj.plan_id, - session_id: stageObj.session_id, - approved: action === 'approved' ? true : false, - }) - }) - .then(response => response.json()) - .then(data => { - - console.log('actionStage', data); - action === 'approved' ? notyf.success(`Stage "${stageObj.action}" approved.`) : notyf.error(`Stage "${stageObj.action}" rejected.`); - - taskDetails(); - - }) - .catch(error => { - console.error('Error:', error); - }) - }) - } - - const actionStages = (task, approve) => { + const actionStages = (task, approve) => { + console.log("approveStages", { + plan_id: task.id, + session_id: task.session_id, + approved: approve, + }); - console.log('approveStages', { - plan_id: task.id, - session_id: task.session_id, - approved: approve, - }); + notyf.open({ + type: "info", + message: `Request sent to action on all stages.`, + }); - notyf.open({ - type: 'info', - message: `Request sent to action on all stages.` + // document.querySelectorAll('.menu-stage-actions').forEach(element => { + // element.style.display = 'none'; + // }); + + window.headers.then((headers) => { + fetch(apiEndpoint + "/approve_step_or_steps", { + method: "POST", + headers: headers, + body: JSON.stringify({ + plan_id: task.id, + session_id: task.session_id, + approved: approve, + }), + }) + .then((response) => response.json()) + .then((data) => { + console.log("approveStages", data); + approve + ? notyf.success(`All stages approved.`) + : notyf.error(`All stages rejected.`); + taskDetails(); + }) + .catch((error) => { + console.error("Error:", error); }); - - // document.querySelectorAll('.menu-stage-actions').forEach(element => { - // element.style.display = 'none'; - // }); - - window.headers - .then(headers =>{ - fetch(apiEndpoint + '/approve_step_or_steps', { - method: 'POST', - headers: headers, - body: JSON.stringify({ - plan_id: task.id, - session_id: task.session_id, - approved: approve, - }) - }) - .then(response => response.json()) - .then(data => { - - console.log('approveStages', data); - approve ? notyf.success(`All stages approved.`) : notyf.error(`All stages rejected.`); - taskDetails(); - - }) - .catch(error => { - console.error('Error:', error); - }) - }) + }); + }; + + const taskDetailsActions = () => { + if (taskAgentsButton) { + taskAgentsButton.addEventListener("click", (event) => { + window.parent.postMessage( + { + button: "taskAgentsButton", + id: taskStore.id, + name: taskStore.name, + }, + "*" + ); + }); } - const taskDetailsActions = () => { - - if (taskAgentsButton) { - taskAgentsButton.addEventListener('click', (event) => { - window.parent.postMessage({ - button: 'taskAgentsButton', - id: taskStore.id, - name: taskStore.name - }, '*'); - }); - } - - if (taskWokFlowButton) { - taskWokFlowButton.addEventListener('click', (event) => { - window.parent.postMessage({ - button: 'taskWokFlowButton', - id: taskStore.id, - name: taskStore.name - }, '*'); - }); + if (taskWokFlowButton) { + taskWokFlowButton.addEventListener("click", (event) => { + window.parent.postMessage( + { + button: "taskWokFlowButton", + id: taskStore.id, + name: taskStore.name, + }, + "*" + ); + }); + } + }; + + let lastDataHash = null; + //Refresh timer + const taskDetails = () => { + if (taskStore) { + taskName.innerHTML = taskStore.name; + + const fetchLoop = async (id) => { + try { + // Fetch the new data from the server + const newData = await fetchPlanDetails(id); + + // Generate a hash of the new data + const newDataHash = await GenerateHash(newData); + + // Check if the new data's hash is different from the last fetched data's hash + if (newDataHash === lastDataHash) { + console.log("Data hasn't changed. Skipping next poll."); + return; // Skip polling if no changes + } + + // Update the lastDataHash to the new hash + lastDataHash = newDataHash; + + // Continue polling by calling fetchLoop again + setTimeout( + () => fetchLoop(id), + Number(sessionStorage.getItem("apiRefreshRate")) + ); + } catch (error) { + console.error("Error in fetchLoop:", error); } + }; + fetchLoop(taskStore.id); // Start the fetch loop } + }; + const taskMessage = () => { + const taskMessageTextarea = document.getElementById("taskMessageTextarea"); - let lastDataHash = null; - //Refresh timer - const taskDetails = () => { - if (taskStore) { - taskName.innerHTML = taskStore.name; - - const fetchLoop = async (id) => { - try { - // Fetch the new data from the server - const newData = await fetchPlanDetails(id); - - // Generate a hash of the new data - const newDataHash = await GenerateHash(newData); - - // Check if the new data's hash is different from the last fetched data's hash - if (newDataHash === lastDataHash) { - console.log("Data hasn't changed. Skipping next poll."); - return; // Skip polling if no changes - } - - // Update the lastDataHash to the new hash - lastDataHash = newDataHash; - - // Continue polling by calling fetchLoop again - setTimeout(() => fetchLoop(id), Number(sessionStorage.getItem('apiRefreshRate'))); - } catch (error) { - console.error('Error in fetchLoop:', error); - } - }; - - fetchLoop(taskStore.id); // Start the fetch loop + if (taskMessageAddButton) { + taskMessageAddButton.addEventListener("click", (event) => { + const messageContent = taskMessageTextarea.value; + + if (!messageContent) { + notyf.error("Please enter a message."); + return; } - }; + taskMessageTextarea.disabled = true; + taskMessageAddButton.disabled = true; + taskMessageAddButton.classList.add("is-loading"); - const taskMessage = () => { - const taskMessageTextarea = document.getElementById('taskMessageTextarea'); + console.log({ + plan_id: taskStore.id, + session_id: taskSessionId, + human_clarification: taskMessageTextarea.value, + }); - - if (taskMessageAddButton) { - taskMessageAddButton.addEventListener('click', (event) => { - const messageContent = taskMessageTextarea.value; - - if (!messageContent) { - notyf.error('Please enter a message.'); - return; - } - - taskMessageTextarea.disabled = true; - taskMessageAddButton.disabled = true; - taskMessageAddButton.classList.add('is-loading'); - - console.log({ - plan_id: taskStore.id, - session_id: taskSessionId, - human_clarification: taskMessageTextarea.value, - }) - - window.headers - .then(headers =>{ - fetch(apiEndpoint + '/human_clarification_on_plan', { - method: 'POST', - headers: headers, - body: JSON.stringify({ - plan_id: taskStore.id, - session_id: taskSessionId, - human_clarification: taskMessageTextarea.value, - }) - }) - .then(response => response.json()) - .then(data => { - - console.log('taskMessage', data); - - taskMessageTextarea.disabled = false; - taskMessageAddButton.disabled = false; - taskMessageAddButton.classList.remove('is-loading'); - - taskMessageTextarea.value = ''; - - notyf.success('Additional details registered in plan.'); - - fetchPlanDetails(taskStore.id); - - }) - .catch(error => { - console.error('Error:', error); - }) - }) + window.headers.then((headers) => { + fetch(apiEndpoint + "/human_clarification_on_plan", { + method: "POST", + headers: headers, + body: JSON.stringify({ + plan_id: taskStore.id, + session_id: taskSessionId, + human_clarification: taskMessageTextarea.value, + }), + }) + .then((response) => response.json()) + .then((data) => { + console.log("taskMessage", data); + + taskMessageTextarea.disabled = false; + taskMessageAddButton.disabled = false; + taskMessageAddButton.classList.remove("is-loading"); + + taskMessageTextarea.value = ""; + + // Reset character count to 0 + const charCount = document.getElementById("charCount"); + if (charCount) { + charCount.textContent = "0"; + } + updateButtonImage(); + notyf.success("Additional details registered in plan."); + }) + .catch((error) => { + console.error("Error:", error); }); - } - - } - - const handleTextAreaTyping = () => { - const newTaskPrompt = document.getElementById('taskMessageTextarea'); - newTaskPrompt.addEventListener('input', () => { - // whenever text is sent to the text area, we want to update the character count and dynamically resize the text area - const textInput = document.getElementById('taskMessageTextarea'); - const charCount = document.getElementById('charCount'); - - // Update character count - charCount.textContent = textInput.value.length; - - // Dynamically adjust height - textInput.style.height = 'auto'; - textInput.style.height = textInput.scrollHeight + 'px'; }); + }); } - - taskHeaderActions(); - taskDetailsActions(); - taskDetails(); - taskMessage(); - handleTextAreaTyping(); - -})(); \ No newline at end of file + }; + + const handleTextAreaTyping = () => { + const newTaskPrompt = document.getElementById("taskMessageTextarea"); + newTaskPrompt.addEventListener("input", () => { + // whenever text is sent to the text area, we want to update the character count and dynamically resize the text area + const textInput = document.getElementById("taskMessageTextarea"); + const charCount = document.getElementById("charCount"); + + // Update character count + charCount.textContent = textInput.value.length; + + // Dynamically adjust height + textInput.style.height = "auto"; + textInput.style.height = textInput.scrollHeight + "px"; + updateButtonImage(); + }); + newTaskPrompt.addEventListener("keydown", (event) => { + const textValue = newTaskPrompt.value.trim(); + if (event.key === "Enter" && !event.shiftKey) { + if (textValue === "") { + event.preventDefault(); + } else { + startTaskButton.click(); + } + } else if (event.key === "Enter" && event.shiftKey) { + return; + } + }); + }; + updateButtonImage(); + taskHeaderActions(); + taskDetailsActions(); + taskDetails(); + taskMessage(); + handleTextAreaTyping(); +})(); From 78778e5e1e761c257f0a0801bb5040d958892388 Mon Sep 17 00:00:00 2001 From: Kiran-Siluveru-Microsoft Date: Tue, 24 Dec 2024 17:45:07 +0530 Subject: [PATCH 02/24] fix: in progress status color after fetch task details (#5) --- src/frontend/wwwroot/task/task.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontend/wwwroot/task/task.js b/src/frontend/wwwroot/task/task.js index b5c62b010..91b18dbb5 100644 --- a/src/frontend/wwwroot/task/task.js +++ b/src/frontend/wwwroot/task/task.js @@ -584,6 +584,9 @@ if (task.overall_status === "completed") { removeClassesExcept(taskStatusTag, "tag"); taskStatusTag.classList.add("is-success"); + } else if (task.overall_status === "in_progress") { + removeClassesExcept(taskStatusTag, "tag"); + taskStatusTag.classList.add("is-info"); } }; From ce008fee7ba829015770e00be0888aef55c3c676 Mon Sep 17 00:00:00 2001 From: Kiran-Siluveru-Microsoft Date: Tue, 24 Dec 2024 17:45:28 +0530 Subject: [PATCH 03/24] cancel notification message updated (#7) --- src/frontend/wwwroot/task/task.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/wwwroot/task/task.js b/src/frontend/wwwroot/task/task.js index 91b18dbb5..5c76caaf5 100644 --- a/src/frontend/wwwroot/task/task.js +++ b/src/frontend/wwwroot/task/task.js @@ -676,7 +676,7 @@ console.log("approveStages", data); approve ? notyf.success(`All stages approved.`) - : notyf.error(`All stages rejected.`); + : notyf.error(`All stages cancelled.`); taskDetails(); }) .catch((error) => { From 45e22fcbc716421998a2c13a49d2c0dddcf57a36 Mon Sep 17 00:00:00 2001 From: Roopan-Microsoft <168007406+Roopan-Microsoft@users.noreply.github.com> Date: Thu, 26 Dec 2024 13:22:18 +0530 Subject: [PATCH 04/24] Update task.js (#9) --- src/frontend/wwwroot/task/task.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/wwwroot/task/task.js b/src/frontend/wwwroot/task/task.js index 5c76caaf5..8df54769b 100644 --- a/src/frontend/wwwroot/task/task.js +++ b/src/frontend/wwwroot/task/task.js @@ -791,6 +791,7 @@ taskMessageAddButton.classList.remove("is-loading"); taskMessageTextarea.value = ""; + fetchPlanDetails(taskStore.id); // Reset character count to 0 const charCount = document.getElementById("charCount"); @@ -798,6 +799,7 @@ charCount.textContent = "0"; } updateButtonImage(); + notyf.success("Additional details registered in plan."); }) .catch((error) => { From dfa236a230a18910846ca67811b60b26266c2723 Mon Sep 17 00:00:00 2001 From: Kiran-Siluveru-Microsoft Date: Thu, 26 Dec 2024 16:11:03 +0530 Subject: [PATCH 05/24] Stages overflow issue fix (#10) --- src/frontend/wwwroot/task/employee.html | 2 +- src/frontend/wwwroot/task/task.css | 1 + src/frontend/wwwroot/task/task.js | 18 +++++++++--------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/frontend/wwwroot/task/employee.html b/src/frontend/wwwroot/task/employee.html index 4efb0337a..0918523c6 100644 --- a/src/frontend/wwwroot/task/employee.html +++ b/src/frontend/wwwroot/task/employee.html @@ -20,7 +20,7 @@