Skip to content

Commit

Permalink
Feature - Color grading for availabilities (#179)
Browse files Browse the repository at this point in the history
* basic frontend stuff complete :3. Finished the general ui

* first attempt, implementing inductee view of their schedule backend only

* Push some frontend changes

* update

* Finish all availabilities display page

* Added backend for getting all availabilities, not single yet

* Update styling

* some post hopefully

* Update all availabilities page

* Add utils file for interview-related pages

* Update sidebar navigation

* Start edit own availability from scratch

* Update response type to const

* Update app url pathing

* Map correct url to schedule overview page

* Move utils file

* inducteeviewset post delete

* Update app routing

* Update user views

* Implement interview get api backends

* Update overall interview schedule

* Skeleton for individual interview schedule

* function for editschedule

* addavailability function

* Complete edit schedule page

* Edit InductionClassViewSet

* Update interview related functions

* Complete initial interview schedule overview

* Finish up interview system

* Delete .venv

* Add comments on functions and files

* Fix clicking on timeslot does not update availability

* Add functionality to see individual inductee availabilities

* Remove TODO comment

* Add gradient colors to schedule

* Bug fix

Fixed bug where selecting then unselecting an empty slot (with officers available) on individual inductee view would cause color to appear.

* Resolve bug in incorrect color after deselecting timeslot

* Bug fix where inductee filter remains when going back to 'all' from individual inductee

---------

Co-authored-by: jyeh2 <yehjames526@gmail.com>
Co-authored-by: niannianwang <niw002@ucsd.edu>
  • Loading branch information
3 people authored Dec 28, 2024
1 parent 461aca2 commit 4c406ed
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 21 deletions.
119 changes: 100 additions & 19 deletions frontend/src/Pages/InterviewSchedule.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
<script>
import Layout from "../Layout.svelte";
import { onMount } from "svelte";
import { generateSchedule, UNAVAILABLE_COLOR, AVAILABLE_COLOR, SELECTED_COLOR, NUM_DAYS, NUM_SLOTS } from "./interviewscheduleutils.js"
import { generateSchedule, UNAVAILABLE_COLOR, MAX_GRADIENT_COLOR, SELECTED_COLOR, NUM_DAYS, NUM_SLOTS } from "./interviewscheduleutils.js"
let availabilities;
let inductee_availabilities = {};
let inductees;
let selected_slot = null;
let loaded = false;
let inductee_slot_colors = [UNAVAILABLE_COLOR];
onMount(async () => {
// Retrieve availabilities of all inductees and officers from backend
Expand All @@ -20,6 +21,7 @@
// Populate the schedule according to availabilities retrieved
if (availabilities != null) {
setColorsInductees();
populateSchedule();
}
Expand Down Expand Up @@ -47,8 +49,11 @@
if (event.target.id == selected_slot) {
selected_slot = null;
try {
if (availabilities[day][slot]['inductees'].length != 0) {
timeslot.style.background = AVAILABLE_COLOR;
if (inductee_option[0] == 'all') {
timeslot.style.background = inductee_slot_colors[availabilities[day][slot]['inductees'].length]
} else if (inductee_availabilities[inductee_option[0]][day][slot] == 1) {
let officer_slot_colors = setColorsOfficers(inductee_availabilities[inductee_option[0]]);
timeslot.style.background = officer_slot_colors[availabilities[day][slot]['officers'].length];
} else {
timeslot.style.background = UNAVAILABLE_COLOR;
}
Expand All @@ -61,8 +66,11 @@
selected_slot = event.target.id;
// Reset previously selected slot
try {
if (availabilities[day][slot]['inductees'].length != 0) {
timeslot.style.background = AVAILABLE_COLOR;
if (inductee_option[0] == 'all') {
timeslot.style.background = inductee_slot_colors[availabilities[day][slot]['inductees'].length]
} else if (inductee_availabilities[inductee_option[0]][day][slot] == 1) {
let officer_slot_colors = setColorsOfficers(inductee_availabilities[inductee_option[0]]);
timeslot.style.background = officer_slot_colors[availabilities[day][slot]['officers'].length];
} else {
timeslot.style.background = UNAVAILABLE_COLOR;
}
Expand All @@ -74,8 +82,11 @@
} else {
// Clicked elsewhere
try {
if (availabilities[day][slot]['inductees'].length != 0) {
timeslot.style.background = AVAILABLE_COLOR;
if (inductee_option[0] == 'all') {
timeslot.style.background = inductee_slot_colors[availabilities[day][slot]['inductees'].length]
} else if (inductee_availabilities[inductee_option[0]][day][slot] == 1) {
let officer_slot_colors = setColorsOfficers(inductee_availabilities[inductee_option[0]]);
timeslot.style.background = officer_slot_colors[availabilities[day][slot]['officers'].length];
} else {
timeslot.style.background = UNAVAILABLE_COLOR;
}
Expand Down Expand Up @@ -134,10 +145,10 @@
let slot = id.split('-')[1];
clearAvailabilityDisplay();
let inductees;
let inductees_filtered;
let officers;
try {
inductees = availabilities[day][slot]['inductees'].filter(inductee => inductee == inductee_option[1] || inductee_option == "all");
inductees_filtered = availabilities[day][slot]['inductees'].filter(inductee => inductee == inductee_option[1] || inductee_option[0] == "all");
officers = availabilities[day][slot]['officers'];
} catch {
return;
Expand All @@ -147,7 +158,7 @@
let available_officers = document.getElementById('available_officers');
const P_STYLE = "margin: 1px 0px 1px 0px;";
inductees.forEach(inductee => {
inductees_filtered.forEach(inductee => {
let name = document.createElement('p');
name.innerText = inductee;
name.style = P_STYLE;
Expand All @@ -174,6 +185,71 @@
available_officers.removeChild(available_officers.firstChild);
}
}
/*
* Find max number of inductees in an individual timeslot and prepare color gradients accordingly.
*/
function setColorsInductees() {
let max = 0;
for (let day = 0; day < NUM_DAYS; day++) {
for (let slotNum = 0; slotNum < NUM_SLOTS; slotNum++) {
if (availabilities[day][slotNum]['inductees'].length > max) {
max = availabilities[day][slotNum]['inductees'].length;
}
}
}
let avail_color = MAX_GRADIENT_COLOR.split(",");
let r = avail_color[0].split("(")[1];
let g = avail_color[1];
let b = avail_color[2].split(")")[0];
// Base color = rgba(95, 192, 249) light blue
let step_r = (255 - r) / (max + 2);
let step_g = (255 - g) / (max + 2);
let step_b = (255 - b) / (max + 2);
for (let i = 0; i < max; i++) {
// TODO: populate inductee_slot_colors with a giadient from starting color to ending color.
// Also need to change color of selected timeslot.
let color = `rgba(${255 - step_r * (i + 2)}, ${255 - step_g * (i + 2)}, ${255 - step_b * (i + 2)})`;
inductee_slot_colors.push(color);
}
}
/*
* Find max number of officers in an individual timeslot (only consider ones that are marked available for this inductee)
* and prepare color gradients accordingly.
*/
function setColorsOfficers(inductee_availability) {
let officer_slot_colors = [UNAVAILABLE_COLOR];
let max = 0;
for (let day = 0; day < NUM_DAYS; day++) {
for (let slotNum = 0; slotNum < NUM_SLOTS; slotNum++) {
if ((inductee_availability[day][slotNum] == 1) && (availabilities[day][slotNum]['officers'].length > max)) {
max = availabilities[day][slotNum]['officers'].length;
}
}
}
let avail_color = MAX_GRADIENT_COLOR.split(",");
let r = avail_color[0].split("(")[1];
let g = avail_color[1];
let b = avail_color[2].split(")")[0];
// Base color = rgba(95, 192, 249) light blue
let step_r = (255 - r) / (max + 2);
let step_g = (255 - g) / (max + 2);
let step_b = (255 - b) / (max + 2);
for (let i = 0; i < max; i++) {
// TODO: populate inductee_slot_colors with a giadient from starting color to ending color.
// Also need to change color of selected timeslot.
let color = `rgba(${255 - step_r * (i + 2)}, ${255 - step_g * (i + 2)}, ${255 - step_b * (i + 2)})`;
officer_slot_colors.push(color);
}
return officer_slot_colors;
}
/*
* Populate the schedule with availabilities
Expand All @@ -185,10 +261,13 @@
for (let slotNum = 0; slotNum < NUM_SLOTS; slotNum++) {
let timeslot = document.getElementById(`${day}-${slotNum}`);
// Make timeslot colored if an inductee has availability at that time
if (availabilities[day][slotNum]['inductees'].length != 0) {
timeslot.style.background = AVAILABLE_COLOR;
timeslot.setAttribute('available', true);
// Make timeslot colored accordingly
let numInductees = availabilities[day][slotNum]['inductees'].length;
timeslot.style.background = inductee_slot_colors[numInductees];
if (numInductees == 0) {
timeslot.setAttribute['available', false];
} else {
timeslot.setAttribute['available', true];
}
// Add mouseover event listener to display inductees and officers at timeslot
Expand Down Expand Up @@ -238,13 +317,15 @@
* Set on click event to only display the selected inductee
*/
function populateInducteeSchedule(inductee_availability) {
let officer_slot_colors = setColorsOfficers(inductee_availability);
for (let day = 0; day < NUM_DAYS; day++) {
for (let slotNum = 0; slotNum < NUM_SLOTS; slotNum++) {
let timeslot = document.getElementById(`${day}-${slotNum}`);
// Make timeslot colored if an inductee has availability at that time
let numOfficers = availabilities[day][slotNum]['officers'].length;
if (inductee_availability[day][slotNum] == 1) {
timeslot.style.background = AVAILABLE_COLOR;
timeslot.style.background = officer_slot_colors[numOfficers];
timeslot.setAttribute('available', true);
}
Expand All @@ -257,9 +338,9 @@
clearAvailabilityDisplay();
// Populate availability display with inductees available at that time
let inductees = availabilities[day][slotNum]['inductees'].filter(inductee => inductee == inductee_option[1]);
let inductees_filtered = availabilities[day][slotNum]['inductees'].filter(inductee => inductee == inductee_option[1]);
let available_inductees = document.getElementById('available_inductees');
inductees.forEach(inductee => {
inductees_filtered.forEach(inductee => {
let name = document.createElement('p');
name.innerText = inductee;
name.style = P_STYLE;
Expand Down Expand Up @@ -339,7 +420,7 @@
<div style="margin-left: 50px">
<form>
<select bind:value={inductee_option} name="inductees">
<option value="all">Filter by Inductee</option>
<option value={{0: "all"}}>Filter by Inductee</option>
{#each inductees as inductee}
<option value={inductee}>{inductee[1]}</option>
{/each}
Expand All @@ -354,7 +435,7 @@
<div id="slot_availability">
<h3 style="margin: 2px 0px 2px 0px;">Available</h3>
<h4 style="margin: 2px 0px 2px 0px;">Inductees:</h4>
<div id="available_inductees"></div>
<div id="available_inductees" style="margin-bottom: 10px;"></div>
<h4 style="margin: 2px 0px 2px 0px;">Officers:</h4>
<div id="available_officers"></div>
</div>
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/Pages/interviewscheduleutils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ const START_TIME = new Date(1970, 0, 1, 8, 0);
/* --- STYLES --- */
export const UNAVAILABLE_COLOR = "rgba(234, 166, 62, 0.4)";
export const AVAILABLE_COLOR = "rgba(92, 185, 240)";
export const SELECTED_COLOR = "rgba(35, 24, 244)";
export const MAX_GRADIENT_COLOR = "rgba(0, 0, 255)";
export const SELECTED_COLOR = "rgba(218, 106, 107)";
const LINE_COLOR = MAX_GRADIENT_COLOR;

// For each column in schedule
const DAY_COL = "display: flex; flex-direction: column;";
Expand Down Expand Up @@ -94,7 +96,7 @@ export function generateSchedule() {
let mod4 = slotNum % 4;
switch (mod4) {
case 0:
timeslot.style.borderTop = `1px solid ${SELECTED_COLOR}`;
timeslot.style.borderTop = `1px solid ${LINE_COLOR}`;
timeslot.style.borderBottom = `1px dotted black`;
break;
case 1:
Expand Down

0 comments on commit 4c406ed

Please sign in to comment.