added UI for viewing intern request status

This commit is contained in:
sairaj mote 2022-09-13 03:59:34 +05:30
parent 6af16888e6
commit 10a87a0058
4 changed files with 248 additions and 55 deletions

View File

@ -386,6 +386,10 @@ ul {
align-items: flex-start;
}
.align-content-start {
align-content: flex-start;
}
.align-start {
align-content: flex-start;
}
@ -781,7 +785,7 @@ ul {
.task {
display: grid;
grid-template-columns: auto 1fr;
margin: 0 1rem;
margin-right: 1rem;
}
.task .task__branch_container {
padding-bottom: 2rem;
@ -1455,6 +1459,38 @@ ul {
fill: var(--accent-color);
}
#internship_requests_list {
padding-bottom: 2rem;
}
.status-card {
display: grid;
gap: 1rem;
padding: 1rem;
border-radius: 0.5rem;
background-color: rgba(var(--foreground-color), 1);
}
.status-card__time {
font-size: 0.8rem;
color: rgba(var(--text-color), 0.8);
}
.status-card__status {
justify-content: flex-end;
}
.status-card__status .icon {
height: 1em;
width: 1em;
}
.status-card.accepted .icon {
fill: var(--green);
}
.status-card.rejected .icon {
fill: var(--danger-color);
}
.status-card.pending .icon {
fill: var(--yellow);
}
#admin_page__left {
height: 100%;
overflow-y: hidden;
@ -1600,6 +1636,12 @@ input[type=date]:focus {
.list-container {
padding-bottom: 5rem;
}
.status-card__status {
grid-area: 1/2/2/3;
}
.status-card__details {
grid-area: 2/1/3/3;
}
}
@media only screen and (min-width: 640px) {
.hide-on-desktop {
@ -1744,6 +1786,11 @@ input[type=date]:focus {
#intern_list_popup {
--height: 80vh;
}
.status-card {
align-items: center;
font-size: 0.9rem;
grid-template-columns: 6rem 1fr 8rem;
}
}
@media only screen and (min-width: 1280px) {
#main_page {

2
css/main.min.css vendored

File diff suppressed because one or more lines are too long

View File

@ -386,6 +386,9 @@ ul {
.align-items-start {
align-items: flex-start;
}
.align-content-start {
align-content: flex-start;
}
.align-start {
align-content: flex-start;
@ -793,7 +796,7 @@ ul {
.task {
display: grid;
grid-template-columns: auto 1fr;
margin: 0 1rem;
margin-right: 1rem;
.task__branch_container {
padding-bottom: 2rem;
}
@ -1447,6 +1450,44 @@ ul {
}
}
#internship_requests_list {
padding-bottom: 2rem;
}
.status-card {
display: grid;
gap: 1rem;
padding: 1rem;
border-radius: 0.5rem;
background-color: rgba(var(--foreground-color), 1);
&__time {
font-size: 0.8rem;
color: rgba(var(--text-color), 0.8);
}
&__status {
justify-content: flex-end;
.icon {
height: 1em;
width: 1em;
}
}
&.accepted {
.icon {
fill: var(--green);
}
}
&.rejected {
.icon {
fill: var(--danger-color);
}
}
&.pending {
.icon {
fill: var(--yellow);
}
}
}
#admin_page__left {
height: 100%;
overflow-y: hidden;
@ -1580,6 +1621,14 @@ input[type="date"] {
.list-container {
padding-bottom: 5rem;
}
.status-card {
&__status {
grid-area: 1/2/2/3;
}
&__details {
grid-area: 2/1/3/3;
}
}
}
@media only screen and (min-width: 640px) {
.hide-on-desktop {
@ -1743,6 +1792,11 @@ input[type="date"] {
#intern_list_popup {
--height: 80vh;
}
.status-card {
align-items: center;
font-size: 0.9rem;
grid-template-columns: 6rem 1fr 8rem;
}
}
@media only screen and (min-width: 1280px) {

View File

@ -191,6 +191,35 @@
Updates
</span>
</a>
<a href="#applications" class="nav-list__item interact not-for-admin" title="See status of applications">
<svg class="icon icon--outlined" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24"
height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
<g>
<path d="M0,0h24v24H0V0z" fill="none" />
</g>
<g>
<g>
<path
d="M15,3H5C3.9,3,3.01,3.9,3.01,5L3,19c0,1.1,0.89,2,1.99,2H19c1.1,0,2-0.9,2-2V9L15,3z M5,19V5h9v5h5v9H5z M9,8 c0,0.55-0.45,1-1,1S7,8.55,7,8s0.45-1,1-1S9,7.45,9,8z M9,12c0,0.55-0.45,1-1,1s-1-0.45-1-1s0.45-1,1-1S9,11.45,9,12z M9,16 c0,0.55-0.45,1-1,1s-1-0.45-1-1s0.45-1,1-1S9,15.45,9,16z" />
</g>
</g>
</svg>
<svg class="icon icon--filled" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24"
height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
<g>
<path d="M0,0h24v24H0V0z" fill="none" />
</g>
<g>
<g>
<path
d="M15,3H5C3.9,3,3.01,3.9,3.01,5L3,19c0,1.1,0.89,2,1.99,2H19c1.1,0,2-0.9,2-2V9L15,3z M8,17c-0.55,0-1-0.45-1-1s0.45-1,1-1 s1,0.45,1,1S8.55,17,8,17z M8,13c-0.55,0-1-0.45-1-1s0.45-1,1-1s1,0.45,1,1S8.55,13,8,13z M8,9C7.45,9,7,8.55,7,8s0.45-1,1-1 s1,0.45,1,1S8.55,9,8,9z M14,10V4.5l5.5,5.5H14z" />
</g>
</g>
</svg>
<span class="nav-list__item_title">
Applications
</span>
</a>
<a href="#admin_page" class="admin-option nav-list__item interact open-first-project"
title="open admin panel">
<svg class="icon icon--outlined" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
@ -465,7 +494,8 @@
<h4>Tasks</h4>
<div id="branch_container"></div>
<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 margin-bottom-1">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()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
@ -555,6 +585,18 @@
<h4 class="empty-state">No related updates</h4>
</section>
</section>
<section id="applications" class="page hidden align-content-start">
<p>Check status of your applications</p>
<div class="hidden">
<ul id="task_requests_list" class="grid gap-0-5 observe-empty-state"></ul>
<h4 class="empty-state">No task requests</h4>
</div>
<div>
<ul id="internship_requests_list" class="grid gap-0-5 observe-empty-state"></ul>
<h4 class="empty-state">No applications</h4>
</div>
</section>
<section id="all_interns_page" class="page hidden flex flex-direction-column align-start">
<div id="all_interns_page__header" class="grid gap-0-5">
<h2>Interns</h2>
@ -889,7 +931,6 @@
</script>
<script id="default_ui_library">
// Global variables
const appPages = ['dashboard_page', 'admin_page', 'settings_page', 'project_explorer', 'updates_page', 'all_interns_page'];
const { html, render: renderElem } = uhtml;
const domRefs = {}
//Checks for internet connection status
@ -1144,7 +1185,8 @@
pageId = targetPage.split('#').pop()
}
}
if (!appPages.includes(pageId)) return
if (!document.querySelector(`#${pageId}.page`)) return
if (searchParams) {
const urlSearchParams = new URLSearchParams('?' + searchParams);
@ -1176,6 +1218,9 @@
renderInternSelectorOptions()
}
break;
case 'applications':
render.internApplications()
break;
case 'all_interns_page':
renderAllInterns()
break;
@ -1203,10 +1248,10 @@
}
break;
case 'admin_page':
if (params) {
if (params && RIBC.getProjectList().includes(params.projectId)) {
const { projectId, branch } = params
editProjectInfo(projectId)
if (params.branch) {
renderAdminProjectView(projectId)
if (branch) {
const branchDetails = {
destination: 'project_editing_panel',
taskListContainer: 'task_list',
@ -1221,6 +1266,7 @@
} else {
getRef('admin_page__left').classList.remove('hide-on-mobile')
getRef('project_editing_panel').classList.add('hide-on-mobile')
history.replaceState(null, '', '#admin_page')
}
break;
}
@ -1392,10 +1438,10 @@
<script id="UI_functions">
// Method object for creating various UI elements
const render = {
projectCard(projectCode, isAdmin = false) { // creates cards containing project information
projectCard(projectCode, isAdmin = false, ref) { // creates cards containing project information
const projectName = RIBC.getProjectDetails(projectCode).projectName
const page = isAdmin ? 'admin_page' : 'project_explorer'
return html`<a class="project-card flex align-center interact" title="Project information" href=${`#${page}?projectId=${projectCode}&branch=mainLine`}>${projectName}</a>`
return html.for(ref, projectCode)`<a class="project-card flex align-center interact" title="Project information" href=${`#${page}?projectId=${projectCode}&branch=mainLine`}>${projectName}</a>`
},
taskCard(currentProject, currentBranch, taskNo) {
const card = getRef('timeline_task_card').content.cloneNode(true),
@ -1460,9 +1506,9 @@
},
// creates cards containing updates provided by interns
updateCard(update) {
const { time, internName, topic, description, isSubAdmin, floID, note } = update
const { time, internName, topic, description, floID, note } = update
let replyButton
if (isSubAdmin && !note) {
if (typeOfUser === 'admin' && !note) {
replyButton = html`<button class="button button--small init-update-replay margin-left-auto">Reply</button>`
}
let adminReply
@ -1643,6 +1689,56 @@
}
console.log(requestCards)
renderElem(getRef('requests_list'), html`${requestCards}`)
},
projectList(container, projects, isAdminList = false) {
renderElem(container, html`${projects.map(projectCode => render.projectCard(projectCode, isAdminList, container))}`)
},
requestStatus(request, type) {
const { comments, floID, projectCode, branch, task, name, status, vectorClock } = request
const timestamp = parseInt(vectorClock.split('_')[0])
let details
let icon = ''
if (status === 'Accepted') {
icon = html`<svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></svg>`
} else if (status === 'Rejected') {
icon = html`<svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM4 12c0-4.42 3.58-8 8-8 1.85 0 3.55.63 4.9 1.69L5.69 16.9C4.63 15.55 4 13.85 4 12zm8 8c-1.85 0-3.55-.63-4.9-1.69L18.31 7.1C19.37 8.45 20 10.15 20 12c0 4.42-3.58 8-8 8z"/></svg>`
} else {
icon = html`<svg class="icon margin-right-0-3" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/></svg>`
}
if (type === 'internship') {
details = html` <h4 class="status-card__details">Internship application</h4> `;
} else {
details = html`
<p class="status-card__details">
You applied for
<b class="capitalize">${RIBC.getTaskDetails(projectCode, branch, task).taskTitle}</b> from
<b class="capitalize">${branch}</b> of
<b class="capitalize">${RIBC.getProjectDetails(projectCode).projectName}</b>
</p>`;
}
return html`
<li class=${`status-card ${status?.toLowerCase() || 'pending'}`}>
<time class="status-card__time">${getFormattedTime(timestamp, 'relative')}</time>
${details}
<div class="flex align-center status-card__status">
${icon}
<span>${status || 'Under review'}</span>
</div>
</li>
`
},
internApplications() {
const internshipRequests = RIBC.getInternRequests(false).reverse()
const taskRequests = RIBC.getTaskRequests(false).reverse()
const requestCards = internshipRequests.map(request => render.requestStatus(request, 'internship'))
renderElem(getRef('internship_requests_list'), html`${requestCards}`)
if (taskRequests.length) {
const taskCards = taskRequests.map(request => render.requestStatus(request, 'task'))
renderElem(getRef('task_requests_list'), html`${taskCards}`)
getRef('task_requests_list').parentElement.classList.remove('hidden')
} else {
getRef('task_requests_list').parentElement.classList.add('hidden')
}
}
}
@ -1655,13 +1751,6 @@
}
//helper functions
function setAttributes(el, attrs) {
for (var key in attrs) {
el.setAttribute(key, attrs[key]);
}
}
let allInternsList = [];
let highPerformingInterns = [];
let watchList = [];
@ -1682,19 +1771,17 @@
function addProjectToList() {
let projectName = getRef('project_name_field').value.trim(),
projectDescription = getRef('project_description_field').value.trim();
if (!projectName) {
notify('Project name is important!', 'error')
return
if (projectName === '') {
return notify('Project name is important!', 'error')
}
if (!projectDescription) {
notify('Project description is important!', 'error')
return
if (projectDescription === '') {
return notify('Project description is important!', 'error')
}
const projectCode = `${new Date().getFullYear()}_project_${RIBC.getProjectList() ? (RIBC.getProjectList().length + 1) : '1'}`;
RIBC.admin.createProject(projectCode)
RIBC.admin.addProjectDetails(projectCode, { projectName, projectDescription })
getRef('admin_page__project_list').append(render.projectCard(projectCode, true))
render.projectList(getRef('admin_page__project_list'), getSortedProjectList(), true)
getRef('admin_page__project_list').querySelector(`[href="#admin_page?projectId=${projectCode}&branch=mainLine"]`)?.click()
closePopup();
}
@ -1716,8 +1803,9 @@
if (e.target.innerText.trim() !== '' && floGlobals.tempEditableContent !== DOMPurify.sanitize(e.target.innerText.trim())) {
const newTitle = DOMPurify.sanitize(getRef('editing_panel__title').innerText.trim())
const newDescription = DOMPurify.sanitize(getRef('editing_panel__description').innerText.trim())
const res = RIBC.admin.addProjectDetails(currentProject, { projectName: newTitle, projectDescription: newDescription })
RIBC.admin.addProjectDetails(currentProject, { projectName: newTitle, projectDescription: newDescription })
notify('Changes saved locally, commit the changes to make them permanent', 'success')
render.projectList(getRef('admin_page__project_list'), getSortedProjectList(), true)
} else {
e.target.innerText = floGlobals.tempEditableContent
}
@ -1765,7 +1853,7 @@
currentProject = '',
currentTask = '',
lastProject;
function editProjectInfo(projectId) {
function renderAdminProjectView(projectId) {
const allProjects = getRef('admin_page__project_list').querySelectorAll('.project-card');
const branchList = document.querySelectorAll('.branch-button');
const frag = document.createDocumentFragment();
@ -1814,19 +1902,10 @@
})
}
if (branch !== 'mainLine') {
const { startPoint } = getAllBranches(projectId).find(({ branchName }) => branchName === branch)
let branchEntryPoint = html`
<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="M7.828 11H20v2H7.828l5.364 5.364-1.414 1.414L4 12l7.778-7.778 1.414 1.414z"/></svg>
${branchTasks[0]}
`
if (startPoint) {
const { taskTitle } = RIBC.getTaskDetails(projectId, branchTasks[0], startPoint)
branchEntryPoint = html`
<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="M7.828 11H20v2H7.828l5.364 5.364-1.414 1.414L4 12l7.778-7.778 1.414 1.414z"/></svg>
${branchTasks[0]} - ${taskTitle}
`
}
taskList.append(render.branchButton({ projectId, branch: branchTasks[0], page: pageId, innerHTML: branchEntryPoint }))
const { startPoint, parentBranch } = getAllBranches(projectId).find(({ branchName }) => branchName === branch)
taskList.append(html.node`<p class="margin-bottom-0-5">
Branched off from <a href=${`#project_explorer?projectId=${projectId}&branch=${parentBranch}`}> ${parentBranch} </a>
</p>`)
}
taskList.appendChild(frag);
}
@ -2138,7 +2217,6 @@
updateStartIndex = 0
updateEndIndex = updates.length > 10 ? 10 : updates.length
}
const isSubAdmin = floGlobals.subAdmins.includes(myFloID);
for (let index = updateStartIndex; index < updateEndIndex; index++) {
const { floID, time, note, update: { projectId, projectBranch, task, topic, description } } = updates[index]
const updateObj = {
@ -2146,7 +2224,6 @@
time: parseInt(time),
topic: topic || `${RIBC.getProjectDetails(project).projectName} / ${RIBC.getTaskDetails(projectId, projectBranch, task).taskTitle}`,
description,
isSubAdmin,
floID,
note
}
@ -2519,7 +2596,7 @@
renderElem(getRef('admin_page__intern_list'), filterInterns(''))
//show projects
renderElem(getRef('admin_page__project_list'), html`${getSortedProjectList().map(projectCode => render.projectCard(projectCode, true))}`)
render.projectList(getRef('admin_page__project_list'), getSortedProjectList(), true)
} else {
document.querySelectorAll('.admin-option').forEach((option) => {
option.classList.add('hidden')
@ -2537,6 +2614,22 @@
elem.classList.add('hidden')
})
}
if (typeOfUser === 'admin') {
document.querySelectorAll('.not-for-admin').forEach((elem) => {
elem.classList.add('hidden')
})
} else {
const internshipRequests = RIBC.getInternRequests(false).reverse()
const hasPendingApplication = internshipRequests.find(request => !request.status)
if (internshipRequests.length === 0 || !hasPendingApplication) {
getRef('application_card').classList.remove('hidden')
} else {
getRef('application_card').classList.add('hidden')
}
document.querySelectorAll('.not-for-admin').forEach((elem) => {
elem.classList.remove('hidden')
})
}
renderElem(getRef('top_interns'), html`${highPerformingInterns.slice(0, 4).map((intern) => {
const { internName, floId, rating } = intern
@ -2546,26 +2639,24 @@
// displays recent projects
let recentProjects = []
if (allInternsList[myFloID] && !floGlobals.subAdmins.includes(myFloID)) {
recentProjects = assignedProjectsList.map((projectCode) => render.projectCard(projectCode))
if (typeOfUser === 'intern') {
render.projectList(getRef('project_list'), assignedProjectsList)
} else {
recentProjects = allProjectsList.reverse().slice(0, 4).map((projectCode) => render.projectCard(projectCode))
render.projectList(getRef('project_list'), allProjectsList.reverse().slice(0, 4))
}
renderElem(getRef('project_list'), html`${recentProjects}`)
if (allInternsList[myFloID] && !floGlobals.subAdmins.includes(myFloID)) {
renderElem(getRef('my_projects'), html`${assignedProjectsList.map((project) => render.projectCard(project))}`)
if (typeOfUser === 'intern') {
render.projectList(getRef('my_projects'), assignedProjectsList)
sortedProjectList = sortedProjectList.filter(val => !assignedProjectsList.includes(val));
}
renderElem(getRef('other_projects'), html`${sortedProjectList.map((project) => render.projectCard(project))}`)
render.projectList(getRef('other_projects'), sortedProjectList)
getRef('explorer_task_list').addEventListener('click', (event) => {
if (event.target.closest('.apply-button')) {
requestForTask(event.target.closest('.apply-button'))
}
})
const greetings = RIBC.getInternList()[myFloID] !== undefined ? `Hi, ${RIBC.getInternList()[myFloID]}` : `Hi, there!`
getRef('username').textContent = greetings;
getRef('username').textContent = typeOfUser === 'intern' ? `Hi, ${RIBC.getInternList()[myFloID]}` : `Hi, there!`;
getRef('user_flo_id').value = myFloID;
getRef('user_role').textContent = typeOfUser;
@ -2635,6 +2726,7 @@
RIBC.applyForIntern(name, brief)
.then((result) => {
notify('Application submitted', 'success')
getRef('application_card').classList.add('hidden')
closePopup()
})
.catch((error) => {