Admin feature update and bug fixes
-- deadline of a task assigned to intern now directly visible in admin view
This commit is contained in:
parent
5f23b0d501
commit
3c543d2610
36
css/main.css
36
css/main.css
@ -1585,10 +1585,11 @@ ul {
|
|||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
padding: 0 0.4rem;
|
padding: 0 0.4rem;
|
||||||
border-radius: 0.3rem;
|
border-radius: 0.3rem;
|
||||||
border: 1px solid rgba(var(--text-color), 0.24);
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
@ -1596,7 +1597,36 @@ ul {
|
|||||||
}
|
}
|
||||||
.assigned-intern .unassign-intern-button {
|
.assigned-intern .unassign-intern-button {
|
||||||
padding: 0.4rem;
|
padding: 0.4rem;
|
||||||
margin-right: -0.4rem;
|
}
|
||||||
|
.assigned-intern .task-status {
|
||||||
|
color: rgba(var(--text-color), 0.8);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
.assigned-intern .task-status:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.assigned-intern .task-status--overdue {
|
||||||
|
color: var(--danger-color);
|
||||||
|
}
|
||||||
|
.assigned-intern .task-status--in-progress {
|
||||||
|
color: var(--green);
|
||||||
|
}
|
||||||
|
.assigned-intern .task-status--completed {
|
||||||
|
color: var(--green);
|
||||||
|
}
|
||||||
|
.assigned-intern--completed {
|
||||||
|
padding: 0.4rem;
|
||||||
|
}
|
||||||
|
.assigned-intern--in-progress::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: var(--progress, 0);
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(64, 255, 64, 0.2);
|
||||||
|
border-radius: 0.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
@ -1619,6 +1649,8 @@ ul {
|
|||||||
}
|
}
|
||||||
.menu__item button {
|
.menu__item button {
|
||||||
padding: 0.6rem 0.8rem;
|
padding: 0.6rem 0.8rem;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
#branch_container {
|
#branch_container {
|
||||||
|
|||||||
2
css/main.min.css
vendored
2
css/main.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1533,17 +1533,49 @@ ul {
|
|||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
padding: 0 0.4rem;
|
padding: 0 0.4rem;
|
||||||
border-radius: 0.3rem;
|
border-radius: 0.3rem;
|
||||||
border: 1px solid rgba(var(--text-color), 0.24);
|
background-color: rgba(var(--foreground-color), 1);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.unassign-intern-button {
|
.unassign-intern-button {
|
||||||
padding: 0.4rem;
|
padding: 0.4rem;
|
||||||
margin-right: -0.4rem;
|
}
|
||||||
|
.task-status {
|
||||||
|
color: rgba(var(--text-color), 0.8);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
&:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&--overdue {
|
||||||
|
color: var(--danger-color);
|
||||||
|
}
|
||||||
|
&--in-progress {
|
||||||
|
color: var(--green);
|
||||||
|
}
|
||||||
|
&--completed {
|
||||||
|
color: var(--green);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&--completed {
|
||||||
|
padding: 0.4rem;
|
||||||
|
}
|
||||||
|
&--in-progress {
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: var(--progress, 0);
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(64, 255, 64, 0.2);
|
||||||
|
border-radius: 0.3rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.menu {
|
.menu {
|
||||||
@ -1564,6 +1596,8 @@ ul {
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
button {
|
button {
|
||||||
padding: 0.6rem 0.8rem;
|
padding: 0.6rem 0.8rem;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
index.html
43
index.html
@ -1907,7 +1907,7 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
const assignedInterns = RIBC.getAssignedInterns(appState.params.id, appState.params.branch, task)
|
const assignedInterns = RIBC.getAssignedInterns(appState.params.id, appState.params.branch, task)
|
||||||
const assignedInternsCards = assignedInterns.map(internFloId => render.assignedInternCard(internFloId));
|
const assignedInternsCards = assignedInterns.map(internFloId => render.assignedInternCard(internFloId, `${appState.params.id}_${appState.params.branch}_${task}`));
|
||||||
const status = RIBC.getTaskStatus(appState.params.id, appState.params.branch, task)
|
const status = RIBC.getTaskStatus(appState.params.id, appState.params.branch, task)
|
||||||
const linkifyDescription = createElement('p', {
|
const linkifyDescription = createElement('p', {
|
||||||
innerHTML: DOMPurify.sanitize(linkify(description)),
|
innerHTML: DOMPurify.sanitize(linkify(description)),
|
||||||
@ -2043,15 +2043,29 @@
|
|||||||
`;
|
`;
|
||||||
},
|
},
|
||||||
assignedInternCard(internFloId, taskId) {
|
assignedInternCard(internFloId, taskId) {
|
||||||
|
const { hasDeadlinePassed, elapsedPercentage, taskDeadline } = getTaskDeadline(taskId, internFloId)
|
||||||
|
const taskCompleted = RIBC.getInternRecord(internFloId).completedTasks.hasOwnProperty(taskId)
|
||||||
|
let taskStatus = 'In progress'
|
||||||
|
let statusModifier = '--in-progress'
|
||||||
|
if (taskCompleted) {
|
||||||
|
taskStatus = 'Completed'
|
||||||
|
statusModifier = '--completed'
|
||||||
|
} else if (hasDeadlinePassed) {
|
||||||
|
taskStatus = 'Overdue'
|
||||||
|
statusModifier = '--overdue'
|
||||||
|
}
|
||||||
|
let isInProgress = !taskCompleted && !hasDeadlinePassed
|
||||||
let optionsButton
|
let optionsButton
|
||||||
if (taskId && !RIBC.getInternRecord(internFloId).completedTasks.hasOwnProperty(taskId)) {
|
if (taskId && !taskCompleted) {
|
||||||
optionsButton = html` <button class="unassign-intern-button">
|
optionsButton = html` <button class="unassign-intern-button">
|
||||||
<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"/><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" 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>
|
||||||
</button> `;
|
</button> `;
|
||||||
}
|
}
|
||||||
|
const title = isInProgress ? `Deadline: ${getFormattedTime(taskDeadline, 'date-only')}` : ''
|
||||||
return html`
|
return html`
|
||||||
<span class="assigned-intern" data-flo-id="${internFloId}">
|
<span class=${`assigned-intern assigned-intern${statusModifier}`} title=${title} data-flo-id="${internFloId}" style=${`--progress: ${elapsedPercentage}`}>
|
||||||
${RIBC.getInternList()[internFloId]}
|
${RIBC.getInternList()[internFloId]}
|
||||||
|
<span class=${`task-status task-status${statusModifier}`}>${taskStatus}</span>
|
||||||
${optionsButton}
|
${optionsButton}
|
||||||
</span>
|
</span>
|
||||||
`
|
`
|
||||||
@ -2283,11 +2297,7 @@
|
|||||||
innerHTML: DOMPurify.sanitize(linkify(description)),
|
innerHTML: DOMPurify.sanitize(linkify(description)),
|
||||||
className: `timeline-task__description ws-pre-line wrap-around`
|
className: `timeline-task__description ws-pre-line wrap-around`
|
||||||
})
|
})
|
||||||
// add days to milliseconds
|
const { hasDeadlinePassed, taskDeadline, elapsedPercentage } = getTaskDeadline(taskId)
|
||||||
const durationMilliseconds = durationType === 'days' ? duration * 24 * 60 * 60 * 1000 : duration * 60 * 60 * 1000
|
|
||||||
const taskDeadline = assignedTasks[taskId].assignedOn + durationMilliseconds
|
|
||||||
const elapsedTime = Math.round((Date.now() - assignedTasks[taskId].assignedOn) / durationMilliseconds * 100)
|
|
||||||
const hasDeadlinePassed = Date.now() > taskDeadline
|
|
||||||
return html`
|
return html`
|
||||||
<li class="task-card" data-unique-id="${taskId}">
|
<li class="task-card" data-unique-id="${taskId}">
|
||||||
<span class="task__project-title">${projectName}</span>
|
<span class="task__project-title">${projectName}</span>
|
||||||
@ -2304,7 +2314,7 @@
|
|||||||
<time style="font-size: 0.9rem;font-weight: 500; white-space: nowrap">${getFormattedTime(assignedTasks[taskId].assignedOn, 'date-only')}</time>
|
<time style="font-size: 0.9rem;font-weight: 500; white-space: nowrap">${getFormattedTime(assignedTasks[taskId].assignedOn, 'date-only')}</time>
|
||||||
</div>
|
</div>
|
||||||
<div class="task__completion-timeline__progress" role="progressbar">
|
<div class="task__completion-timeline__progress" role="progressbar">
|
||||||
<div class="task__completion-timeline__progress__bar" style=${`--progress: ${elapsedTime}%`}></div>
|
<div class="task__completion-timeline__progress__bar" style=${`--progress: ${elapsedPercentage}%`}></div>
|
||||||
<div class="task__completion-timeline__progress__disc"></div>
|
<div class="task__completion-timeline__progress__disc"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-direction-column gap-0-3">
|
<div class="flex flex-direction-column gap-0-3">
|
||||||
@ -2660,6 +2670,21 @@
|
|||||||
addEventListener("beforeunload", beforeUnloadListener, { capture: true });
|
addEventListener("beforeunload", beforeUnloadListener, { capture: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function getTaskDeadline(taskId, internId = floGlobals.myFloID) {
|
||||||
|
const [projectCode, branch, task] = taskId.split('_');
|
||||||
|
const { title, description, duration, durationType } = RIBC.getTaskDetails(projectCode, branch, task)
|
||||||
|
const { assignedTasks } = RIBC.getInternRecord(internId)
|
||||||
|
const assignedOn = assignedTasks[taskId].assignedOn || assignedTasks[taskId]
|
||||||
|
const durationMilliseconds = durationType === 'days' ? duration * 24 * 60 * 60 * 1000 : duration * 60 * 60 * 1000
|
||||||
|
const taskDeadline = assignedOn + durationMilliseconds
|
||||||
|
const elapsedPercentage = Math.round((Date.now() - assignedOn) / durationMilliseconds * 100)
|
||||||
|
const hasDeadlinePassed = Date.now() > taskDeadline
|
||||||
|
return {
|
||||||
|
taskDeadline,
|
||||||
|
elapsedPercentage,
|
||||||
|
hasDeadlinePassed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const filterTasks = debounce((e) => {
|
const filterTasks = debounce((e) => {
|
||||||
const searchQuery = getRef('task_search_input')?.value.trim() || '';
|
const searchQuery = getRef('task_search_input')?.value.trim() || '';
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user