New scoring system display UI

This commit is contained in:
sairaj mote 2022-11-09 16:53:15 +05:30
parent 0ecde83eb1
commit 75a55b2252
6 changed files with 242 additions and 111 deletions

View File

@ -158,26 +158,25 @@ button:not(:disabled),
border: solid thin rgba(var(--text-color), 0.06);
}
.button--primary {
color: rgba(var(--background-color), 1) !important;
color: rgba(var(--background-color), 1);
background-color: var(--accent-color);
}
.button--primary .icon {
fill: rgba(var(--background-color), 1);
}
.button--danger {
color: var(--danger-color);
}
.button--danger .icon {
fill: var(--danger-color);
}
.button--primary {
background-color: var(--accent-color);
}
.button--colored {
color: var(--accent-color);
}
.button--colored .icon {
fill: var(--accent-color);
}
.button--danger {
background-color: rgba(255, 115, 115, 0.062745098);
color: var(--danger-color);
}
.button--danger .icon {
fill: var(--danger-color);
}
.button--small {
padding: 0.4rem 0.6rem;
}
@ -246,18 +245,6 @@ details[open] > summary .down-arrow {
transform: rotate(180deg);
}
fieldset {
border: none;
}
input {
accent-color: var(--accent-color);
}
input[type=range]:active {
cursor: -webkit-grab;
cursor: grab;
}
sm-input,
sm-textarea,
tags-input {
@ -422,6 +409,10 @@ ul {
align-items: flex-start;
}
.align-items-center {
align-items: center;
}
.align-content-start {
align-content: flex-start;
}
@ -450,7 +441,7 @@ ul {
justify-content: start;
}
.justify-center {
.justify-content-center {
justify-content: center;
}
@ -486,6 +477,10 @@ ul {
justify-content: space-between;
}
.space-evenly {
justify-content: space-evenly;
}
.w-100 {
width: 100%;
}
@ -1291,14 +1286,6 @@ ul {
flex: 1;
}
#intern_info_popup .grid > * {
justify-self: center;
}
#intern_info_popup #update_intern_score {
width: 100%;
margin-top: 1rem;
}
#intern_info__initials {
position: relative;
height: 4rem;
@ -1309,15 +1296,81 @@ ul {
#intern_info__name {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
.icon--star {
fill: var(--orange);
}
#intern_info__score {
font-size: 1.5rem;
#stats_wrapper {
display: flex;
gap: 1rem;
justify-content: space-evenly;
margin-top: 1.5rem;
}
#stats_wrapper > * {
flex: 1;
}
.stat {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 0.5rem;
}
.stat__display {
display: grid;
justify-items: center;
align-items: center;
height: 4rem;
}
.stat__display > * {
grid-area: 1/1;
}
.stat__circle {
height: 100%;
stroke-width: 10;
overflow: visible;
stroke-dasharray: 400;
stroke-dashoffset: var(--progress, 400);
transform: rotate(-90deg);
stroke-linecap: round;
transition: stroke-dashoffset 1s;
}
.stat__count {
font-size: 1.2rem;
font-weight: 700;
}
.stat p {
color: rgba(var(--text-color), 0.8);
font-size: 0.9rem;
max-width: 8ch;
line-height: 1.2;
}
#intern_rating .stat__circle {
fill: rgba(var(--text-color), 0.06);
stroke: var(--rating-color, --green);
}
#intern_rating .stat__count {
color: var(--rating-color, --green);
}
#intern_complete_tasks .stat__circle {
fill: rgba(255, 61, 0, 0.062745098);
stroke-dashoffset: 0;
}
#intern_complete_tasks .stat__count {
color: var(--orange);
}
#intern_points .stat__circle {
fill: rgba(109, 131, 255, 0.062745098);
stroke-dashoffset: 0;
}
#intern_points .stat__count {
color: var(--nice-blue);
}
#project_info {
@ -1762,8 +1815,8 @@ input[type=date] {
display: flex;
width: 100%;
padding: 0.5rem;
border: rgba(var(--text-color), 0.2) solid thin;
border-radius: 0.3rem;
border: none;
border-radius: 0.5rem;
font-family: inherit;
font-size: inherit;
color: inherit;

2
css/main.min.css vendored

File diff suppressed because one or more lines are too long

View File

@ -145,28 +145,26 @@ button,
background-color: rgba(var(--text-color), 0.02);
border: solid thin rgba(var(--text-color), 0.06);
&--primary {
color: rgba(var(--background-color), 1) !important;
color: rgba(var(--background-color), 1);
background-color: var(--accent-color);
.icon {
fill: rgba(var(--background-color), 1);
}
}
&--danger {
color: var(--danger-color);
.icon {
fill: var(--danger-color);
}
}
&--primary {
background-color: var(--accent-color);
}
&--colored {
color: var(--accent-color);
.icon {
fill: var(--accent-color);
}
}
&--danger {
background-color: #ff737310;
color: var(--danger-color);
.icon {
fill: var(--danger-color);
}
}
&--small {
padding: 0.4rem 0.6rem;
@ -230,20 +228,6 @@ details[open] {
}
}
fieldset {
border: none;
}
input {
accent-color: var(--accent-color);
&[type="range"] {
&:active {
cursor: grab;
}
}
}
sm-input,
sm-textarea,
tags-input {
@ -411,6 +395,9 @@ ul {
.align-items-start {
align-items: flex-start;
}
.align-items-center {
align-items: center;
}
.align-content-start {
align-content: flex-start;
}
@ -438,7 +425,7 @@ ul {
justify-content: start;
}
.justify-center {
.justify-content-center {
justify-content: center;
}
@ -473,6 +460,9 @@ ul {
.space-between {
justify-content: space-between;
}
.space-evenly {
justify-content: space-evenly;
}
.w-100 {
width: 100%;
@ -1283,15 +1273,6 @@ ul {
}
#intern_info_popup {
.grid {
& > * {
justify-self: center;
}
}
#update_intern_score {
width: 100%;
margin-top: 1rem;
}
}
#intern_info__initials {
position: relative;
@ -1302,15 +1283,82 @@ ul {
}
#intern_info__name {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
.icon--star {
fill: var(--orange);
}
#intern_info__score {
font-size: 1.5rem;
#stats_wrapper {
display: flex;
gap: 1rem;
justify-content: space-evenly;
margin-top: 1.5rem;
& > * {
flex: 1;
}
}
.stat {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 0.5rem;
&__display {
display: grid;
justify-items: center;
align-items: center;
height: 4rem;
& > * {
grid-area: 1/1;
}
}
&__circle {
height: 100%;
stroke-width: 10;
overflow: visible;
stroke-dasharray: 400;
stroke-dashoffset: var(--progress, 400);
transform: rotate(-90deg);
stroke-linecap: round;
transition: stroke-dashoffset 1s;
}
&__count {
font-size: 1.2rem;
font-weight: 700;
}
p {
color: rgba(var(--text-color), 0.8);
font-size: 0.9rem;
max-width: 8ch;
line-height: 1.2;
}
}
#intern_rating {
.stat__circle {
fill: rgba(var(--text-color), 0.06);
stroke: var(--rating-color, --green);
}
.stat__count {
color: var(--rating-color, --green);
}
}
#intern_complete_tasks {
.stat__circle {
fill: #ff3d0010;
stroke-dashoffset: 0;
}
.stat__count {
color: var(--orange);
}
}
#intern_points {
.stat__circle {
fill: #6d83ff10;
stroke-dashoffset: 0;
}
.stat__count {
color: var(--nice-blue);
}
}
#project_info {
flex-direction: column;
}
@ -1739,8 +1787,8 @@ input[type="date"] {
display: flex;
width: 100%;
padding: 0.5rem;
border: rgba(var(--text-color), 0.2) solid thin;
border-radius: 0.3rem;
border: none;
border-radius: 0.5rem;
font-family: inherit;
font-size: inherit;
color: inherit;

View File

@ -105,7 +105,7 @@
</div>
<img src="assets/working-intern.svg" alt="">
</div>
<div id="landing_tasks_wrapper" class="flex flex-direction-column justify-center"></div>
<div id="landing_tasks_wrapper" class="flex flex-direction-column justify-content-center"></div>
</div>
<article id="sign_in" class="inner-page hidden">
<section>
@ -878,7 +878,7 @@
// displays a popup for asking permission. Use this instead of JS confirm
const getConfirmation = (title, options = {}) => {
return new Promise(resolve => {
const { message = '', cancelText = 'Cancel', confirmText = 'OK' } = options
const { message = '', cancelText = 'Cancel', confirmText = 'OK', danger = false } = options
openPopup('confirmation_popup', true)
getRef('confirm_title').innerText = title;
getRef('confirm_message').innerText = message;
@ -886,6 +886,10 @@
const confirmButton = getRef('confirmation_popup').querySelector('.confirm-button')
confirmButton.textContent = confirmText
cancelButton.textContent = cancelText
if (danger)
confirmButton.classList.add('button--danger')
else
confirmButton.classList.remove('button--danger')
confirmButton.onclick = () => {
closePopup()
resolve(true);
@ -1142,7 +1146,7 @@
<ul id="assigned_task_list">${renderedAssignedTasks}</ul>
</section>
` : ''}
<div id="dashboard_tasks_wrapper" class=${`flex flex-direction-column justify-center dashboard-view__item ${typeOfUser === 'intern' ? 'hidden' : ''}`}>${render.displayTasks(params?.category, params?.search)}</div>
<div id="dashboard_tasks_wrapper" class=${`flex flex-direction-column justify-content-center dashboard-view__item ${typeOfUser === 'intern' ? 'hidden' : ''}`}>${render.displayTasks(params?.category, params?.search)}</div>
<div id="projects_wrapper" class="grid gap-2 align-items-start dashboard-view__item hidden">${render.dashProjects()}</div>
<div id="intern_leaderboard_container" class="container-card dashboard-view__item hide-on-mobile">
<div class="container-header">
@ -1975,7 +1979,7 @@
</div>
<h4 class="task-title capitalize" data-editable data-edit-field="title">${title}</h4>
<div class="assigned-interns">
<button class="button--outlined button--small button--colored" onclick="currentTask=this.closest('.admin-task');openPopup('intern_list_popup')">
<button class="button button--small button--colored" onclick="currentTask=this.closest('.admin-task');openPopup('intern_list_popup')">
<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 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0-6c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zM5 18c.2-.63 2.57-1.68 4.96-1.94l2.04-2c-.39-.04-.68-.06-1-.06-2.67 0-8 1.34-8 4v2h9l-2-2H5zm15.6-5.5l-5.13 5.17-2.07-2.08L12 17l3.47 3.5L22 13.91z"/></svg>
Assign intern
</button>
@ -2509,31 +2513,57 @@
// opens a popup containing various intern information
function showInternInfo(e) {
const internFloId = e.target.closest('.intern-card').dataset.internFloId;
const { completedTasks = {}, active = true } = RIBC.getInternRecord(internFloId) || {}
const internName = RIBC.getInternList()[internFloId]
const { completedTasks = [], active = true } = RIBC.getInternRecord(internFloId) || {}
const rating = RIBC.getInternRating(internFloId)
let completedTasksCount = 0;
let totalPoints = 0
for (const task in completedTasks) {
completedTasksCount++
totalPoints += completedTasks[task].points
}
renderElem(getRef('intern_info__wrapper'), html`
<div id="intern_info__initials" class="intern-card__initials" style=${`--color: var(${getInternColor(internFloId)})`}>${internName.split(' ').map(v => v.charAt(0)).join('')}</div>
<h3 id="intern_info__name">${internName}</h3>
<div class="flex flex-direction-column align-items-center gap-1 text-center">
<div id="intern_info__initials" class="intern-card__initials" style=${`--color: var(${getInternColor(internFloId)})`}>${internName.split(' ').map(v => v.charAt(0)).join('')}</div>
<h3 id="intern_info__name">${internName}</h3>
</div>
<sm-copy id="intern_info__flo_id" value=${internFloId}></sm-copy>
<div id="update_intern_score" class="flex align-center">
<svg class="icon icon--star 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 18.26l-7.053 3.948 1.575-7.928L.587 8.792l8.027-.952L12 .5l3.386 7.34 8.027.952-5.935 5.488 1.575 7.928z" /> </svg>
<h4 id="intern_info__score">0</h4>
<div id="stats_wrapper">
<div id="intern_rating" class="stat">
<div class="stat__display">
<svg class="stat__circle" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="64" cy="64" r="63.5"/> </svg>
<span class="stat__count">${rating}%</h4>
</div>
<p>Rating</p>
</div>
<div id="intern_complete_tasks" class="stat">
<div class="stat__display">
<svg class="stat__circle" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="64" cy="64" r="63.5"/> </svg>
<span class="stat__count">${completedTasksCount}</h4>
</div>
<p>Task completed</p>
</div>
<div id="intern_points" class="stat">
<div class="stat__display">
<svg class="stat__circle" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="64" cy="64" r="63.5"/> </svg>
<span class="stat__count">${totalPoints}</h4>
</div>
<p>Points earned</p>
</div>
</div>
`)
openPopup('intern_info_popup');
// animate number counting up within 1s from 0 to the intern's score
const scoreElem = getRef('intern_info__score')
const finalScore = RIBC.getInternRating(internFloId)
let score = 0;
const scoreInterval = setInterval(incrementNumber, 600 / finalScore)
function incrementNumber() {
scoreElem.textContent = score;
score++;
if (score > finalScore) {
clearInterval(scoreInterval)
}
// TODO: popup opening should return a promise
let color = '--green';
if (rating < 50) {
color = '--danger-color'
} else if (rating < 80) {
color = '--orange'
}
incrementNumber()
setTimeout(() => {
document.getElementById('intern_rating').style.setProperty('--progress', `${400 - (rating * 4)}`)
document.getElementById('intern_rating').style.setProperty('--rating-color', `var(${color})`)
}, 0);
}
// opens a popup containing various project information
@ -2634,7 +2664,7 @@
<div class="flex flex-direction-column gap-0-5 rating-part" data-intern-id=${intern}>
<h4>${RIBC.getInternList()[intern]}</h4>
<div class="flex gap-0-5">
<sm-input type="number" min="0" max="50" class="flex-1" placeholder="Rate out of 50" error-text="Points must be between 0-50" autofocus required></sm-input>
<sm-input type="number" min="0" max="50" class="flex-1" placeholder="Rate out of 50" error-text="Points must be between 0-50" ?autofocus=${index === 0} required></sm-input>
<input class="flex-1" type="date" value=${formatDate(new Date())} placeholder="Completion date" aria-label="Set date of completion" required>
</div>
</div>
@ -2646,7 +2676,7 @@
}
function markTaskAsIncomplete(e) {
currentTask = e.target.closest('.admin-task');
getConfirmation('Mark this task as incomplete?', { message: 'Score given to participants regarding this task will also be removed', confirmText: 'Mark as incomplete' }).then(res => {
getConfirmation('Mark this task as incomplete?', { message: 'Score given to participants regarding this task will also be removed', confirmText: 'Mark as incomplete', danger: true }).then(res => {
if (res) {
RIBC.admin.putTaskStatus('incomplete', appState.params.id, appState.params.branch, currentTask.dataset.taskId)
// TODO: remove task scores from intern rating
@ -2674,9 +2704,9 @@
document.querySelectorAll('.rating-part').forEach((ratingPart) => {
const taskId = `${appState.params.id}_${appState.params.branch}_${currentTask.dataset.taskId}`;
const internId = ratingPart.dataset.internId;
const score = 50 + parseInt(ratingPart.querySelector('sm-input').value.trim());
const points = 50 + parseInt(ratingPart.querySelector('sm-input').value.trim());
const completionDate = ratingPart.querySelector('input').value;
RIBC.admin.addTaskScore(internId, taskId, score, { completionDate })
RIBC.admin.addTaskScore(internId, taskId, points, { completionDate })
})
markTaskAsCompleted()
closePopup()
@ -2758,7 +2788,7 @@
}
}
else if (e.target.closest('.assigned-intern button')) {
getConfirmation('Do you want to unassign this intern from this task?', { confirmText: 'Unassign' }).then((result) => {
getConfirmation('Do you want to unassign this intern from this task?', { confirmText: 'Unassign', danger: true }).then((result) => {
if (result) {
RIBC.admin.unassignInternFromTask(e.target.closest('.assigned-intern').dataset.floId, appState.params.id, appState.params.branch, currentTask.dataset.taskId)
notify('Intern removed from the task')
@ -2863,7 +2893,7 @@
})
}
function removeThisTask() {
getConfirmation("Are you sure to delete this task?", { confirmText: 'Delete' }).then((result) => {
getConfirmation("Are you sure to delete this task?", { confirmText: 'Delete', danger: true }).then((result) => {
if (result) {
RIBC.admin.deleteTaskInMap(appState.params.id, appState.params.branch, currentTask.dataset.taskId)
const taskId = `${appState.params.id}_${appState.params.branch}_${currentTask.dataset.taskId}`;

View File

@ -191,7 +191,7 @@
}
return true;
}
Admin.addTaskScore = function (floID, taskId, score, details = {}) {
Admin.addTaskScore = function (floID, taskId, points, details = {}) {
if (!(floID in _.internList))
return false;
if (!_.internRecord[floID])
@ -200,14 +200,14 @@
completedTasks: {},
}
_.internRecord[floID].completedTasks[taskId] = {
score,
points,
...details
};
let totalScore = 0;
for (const taskId in _.internRecord[floID].completedTasks) {
totalScore += _.internRecord[floID].completedTasks[taskId].score;
totalScore += _.internRecord[floID].completedTasks[taskId].points;
}
_.internRating[floID] = totalScore;
_.internRating[floID] = parseInt(totalScore / (Object.keys(_.internRecord[floID].completedTasks).length || 1));
return true;
}
Admin.setInternStatus = function (floID, active = true) {

2
scripts/ribc.min.js vendored

File diff suppressed because one or more lines are too long