Indicate session and timeslot conflicts more prominently in schedule editor. Fixes #3221. Commit ready for merge.

- Legacy-Id: 19133
This commit is contained in:
Jennifer Richards 2021-06-15 13:46:44 +00:00
parent 197194a41c
commit 1d12391c5c
3 changed files with 102 additions and 16 deletions

View file

@ -171,10 +171,31 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
s1_element = self.driver.find_element_by_css_selector('#session{}'.format(s1.pk))
s1_element.click()
# violated due to constraints
# violated due to constraints - both the timeslot and its timeslot label
self.assertTrue(self.driver.find_elements_by_css_selector('#timeslot{}.would-violate-hint'.format(slot1.pk)))
# Find the timeslot label for slot1 - it's the first timeslot in the first room group
slot1_roomgroup_elt = self.driver.find_element_by_css_selector(
'.day-flow .day:first-child .room-group:nth-child(2)' # count from 2 - first-child is the day label
)
self.assertTrue(
slot1_roomgroup_elt.find_elements_by_css_selector(
'.time-header > .time-label.would-violate-hint:first-child'
),
'Timeslot header label should show a would-violate hint for a constraint violation'
)
# violated due to missing capacity
self.assertTrue(self.driver.find_elements_by_css_selector('#timeslot{}.would-violate-hint'.format(slot3.pk)))
# Find the timeslot label for slot3 - it's the second timeslot in the second room group
slot3_roomgroup_elt = self.driver.find_element_by_css_selector(
'.day-flow .day:first-child .room-group:nth-child(3)' # count from 2 - first-child is the day label
)
self.assertFalse(
slot3_roomgroup_elt.find_elements_by_css_selector(
'.time-header > .time-label.would-violate-hint:nth-child(2)'
),
'Timeslot header label should not show a would-violate hint for room capacity violation'
)
# reschedule
self.driver.execute_script("jQuery('#session{}').simulateDragDrop({{dropTarget: '#timeslot{} .drop-target'}});".format(s2.pk, slot2.pk))
@ -192,6 +213,7 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
# constraint hints
s1_element.click()
self.assertIn('would-violate-hint', s2_element.get_attribute('class'))
constraint_element = s2_element.find_element_by_css_selector(".constraints span[data-sessions=\"{}\"].would-violate-hint".format(s1.pk))
self.assertTrue(constraint_element.is_displayed())

View file

