');
+ });
});
diff --git a/extension-requests/local-utils.js b/extension-requests/local-utils.js
index c802d21a..1c2a3f37 100644
--- a/extension-requests/local-utils.js
+++ b/extension-requests/local-utils.js
@@ -205,38 +205,6 @@ function formDataToObject(formData) {
return result;
}
-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;
-}
-
const addSpinner = (container) => {
const spinner = createElement({
type: 'div',
diff --git a/footerTest.js b/footerTest.js
index c6c03fe7..d292d8b1 100644
--- a/footerTest.js
+++ b/footerTest.js
@@ -2,7 +2,7 @@ const puppeteer = require('puppeteer');
let config = {
launchOptions: {
- headless: true,
+ headless: 'new',
ignoreHTTPSErrors: true,
},
};
diff --git a/mock-data/task-card-date-hover/index.js b/mock-data/task-card-date-hover/index.js
index f56d92b5..62c6d967 100644
--- a/mock-data/task-card-date-hover/index.js
+++ b/mock-data/task-card-date-hover/index.js
@@ -35,7 +35,7 @@ const userDetailsApi = {
},
{
id: 'F2A6XVGgM3IshzEd5niL',
- percentCompleted: 100,
+ percentCompleted: 30,
endsOn: 1688445662, //Jul 4 2023
isNoteworthy: false,
createdBy: 'ankush',
@@ -52,4 +52,307 @@ const userDetailsApi = {
],
};
-module.exports = { userDetailsApi };
+const usersTasksInDev = {
+ initial: {
+ message: 'Tasks returned successfully!',
+ tasks: [
+ {
+ id: 'leX6wahONOe2BPxJxTkt',
+ percentCompleted: 100,
+ endsOn: 1694629740,
+ github: {
+ issue: {
+ id: 1872244306,
+ status: 'open',
+ },
+ },
+ createdBy: 'ankush',
+ assignee: 'ajeyakrishna',
+ title: 'Bugs in extension requests page',
+ type: 'feature',
+ priority: 'TBD',
+ status: 'COMPLETED',
+ assigneeId: '36AGeYsrIZQcPYWlFb1a',
+ dependsOn: [],
+ },
+ {
+ id: '1TJzTISp2fuKjZ53ZZfG',
+ percentCompleted: 100,
+ endsOn: 1690591252,
+ github: {
+ issue: {
+ id: 1812126813,
+ status: 'open',
+ },
+ },
+ createdBy: 'ankush',
+ assignee: 'ajeyakrishna',
+ title: 'Cache new response iff successful',
+ type: 'feature',
+ priority: 'TBD',
+ status: 'DONE',
+ assigneeId: '36AGeYsrIZQcPYWlFb1a',
+ dependsOn: [],
+ },
+ {
+ id: 'SFhZrVn7zBWKEup3M6ZC',
+ percentCompleted: 100,
+ endsOn: 1690250700,
+ github: {
+ issue: {
+ id: 1805209342,
+ assignee: 'Aryex82',
+ status: 'open',
+ assigneeRdsInfo: {
+ firstName: '',
+ lastName: '',
+ username: '',
+ },
+ },
+ },
+ createdBy: 'ankush',
+ assignee: 'ajeyakrishna',
+ title: "Clear cache related to tasks when there's an update",
+ type: 'feature',
+ priority: 'TBD',
+ startedOn: 1689552640.848,
+ status: 'VERIFIED',
+ assigneeId: '36AGeYsrIZQcPYWlFb1a',
+ dependsOn: [],
+ },
+ ],
+ prev: '',
+ next: '/tasks?dev=true&assignee=ajeyakrishna&size=3&next=vvTPGHAs9w36oY1UnV8r',
+ },
+ vvTPGHAs9w36oY1UnV8r: {
+ message: 'Tasks returned successfully!',
+ tasks: [
+ {
+ id: 'vvTPGHAs9w36oY1UnV8r',
+ percentCompleted: 90,
+ endsOn: 1699833600,
+ github: {
+ issue: {
+ url: 'https://api.github.com/repos/Real-Dev-Squad/website-status/issues/977',
+ },
+ },
+ assignee: 'ajeyakrishna',
+ title:
+ 'Create a success message and close icon for task creation requests',
+ type: 'feature',
+ priority: 'TBD',
+ startedOn: 1698883200,
+ status: 'IN_PROGRESS',
+ assigneeId: '36AGeYsrIZQcPYWlFb1a',
+ dependsOn: [],
+ },
+ {
+ id: '4P8Ax2YyssIX2y2kwGBX',
+ percentCompleted: 100,
+ endsOn: 1698221844,
+ github: {
+ issue: {
+ html_url:
+ 'https://github.com/Real-Dev-Squad/website-backend/issues/1519',
+ id: 1897464488,
+ status: 'open',
+ },
+ },
+ createdBy: 'ankush',
+ assignee: 'ajeyakrishna',
+ title: 'Design a Data restriction layer',
+ type: 'feature',
+ priority: 'TBD',
+ status: 'VERIFIED',
+ assigneeId: '36AGeYsrIZQcPYWlFb1a',
+ dependsOn: [],
+ },
+ {
+ id: 'hW1oz6pqeVeYg4GRT5qT',
+ percentCompleted: 100,
+ endsOn: 1691858954,
+ github: {
+ issue: {
+ id: 1824823052,
+ assignee: 'Aryex82',
+ status: 'open',
+ assigneeRdsInfo: {
+ firstName: '',
+ lastName: '',
+ username: '',
+ },
+ },
+ },
+ createdBy: 'ankush',
+ assignee: 'ajeyakrishna',
+ title: 'Improve UI & UX of Extension Request Cards',
+ type: 'feature',
+ priority: 'TBD',
+ startedOn: 1690590131.99,
+ status: 'VERIFIED',
+ assigneeId: '36AGeYsrIZQcPYWlFb1a',
+ dependsOn: [],
+ },
+ ],
+ prev: '/tasks?dev=true&assignee=ajeyakrishna&size=3&prev=SFhZrVn7zBWKEup3M6ZC',
+ next: '/tasks?dev=true&assignee=ajeyakrishna&size=3&next=i1LQOKkGhhpOxE6yEo3A',
+ },
+ i1LQOKkGhhpOxE6yEo3A: {
+ message: 'Tasks returned successfully!',
+ tasks: [
+ {
+ id: 'i1LQOKkGhhpOxE6yEo3A',
+ percentCompleted: 100,
+ endsOn: 1690760553,
+ github: {
+ issue: {
+ id: 1797904552,
+ assignee: 'Aryex82',
+ status: 'open',
+ assigneeRdsInfo: {
+ firstName: '',
+ lastName: '',
+ username: '',
+ },
+ },
+ },
+ createdBy: 'ankush',
+ assignee: 'ajeyakrishna',
+ title: 'Improve UX on input slider in task cards',
+ type: 'feature',
+ priority: 'TBD',
+ startedOn: 1689901777.322,
+ status: 'VERIFIED',
+ assigneeId: '36AGeYsrIZQcPYWlFb1a',
+ dependsOn: [],
+ },
+ {
+ id: 'Djvf118D3Mo3VRhkF0g2',
+ percentCompleted: 100,
+ endsOn: 1693996834,
+ github: {
+ issue: {
+ id: 1862345646,
+ status: 'open',
+ },
+ },
+ createdBy: 'ankush',
+ assignee: 'ajeyakrishna',
+ title: 'Merge Update user and external sync api ',
+ type: 'feature',
+ priority: 'TBD',
+ status: 'VERIFIED',
+ assigneeId: '36AGeYsrIZQcPYWlFb1a',
+ dependsOn: [],
+ },
+ {
+ id: 'HtC5LU45raW2YtwwxySA',
+ percentCompleted: 70,
+ endsOn: 1703634600,
+ github: {
+ issue: {
+ url: 'https://api.github.com/repos/Real-Dev-Squad/website-backend/issues/1613',
+ },
+ },
+ assignee: 'ajeyakrishna',
+ title: 'Migrate old TaskRequest model schema to new one',
+ type: 'feature',
+ priority: 'TBD',
+ startedOn: 1698192000,
+ status: 'IN_PROGRESS',
+ assigneeId: '36AGeYsrIZQcPYWlFb1a',
+ dependsOn: [],
+ },
+ ],
+ prev: '/tasks?dev=true&assignee=ajeyakrishna&size=3&prev=hW1oz6pqeVeYg4GRT5qT',
+ next: '/tasks?dev=true&assignee=ajeyakrishna&size=3&next=OhNeSTj5J72PhrA4mtrr',
+ },
+ OhNeSTj5J72PhrA4mtrr: {
+ message: 'Tasks returned successfully!',
+ tasks: [
+ {
+ id: 'OhNeSTj5J72PhrA4mtrr',
+ percentCompleted: 100,
+ endsOn: 1698191717,
+ github: {
+ issue: {
+ html_url:
+ 'https://github.com/Real-Dev-Squad/website-status/issues/920',
+ id: 1930302167,
+ assignee: 'Ajeyakrishna-k',
+ status: 'open',
+ assigneeRdsInfo: {
+ firstName: 'Ajeyakrishna',
+ lastName: 'Karanth',
+ username: 'ajeyakrishna',
+ },
+ },
+ },
+ createdBy: 'amitprakash',
+ assignee: 'ajeyakrishna',
+ title: 'Milestone 1 release for Task creation request. ',
+ type: 'feature',
+ priority: 'TBD',
+ startedOn: 1696605652,
+ status: 'VERIFIED',
+ assigneeId: '36AGeYsrIZQcPYWlFb1a',
+ dependsOn: [],
+ },
+ {
+ id: 'vDBopzwCEuPHDor7XXla',
+ percentCompleted: 100,
+ endsOn: 1693594060,
+ github: {
+ issue: {
+ id: 1824822401,
+ assignee: 'Aryex82',
+ status: 'open',
+ assigneeRdsInfo: {
+ firstName: '',
+ lastName: '',
+ username: '',
+ },
+ },
+ },
+ createdBy: 'ankush',
+ assignee: 'ajeyakrishna',
+ title: 'Revamp UI & UX of Extensions Requests page',
+ type: 'feature',
+ priority: 'TBD',
+ startedOn: 1692056863.291,
+ status: 'VERIFIED',
+ assigneeId: '36AGeYsrIZQcPYWlFb1a',
+ dependsOn: [],
+ },
+ {
+ id: 'kdoBS8Z30r2d7FQOonQx',
+ percentCompleted: 100,
+ endsOn: 1689258628,
+ github: {
+ issue: {
+ id: 1779908116,
+ assignee: 'Aryex82',
+ status: 'open',
+ assigneeRdsInfo: {
+ firstName: '',
+ lastName: '',
+ username: '',
+ },
+ },
+ },
+ createdBy: 'ankush',
+ assignee: 'ajeyakrishna',
+ title: 'Task cards not reverting changes when api fails.',
+ type: 'feature',
+ priority: 'TBD',
+ startedOn: 1688775795.152,
+ status: 'VERIFIED',
+ assigneeId: '36AGeYsrIZQcPYWlFb1a',
+ dependsOn: [],
+ },
+ ],
+ prev: '/tasks?dev=true&assignee=ajeyakrishna&size=3&prev=HtC5LU45raW2YtwwxySA',
+ next: '',
+ },
+};
+module.exports = { userDetailsApi, usersTasksInDev };
diff --git a/users/details/constants.js b/users/details/constants.js
index 7de75182..ba2cdf9a 100644
--- a/users/details/constants.js
+++ b/users/details/constants.js
@@ -20,3 +20,4 @@ const iconMapper = {
const MESSAGE_NOT_FOUND = 'Not Found';
const MESSAGE_YEARS_OF_EXPERIENCE = 'Years of Experience';
+const noProgressbarStatuses = ['COMPLETED', 'DONE', 'VERIFIED'];
diff --git a/users/details/script.js b/users/details/script.js
index d14f6338..1a7fdf8a 100644
--- a/users/details/script.js
+++ b/users/details/script.js
@@ -2,15 +2,20 @@ const params = new URLSearchParams(window.location.search);
let userData = {};
let userAllTasks = [];
+let taskSearchQuery;
+let allTasksFetched = false;
+let isTaskFetching = false;
let userSkills = [];
let userAllPrs = [];
let userStatusData = {};
let currentPageIndex = 1;
let taskPerPage = 3;
let prsPerPage = 3;
+let isTaskAccordionOpen = false;
let totalPrsPages = 0;
let totalPages = Math.ceil(userAllTasks.length / taskPerPage);
const username = new URLSearchParams(window.location.search).get('username');
+const isDev = params.get('dev') === 'true';
function createElement({ type, classList = [] }) {
const element = document.createElement(type);
@@ -187,6 +192,16 @@ function toggleAccordionTabsVisibility() {
.querySelectorAll('.visible-content');
accordionTabs.forEach((tab) => {
tab.addEventListener('click', () => {
+ if (tab.innerText === 'Tasks' && isDev) {
+ isTaskAccordionOpen = !isTaskAccordionOpen;
+ if (isTaskAccordionOpen) {
+ tab.classList.add('sticky-header');
+ document.addEventListener('scroll', onScrollHandler);
+ } else {
+ tab.classList.remove('sticky-header');
+ document.removeEventListener('scroll', onScrollHandler);
+ }
+ }
const hiddenContent = tab.nextElementSibling;
const arrowIcon = tab.querySelector('img');
if (hiddenContent) {
@@ -233,38 +248,64 @@ function generateTasksTabDetails() {
type: 'div',
classList: ['hidden-content', 'hide'],
});
- const tasks = createElement({ type: 'div', classList: ['user-tasks'] });
- const pagination = createElement({ type: 'div', classList: ['pagination'] });
- const prevBtn = createElement({
- type: 'button',
- classList: ['pagination-prev-page'],
- });
- prevBtn.appendChild(createTextNode('Prev'));
- prevBtn.addEventListener('click', fetchPrevTasks);
- const nextBtn = createElement({
- type: 'button',
- classList: ['pagination-next-page'],
+
+ const tasks = createElement({
+ type: 'div',
+ classList: isDev ? ['user-tasks', 'user-tasks-dev'] : ['user-tasks'],
});
- nextBtn.appendChild(createTextNode('Next'));
- nextBtn.addEventListener('click', fetchNextTasks);
+ div.append(tasks);
+ if (!isDev) {
+ const pagination = createElement({
+ type: 'div',
+ classList: ['pagination'],
+ });
+ const prevBtn = createElement({
+ type: 'button',
+ classList: ['pagination-prev-page'],
+ });
+ prevBtn.appendChild(createTextNode('Prev'));
+ prevBtn.addEventListener('click', fetchPrevTasks);
+ const nextBtn = createElement({
+ type: 'button',
+ classList: ['pagination-next-page'],
+ });
+ nextBtn.appendChild(createTextNode('Next'));
+ nextBtn.addEventListener('click', fetchNextTasks);
+
+ pagination.append(prevBtn, nextBtn);
+ div.append(tasks, pagination);
+ }
- pagination.append(prevBtn, nextBtn);
- div.append(tasks, pagination);
document.querySelector('.accordion-tasks').appendChild(div);
}
async function getUserTasks() {
try {
- const res = await makeApiCall(`${API_BASE_URL}/tasks/${username}`);
- if (res.status === 200) {
- const data = await res.json();
- userAllTasks = data.tasks;
- totalPages = Math.ceil(userAllTasks.length / taskPerPage);
- const tasks = getTasksToFetch(userAllTasks, currentPageIndex);
- generateTasksTabDetails();
- generateUserTaskList(tasks);
- getUserSkills();
- getUserAvailabilityStatus();
+ taskSearchQuery = isDev
+ ? taskSearchQuery || `/tasks/?size=3&dev=true&assignee=${username}`
+ : `/tasks/${username}`;
+
+ //Flag to avoid multiple API calls with same payload
+ if (!(isDev && isTaskFetching) && !allTasksFetched) {
+ isTaskFetching = true;
+ const res = await makeApiCall(`${API_BASE_URL}${taskSearchQuery}`);
+ if (res.status === 200) {
+ const data = await res.json();
+ generateTasksTabDetails();
+ if (isDev) {
+ taskSearchQuery = data.next;
+ if (data.next === '') {
+ allTasksFetched = true;
+ }
+ generateUserTaskList(data.tasks);
+ } else {
+ userAllTasks = data.tasks;
+ totalPages = Math.ceil(userAllTasks.length / taskPerPage);
+ const tasks = getTasksToFetch(userAllTasks, currentPageIndex);
+ generateUserTaskList(tasks);
+ }
+ }
+ isTaskFetching = false;
}
} catch (err) {
const div = createElement({
@@ -275,6 +316,7 @@ async function getUserTasks() {
errorEl.appendChild(createTextNode('Something Went Wrong!'));
div.appendChild(errorEl);
document.querySelector('.accordion-tasks').appendChild(div);
+ isTaskFetching = false;
}
}
@@ -285,9 +327,16 @@ function getTasksToFetch(userTasks, currentIndex) {
(_, index) => index >= startIndex && index < endIndex,
);
}
+function onScrollHandler() {
+ const accordionTasks = document.getElementsByClassName('accordion-tasks');
+ if (isTaskAccordionOpen && isBottomBorderInView(accordionTasks[0])) {
+ getUserTasks();
+ }
+}
function generateUserTaskList(userTasks) {
- document.querySelector('.user-tasks').innerHTML = '';
+ if (isDev !== true) document.querySelector('.user-tasks').innerHTML = '';
+
if (!userTasks.length) {
const errorEl = createElement({ type: 'p', classList: ['error'] });
errorEl.appendChild(createTextNode('No Data Found'));
@@ -302,22 +351,29 @@ function generateUserTaskList(userTasks) {
document.querySelector('.user-tasks').appendChild(taskCard);
});
- if (currentPageIndex === 1) {
- document.querySelector('.pagination-prev-page').disabled = true;
- } else {
- document.querySelector('.pagination-prev-page').disabled = false;
- }
+ if (!isDev) {
+ if (currentPageIndex === 1) {
+ document.querySelector('.pagination-prev-page').disabled = true;
+ } else {
+ document.querySelector('.pagination-prev-page').disabled = false;
+ }
- if (currentPageIndex === totalPages) {
- document.querySelector('.pagination-next-page').disabled = true;
- } else {
- document.querySelector('.pagination-next-page').disabled = false;
+ if (currentPageIndex === totalPages) {
+ document.querySelector('.pagination-next-page').disabled = true;
+ } else {
+ document.querySelector('.pagination-next-page').disabled = false;
+ }
}
}
}
function createSingleTaskCard(task) {
- const container = createElement({ type: 'div', classList: ['user-taks'] });
+ 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'] });
@@ -416,8 +472,10 @@ function fetchPrevTasks() {
}
}
-function fetchNextTasks() {
- if (currentPageIndex < totalPages) {
+async function fetchNextTasks() {
+ if (isDev) {
+ await getUserTasks();
+ } else if (currentPageIndex < totalPages) {
currentPageIndex++;
const tasks = getTasksToFetch(userAllTasks, currentPageIndex);
generateUserTaskList(tasks);
@@ -895,10 +953,12 @@ function generateUserPrsList(userPrs) {
const prsCard = createSinglePrCard(pr);
document.querySelector('.user-pr').appendChild(prsCard);
});
- document.querySelector('.pagination-next-page').disabled =
- currentPageIndex === totalPrsPages;
- document.querySelector('.pagination-prev-page').disabled =
- currentPageIndex === 1;
+ if (!isDev) {
+ document.querySelector('.pagination-next-page').disabled =
+ currentPageIndex === totalPrsPages;
+ document.querySelector('.pagination-prev-page').disabled =
+ currentPageIndex === 1;
+ }
}
}
@@ -1090,6 +1150,8 @@ async function accessingUserData() {
if (isSuperUser) {
getUserTasks();
getUserPrs();
+ getUserSkills();
+ getUserAvailabilityStatus();
generateAcademicTabDetails();
toggleAccordionTabsVisibility();
} else {
diff --git a/users/details/style.css b/users/details/style.css
index ad4b97fd..d1255fe7 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;
@@ -489,3 +495,88 @@ footer p a {
position: absolute;
color: black;
}
+.sticky-header {
+ position: sticky;
+ top: 0;
+ background-color: white;
+}
+.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;
+ gap: 17px;
+}
+
+#task-loader {
+ text-align: center;
+}
+.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;
+}
+
+progress.red::-webkit-progress-value {
+ background-color: var(--red);
+}
+
+progress.green::-webkit-progress-value {
+ background-color: var(--green);
+}
+
+progress.orange::-webkit-progress-value {
+ background-color: var(--orange);
+}
+progress.yellow::-webkit-progress-value {
+ background-color: var(--yellow);
+}
diff --git a/users/details/utils.js b/users/details/utils.js
index 1f8d6cc1..50a82201 100644
--- a/users/details/utils.js
+++ b/users/details/utils.js
@@ -36,3 +36,143 @@ function generateNoDataFoundSection(message) {
const container = document.querySelector('.user-details-header');
container.appendChild(notFoundDiv);
}
+
+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';
+};
+/**
+ * Verify if element's border bottom is in the view or not
+ * @param {HTML element} element - UI Element
+ * @returns {boolean}
+ */
+function isBottomBorderInView(element) {
+ const rect = element.getBoundingClientRect();
+ return rect.bottom <= window.innerHeight;
+}
+
+/**
+ * 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 `
+
+
+
+ ${
+ !noProgressbarStatuses.includes(task.status)
+ ? `
+
+
${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'}
+
+
+
+ `;
+};
diff --git a/utils.js b/utils.js
index 89670272..07f1003b 100644
--- a/utils.js
+++ b/utils.js
@@ -10,7 +10,7 @@ async function getSelfUser(endpoint = '/users/self') {
if (endpoint === '/users/self') {
const self_user = await res.json();
if (res.status === 200) {
- return self_user;
+ return self_user?.user || self_user;
}
} else {
location.reload();
@@ -69,3 +69,34 @@ function debounce(func, delay) {
async function addDelay(milliSeconds) {
await new Promise((resolve) => setTimeout(resolve, milliSeconds));
}
+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;
+}