Added UI for replying to intern updates

This commit is contained in:
sairaj mote 2022-09-12 18:34:45 +05:30
parent fccc29a8ba
commit 6af16888e6
4 changed files with 291 additions and 105 deletions

View File

@ -42,13 +42,12 @@ body[data-theme=dark] ::-webkit-calendar-picker-indicator {
filter: invert(1); filter: invert(1);
} }
p { p,
max-width: 70ch; strong {
font-size: 0.9rem;
max-width: 65ch;
line-height: 1.7; line-height: 1.7;
color: rgba(var(--text-color), 0.8); color: rgba(var(--text-color), 0.9);
}
p:not(:last-of-type) {
margin-bottom: 1.5rem;
} }
img { img {
@ -487,6 +486,10 @@ ul {
margin-bottom: 2rem; margin-bottom: 2rem;
} }
.margin-block-0-5 {
margin-block: 0.5rem;
}
.margin-block-1 { .margin-block-1 {
margin-block: 1rem; margin-block: 1rem;
} }
@ -838,7 +841,8 @@ ul {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.timeline-task__description { .timeline-task__description,
.admin-reply__description {
white-space: pre-line; white-space: pre-line;
} }
@ -970,6 +974,9 @@ ul {
display: grid; display: grid;
position: relative; position: relative;
padding: 1rem; padding: 1rem;
margin: 0.2rem;
background-color: rgba(var(--text-color), 0.06);
border-radius: 0.5rem;
} }
.request-card sm-button { .request-card sm-button {
--padding: 0.5rem 0.8rem; --padding: 0.5rem 0.8rem;
@ -986,6 +993,9 @@ ul {
margin-right: 0.5rem; margin-right: 0.5rem;
} }
#updates_page {
align-content: flex-start;
}
#updates_page sm-select { #updates_page sm-select {
--max-height: 50vh; --max-height: 50vh;
} }
@ -1014,10 +1024,14 @@ ul {
max-width: 65ch; max-width: 65ch;
} }
.update__sender { .update__sender,
color: var(--accent-color); .admin-reply__title {
font-size: 0.9rem; font-size: 0.85rem;
font-weight: 500; font-weight: 500;
background-color: rgba(var(--text-color), 0.06);
padding: 0.3rem 0.5rem;
margin: 0 -0.5rem;
border-radius: 1rem;
} }
.update__time { .update__time {
@ -1029,6 +1043,25 @@ ul {
white-space: pre-line; white-space: pre-line;
} }
.admin-reply {
position: relative;
padding: 1rem;
padding-left: 1.5rem;
margin-left: 0.5rem;
gap: 0.3rem;
}
.admin-reply::before {
content: "";
position: absolute;
width: 0.1rem;
height: calc(100% - 1rem);
left: 0;
background-color: rgba(var(--text-color), 0.5);
}
.admin-reply__title {
justify-self: flex-start;
}
.container-header { .container-header {
display: flex; display: flex;
align-items: center; align-items: center;
@ -1095,6 +1128,7 @@ ul {
} }
#task_list { #task_list {
gap: 0.5rem;
padding: 1rem 0 1.5rem 0; padding: 1rem 0 1.5rem 0;
} }
@ -1106,7 +1140,7 @@ ul {
padding: 1rem; padding: 1rem;
gap: 0.5rem; gap: 0.5rem;
border-radius: 0.5rem; border-radius: 0.5rem;
background: rgba(var(--text-color), 0.02); background: rgba(var(--foreground-color), 1);
} }
.task-list-item sm-checkbox { .task-list-item sm-checkbox {
grid-area: status; grid-area: status;
@ -1410,13 +1444,14 @@ ul {
margin-top: 0.2rem; margin-top: 0.2rem;
} }
.send-update-button { .send-update-button,
padding: 0.6rem 0.8rem; .init-update-replay {
color: var(--accent-color); color: var(--accent-color);
background-color: rgba(var(--text-color), 0.04); background-color: rgba(var(--text-color), 0.04);
margin-left: auto; margin-left: auto;
} }
.send-update-button .icon { .send-update-button .icon,
.init-update-replay .icon {
fill: var(--accent-color); fill: var(--accent-color);
} }

2
css/main.min.css vendored

File diff suppressed because one or more lines are too long

View File

@ -42,14 +42,12 @@ body[data-theme="dark"] {
} }
} }
p { p,
max-width: 70ch; strong {
font-size: 0.9rem;
max-width: 65ch;
line-height: 1.7; line-height: 1.7;
color: rgba(var(--text-color), 0.8); color: rgba(var(--text-color), 0.9);
&:not(:last-of-type) {
margin-bottom: 1.5rem;
}
} }
img { img {
@ -484,6 +482,9 @@ ul {
margin-bottom: 2rem; margin-bottom: 2rem;
} }
.margin-block-0-5 {
margin-block: 0.5rem;
}
.margin-block-1 { .margin-block-1 {
margin-block: 1rem; margin-block: 1rem;
} }
@ -854,7 +855,8 @@ ul {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.timeline-task__description { .timeline-task__description,
.admin-reply__description {
white-space: pre-line; white-space: pre-line;
} }
@ -974,6 +976,9 @@ ul {
display: grid; display: grid;
position: relative; position: relative;
padding: 1rem; padding: 1rem;
margin: 0.2rem;
background-color: rgba(var(--text-color), 0.06);
border-radius: 0.5rem;
sm-button { sm-button {
--padding: 0.5rem 0.8rem; --padding: 0.5rem 0.8rem;
} }
@ -990,6 +995,7 @@ ul {
} }
#updates_page { #updates_page {
align-content: flex-start;
sm-select { sm-select {
--max-height: 50vh; --max-height: 50vh;
} }
@ -1002,8 +1008,6 @@ ul {
font-size: 0.9rem; font-size: 0.9rem;
} }
} }
#all_updates_list {
}
.intern-update { .intern-update {
display: grid; display: grid;
gap: 0.5rem; gap: 0.5rem;
@ -1020,10 +1024,14 @@ ul {
max-width: 65ch; max-width: 65ch;
} }
.update__sender { .update__sender,
color: var(--accent-color); .admin-reply__title {
font-size: 0.9rem; font-size: 0.85rem;
font-weight: 500; font-weight: 500;
background-color: rgba(var(--text-color), 0.06);
padding: 0.3rem 0.5rem;
margin: 0 -0.5rem;
border-radius: 1rem;
} }
.update__time { .update__time {
@ -1034,6 +1042,24 @@ ul {
.update__message { .update__message {
white-space: pre-line; white-space: pre-line;
} }
.admin-reply {
position: relative;
padding: 1rem;
padding-left: 1.5rem;
margin-left: 0.5rem;
gap: 0.3rem;
&::before {
content: "";
position: absolute;
width: 0.1rem;
height: calc(100% - 1rem);
left: 0;
background-color: rgba(var(--text-color), 0.5);
}
&__title {
justify-self: flex-start;
}
}
.container-header { .container-header {
display: flex; display: flex;
@ -1099,6 +1125,7 @@ ul {
} }
#task_list { #task_list {
gap: 0.5rem;
padding: 1rem 0 1.5rem 0; padding: 1rem 0 1.5rem 0;
} }
.task-list-item { .task-list-item {
@ -1113,7 +1140,7 @@ ul {
padding: 1rem; padding: 1rem;
gap: 0.5rem; gap: 0.5rem;
border-radius: 0.5rem; border-radius: 0.5rem;
background: rgba(var(--text-color), 0.02); background: rgba(var(--foreground-color), 1);
sm-checkbox { sm-checkbox {
grid-area: status; grid-area: status;
align-self: flex-start; align-self: flex-start;
@ -1371,8 +1398,6 @@ ul {
padding: 0; padding: 0;
list-style: none; list-style: none;
} }
#intern_view {
}
#assigned_task_list { #assigned_task_list {
display: grid; display: grid;
@ -1412,8 +1437,8 @@ ul {
margin-top: 0.2rem; margin-top: 0.2rem;
} }
.send-update-button { .send-update-button,
padding: 0.6rem 0.8rem; .init-update-replay {
color: var(--accent-color); color: var(--accent-color);
background-color: rgba(var(--text-color), 0.04); background-color: rgba(var(--text-color), 0.04);
margin-left: auto; margin-left: auto;

View File

@ -191,7 +191,7 @@
Updates Updates
</span> </span>
</a> </a>
<a id="admin_page_btn" href="#admin_page" class="admin-option nav-list__item interact" <a href="#admin_page" class="admin-option nav-list__item interact open-first-project"
title="open admin panel"> title="open admin panel">
<svg class="icon icon--outlined" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" <svg class="icon icon--outlined" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24"> height="24">
@ -329,7 +329,13 @@
<section id="project_watching_section" class="w-100"> <section id="project_watching_section" class="w-100">
<h4>Projects watchlist</h4> <h4>Projects watchlist</h4>
<div id="project_watchlist" class="observe-empty-state"></div> <div id="project_watchlist" class="observe-empty-state"></div>
<h4 class="empty-state">No project added to watchlist.</h4> <div class="empty-state">
<h4>No project added to watchlist. </h4>
<p class="margin-block-0-5">
You can add projects to watchlist by clicking on the 'Watch' button in opened project.
</p>
<a href="#project_explorer" class="button open-first-project">See all projects</a>
</div>
</section> </section>
<section id="intern_view" class="hidden intern-option"> <section id="intern_view" class="hidden intern-option">
<h2>My tasks</h2> <h2>My tasks</h2>
@ -359,7 +365,7 @@
d="M6 7V4a1 1 0 0 1 1-1h6.414l2 2H21a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1h-3v3a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1h3zm0 2H4v10h12v-2H6V9z" /> d="M6 7V4a1 1 0 0 1 1-1h6.414l2 2H21a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1h-3v3a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1h3zm0 2H4v10h12v-2H6V9z" />
</svg> </svg>
<h4>Projects</h4> <h4>Projects</h4>
<a href="#project_explorer" id="all_projects_btn" class="button">All</a> <a href="#project_explorer" class="button open-first-project">All</a>
</div> </div>
<div id="project_list"></div> <div id="project_list"></div>
</div> </div>
@ -391,7 +397,7 @@
</sm-tab-header> </sm-tab-header>
<sm-tab-panels id="admin_tabs"> <sm-tab-panels id="admin_tabs">
<section id="projects_container"> <section id="projects_container">
<button class="button button--small margin-left-auto" <button class="button button--small margin-left-auto margin-block-0-5"
onclick="openPopup('add_project_popup')"> onclick="openPopup('add_project_popup')">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg" <svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" width="24" height="24"> viewBox="0 0 24 24" width="24" height="24">
@ -406,7 +412,7 @@
<h4 class="empty-state">No project added</h4> <h4 class="empty-state">No project added</h4>
</section> </section>
<section id="interns_container"> <section id="interns_container">
<button class="button button--small margin-left-auto" <button class="button button--small margin-left-auto margin-block-0-5"
onclick="openPopup('add_intern_popup')"> onclick="openPopup('add_intern_popup')">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg" <svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" width="24" height="24"> viewBox="0 0 24 24" width="24" height="24">
@ -420,6 +426,10 @@
<h4 class="empty-state">No interns added</h4> <h4 class="empty-state">No interns added</h4>
</section> </section>
<section> <section>
<strip-select id="request_type_selector" class="margin-left-auto margin-block-0-5">
<strip-option value="task" selected>Task</strip-option>
<strip-option value="intern">Internship</strip-option>
</strip-select>
<ul id="requests_list" class="list-container observe-empty-state"></ul> <ul id="requests_list" class="list-container observe-empty-state"></ul>
<h4 class="empty-state">No pending requests</h4> <h4 class="empty-state">No pending requests</h4>
</section> </section>
@ -454,7 +464,7 @@
</div> </div>
<h4>Tasks</h4> <h4>Tasks</h4>
<div id="branch_container"></div> <div id="branch_container"></div>
<ul id="task_list" class="grid gap-1 observe-empty-state"></ul> <ul id="task_list" class="grid observe-empty-state"></ul>
<h4 class="empty-state">No tasks added yet, tasks will appear here after adding them.</h4> <h4 class="empty-state">No tasks added yet, tasks will appear here after adding them.</h4>
<sm-button id="add_task" title="show element to add new task" onclick="addPlaceholderTask()"> <sm-button id="add_task" title="show element to add new task" onclick="addPlaceholderTask()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
@ -733,13 +743,15 @@
<h5 id="update_of_project"></h5> <h5 id="update_of_project"></h5>
<h3 id="update_of_task"></h3> <h3 id="update_of_task"></h3>
<sm-form> <sm-form>
<sm-textarea id="update_description" placeholder="Type the update" rows="4" required></sm-textarea> <sm-textarea id="update_description" placeholder="Type the update" rows="4" autofocus required>
</sm-textarea>
<sm-button id="post_update_btn" title="post this update" variant="primary" disabled onclick="postUpdate()"> <sm-button id="post_update_btn" title="post this update" variant="primary" disabled onclick="postUpdate()">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" <svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24"> height="24">
<path fill="none" d="M0 0h24v24H0z" /> <path fill="none" d="M0 0h24v24H0z"></path>
<path <path
d="M1.923 9.37c-.51-.205-.504-.51.034-.689l19.086-6.362c.529-.176.832.12.684.638l-5.454 19.086c-.15.529-.475.553-.717.07L11 13 1.923 9.37zm4.89-.2l5.636 2.255 3.04 6.082 3.546-12.41L6.812 9.17z" /> d="M1.946 9.315c-.522-.174-.527-.455.01-.634l19.087-6.362c.529-.176.832.12.684.638l-5.454 19.086c-.15.529-.455.547-.679.045L12 14l6-8-8 6-8.054-2.685z">
</path>
</svg> </svg>
Post update Post update
</sm-button> </sm-button>
@ -777,17 +789,6 @@
<!-- Templates --> <!-- Templates -->
<template id="update_card_template">
<li class="intern-update">
<div class="flex align-center space-between">
<h5 class="update__sender"></h5>
<span class="update__time"></span>
</div>
<h4 class="update__topic"></h4>
<p class="update__message"></p>
</li>
</template>
<template id="placeholder_task_card_template"> <template id="placeholder_task_card_template">
<div class="temp-task grid gap-0-5"> <div class="temp-task grid gap-0-5">
<sm-input class="placeholder-task__title" placeholder="task title"></sm-input> <sm-input class="placeholder-task__title" placeholder="task title"></sm-input>
@ -1153,8 +1154,8 @@
document.querySelector('.page:not(.hidden)').classList.add('hidden') document.querySelector('.page:not(.hidden)').classList.add('hidden')
} }
if (document.querySelector('.nav-list__item--active')) if (document.querySelector('.nav-list__item--active'))
document.querySelector('.nav-list__item--active').classList.remove('nav-list__item--active') document.querySelector('.nav-list__item--active').classList.remove('nav-list__item--active');
const targetListItem = document.querySelector(`.nav-list__item[href="#${pageId}"]`) const targetListItem = [...document.querySelectorAll(`a.nav-list__item`)].find(item => item.href.includes(pageId))
if (targetListItem) if (targetListItem)
targetListItem.classList.add('nav-list__item--active') targetListItem.classList.add('nav-list__item--active')
switch (pageId) { switch (pageId) {
@ -1184,7 +1185,7 @@
if (lastParams.projectId !== projectId) { if (lastParams.projectId !== projectId) {
showProjectInfo(projectId) showProjectInfo(projectId)
} }
if (params.branch) { if (branch) {
const branchDetails = { const branchDetails = {
destination: 'project_explorer', destination: 'project_explorer',
taskListContainer: 'explorer_task_list', taskListContainer: 'explorer_task_list',
@ -1406,7 +1407,7 @@
if (assignedInterns) { if (assignedInterns) {
renderElem(card.querySelector('.assigned-interns'), html`${assignedInterns.map((internFloId) => render.assignedInternCard(internFloId))}`) renderElem(card.querySelector('.assigned-interns'), html`${assignedInterns.map((internFloId) => render.assignedInternCard(internFloId))}`)
} }
if (typeOfUser === 'intern') { if (typeOfUser === 'intern' && !assignedInterns.includes(myFloID)) {
const applyButton = createElement('sm-button', { const applyButton = createElement('sm-button', {
textContent: 'Apply', textContent: 'Apply',
className: 'apply-button', className: 'apply-button',
@ -1444,7 +1445,7 @@
internCard(internName, internFloID, internPoints) { internCard(internName, internFloID, internPoints) {
const initials = internName.split(' ').map(v => v.charAt(0)).join(''); const initials = internName.split(' ').map(v => v.charAt(0)).join('');
return html` return html`
<li class="intern-card grid align-center interact" data-intern-flo-id=${internFloID} title="Intern Information"}> <li class="intern-card grid align-center interact" data-intern-flo-id=${internFloID} title="Intern Information">
<div class="intern-card__initials" style=${`background-color: ${getInternColor(internFloID)}`}>${initials}</div> <div class="intern-card__initials" style=${`background-color: ${getInternColor(internFloID)}`}>${initials}</div>
<div class="intern-card__name capitalize">${internName}</div> <div class="intern-card__name capitalize">${internName}</div>
<div class="intern-card__score-wrapper flex align-center"> <div class="intern-card__score-wrapper flex align-center">
@ -1459,13 +1460,29 @@
}, },
// creates cards containing updates provided by interns // creates cards containing updates provided by interns
updateCard(update) { updateCard(update) {
const { time, internName, topic, description } = update const { time, internName, topic, description, isSubAdmin, floID, note } = update
const card = getRef('update_card_template').content.cloneNode(true) let replyButton
card.querySelector('.update__sender').textContent = internName if (isSubAdmin && !note) {
card.querySelector('.update__time').textContent = getFormattedTime(time) replyButton = html`<button class="button button--small init-update-replay margin-left-auto">Reply</button>`
card.querySelector('.update__topic').textContent = topic }
card.querySelector('.update__message').textContent = description let adminReply
return card; if (note) {
adminReply = html`<div class="admin-reply grid">
<h4 class="admin-reply__title">Admin</h4>
<p class="admin-reply__description">${note}</p>
</div>`
}
return html.node`
<li class="intern-update" data-vector-clock="${`${time}_${floID}`}">
<div class="flex align-center space-between">
<span class="update__sender">${internName}</span>
<span class="update__time">${getFormattedTime(time)}</span>
</div>
<h4 class="update__topic">${topic}</h4>
<p class="update__message">${description}</p>
${replyButton}
${adminReply}
</li>`;
}, },
branchButton(obj = {}) { branchButton(obj = {}) {
const { projectId, branch, page, innerHTML } = obj const { projectId, branch, page, innerHTML } = obj
@ -1521,11 +1538,11 @@
} }
return card; return card;
}, },
requestCard(details) { taskRequestCard(details) {
const { projectCode, branch, task, floId, vectorClock } = details const { projectCode, branch, task, floID, vectorClock } = details
const internName = RIBC.getInternList()[floId]; const internName = RIBC.getInternList()[floID];
return html` return html`
<li class="request-card" .dataset=${{ vectorClock }}> <li class="request-card" .dataset=${{ vectorClock, type: 'task' }}>
<p class="request-card__description"> <p class="request-card__description">
<b class="capitalize">${internName}</b> applied for <b class="capitalize">${internName}</b> applied for
<b class="capitalize">${RIBC.getTaskDetails(projectCode, branch, task).taskTitle}</b> from <b class="capitalize">${RIBC.getTaskDetails(projectCode, branch, task).taskTitle}</b> from
@ -1534,11 +1551,30 @@
</p> </p>
<div class="flex gap-0-3 margin-left-auto"> <div class="flex gap-0-3 margin-left-auto">
<button class="button button--small reject-request"> <button class="button button--small reject-request">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></svg> <svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></svg>
Reject Reject
</button> </button>
<button class="button button--small accept-request"> <button class="button button--small accept-request">
<svg class="icon margin-right-0-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"/></svg> <svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"/></svg>
Accept
</button>
</div>
</li>
`;
},
internshipRequest(details) {
const { comments, floID, name, vectorClock } = details
return html`
<li class="request-card" .dataset=${{ vectorClock, type: 'internship' }}>
<h4>${name}</h4>
<p class="request-card__description">${comments}</p>
<div class="flex gap-0-3 margin-left-auto">
<button class="button button--small reject-request">
<svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></svg>
Reject
</button>
<button class="button button--small accept-request">
<svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"/></svg>
Accept Accept
</button> </button>
</div> </div>
@ -1592,6 +1628,21 @@
projectCard.querySelector('.progress-value').style.width = `${completePercent}%` projectCard.querySelector('.progress-value').style.width = `${completePercent}%`
projectCard.querySelector('.project__complete-percent').textContent = `${completePercent}% complete` projectCard.querySelector('.project__complete-percent').textContent = `${completePercent}% complete`
return projectCard return projectCard
},
internRequests() {
const selectedRequestType = getRef('request_type_selector').value;
let requestCards = [];
if (selectedRequestType === 'task') {
RIBC.getTaskRequests().forEach((request) => {
const { projectCode, branch, task } = request
if (typeof RIBC.getTaskDetails(projectCode, branch, task) !== 'undefined')
requestCards.push(render.taskRequestCard(request))
})
} else {
requestCards = RIBC.getInternRequests().map((request) => render.internshipRequest(request))
}
console.log(requestCards)
renderElem(getRef('requests_list'), html`${requestCards}`)
} }
} }
@ -1643,7 +1694,7 @@
const projectCode = `${new Date().getFullYear()}_project_${RIBC.getProjectList() ? (RIBC.getProjectList().length + 1) : '1'}`; const projectCode = `${new Date().getFullYear()}_project_${RIBC.getProjectList() ? (RIBC.getProjectList().length + 1) : '1'}`;
RIBC.admin.createProject(projectCode) RIBC.admin.createProject(projectCode)
RIBC.admin.addProjectDetails(projectCode, { projectName, projectDescription }) RIBC.admin.addProjectDetails(projectCode, { projectName, projectDescription })
getRef('admin_page__project_list').prepend(render.projectCard(projectCode, true)) getRef('admin_page__project_list').append(render.projectCard(projectCode, true))
closePopup(); closePopup();
} }
@ -2087,14 +2138,17 @@
updateStartIndex = 0 updateStartIndex = 0
updateEndIndex = updates.length > 10 ? 10 : updates.length updateEndIndex = updates.length > 10 ? 10 : updates.length
} }
const isSubAdmin = floGlobals.subAdmins.includes(myFloID);
for (let index = updateStartIndex; index < updateEndIndex; index++) { for (let index = updateStartIndex; index < updateEndIndex; index++) {
const element = updates[index]; const { floID, time, note, update: { projectId, projectBranch, task, topic, description } } = updates[index]
const { floID, time, update: { projectId, projectBranch, task, topic, description } } = element
const updateObj = { const updateObj = {
internName: RIBC.getInternList()[floID], internName: RIBC.getInternList()[floID],
time: parseInt(time), time: parseInt(time),
topic: topic || `${RIBC.getProjectDetails(project).projectName} / ${RIBC.getTaskDetails(projectId, projectBranch, task).taskTitle}`, topic: topic || `${RIBC.getProjectDetails(project).projectName} / ${RIBC.getTaskDetails(projectId, projectBranch, task).taskTitle}`,
description description,
isSubAdmin,
floID,
note
} }
frag.append(render.updateCard(updateObj)) frag.append(render.updateCard(updateObj))
} }
@ -2103,6 +2157,47 @@
intersectionObserver.observe(getRef('all_updates_list').lastElementChild) intersectionObserver.observe(getRef('all_updates_list').lastElementChild)
} }
} }
delegate(getRef('all_updates_list'), 'click', '.init-update-replay', (e) => {
const vectorClock = e.delegateTarget.closest('.intern-update').dataset.vectorClock;
e.delegateTarget.after(html.node`
<div class="update-replay grid gap-0-5">
<div class="update-replay__input">
<sm-textarea placeholder="Enter your reply here" id="update_reply_textarea" rows="4"></sm-textarea>
</div>
<div class="flex align-center gap-0-3 margin-left-auto">
<button class="update-replay__cancel button button--small" onclick="cancelUpdateReply(this.closest('.update-replay'))">Cancel</button>
<div class="multi-state-button">
<button class="update-replay__submit button button--small button--primary" onclick="submitUpdateReply(this.closest('.update-replay'))">Submit</button>
</div>
</div>
</div>
`)
e.delegateTarget.classList.add('hidden')
e.target.closest('.intern-update').querySelector('#update_reply_textarea').focusIn()
})
function cancelUpdateReply(replayBox) {
replayBox.previousElementSibling.classList.remove('hidden')
replayBox.remove()
}
function submitUpdateReply(replayBox) {
buttonLoader(replayBox.querySelector('.update-replay__submit'), true)
const vectorClock = replayBox.previousElementSibling.closest('.intern-update').dataset.vectorClock;
const replyText = replayBox.querySelector('#update_reply_textarea').value.trim()
if (replyText !== '') {
RIBC.admin.commentInternUpdate(vectorClock, replyText).then(res => {
replayBox.previousElementSibling.remove()
replayBox.replaceWith(html.node`
<div class="admin-reply grid">
<h4 class="admin-reply__title">Admin</h4>
<p class="admin-reply__description">${replyText}</p>
</div>`)
}).catch(err => {
notify(err, 'error')
buttonLoader(replayBox.querySelector('.update-replay__submit'), false)
})
}
}
const renderFilteredUpdates = debounce(async (options = {}) => { const renderFilteredUpdates = debounce(async (options = {}) => {
const { lazyLoad = false } = options const { lazyLoad = false } = options
@ -2169,7 +2264,7 @@
btn.disabled = true btn.disabled = true
RIBC.applyForTask(btn.dataset.projectCode, btn.dataset.branch, btn.dataset.taskNo) RIBC.applyForTask(btn.dataset.projectCode, btn.dataset.branch, btn.dataset.taskNo)
.then((result) => { .then((result) => {
notify('', 'Applied successfully.') notify('Applied successfully.', 'success')
allRequests = JSON.parse(localStorage.getItem('requests')) allRequests = JSON.parse(localStorage.getItem('requests'))
allRequests.push({ projectCode: btn.dataset.projectCode, branch: btn.dataset.branch, taskNo: btn.dataset.taskNo }) allRequests.push({ projectCode: btn.dataset.projectCode, branch: btn.dataset.branch, taskNo: btn.dataset.taskNo })
localStorage.setItem('requests', JSON.stringify(allRequests)) localStorage.setItem('requests', JSON.stringify(allRequests))
@ -2243,6 +2338,12 @@
floGlobals.assignedTasks = {} floGlobals.assignedTasks = {}
function renderAllElements() { function renderAllElements() {
let sortedProjectList = getSortedProjectList()
document.querySelectorAll('.open-first-project').forEach(link => {
link.href = `${link.href}?projectId=${sortedProjectList[0]}&branch=mainLine`
})
watchList = localStorage.getItem(`${myFloID}_watchlist`) ? localStorage.getItem(`${myFloID}_watchlist`).split(',') : [] watchList = localStorage.getItem(`${myFloID}_watchlist`) ? localStorage.getItem(`${myFloID}_watchlist`).split(',') : []
const frag = document.createDocumentFragment() const frag = document.createDocumentFragment()
let allProjectsList = RIBC.getProjectList(); let allProjectsList = RIBC.getProjectList();
@ -2276,15 +2377,13 @@
// store all the projects assigned to interns in array // store all the projects assigned to interns in array
allProjectsList.forEach(projectId => { sortedProjectList.forEach(projectId => {
nextProject: nextProject:
for (branch in RIBC.getProjectMap(projectId)) { for (const branch in RIBC.getProjectMap(projectId)) {
projectTaskNos = RIBC.getProjectMap(projectId)[branch].slice(4); projectTaskNos = RIBC.getProjectMap(projectId)[branch].slice(4);
for (let j = 0; j < projectTaskNos.length; j++) { for (let j = 0; j < projectTaskNos.length; j++) {
const assignedInterns = RIBC.getAssignedInterns(projectId, branch, projectTaskNos[j]) const assignedInterns = RIBC.getAssignedInterns(projectId, branch, projectTaskNos[j])
console.log(assignedInterns)
if (Array.isArray(assignedInterns) && assignedInterns.includes(myFloID)) { if (Array.isArray(assignedInterns) && assignedInterns.includes(myFloID)) {
console.log('has me')
assignedProjectsList.push(projectId) assignedProjectsList.push(projectId)
break nextProject; break nextProject;
} }
@ -2347,13 +2446,6 @@
// admin view // admin view
if (floGlobals.subAdmins.includes(myFloID)) { if (floGlobals.subAdmins.includes(myFloID)) {
typeOfUser = 'admin' typeOfUser = 'admin'
const requestCards = [];
RIBC.getTaskRequests().forEach((request) => {
const { projectCode, branch, task } = request
if (typeof RIBC.getTaskDetails(projectCode, branch, task) !== 'undefined')
requestCards.push(render.requestCard(request))
})
renderElem(getRef('requests_list'), html`${requestCards}`)
function removeRequest(requestCard) { function removeRequest(requestCard) {
requestCard.animate([ requestCard.animate([
{ {
@ -2371,23 +2463,52 @@
requestCard.remove() requestCard.remove()
} }
} }
render.internRequests()
// accept task request // accept task request
delegate(getRef('requests_list'), 'click', '.accept-request', (e) => { delegate(getRef('requests_list'), 'click', '.accept-request', (e) => {
const vectorClock = e.delegateTarget.closest('.request-card').dataset.vectorClock getConfirmation('Are you sure you want to accept this request?', (result) => {
const result = RIBC.admin.processTaskRequest(vectorClock, true) if (result) {
if (result === 'Accepted') { const vectorClock = e.delegateTarget.closest('.request-card').dataset.vectorClock
notify('Intern assigned.', 'success') const type = e.delegateTarget.closest('.request-card').dataset.type
removeRequest(e.delegateTarget.closest('.request-card')) let result
} if (type === 'task') {
result = RIBC.admin.processTaskRequest(vectorClock, true)
if (result === 'Accepted') {
notify('Intern assigned.', 'success')
removeRequest(e.delegateTarget.closest('.request-card'))
}
} else if (type === 'internship') {
result = RIBC.admin.processInternRequest(vectorClock, true)
if (result === 'Accepted') {
notify('Added intern', 'success')
removeRequest(e.delegateTarget.closest('.request-card'))
}
}
}
})
}) })
// reject task request // reject task request
delegate(getRef('requests_list'), 'click', '.reject-request', (e) => { delegate(getRef('requests_list'), 'click', '.reject-request', (e) => {
const vectorClock = e.delegateTarget.closest('.request-card').dataset.vectorClock getConfirmation('Are you sure you want to reject this request?').then((result) => {
const result = RIBC.admin.processTaskRequest(vectorClock, false) if (result) {
if (result === 'Rejected') { const vectorClock = e.delegateTarget.closest('.request-card').dataset.vectorClock
notify('Request rejected', 'success') const type = e.delegateTarget.closest('.request-card').dataset.type
removeRequest(e.delegateTarget.closest('.request-card')) let result
} if (type === 'task') {
result = RIBC.admin.processTaskRequest(vectorClock, false)
if (result === 'Rejected') {
notify('Request rejected', 'success')
removeRequest(e.delegateTarget.closest('.request-card'))
}
} else if (type === 'internship') {
result = RIBC.admin.processInternRequest(vectorClock, false)
if (result === 'Rejected') {
notify('Request rejected', 'success')
removeRequest(e.delegateTarget.closest('.request-card'))
}
}
}
})
}) })
document.querySelectorAll('.admin-option').forEach((option) => { document.querySelectorAll('.admin-option').forEach((option) => {
@ -2398,9 +2519,8 @@
renderElem(getRef('admin_page__intern_list'), filterInterns('')) renderElem(getRef('admin_page__intern_list'), filterInterns(''))
//show projects //show projects
renderElem(getRef('admin_page__project_list'), html`${allProjectsList.map(projectCode => render.projectCard(projectCode, true))}`) renderElem(getRef('admin_page__project_list'), html`${getSortedProjectList().map(projectCode => render.projectCard(projectCode, true))}`)
} } else {
else {
document.querySelectorAll('.admin-option').forEach((option) => { document.querySelectorAll('.admin-option').forEach((option) => {
option.classList.add('hidden') option.classList.add('hidden')
}) })
@ -2426,7 +2546,7 @@
// displays recent projects // displays recent projects
let recentProjects = [] let recentProjects = []
if (allInternsList.myFloID && !floGlobals.subAdmins.includes(myFloID)) { if (allInternsList[myFloID] && !floGlobals.subAdmins.includes(myFloID)) {
recentProjects = assignedProjectsList.map((projectCode) => render.projectCard(projectCode)) recentProjects = assignedProjectsList.map((projectCode) => render.projectCard(projectCode))
} else { } else {
recentProjects = allProjectsList.reverse().slice(0, 4).map((projectCode) => render.projectCard(projectCode)) recentProjects = allProjectsList.reverse().slice(0, 4).map((projectCode) => render.projectCard(projectCode))
@ -2435,9 +2555,9 @@
if (allInternsList[myFloID] && !floGlobals.subAdmins.includes(myFloID)) { if (allInternsList[myFloID] && !floGlobals.subAdmins.includes(myFloID)) {
renderElem(getRef('my_projects'), html`${assignedProjectsList.map((project) => render.projectCard(project))}`) renderElem(getRef('my_projects'), html`${assignedProjectsList.map((project) => render.projectCard(project))}`)
allProjectsList = allProjectsList.filter(val => !assignedProjectsList.includes(val)); sortedProjectList = sortedProjectList.filter(val => !assignedProjectsList.includes(val));
} }
renderElem(getRef('other_projects'), html`${allProjectsList.map((project) => render.projectCard(project))}`) renderElem(getRef('other_projects'), html`${sortedProjectList.map((project) => render.projectCard(project))}`)
getRef('explorer_task_list').addEventListener('click', (event) => { getRef('explorer_task_list').addEventListener('click', (event) => {
if (event.target.closest('.apply-button')) { if (event.target.closest('.apply-button')) {
@ -2451,6 +2571,8 @@
console.log(typeOfUser) console.log(typeOfUser)
} }
getRef('request_type_selector').addEventListener('change', render.internRequests)
function postUpdate() { function postUpdate() {
const { projectId, projectBranch, task } = floGlobals.assignedTasks[currentTaskId] const { projectId, projectBranch, task } = floGlobals.assignedTasks[currentTaskId]
const description = getRef('update_description').value.trim() const description = getRef('update_description').value.trim()
@ -2521,6 +2643,10 @@
buttonLoader(getRef('intern_apply__button'), false) buttonLoader(getRef('intern_apply__button'), false)
}) })
} }
function getSortedProjectList() {
return RIBC.getProjectList().sort((a, b) => RIBC.getProjectDetails(a).projectName.toLowerCase().localeCompare(RIBC.getProjectDetails(b).projectName.toLowerCase()))
}
</script> </script>
</body> </body>