diff --git a/users/details/script.js b/users/details/script.js index 2ba75fe2..80adbba1 100644 --- a/users/details/script.js +++ b/users/details/script.js @@ -358,6 +358,11 @@ function generateUserTaskList(userTasks) { function createSingleTaskCard(task) { const container = createElement({ type: 'div', classList: ['user-task'] }); + if (isDev === true) { + const innerHTMl = generateCardUIInDev(task); + container.innerHTML = innerHTMl; + return container; + } const h2 = createElement({ type: 'h2', classList: ['task-title'] }); h2.appendChild(createTextNode(task?.title)); const p = createElement({ type: 'p', classList: ['task-description'] }); diff --git a/users/details/style.css b/users/details/style.css index 4c060c5a..5e0e3cab 100644 --- a/users/details/style.css +++ b/users/details/style.css @@ -11,6 +11,12 @@ html { --blue-color: #1d1283; --white-color: white; --black-color: black; + --red-background: #ffebee; + --border-color: #d9d9d9; + --green: #008000; + --orange: #ffa500; + --red: #ff0000; + --yellow: #ecef08; --light-gray-color: lightgray; --button-active-color: #a8a8a8; --loader-gray-color: #f3f3f3; @@ -493,3 +499,81 @@ footer p a { position: absolute; color: black; } + +.task { + border: 2px solid var(--border-color); + padding: 1rem; + border-radius: 10px; +} +.task-red { + background-color: var(--red-background); + border: 2px solid red; +} +.row { + padding: 10px; + display: flex; + justify-content: space-between; + flex-wrap: wrap; +} + +.task-title { + max-width: 27rem; +} +.task-title a { + text-decoration: none; + font-size: 1.6rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: normal; + font-weight: 500; + color: var(--blue-color); + max-width: 24rem; + word-break: break-word; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; +} + +.progress-content { + display: flex; + gap: 8px; + align-items: center; +} +progress { + height: 8px; + border-radius: 30px; + border: 1px solid; +} + +.detail-block { + display: flex; + align-items: center; + gap: 5px; +} +.div-heading { + font-size: 1.1rem; + font-weight: 500; + color: #aeaeae; +} +.detail { + padding-left: 0.5rem; +} +.eta { + flex-wrap: wrap; +} +.red { + background-color: var(--red); + border: 1px solid red; +} +.green { + background-color: var(--green); + border: 1px solid green; +} +.orange { + background-color: var(--orange); + border: 1px solid orange; +} +.yellow { + color: var(--yellow); + border: 1px solid yellow; +} diff --git a/users/details/utils.js b/users/details/utils.js index 1f8d6cc1..e090db6b 100644 --- a/users/details/utils.js +++ b/users/details/utils.js @@ -36,3 +36,160 @@ function generateNoDataFoundSection(message) { const container = document.querySelector('.user-details-header'); container.appendChild(notFoundDiv); } + +function dateDiff(date1, date2, formatter) { + if (date2 > date1) { + return dateDiff(date2, date1, formatter); + } + + const timeDifference = new Date(date1).getTime() - new Date(date2).getTime(); + + const seconds = Math.floor(timeDifference / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); + const months = Math.floor(days / 30); + const years = Math.floor(days / 365); + + let res; + if (seconds < 60) { + res = `${seconds} ${seconds === 1 ? 'second' : 'seconds'}`; + } else if (minutes < 60) { + res = `${minutes} ${minutes === 1 ? 'minute' : 'minutes'}`; + } else if (hours < 24) { + res = `${hours} ${hours === 1 ? 'hour' : 'hours'}`; + } else if (days < 30) { + res = `${days} ${days === 1 ? 'day' : 'days'}`; + } else if (months < 12) { + res = `${months} ${months === 1 ? 'month' : 'months'}`; + } else { + res = `${years} ${years === 1 ? 'year' : 'years'}`; + } + + return formatter ? formatter(res) : res; +} + +function inputParser(input) { + const parsedDate = new Date(parseInt(input, 10) * 1000); + return parsedDate; +} + +/** + * Calculates the percentage of days remaining between two dates. + * + * @param {Date} startedOn - The start date of the task. + * @param {Date} endsOn - The end date of the task. + * @returns {number} The percentage of days remaining as a value between 0 and 100. + */ +const getPercentageOfDaysLeft = (startedOn, endsOn) => { + const startDate = inputParser(startedOn); + const endDate = inputParser(endsOn); + const totalDays = Math.floor((endDate - startDate) / (1000 * 60 * 60 * 24)); + const currentDate = new Date(); + const daysLeft = Math.floor((endDate - currentDate) / (1000 * 60 * 60 * 24)); + const percentageOfDaysLeft = (daysLeft / totalDays) * 100; + return percentageOfDaysLeft; +}; + +/** + * Determines the color code based on the progress of a task. + * + * @param {number} percentCompleted - The percentage of the task completed. + * @param {Date} startedOn - The date the task was started. + * @param {Date} endsOn - The date the task is expected to end. + * @returns {string} The color code representing the progress status: + */ +const handleProgressColor = (percentCompleted, startedOn, endsOn) => { + const percentageOfDaysLeft = getPercentageOfDaysLeft(startedOn, endsOn); + const percentIncomplete = 100 - percentCompleted; + if (percentCompleted === 100 || percentageOfDaysLeft >= percentIncomplete) { + return 'green'; + } + + if ( + (percentageOfDaysLeft < 25 && percentIncomplete > 35) || + (percentageOfDaysLeft <= 0 && percentIncomplete > 0) + ) { + return 'red'; + } + + if (percentageOfDaysLeft < 50 && percentIncomplete > 75) { + return 'orange'; + } + + return 'yellow'; +}; + +/** + * Calculates the percentage of days remaining between two dates. + * + * @param {Object} task - Task from list of tasks assigned to a user + * @returns {number} A new UI HTML for better UI/UX + */ +const generateCardUIInDev = (task) => { + const isDeadLineCrossed = Date.now() > task.endsOn * 1000; + const isTaskRed = + isDeadLineCrossed && + task?.percentCompleted !== 0 && + task?.status !== 'COMPLETED'; + + const deadlineDays = task?.endsOn + ? dateDiff(Date.now(), task.endsOn * 1000, (d) => + isDeadLineCrossed ? d + ' ago' : 'in ' + d, + ) + : 'N/A'; + const progressBarClassname = handleProgressColor( + task?.percentCompleted, + task?.startedOn, + task?.endsOn, + ); + const startedOnDays = task?.startedOn + ? dateDiff(Date.now(), task?.startedOn * 1000, (d) => d + ' ago') + : 'N/A'; + + return ` +
+
+ +
+ ${task?.percentCompleted} +
${task?.percentCompleted ?? 'N/A'}%
+
+
+
+
+

Estimated Completion

+

${deadlineDays || 'N/A'}

+
+
+

Status

+

${task.status || 'N/A'}

+
+
+
+
+

Started On

+

${startedOnDays || 'N/A'}

+
+
+

Priority

+

${task?.priority || 'N/A'}

+
+
+
+
+

Created By

+

${task?.createdBy || 'N/A'}

+
+
+

Type

+

${task?.type || 'N/A'}

+
+
+
+ `; +};