diff --git a/taskRequests/details/index.html b/taskRequests/details/index.html new file mode 100644 index 00000000..8197e1f1 --- /dev/null +++ b/taskRequests/details/index.html @@ -0,0 +1,71 @@ + + + + + + + + + + + + + Task Requests | Real Dev Squad + + + + + +
+
+ RDS logo + Home +
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ +
+
+

Requestors

+
    +
      +
    • +
    • +
    • +
    • +
    +
    +
    +
    + + diff --git a/taskRequests/details/script.js b/taskRequests/details/script.js new file mode 100644 index 00000000..6df3faf5 --- /dev/null +++ b/taskRequests/details/script.js @@ -0,0 +1,240 @@ +const API_BASE_URL = window.API_BASE_URL; + +const taskRequestStatus = { + WAITING: 'WAITING', + APPROVED: 'APPROVED', +}; + +let taskRequest; + +const taskRequestSkeleton = document.querySelector('.taskRequest__skeleton'); +const taskSkeleton = document.querySelector('.task__skeleton'); +const requestorSkeleton = document.querySelector( + '.requestors__container__list__skeleton', +); + +const taskRequestContainer = document.getElementById('task-request-details'); +const taskContainer = document.getElementById('task-details'); +const requestorsContainer = document.getElementById('requestors-details'); + +const taskRequestId = new URLSearchParams(window.location.search).get('id'); +history.pushState({}, '', window.location.href); + +let taskId; + +function renderTaskRequestDetails(taskRequest) { + taskRequestContainer.append( + createCustomElement({ + tagName: 'h1', + textContent: `Task Request `, + class: 'taskRequest__title', + child: [ + createCustomElement({ + tagName: 'span', + class: 'taskRequest__title__subtitle', + textContent: `#${taskRequest.id}`, + }), + ], + }), + createCustomElement({ + tagName: 'p', + textContent: 'Status: ', + class: 'taskRequest__status', + child: [ + createCustomElement({ + tagName: 'span', + textContent: taskRequest.status, + class: [ + 'taskRequest__status__chip', + `taskRequest__status__chip--${taskRequest.status.toLowerCase()}`, + ], + }), + ], + }), + ); +} + +async function renderTaskDetails(taskId) { + try { + const res = await fetch(`${API_BASE_URL}/tasks/${taskId}/details`); + taskSkeleton.classList.add('hidden'); + const data = await res.json(); + + const { taskData } = data ?? {}; + + taskContainer.append( + createCustomElement({ + tagName: 'h2', + class: 'task__title', + textContent: taskData?.title || 'N/A', + }), + createCustomElement({ + tagName: 'p', + class: 'task_type', + textContent: 'Type: ', + child: [ + taskData?.type + ? createCustomElement({ + tagName: 'span', + class: [ + 'task__type__chip', + `task__type__chip--${taskData.type}`, + ], + textContent: taskData?.type, + }) + : '', + taskData?.isNoteworthy + ? createCustomElement({ + tagName: 'span', + class: ['task__type__chip', `task__type__chip--noteworthy`], + textContent: 'Note worthy', + }) + : '', + ], + }), + createCustomElement({ + tagName: 'p', + class: 'task__createdBy', + textContent: `Created By: `, + child: [ + createCustomElement({ + tagName: 'p', + textContent: taskData?.createdBy || 'N/A', + }), + ], + }), + createCustomElement({ + tagName: 'p', + class: 'task__purpose', + textContent: taskData?.purpose || 'N/A', + }), + ); + } catch (e) { + console.error(e); + } +} + +function getAvatar(user) { + if (user.user?.picture?.url) { + return createCustomElement({ + tagName: 'img', + src: user.user.picture.url, + alt: user.user.first_name, + title: user.user.first_name, + }); + } + return createCustomElement({ + tagName: 'span', + title: user.user.first_name, + textContent: user.user.first_name[0], + }); +} + +async function approveTaskRequest(userId) { + try { + console.error(taskRequestId, userId); + const res = await fetch(`${API_BASE_URL}/taskRequests/approve`, { + credentials: 'include', + method: 'PATCH', + body: JSON.stringify({ + taskRequestId: taskRequestId, + userId, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (res.ok) { + taskRequest = await fetchTaskRequest(); + requestorsContainer.innerHTML = ''; + renderRequestors(taskRequest.requestors); + } + } catch (e) { + console.error(e); + } +} + +function getActionButton(requestor) { + if (taskRequest.status === taskRequestStatus.APPROVED) { + if (taskRequest?.approvedTo === requestor.user.id) { + return createCustomElement({ + tagName: 'p', + textContent: 'Approved', + class: ['requestors__container__list__approved'], + }); + } else { + return ''; + } + } + return createCustomElement({ + tagName: 'button', + textContent: 'Approve', + class: 'requestors__conatainer__list__button', + eventListeners: [ + { event: 'click', func: () => approveTaskRequest(requestor.user.id) }, + ], + }); +} + +async function renderRequestors(requestors) { + requestorSkeleton.classList.remove('hidden'); + const data = await Promise.all( + requestors.map((requestor) => { + return fetch(`${API_BASE_URL}/users/userId/${requestor}`).then((res) => + res.json(), + ); + }), + ); + + requestorSkeleton.classList.add('hidden'); + + data.forEach((requestor) => { + requestorsContainer.append( + createCustomElement({ + tagName: 'li', + child: [ + createCustomElement({ + tagName: 'div', + class: 'requestors__container__list__userDetails', + child: [ + createCustomElement({ + tagName: 'div', + class: 'requestors__container__list__userDetails__avatar', + child: [getAvatar(requestor)], + }), + createCustomElement({ + tagName: 'p', + textContent: requestor.user.first_name, + }), + ], + }), + getActionButton(requestor), + ], + }), + ); + }); +} + +async function fetchTaskRequest() { + const res = await fetch(`${API_BASE_URL}/taskRequests/${taskRequestId}`); + const data = await res.json(); + return data.data; +} + +const renderTaskRequest = async () => { + taskRequestSkeleton.classList.remove('hidden'); + taskContainer.classList.remove('hidden'); + try { + taskRequest = await fetchTaskRequest(); + taskRequestSkeleton.classList.add('hidden'); + + renderTaskRequestDetails(taskRequest); + renderTaskDetails(taskRequest.taskId); + renderRequestors(taskRequest.requestors); + } catch (e) { + console.error(e); + } +}; + +renderTaskRequest(); diff --git a/taskRequests/details/style.css b/taskRequests/details/style.css new file mode 100644 index 00000000..e3ec5a74 --- /dev/null +++ b/taskRequests/details/style.css @@ -0,0 +1,250 @@ +:root { + font-family: 'Inter', sans-serif; +} + +body { + padding: 0; + margin: 0; +} + +.skeleton { + animation: skeleton 2s linear infinite; + border-radius: 0.5rem; + min-height: 0.5rem; + margin: 0.5rem 0; +} + +.header { + background: #1d1283; + padding: 1rem; +} +.header__contents { + max-width: 1440px; + padding: 0.5rem 1rem; + margin: 0 auto; + color: white; + display: flex; + align-items: center; + gap: 0.5rem; +} +.header__contents__navlink { + color: white; + text-decoration: none; +} +.header__contents__navlink:hover { + text-decoration: underline; +} + +.container { + max-width: 1440px; + margin: 0 auto; + display: grid; + grid-template-columns: repeat(12, 1fr); +} + +.taskRequest { + padding: 1rem; + grid-column: 1 / span 12; +} +.taskRequest__skeleton__title { + height: 1.5rem; + width: 50ch; + margin: 0.5rem 0; +} +.taskRequest__skeleton__subtitle { + height: 1rem; + max-width: 30ch; + animation: skeleton 2s linear infinite; +} +.taskRequest__title { + font-weight: 400; + font-size: 2rem; + line-height: 2.5rem; +} +.taskRequest__title__subtitle { + font-size: 1rem; + font-weight: 700; + color: #888; + font-size: 0.875rem; +} +.taskRequest__status__chip { + padding: 0.5rem; + line-height: 1.5rem; + border-radius: 1rem; + font-weight: 700; +} +.taskRequest__status__chip--approved { + background: #e1f9f1; + color: #19805e; +} +.taskRequest__status__chip--waiting { + background: #fcf1e0; + color: #c78112; +} + +.task__skeleton__title { + height: 1.25rem; + max-width: 45ch; +} +.task__skeleton__details { + height: 0.75rem; + max-width: 20ch; +} +.task__skeleton__description { + height: 0.75rem; + max-width: 75ch; +} + +.task { + grid-column: 1 / span 8; + padding: 1rem; +} +.task__title { + font-size: 1.5rem; + line-height: 2rem; + color: #1d1283; + margin: 0; +} +.task__purpose { + font-size: 0.875rem; + line-height: 1.25rem; + margin-top: 1rem; + max-width: 80ch; +} +.task__type__chip { + padding: 0.5rem; + line-height: 1.5rem; + border-radius: 1rem; + font-weight: 700; + margin: 0 0.25rem; + white-space: nowrap; +} +.task__type__chip--feature { + background: #dfe4ff; + border: solid 1px #9eadfe; + color: #0224df; +} +.task__type__chip--refactor { + background: #fadee0; + border: solid 1px #f19ca1; + color: #ae1820; +} +.task__type__chip--bug { + background: #e1f9f1; + border: solid 1px #7fe6c4; + color: #14664b; +} +.task__type__chip--noteworthy { + background: #14664b; + color: white; +} + +.requestors { + grid-column: auto / span 4; + padding: 1rem; + align-self: flex-start; + border-left: solid 1px rgba(0, 0, 0, 0.1); +} +.requestors__container__title { + font-size: 1.375rem; + line-height: 1.75rem; + font-weight: 400; + margin: 0; +} +.requestors__container__list { + list-style-type: none; + padding: 0; +} +.requestors__container__list li { + padding: 1rem; + display: flex; + justify-content: space-between; + align-items: center; +} +.requestors__container__list__userDetails { + display: flex; + gap: 1rem; + align-items: center; +} +.requestors__container__list__userDetails__avatar { + height: 2rem; + width: 2rem; + display: grid; + background-color: #e2e2e2; + place-items: center; + border-radius: 50%; +} +.requestors__container__list li:nth-child(even) { + background: #eee; +} +.requestors__conatainer__list__button { + padding: 0.375rem 0.5rem; + background: #fff; + border: solid 1px #19805e; + font-weight: 700; + font-size: 1rem; + line-height: 1.5rem; + color: #19805e; + border-radius: 0.25rem; + cursor: pointer; +} +.requestors__conatainer__list__button:hover { + color: white; + background: #19805e; + transition: 0.3s ease-in-out; +} +.requestors__container__list__approved { + background: transparent; + border: none; + color: #c3c3c3; + font-weight: 600; +} + +.hidden { + display: none; +} + +@keyframes skeleton { + 0% { + background: hsl(0, 0%, 75%); + } + 50% { + background: hsl(0, 0%, 95%); + } + 100% { + background: hsl(0, 0%, 75%); + } +} + +@media (max-width: 599px) { + .taskRequest__title { + font-size: 1.5rem; + line-height: 1.75rem; + } + .taskRequest__title__subtitle { + font-size: 0.875rem; + line-height: 1rem; + } + .taskRequest__status { + font-size: 0.75rem; + } +} + +@media (max-width: 904px) { + .task { + grid-column: 1 / span 12; + } + + .requestors { + grid-column: 1 / span 12; + border: none; + } + + .taskRequest__skeleton__title { + max-width: 80%; + height: 1rem; + } + .taskRequest__skeleton__subtitle { + max-width: 40%; + } +} diff --git a/taskRequests/index.html b/taskRequests/index.html index 37fff320..76b23433 100644 --- a/taskRequests/index.html +++ b/taskRequests/index.html @@ -16,6 +16,7 @@ + diff --git a/taskRequests/script.js b/taskRequests/script.js index d19a5ab2..e17c53d3 100644 --- a/taskRequests/script.js +++ b/taskRequests/script.js @@ -9,32 +9,6 @@ const loader = document.querySelector('.container__body__loader'); const startLoading = () => loader.classList.remove('hidden'); const stopLoading = () => loader.classList.add('hidden'); -function createCustomElement(domObjectMap) { - const el = document.createElement(domObjectMap.tagName); - for (const [key, value] of Object.entries(domObjectMap)) { - if (key === 'tagName') { - continue; - } - if (key === 'eventListeners') { - value.forEach((obj) => { - el.addEventListener(obj.event, obj.func); - }); - } - if (key === 'class') { - if (Array.isArray(value)) { - el.classList.add(...value); - } else { - el.classList.add(value); - } - } else if (key === 'child') { - el.append(...value); - } else { - el[key] = value; - } - } - return el; -} - async function getTaskRequests() { startLoading(); try { @@ -63,7 +37,7 @@ async function getTaskRequests() { showMessage('ERROR', ErrorMessages.SERVER_ERROR); } catch (e) { - console.log(e); + console.error(e); } finally { stopLoading(); } @@ -101,9 +75,11 @@ function getRemainingCount(requestors) { }); } } - function openTaskDetails(id) { - window.location.href = new URL(`/taskRequest/details?id=${id}`, API_BASE_URL); + const url = new URL(`/taskRequests/details`, window.location.href); + + url.searchParams.append('id', id); + window.location.href = url; } function createTaskRequestCard({ id, task, requestors, status }) { diff --git a/taskRequests/util.js b/taskRequests/util.js new file mode 100644 index 00000000..99a9a276 --- /dev/null +++ b/taskRequests/util.js @@ -0,0 +1,25 @@ +function createCustomElement(domObjectMap) { + const el = document.createElement(domObjectMap.tagName); + for (const [key, value] of Object.entries(domObjectMap)) { + if (key === 'tagName') { + continue; + } + if (key === 'eventListeners') { + value.forEach((obj) => { + el.addEventListener(obj.event, obj.func); + }); + } + if (key === 'class') { + if (Array.isArray(value)) { + el.classList.add(...value); + } else { + el.classList.add(value); + } + } else if (key === 'child') { + el.append(...value); + } else { + el[key] = value; + } + } + return el; +}