From 7d675265144fd64e2fed48b5a299063bd727ee65 Mon Sep 17 00:00:00 2001 From: Tanu Chahal Date: Sun, 27 Oct 2024 14:38:29 +0530 Subject: [PATCH 1/6] Input Validation for Reason and Title Done. --- extension-requests/script.js | 42 +++++++++++++++++++++++++++++++----- extension-requests/style.css | 12 ++++++++++- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/extension-requests/script.js b/extension-requests/script.js index dfe1818b..7d17e424 100644 --- a/extension-requests/script.js +++ b/extension-requests/script.js @@ -540,6 +540,17 @@ async function createExtensionCard(data, dev) { value: data.title, }, }); + const titleInputWrapper = createElement({ + type: 'div', + attributes: { class: 'title-input-wrapper hidden' }, + }); + const titleInputError = createElement({ + type: 'div', + attributes: { class: 'title-input-error hidden' }, + innerText: 'Title is required', + }); + titleInputWrapper.appendChild(titleInput); + titleInputWrapper.appendChild(titleInputError); const commitedHoursHoverCard = createElement({ type: 'div', attributes: { class: 'comitted-hours hidden' }, @@ -562,7 +573,7 @@ async function createExtensionCard(data, dev) { }); commitedHoursHoverCard.appendChild(CommitedHourslabel); commitedHoursHoverCard.appendChild(CommitedHoursContent); - extensionCardHeaderWrapper.appendChild(titleInput); + extensionCardHeaderWrapper.appendChild(titleInputWrapper); extensionCardHeaderWrapper.appendChild(titleText); extensionCardHeaderWrapper.appendChild(commitedHoursHoverTrigger); extensionCardHeaderWrapper.appendChild(commitedHoursHoverCard); @@ -971,10 +982,19 @@ async function createExtensionCard(data, dev) { updateAccordionHeight(panel); }); updateButton.addEventListener('click', (event) => { - toggleInputs(); - toggleActionButtonVisibility(); - editButton.classList.toggle('hidden'); - updateWrapper.classList.toggle('hidden'); + const isTitleMissing = !titleInput.value; + const isReasonMissing = !reasonInput.value; + + titleInputError.classList.toggle('hidden', !isTitleMissing); + reasonInputError.classList.toggle('hidden', !isReasonMissing); + + if (!isTitleMissing && !isReasonMissing) { + toggleInputs(); + toggleActionButtonVisibility(); + editButton.classList.toggle('hidden'); + updateWrapper.classList.toggle('hidden'); + titleInputWrapper.classList.add('hidden') + } }); cancelButton.addEventListener('click', (event) => { titleInput.value = data.title; @@ -985,6 +1005,8 @@ async function createExtensionCard(data, dev) { toggleActionButtonVisibility(); editButton.classList.toggle('hidden'); updateWrapper.classList.toggle('hidden'); + titleInputError.classList.add('hidden'); + reasonInputError.classList.add('hidden'); }); const payloadForLog = { body: {}, @@ -1115,7 +1137,13 @@ async function createExtensionCard(data, dev) { }, innerText: data.reason, }); + const reasonInputError = createElement({ + type: 'span', + attributes: { class: 'reason-input-error red-text hidden' }, + innerText: 'Reason is required', + }); reasonContainer.appendChild(reasonInput); + reasonContainer.appendChild(reasonInputError); reasonContainer.appendChild(reasonParagraph); const renderExtensionCreatedLog = () => { @@ -1171,6 +1199,9 @@ async function createExtensionCard(data, dev) { e.preventDefault(); let formData = formDataToObject(new FormData(e.target)); formData['newEndsOn'] = new Date(formData['newEndsOn']).getTime() / 1000; + if (!formData.title || !formData.reason) { + return; + } const removeSpinner = addSpinner(rootElement); rootElement.classList.add('disabled'); const revertDataChange = updateCardData(formData); @@ -1257,6 +1288,7 @@ async function createExtensionCard(data, dev) { return revertDataChange; } function toggleInputs() { + titleInputWrapper.classList.toggle('hidden') titleInput.classList.toggle('hidden'); titleText.classList.toggle('hidden'); reasonInput.classList.toggle('hidden'); diff --git a/extension-requests/style.css b/extension-requests/style.css index bfcafebe..642c1452 100644 --- a/extension-requests/style.css +++ b/extension-requests/style.css @@ -773,7 +773,17 @@ body { align-items: center; justify-content: space-between; position: relative; - height: 1.6rem; + height: 2rem; +} +.title-input-wrapper { + display: flex; + flex-direction: column; + gap: 0.5rem; +} +.title-input-error, +.reason-input-error { + font-size: 12px; + color: var(--red-color); } .disabled { opacity: 0.5; From 482f3a8d0263d99aa0f1efcd245542fc6f60195d Mon Sep 17 00:00:00 2001 From: Tanu Chahal Date: Sun, 27 Oct 2024 15:27:50 +0530 Subject: [PATCH 2/6] Input validation for selecting past date as deadline added. --- extension-requests/script.js | 15 +++++++++++++-- extension-requests/style.css | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/extension-requests/script.js b/extension-requests/script.js index 7d17e424..7a1f4590 100644 --- a/extension-requests/script.js +++ b/extension-requests/script.js @@ -817,7 +817,13 @@ async function createExtensionCard(data, dev) { value: dateString(secondsToMilliSeconds(data.newEndsOn)), }, }); + const extensionInputError = createElement({ + type: 'div', + attributes: { class: 'extension-input-error hidden' }, + innerText: "Past date can't be the new deadline", + }); newDeadlineContainer.appendChild(extensionInput); + newDeadlineContainer.appendChild(extensionInputError) extensionForContainer.appendChild(extensionForValue); const extensionRequestNumberContainer = createElement({ type: 'div' }); @@ -984,11 +990,15 @@ async function createExtensionCard(data, dev) { updateButton.addEventListener('click', (event) => { const isTitleMissing = !titleInput.value; const isReasonMissing = !reasonInput.value; + const todayDate = Math.floor(new Date().getTime() / 1000) + const newDeadline = new Date(extensionInput.value).getTime() / 1000 + const isDeadlineInPast = newDeadline Date: Sun, 27 Oct 2024 19:38:47 +0530 Subject: [PATCH 3/6] Fixed ExtensionInputError visibility on edit and cancel button click. --- extension-requests/script.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extension-requests/script.js b/extension-requests/script.js index 7a1f4590..42c1143a 100644 --- a/extension-requests/script.js +++ b/extension-requests/script.js @@ -980,6 +980,7 @@ async function createExtensionCard(data, dev) { handleFormPropagation(event); toggleInputs(); toggleActionButtonVisibility(); + extensionInputError.classList.add('hidden') editButton.classList.toggle('hidden'); updateWrapper.classList.toggle('hidden'); if (!panel.style.maxHeight) { @@ -1017,6 +1018,7 @@ async function createExtensionCard(data, dev) { updateWrapper.classList.toggle('hidden'); titleInputError.classList.add('hidden'); reasonInputError.classList.add('hidden'); + extensionInputError.classList.add('hidden') }); const payloadForLog = { body: {}, From c4831e825e36b1a95435c60411a2b3189321bf12 Mon Sep 17 00:00:00 2001 From: Tanu Chahal Date: Tue, 29 Oct 2024 17:43:15 +0530 Subject: [PATCH 4/6] Conditional rendering of edit button done. --- extension-requests/script.js | 44 +++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/extension-requests/script.js b/extension-requests/script.js index 42c1143a..1c904700 100644 --- a/extension-requests/script.js +++ b/extension-requests/script.js @@ -25,9 +25,11 @@ let currentUserDetails; const filterStates = {}; let assigneeUsernamesList = []; const isDev = params.get('dev') === 'true'; +let extensionRequestsData; getSelfUser().then((response) => { currentUserDetails = response; + if(extensionRequestsData){renderEditButtonsIfNotRendered();} // Render editButton after user details have }); const updateUrl = () => { @@ -239,6 +241,7 @@ async function populateExtensionRequests(query = {}, newLink) { if (currentVersion !== extensionPageVersion) { return; } + extensionRequestsData = allExtensionRequests; for (let data of allExtensionRequests) { if (query.dev) { createExtensionCard(data, true); @@ -926,7 +929,9 @@ async function createExtensionCard(data, dev) { type: 'button', attributes: { class: 'edit-button' }, }); - extensionCardButtons.appendChild(editButton); + if (shouldDisplayEditButton(data.assigneeId)) { + extensionCardButtons.appendChild(editButton); + } const editIcon = createElement({ type: 'img', attributes: { src: EDIT_ICON, alt: 'edit-icon' }, @@ -1443,6 +1448,43 @@ async function createExtensionCard(data, dev) { } } +function shouldDisplayEditButton(assigneeId) { + return ( + currentUserDetails && + (assigneeId === currentUserDetails.id || currentUserDetails.roles.super_user) + ); +} + +function renderEditButtonsIfNotRendered(){ + const allCards = document.querySelectorAll(".extension-card"); + + allCards.forEach((card, index) => { + const assigneeId = extensionRequestsData[index].assigneeId; + if (!assigneeId) return; + + if (shouldDisplayEditButton(assigneeId)) { + let editButton = card.querySelector(".edit-button"); + + if (!editButton) { + editButton = createElement({ + type: 'button', + attributes: { class: 'edit-button' }, + }); + const editIcon = createElement({ + type: 'img', + attributes: { src: EDIT_ICON, alt: 'edit-icon' }, + }); + editButton.appendChild(editIcon); + } + + const extensionCardButtons = card.querySelector(".extension-card-buttons"); + if (extensionCardButtons) { + extensionCardButtons.prepend(editButton); + } + } + }); +} + function generateSentence(response, parentClassName, id) { let arraySentence = []; let sentence = ''; From 2cdc1845c8003cd317f3c90e9091f4544345dff6 Mon Sep 17 00:00:00 2001 From: Tanu Chahal Date: Sun, 3 Nov 2024 10:55:34 +0530 Subject: [PATCH 5/6] Fixed bug in conditional rendering of editButton. --- extension-requests/script.js | 44 +++++++++--------------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/extension-requests/script.js b/extension-requests/script.js index 1c904700..0c12a0a3 100644 --- a/extension-requests/script.js +++ b/extension-requests/script.js @@ -25,11 +25,9 @@ let currentUserDetails; const filterStates = {}; let assigneeUsernamesList = []; const isDev = params.get('dev') === 'true'; -let extensionRequestsData; getSelfUser().then((response) => { currentUserDetails = response; - if(extensionRequestsData){renderEditButtonsIfNotRendered();} // Render editButton after user details have }); const updateUrl = () => { @@ -229,7 +227,18 @@ const getExtensionColor = (deadline, createdTime) => { return 'orange-text'; }; +const currentUserDetailsPromise = getSelfUser() + .then((response) => { + currentUserDetails = response; + }) + .catch((error) => { + currentUserDetails = null; + }); + async function populateExtensionRequests(query = {}, newLink) { + if(!currentUserDetails){ + await currentUserDetailsPromise; + } extensionPageVersion++; const currentVersion = extensionPageVersion; try { @@ -241,7 +250,6 @@ async function populateExtensionRequests(query = {}, newLink) { if (currentVersion !== extensionPageVersion) { return; } - extensionRequestsData = allExtensionRequests; for (let data of allExtensionRequests) { if (query.dev) { createExtensionCard(data, true); @@ -1455,36 +1463,6 @@ function shouldDisplayEditButton(assigneeId) { ); } -function renderEditButtonsIfNotRendered(){ - const allCards = document.querySelectorAll(".extension-card"); - - allCards.forEach((card, index) => { - const assigneeId = extensionRequestsData[index].assigneeId; - if (!assigneeId) return; - - if (shouldDisplayEditButton(assigneeId)) { - let editButton = card.querySelector(".edit-button"); - - if (!editButton) { - editButton = createElement({ - type: 'button', - attributes: { class: 'edit-button' }, - }); - const editIcon = createElement({ - type: 'img', - attributes: { src: EDIT_ICON, alt: 'edit-icon' }, - }); - editButton.appendChild(editIcon); - } - - const extensionCardButtons = card.querySelector(".extension-card-buttons"); - if (extensionCardButtons) { - extensionCardButtons.prepend(editButton); - } - } - }); -} - function generateSentence(response, parentClassName, id) { let arraySentence = []; let sentence = ''; From 0a95c8f1b282d7af7baeefd561775723f8cc6d48 Mon Sep 17 00:00:00 2001 From: Tanu Chahal Date: Tue, 5 Nov 2024 09:47:45 +0530 Subject: [PATCH 6/6] Added toast onupdate request api response. --- extension-requests/script.js | 25 ++++++++++++++++++++++++- extension-requests/style.css | 23 +++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/extension-requests/script.js b/extension-requests/script.js index 0c12a0a3..c2c92e8e 100644 --- a/extension-requests/script.js +++ b/extension-requests/script.js @@ -1264,11 +1264,15 @@ async function createExtensionCard(data, dev) { data.tile = formData.title; data.newEndsOn = data.newEndsOn; handleSuccess(rootElement); + const successMessage = 'Extension request successfully updated.'; + showToast(successMessage, 'success'); appendLogs(payloadForLog, data.id); }) - .catch(() => { + .catch((error) => { revertDataChange(); handleFailure(rootElement); + const errorMessage = error?.response?.data?.message || error?.message || 'An error occurred. Please try again.'; + showToast(errorMessage, 'error'); }) .finally(() => { rootElement.classList.remove('disabled'); @@ -1463,6 +1467,25 @@ function shouldDisplayEditButton(assigneeId) { ); } +function showToast(message, type) { + const existingToast = document.querySelector('.extension-request-update-toast'); + if (existingToast) { + existingToast.remove(); + } + const toast = document.createElement('div'); + toast.className = `extension-request-update-toast toast-${type}`; + toast.textContent = message; + + document.body.appendChild(toast); + + setTimeout(() => { + toast.classList.add('fade-out'); + toast.addEventListener('transitionend', () => { + toast.remove(); + }); + }, 3000); +} + function generateSentence(response, parentClassName, id) { let arraySentence = []; let sentence = ''; diff --git a/extension-requests/style.css b/extension-requests/style.css index d4decdcf..121c8fb1 100644 --- a/extension-requests/style.css +++ b/extension-requests/style.css @@ -785,6 +785,29 @@ body { font-size: 12px; color: var(--red-color); } + +.extension-request-update-toast { + position: fixed; + bottom: 20px; + right: 20px; + padding: 15px 20px; + background-color: var(--red-color); + color: #fff; + font-size: 14px; + border-radius: 5px; + opacity: 1; + transition: opacity 0.3s ease; + z-index: 1000; +} + +.extension-request-update-toast.fade-out { + opacity: 0; +} + +.extension-request-update-toast.toast-success { + background-color: var(--green-color); +} + .disabled { opacity: 0.5; pointer-events: none;