@ -1080,6 +1080,11 @@ a.fc-event, .fc-event, .fc-content, .fc-title, .fc-event-container {
align-items: center;
}
.edit-meeting-schedule .edit-grid .time-header .time-label.would-violate-hint {
background-color: #ffe0e0;
outline: #ffe0e0 solid 0.4em;
}
.edit-meeting-schedule .edit-grid .time-header .time-label span {
display: inline-block;
width: 100%;
@ -1124,11 +1129,12 @@ a.fc-event, .fc-event, .fc-content, .fc-title, .fc-event-container {
}
.edit-meeting-schedule .edit-grid .timeslot.overfull {
border-right: 2px dashed #fff; /* cut-off illusion */
border-right: 0.3em dashed #f55000; /* cut-off illusion */
}
.edit-meeting-schedule .edit-grid .timeslot.would-violate-hint {
background-color: #ffe0e0;
outline: #ffe0e0 solid 0.4em;
}
.edit-meeting-schedule .constraints .encircled,
@ -1177,6 +1183,11 @@ a.fc-event, .fc-event, .fc-content, .fc-title, .fc-event-container {
background-color: #f3f3f3;
}
.edit-meeting-schedule .session.would-violate-hint {
outline: 0.3em solid #F55000;
z-index: 1; /* raise up so the outline is not overdrawn */
}
.edit-meeting-schedule .session.highlight .session-label {
font-weight: bold;
}
@ -1259,6 +1270,7 @@ a.fc-event, .fc-event, .fc-content, .fc-title, .fc-event-container {
margin-bottom: 2em;
background-color: #fff;
opacity: 0.95;
z-index: 5; /* raise above edit-grid items */
}
.edit-meeting-schedule .scheduling-panel .unassigned-container {

View file

@ -10,6 +10,7 @@ jQuery(document).ready(function () {
let sessions = content.find(".session").not(".readonly");
let timeslots = content.find(".timeslot");
let timeslotLabels = content.find(".time-label");
let days = content.find(".day-flow .day");
// hack to work around lack of position sticky support in old browsers, see https://caniuse.com/#feat=css-sticky
@ -68,25 +69,74 @@ jQuery(document).ready(function () {
}
}
/**
* Mark or unmark a session that conflicts with the selected session
*
* @param constraintElt The element corresponding to the specific constraint
* @param wouldViolate True to mark or false to unmark
*/
function setSessionWouldViolate(constraintElt, wouldViolate) {
constraintElt = jQuery(constraintElt);
let constraintDiv = constraintElt.closest('div.session'); // find enclosing session div
constraintDiv.toggleClass('would-violate-hint', wouldViolate); // mark the session container
constraintElt.toggleClass('would-violate-hint', wouldViolate); // and the specific constraint
}
/**
* Mark or unmark a timeslot that conflicts with the selected session
*
* If wholeInterval is true, marks the entire column in addition to the timeslot.
* This currently works by setting the class for the timeslot and the time-label
* in its column. Because this is called for every timeslot in the interval, the
* overall effect is to highlight the entire column.
*
* @param timeslotElt Timeslot element to be marked/unmarked
* @param wouldViolate True to mark or false to unmark
* @param wholeInterval Should the entire time interval be flagged or just the timeslot?
*/
function setTimeslotWouldViolate(timeslotElt, wouldViolate, wholeInterval) {
timeslotElt = jQuery(timeslotElt);
timeslotElt.toggleClass('would-violate-hint', wouldViolate);
if (wholeInterval) {
let index = timeslotElt.index(); // position of this timeslot relative to its container
let label = timeslotElt
.closest('div.room-group')
.find('div.time-header .time-label')
.get(index); // get time-label corresponding to this timeslot
jQuery(label).toggleClass('would-violate-hint', wouldViolate);
}
}
/**
* Remove all would-violate-hint classes on timeslots
*/
function resetTimeslotsWouldViolate() {
timeslots.removeClass("would-violate-hint");
timeslotLabels.removeClass("would-violate-hint");
}
function showConstraintHints(selectedSession) {
let sessionId = selectedSession ? selectedSession.id.slice("session".length) : null;
// hints on the sessions
sessions.find(".constraints > span").each(function () {
if (!sessionId) {
jQuery(this).removeClass("would-violate-hint");
return;
let wouldViolate = false;
let applyChange = true;
if (sessionId) {
let sessionIds = this.dataset.sessions;
if (!sessionIds) {
applyChange = False;
} else {
wouldViolate = sessionIds.split(",").indexOf(sessionId) !== -1;
}
}
let sessionIds = this.dataset.sessions;
if (!sessionIds)
return;
let wouldViolate = sessionIds.split(",").indexOf(sessionId) != -1;
jQuery(this).toggleClass("would-violate-hint", wouldViolate);
if (applyChange) {
setSessionWouldViolate(this, wouldViolate);
}
});
// hints on timeslots
timeslots.removeClass("would-violate-hint");
resetTimeslotsWouldViolate();
if (selectedSession) {
let intervals = [];
timeslots.filter(":has(.session .constraints > span.would-violate-hint)").each(function () {
@ -94,15 +144,17 @@ jQuery(document).ready(function () {
});
let overlappingTimeslots = findTimeslotsOverlapping(intervals);
for (let i = 0; i < overlappingTimeslots.length; ++i)
overlappingTimeslots[i].addClass("would-violate-hint");
for (let i = 0; i < overlappingTimeslots.length; ++i) {
setTimeslotWouldViolate(overlappingTimeslots[i], true, true);
}
// check room sizes
let attendees = +selectedSession.dataset.attendees;
if (attendees) {
timeslots.not(".would-violate-hint").each(function () {
if (attendees > +jQuery(this).closest(".timeslots").data("roomcapacity"))
jQuery(this).addClass("would-violate-hint");
if (attendees > +jQuery(this).closest(".timeslots").data("roomcapacity")) {
setTimeslotWouldViolate(this, true, false);
}
});
}
}