Hide timeslots type is disabled plus other schedule editor debugging/improvements. Fixes #3510. Fixes #3430. Commit ready for merge.
- Legacy-Id: 19878
This commit is contained in:
parent
c4bf508cd8
commit
795d861d96
|
@ -662,11 +662,15 @@ class SessionDetailsForm(forms.ModelForm):
|
||||||
|
|
||||||
def __init__(self, group, *args, **kwargs):
|
def __init__(self, group, *args, **kwargs):
|
||||||
session_purposes = group.features.session_purposes
|
session_purposes = group.features.session_purposes
|
||||||
kwargs.setdefault('initial', {})
|
# Default to the first allowed session_purposes. Do not do this if we have an instance,
|
||||||
kwargs['initial'].setdefault(
|
# though, because ModelForm will override instance data with initial data if it gets both.
|
||||||
'purpose',
|
# When we have an instance we want to keep its value.
|
||||||
session_purposes[0] if len(session_purposes) > 0 else None,
|
if 'instance' not in kwargs:
|
||||||
)
|
kwargs.setdefault('initial', {})
|
||||||
|
kwargs['initial'].setdefault(
|
||||||
|
'purpose',
|
||||||
|
session_purposes[0] if len(session_purposes) > 0 else None,
|
||||||
|
)
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.fields['type'].widget.attrs.update({
|
self.fields['type'].widget.attrs.update({
|
||||||
|
@ -678,11 +682,11 @@ class SessionDetailsForm(forms.ModelForm):
|
||||||
self.fields['purpose'].queryset = SessionPurposeName.objects.filter(pk__in=session_purposes)
|
self.fields['purpose'].queryset = SessionPurposeName.objects.filter(pk__in=session_purposes)
|
||||||
if not group.features.acts_like_wg:
|
if not group.features.acts_like_wg:
|
||||||
self.fields['requested_duration'].durations = [datetime.timedelta(minutes=m) for m in range(30, 241, 30)]
|
self.fields['requested_duration'].durations = [datetime.timedelta(minutes=m) for m in range(30, 241, 30)]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Session
|
model = Session
|
||||||
fields = (
|
fields = (
|
||||||
'name', 'short', 'purpose', 'type', 'requested_duration',
|
'purpose', 'name', 'short', 'type', 'requested_duration',
|
||||||
'on_agenda', 'remote_instructions', 'attendees', 'comments',
|
'on_agenda', 'remote_instructions', 'attendees', 'comments',
|
||||||
)
|
)
|
||||||
labels = {'requested_duration': 'Length'}
|
labels = {'requested_duration': 'Length'}
|
||||||
|
|
|
@ -541,21 +541,21 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
|
||||||
|
|
||||||
past_swap_ts_buttons = self.driver.find_elements(By.CSS_SELECTOR,
|
past_swap_ts_buttons = self.driver.find_elements(By.CSS_SELECTOR,
|
||||||
','.join(
|
','.join(
|
||||||
'.swap-timeslot-col[data-start="{}"]'.format(ts.utc_start_time().isoformat()) for ts in past_timeslots
|
'*[data-start="{}"] .swap-timeslot-col'.format(ts.utc_start_time().isoformat()) for ts in past_timeslots
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.assertEqual(len(past_swap_ts_buttons), len(past_timeslots), 'Missing past swap timeslot col buttons')
|
self.assertEqual(len(past_swap_ts_buttons), len(past_timeslots), 'Missing past swap timeslot col buttons')
|
||||||
|
|
||||||
future_swap_ts_buttons = self.driver.find_elements(By.CSS_SELECTOR,
|
future_swap_ts_buttons = self.driver.find_elements(By.CSS_SELECTOR,
|
||||||
','.join(
|
','.join(
|
||||||
'.swap-timeslot-col[data-start="{}"]'.format(ts.utc_start_time().isoformat()) for ts in future_timeslots
|
'*[data-start="{}"] .swap-timeslot-col'.format(ts.utc_start_time().isoformat()) for ts in future_timeslots
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.assertEqual(len(future_swap_ts_buttons), len(future_timeslots), 'Missing future swap timeslot col buttons')
|
self.assertEqual(len(future_swap_ts_buttons), len(future_timeslots), 'Missing future swap timeslot col buttons')
|
||||||
|
|
||||||
now_swap_ts_buttons = self.driver.find_elements(By.CSS_SELECTOR,
|
now_swap_ts_buttons = self.driver.find_elements(By.CSS_SELECTOR,
|
||||||
','.join(
|
','.join(
|
||||||
'.swap-timeslot-col[data-start="{}"]'.format(ts.utc_start_time().isoformat()) for ts in now_timeslots
|
'[data-start="{}"] .swap-timeslot-col'.format(ts.utc_start_time().isoformat()) for ts in now_timeslots
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.assertEqual(len(now_swap_ts_buttons), len(now_timeslots), 'Missing "now" swap timeslot col buttons')
|
self.assertEqual(len(now_swap_ts_buttons), len(now_timeslots), 'Missing "now" swap timeslot col buttons')
|
||||||
|
|
|
@ -536,15 +536,17 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
|
||||||
for s in sessions:
|
for s in sessions:
|
||||||
s.requested_by_person = requested_by_lookup.get(s.requested_by)
|
s.requested_by_person = requested_by_lookup.get(s.requested_by)
|
||||||
|
|
||||||
s.scheduling_label = "???"
|
|
||||||
s.purpose_label = None
|
s.purpose_label = None
|
||||||
if (s.purpose.slug in ('none', 'regular')) and s.group:
|
if s.group:
|
||||||
s.scheduling_label = s.group.acronym
|
if (s.purpose.slug in ('none', 'regular')):
|
||||||
s.purpose_label = 'BoF' if s.group.is_bof() else s.group.type.name
|
s.scheduling_label = s.group.acronym
|
||||||
|
s.purpose_label = 'BoF' if s.group.is_bof() else s.group.type.name
|
||||||
|
else:
|
||||||
|
s.scheduling_label = s.name if s.name else f'??? [{s.group.acronym}]'
|
||||||
|
s.purpose_label = s.purpose.name
|
||||||
else:
|
else:
|
||||||
s.purpose_label = s.purpose.name
|
s.scheduling_label = s.name if s.name else '???'
|
||||||
if s.name:
|
s.purpose_label = s.purpose.name
|
||||||
s.scheduling_label = s.name
|
|
||||||
|
|
||||||
s.requested_duration_in_hours = round(s.requested_duration.seconds / 60.0 / 60.0, 1)
|
s.requested_duration_in_hours = round(s.requested_duration.seconds / 60.0 / 60.0, 1)
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,7 @@ class MiscSessionForm(TimeSlotForm):
|
||||||
Plenary = IETF''',
|
Plenary = IETF''',
|
||||||
required=False)
|
required=False)
|
||||||
location = forms.ModelChoiceField(queryset=Room.objects, required=False)
|
location = forms.ModelChoiceField(queryset=Room.objects, required=False)
|
||||||
remote_instructions = forms.CharField(max_length=255)
|
remote_instructions = forms.CharField(max_length=255, required=False)
|
||||||
show_location = forms.BooleanField(required=False)
|
show_location = forms.BooleanField(required=False)
|
||||||
|
|
||||||
def __init__(self,*args,**kwargs):
|
def __init__(self,*args,**kwargs):
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
/* globals alert, jQuery, moment */
|
/* globals alert, jQuery, moment */
|
||||||
jQuery(document).ready(function () {
|
jQuery(document).ready(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
let content = jQuery(".edit-meeting-schedule");
|
let content = jQuery(".edit-meeting-schedule");
|
||||||
/* Drag data stored via the drag event dataTransfer interface is only accessible on
|
/* Drag data stored via the drag event dataTransfer interface is only accessible on
|
||||||
* dragstart and dragend events. Other drag events can see only the MIME types that have
|
* dragstart and dragend events. Other drag events can see only the MIME types that have
|
||||||
|
@ -12,8 +14,9 @@ jQuery(document).ready(function () {
|
||||||
|
|
||||||
function reportServerError(xhr, textStatus, error) {
|
function reportServerError(xhr, textStatus, error) {
|
||||||
let errorText = error || textStatus;
|
let errorText = error || textStatus;
|
||||||
if (xhr && xhr.responseText)
|
if (xhr && xhr.responseText) {
|
||||||
errorText += "\n\n" + xhr.responseText;
|
errorText += '\n\n' + xhr.responseText;
|
||||||
|
}
|
||||||
alert("Error: " + errorText);
|
alert("Error: " + errorText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,9 +36,14 @@ jQuery(document).ready(function () {
|
||||||
let swapTimeslotButtons = content.find('.swap-timeslot-col');
|
let swapTimeslotButtons = content.find('.swap-timeslot-col');
|
||||||
let days = content.find(".day-flow .day");
|
let days = content.find(".day-flow .day");
|
||||||
let officialSchedule = content.hasClass('official-schedule');
|
let officialSchedule = content.hasClass('official-schedule');
|
||||||
|
let timeSlotTypeInputs = content.find('.timeslot-type-toggles input');
|
||||||
|
let sessionPurposeInputs = content.find('.session-purpose-toggles input');
|
||||||
|
let timeSlotGroupInputs = content.find("#timeslot-group-toggles-modal .modal-body .individual-timeslots input");
|
||||||
|
let sessionParentInputs = content.find(".session-parent-toggles input");
|
||||||
|
const classes_to_hide = '.hidden-timeslot-group,.hidden-timeslot-type';
|
||||||
|
|
||||||
// hack to work around lack of position sticky support in old browsers, see https://caniuse.com/#feat=css-sticky
|
// hack to work around lack of position sticky support in old browsers, see https://caniuse.com/#feat=css-sticky
|
||||||
if (content.find(".scheduling-panel").css("position") != "sticky") {
|
if (content.find(".scheduling-panel").css("position") !== "sticky") {
|
||||||
content.find(".scheduling-panel").css("position", "fixed");
|
content.find(".scheduling-panel").css("position", "fixed");
|
||||||
content.css("padding-bottom", "14em");
|
content.css("padding-bottom", "14em");
|
||||||
}
|
}
|
||||||
|
@ -59,7 +67,7 @@ jQuery(document).ready(function () {
|
||||||
let res = [];
|
let res = [];
|
||||||
|
|
||||||
timeslots.each(function () {
|
timeslots.each(function () {
|
||||||
var timeslot = jQuery(this);
|
const timeslot = jQuery(this);
|
||||||
let start = startMoment(timeslot);
|
let start = startMoment(timeslot);
|
||||||
let end = endMoment(timeslot);
|
let end = endMoment(timeslot);
|
||||||
|
|
||||||
|
@ -97,10 +105,11 @@ jQuery(document).ready(function () {
|
||||||
let timeElement = jQuery(this).find(".time");
|
let timeElement = jQuery(this).find(".time");
|
||||||
|
|
||||||
otherSessionElement.addClass("other-session-selected");
|
otherSessionElement.addClass("other-session-selected");
|
||||||
if (scheduledAt)
|
if (scheduledAt) {
|
||||||
timeElement.text(timeElement.data("scheduled").replace("{time}", scheduledAt));
|
timeElement.text(timeElement.data('scheduled').replace('{time}', scheduledAt));
|
||||||
else
|
} else {
|
||||||
timeElement.text(timeElement.data("notscheduled"));
|
timeElement.text(timeElement.data('notscheduled'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -256,10 +265,10 @@ jQuery(document).ready(function () {
|
||||||
// hide swap day/timeslot column buttons
|
// hide swap day/timeslot column buttons
|
||||||
if (officialSchedule) {
|
if (officialSchedule) {
|
||||||
swapDaysButtons.filter(
|
swapDaysButtons.filter(
|
||||||
(_, elt) => parseISOTimestamp(elt.dataset.start).isSameOrBefore(now, 'day')
|
(_, elt) => parseISOTimestamp(elt.closest('*[data-start]').dataset.start).isSameOrBefore(now, 'day')
|
||||||
).hide();
|
).hide();
|
||||||
swapTimeslotButtons.filter(
|
swapTimeslotButtons.filter(
|
||||||
(_, elt) => parseISOTimestamp(elt.dataset.start).isSameOrBefore(now, 'minute')
|
(_, elt) => parseISOTimestamp(elt.closest('*[data-start]').dataset.start).isSameOrBefore(now, 'minute')
|
||||||
).hide();
|
).hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,9 +287,12 @@ jQuery(document).ready(function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
content.on("click", function (event) {
|
content.on("click", function (event) {
|
||||||
if (jQuery(event.target).is(".session-info-container") || jQuery(event.target).closest(".session-info-container").length > 0)
|
if (!(
|
||||||
return;
|
jQuery(event.target).is('.session-info-container') ||
|
||||||
selectSessionElement(null);
|
jQuery(event.target).closest('.session-info-container').length > 0
|
||||||
|
)) {
|
||||||
|
selectSessionElement(null);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
sessions.on("click", function (event) {
|
sessions.on("click", function (event) {
|
||||||
|
@ -418,12 +430,14 @@ jQuery(document).ready(function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
dropElement.append(sessionElement); // move element
|
dropElement.append(sessionElement); // move element
|
||||||
if (response.tombstone)
|
if (response.tombstone) {
|
||||||
dragParent.append(response.tombstone);
|
dragParent.append(response.tombstone);
|
||||||
|
}
|
||||||
|
|
||||||
updateCurrentSchedulingHints();
|
updateCurrentSchedulingHints();
|
||||||
if (dropParent.hasClass("unassigned-sessions"))
|
if (dropParent.hasClass("unassigned-sessions")) {
|
||||||
sortUnassigned();
|
sortUnassigned();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dropParent.hasClass("unassigned-sessions")) {
|
if (dropParent.hasClass("unassigned-sessions")) {
|
||||||
|
@ -436,8 +450,7 @@ jQuery(document).ready(function () {
|
||||||
session: sessionElement.id.slice("session".length)
|
session: sessionElement.id.slice("session".length)
|
||||||
}
|
}
|
||||||
}).fail(failHandler).done(done);
|
}).fail(failHandler).done(done);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
jQuery.ajax({
|
jQuery.ajax({
|
||||||
url: window.location.href,
|
url: window.location.href,
|
||||||
method: "post",
|
method: "post",
|
||||||
|
@ -476,7 +489,7 @@ jQuery(document).ready(function () {
|
||||||
// disable any that have passed
|
// disable any that have passed
|
||||||
const now=effectiveNow();
|
const now=effectiveNow();
|
||||||
const past_radios = radios.filter(
|
const past_radios = radios.filter(
|
||||||
(_, radio) => parseISOTimestamp(radio.dataset.start).isSameOrBefore(now, datePrecision)
|
(_, radio) => parseISOTimestamp(radio.closest('*[data-start]').dataset.start).isSameOrBefore(now, datePrecision)
|
||||||
);
|
);
|
||||||
past_radios.parent().addClass('text-muted');
|
past_radios.parent().addClass('text-muted');
|
||||||
past_radios.prop('disabled', true);
|
past_radios.prop('disabled', true);
|
||||||
|
@ -489,7 +502,7 @@ jQuery(document).ready(function () {
|
||||||
let swapDaysLabels = swapDaysModal.find(".modal-body label");
|
let swapDaysLabels = swapDaysModal.find(".modal-body label");
|
||||||
let swapDaysRadios = swapDaysLabels.find('input[name=target_day]');
|
let swapDaysRadios = swapDaysLabels.find('input[name=target_day]');
|
||||||
let updateSwapDaysSubmitButton = function () {
|
let updateSwapDaysSubmitButton = function () {
|
||||||
updateSwapSubmitButton(swapDaysModal, 'target_day')
|
updateSwapSubmitButton(swapDaysModal, 'target_day');
|
||||||
};
|
};
|
||||||
// handler to prep and open the modal
|
// handler to prep and open the modal
|
||||||
content.find(".swap-days").on("click", function () {
|
content.find(".swap-days").on("click", function () {
|
||||||
|
@ -505,7 +518,7 @@ jQuery(document).ready(function () {
|
||||||
updateSwapDaysSubmitButton();
|
updateSwapDaysSubmitButton();
|
||||||
swapDaysModal.modal('show'); // show via JS so it won't open until it is initialized
|
swapDaysModal.modal('show'); // show via JS so it won't open until it is initialized
|
||||||
});
|
});
|
||||||
swapDaysRadios.on("change", function () {updateSwapDaysSubmitButton()});
|
swapDaysRadios.on("change", function () {updateSwapDaysSubmitButton();});
|
||||||
|
|
||||||
// swap timeslot columns
|
// swap timeslot columns
|
||||||
let swapTimeslotsModal = content.find('#swap-timeslot-col-modal');
|
let swapTimeslotsModal = content.find('#swap-timeslot-col-modal');
|
||||||
|
@ -534,7 +547,7 @@ jQuery(document).ready(function () {
|
||||||
updateSwapTimeslotsSubmitButton();
|
updateSwapTimeslotsSubmitButton();
|
||||||
swapTimeslotsModal.modal('show');
|
swapTimeslotsModal.modal('show');
|
||||||
});
|
});
|
||||||
swapTimeslotsRadios.on("change", function () {updateSwapTimeslotsSubmitButton()});
|
swapTimeslotsRadios.on("change", function () {updateSwapTimeslotsSubmitButton();});
|
||||||
}
|
}
|
||||||
|
|
||||||
// hints for the current schedule
|
// hints for the current schedule
|
||||||
|
@ -557,22 +570,44 @@ jQuery(document).ready(function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
scheduledSessions.sort(function (a, b) {
|
scheduledSessions.sort(function (a, b) {
|
||||||
if (a.start < b.start)
|
if (a.start < b.start) {
|
||||||
return -1;
|
return -1;
|
||||||
if (a.start > b.start)
|
}
|
||||||
|
if (a.start > b.start) {
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
let currentlyOpen = {};
|
let currentlyOpen = {};
|
||||||
let openedIndex = 0;
|
let openedIndex = 0;
|
||||||
|
let markSessionConstraintViolations = function (sess, currentlyOpen) {
|
||||||
|
sess.element.find(".constraints > span").each(function() {
|
||||||
|
let sessionIds = this.dataset.sessions;
|
||||||
|
|
||||||
|
let violated = sessionIds && sessionIds.split(",").filter(function (v) {
|
||||||
|
return (
|
||||||
|
v !== sess.id &&
|
||||||
|
v in currentlyOpen &&
|
||||||
|
// ignore errors within the same timeslot
|
||||||
|
// under the assumption that the sessions
|
||||||
|
// in the timeslot happen sequentially
|
||||||
|
sess.timeslot !== currentlyOpen[v].timeslot
|
||||||
|
);
|
||||||
|
}).length > 0;
|
||||||
|
|
||||||
|
jQuery(this).toggleClass("violated-hint", violated);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
for (let i = 0; i < scheduledSessions.length; ++i) {
|
for (let i = 0; i < scheduledSessions.length; ++i) {
|
||||||
let s = scheduledSessions[i];
|
let s = scheduledSessions[i];
|
||||||
|
|
||||||
// prune
|
// prune
|
||||||
for (let sessionIdStr in currentlyOpen) {
|
for (let sessionIdStr in currentlyOpen) {
|
||||||
if (currentlyOpen[sessionIdStr].end <= s.start)
|
if (currentlyOpen[sessionIdStr].end <= s.start) {
|
||||||
delete currentlyOpen[sessionIdStr];
|
delete currentlyOpen[sessionIdStr];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// expand
|
// expand
|
||||||
|
@ -583,20 +618,7 @@ jQuery(document).ready(function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for violated constraints
|
// check for violated constraints
|
||||||
s.element.find(".constraints > span").each(function () {
|
markSessionConstraintViolations(s, currentlyOpen);
|
||||||
let sessionIds = this.dataset.sessions;
|
|
||||||
|
|
||||||
let violated = sessionIds && sessionIds.split(",").filter(function (v) {
|
|
||||||
return (v != s.id
|
|
||||||
&& v in currentlyOpen
|
|
||||||
// ignore errors within the same timeslot
|
|
||||||
// under the assumption that the sessions
|
|
||||||
// in the timeslot happen sequentially
|
|
||||||
&& s.timeslot != currentlyOpen[v].timeslot);
|
|
||||||
}).length > 0;
|
|
||||||
|
|
||||||
jQuery(this).toggleClass("violated-hint", violated);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,8 +636,9 @@ jQuery(document).ready(function () {
|
||||||
function updateAttendeesViolations() {
|
function updateAttendeesViolations() {
|
||||||
sessions.each(function () {
|
sessions.each(function () {
|
||||||
let roomCapacity = jQuery(this).closest(".timeslots").data("roomcapacity");
|
let roomCapacity = jQuery(this).closest(".timeslots").data("roomcapacity");
|
||||||
if (roomCapacity && this.dataset.attendees)
|
if (roomCapacity && this.dataset.attendees) {
|
||||||
jQuery(this).toggleClass("too-many-attendees", +this.dataset.attendees > +roomCapacity);
|
jQuery(this).toggleClass("too-many-attendees", +this.dataset.attendees > +roomCapacity);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,10 +657,12 @@ jQuery(document).ready(function () {
|
||||||
let ai = a[i];
|
let ai = a[i];
|
||||||
let bi = b[i];
|
let bi = b[i];
|
||||||
|
|
||||||
if (ai > bi)
|
if (ai > bi) {
|
||||||
return 1;
|
return 1;
|
||||||
else if (ai < bi)
|
}
|
||||||
|
else if (ai < bi) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -645,8 +670,9 @@ jQuery(document).ready(function () {
|
||||||
|
|
||||||
let arrayWithSortKeys = array.map(function (a) {
|
let arrayWithSortKeys = array.map(function (a) {
|
||||||
let res = [a];
|
let res = [a];
|
||||||
for (let i = 0; i < keyFunctions.length; ++i)
|
for (let i = 0; i < keyFunctions.length; ++i) {
|
||||||
res.push(keyFunctions[i](a));
|
res.push(keyFunctions[i](a));
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -693,8 +719,9 @@ jQuery(document).ready(function () {
|
||||||
let unassignedSessionsContainer = content.find(".unassigned-sessions .drop-target");
|
let unassignedSessionsContainer = content.find(".unassigned-sessions .drop-target");
|
||||||
|
|
||||||
let sortedSessions = sortArrayWithKeyFunctions(unassignedSessionsContainer.children(".session").toArray(), keyFunctions);
|
let sortedSessions = sortArrayWithKeyFunctions(unassignedSessionsContainer.children(".session").toArray(), keyFunctions);
|
||||||
for (let i = 0; i < sortedSessions.length; ++i)
|
for (let i = 0; i < sortedSessions.length; ++i) {
|
||||||
unassignedSessionsContainer.append(sortedSessions[i]);
|
unassignedSessionsContainer.append(sortedSessions[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content.find("select[name=sort_unassigned]").on("change click", function () {
|
content.find("select[name=sort_unassigned]").on("change click", function () {
|
||||||
|
@ -704,7 +731,6 @@ jQuery(document).ready(function () {
|
||||||
sortUnassigned();
|
sortUnassigned();
|
||||||
|
|
||||||
// toggling visible sessions by session parents
|
// toggling visible sessions by session parents
|
||||||
let sessionParentInputs = content.find(".session-parent-toggles input");
|
|
||||||
|
|
||||||
function setSessionHiddenParent(sess, hide) {
|
function setSessionHiddenParent(sess, hide) {
|
||||||
sess.toggleClass('hidden-parent', hide);
|
sess.toggleClass('hidden-parent', hide);
|
||||||
|
@ -725,7 +751,6 @@ jQuery(document).ready(function () {
|
||||||
updateSessionParentToggling();
|
updateSessionParentToggling();
|
||||||
|
|
||||||
// Toggling timeslot types
|
// Toggling timeslot types
|
||||||
let timeSlotTypeInputs = content.find('.timeslot-type-toggles input');
|
|
||||||
function updateTimeSlotTypeToggling() {
|
function updateTimeSlotTypeToggling() {
|
||||||
let checked = [];
|
let checked = [];
|
||||||
timeSlotTypeInputs.filter(":checked").each(function () {
|
timeSlotTypeInputs.filter(":checked").each(function () {
|
||||||
|
@ -736,27 +761,12 @@ jQuery(document).ready(function () {
|
||||||
sessions.not(checked.join(",")).addClass('hidden-timeslot-type');
|
sessions.not(checked.join(",")).addClass('hidden-timeslot-type');
|
||||||
timeslots.filter(checked.join(",")).removeClass('hidden-timeslot-type');
|
timeslots.filter(checked.join(",")).removeClass('hidden-timeslot-type');
|
||||||
timeslots.not(checked.join(",")).addClass('hidden-timeslot-type');
|
timeslots.not(checked.join(",")).addClass('hidden-timeslot-type');
|
||||||
}
|
updateGridVisibility();
|
||||||
if (timeSlotTypeInputs.length > 0) {
|
|
||||||
timeSlotTypeInputs.on("change", updateTimeSlotTypeToggling);
|
|
||||||
updateTimeSlotTypeToggling();
|
|
||||||
content.find('#timeslot-group-toggles-modal .timeslot-type-toggles .select-all').get(0).addEventListener(
|
|
||||||
'click',
|
|
||||||
function() {
|
|
||||||
timeSlotTypeInputs.prop('checked', true);
|
|
||||||
updateTimeSlotTypeToggling();
|
|
||||||
});
|
|
||||||
content.find('#timeslot-group-toggles-modal .timeslot-type-toggles .clear-all').get(0).addEventListener(
|
|
||||||
'click',
|
|
||||||
function() {
|
|
||||||
timeSlotTypeInputs.prop('checked', false);
|
|
||||||
updateTimeSlotTypeToggling();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Toggling session purposes
|
// Toggling session purposes
|
||||||
let sessionPurposeInputs = content.find('.session-purpose-toggles input');
|
function updateSessionPurposeToggling() {
|
||||||
function updateSessionPurposeToggling(evt) {
|
|
||||||
let checked = [];
|
let checked = [];
|
||||||
sessionPurposeInputs.filter(":checked").each(function () {
|
sessionPurposeInputs.filter(":checked").each(function () {
|
||||||
checked.push(".purpose-" + this.value);
|
checked.push(".purpose-" + this.value);
|
||||||
|
@ -765,50 +775,193 @@ jQuery(document).ready(function () {
|
||||||
sessions.filter(checked.join(",")).removeClass('hidden-purpose');
|
sessions.filter(checked.join(",")).removeClass('hidden-purpose');
|
||||||
sessions.not(checked.join(",")).addClass('hidden-purpose');
|
sessions.not(checked.join(",")).addClass('hidden-purpose');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (timeSlotTypeInputs.length > 0) {
|
||||||
|
timeSlotTypeInputs.on("change", updateTimeSlotTypeToggling);
|
||||||
|
updateTimeSlotTypeToggling();
|
||||||
|
content.find('#timeslot-type-toggles-modal .timeslot-type-toggles .select-all').get(0).addEventListener(
|
||||||
|
'click',
|
||||||
|
function() {
|
||||||
|
timeSlotTypeInputs.prop('checked', true);
|
||||||
|
updateTimeSlotTypeToggling();
|
||||||
|
});
|
||||||
|
content.find('#timeslot-type-toggles-modal .timeslot-type-toggles .clear-all').get(0).addEventListener(
|
||||||
|
'click',
|
||||||
|
function() {
|
||||||
|
timeSlotTypeInputs.prop('checked', false);
|
||||||
|
updateTimeSlotTypeToggling();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (sessionPurposeInputs.length > 0) {
|
if (sessionPurposeInputs.length > 0) {
|
||||||
sessionPurposeInputs.on("change", updateSessionPurposeToggling);
|
sessionPurposeInputs.on("change", updateSessionPurposeToggling);
|
||||||
updateSessionPurposeToggling();
|
updateSessionPurposeToggling();
|
||||||
content.find('#session-toggles-modal .select-all').get(0).addEventListener(
|
content.find('#session-toggles-modal .select-all').get(0).addEventListener(
|
||||||
'click',
|
'click',
|
||||||
function() {
|
function() {
|
||||||
sessionPurposeInputs.prop('checked', true);
|
sessionPurposeInputs.not(':disabled').prop('checked', true);
|
||||||
updateSessionPurposeToggling();
|
updateSessionPurposeToggling();
|
||||||
});
|
});
|
||||||
content.find('#session-toggles-modal .clear-all').get(0).addEventListener(
|
content.find('#session-toggles-modal .clear-all').get(0).addEventListener(
|
||||||
'click',
|
'click',
|
||||||
function() {
|
function() {
|
||||||
sessionPurposeInputs.prop('checked', false);
|
sessionPurposeInputs.not(':disabled').prop('checked', false);
|
||||||
updateSessionPurposeToggling();
|
updateSessionPurposeToggling();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// toggling visible timeslots
|
// toggling visible timeslots
|
||||||
let timeSlotGroupInputs = content.find("#timeslot-group-toggles-modal .modal-body .individual-timeslots input");
|
|
||||||
function updateTimeSlotGroupToggling() {
|
function updateTimeSlotGroupToggling() {
|
||||||
let checked = [];
|
let checked = [];
|
||||||
timeSlotGroupInputs.filter(":checked").each(function () {
|
timeSlotGroupInputs.filter(":checked").each(function () {
|
||||||
checked.push("." + this.value);
|
checked.push("." + this.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
timeslots.filter(checked.join(",")).removeClass("hidden");
|
timeslots.filter(checked.join(",")).removeClass("hidden-timeslot-group");
|
||||||
timeslots.not(checked.join(",")).addClass("hidden");
|
timeslots.not(checked.join(",")).addClass("hidden-timeslot-group");
|
||||||
|
updateGridVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
days.each(function () {
|
function updateSessionPurposeOptions() {
|
||||||
jQuery(this).toggle(jQuery(this).find(".timeslot:not(.hidden)").length > 0);
|
sessionPurposeInputs.each((_, purpose_input) => {
|
||||||
|
if (sessions
|
||||||
|
.filter('.purpose-' + purpose_input.value)
|
||||||
|
.not('.hidden')
|
||||||
|
.length === 0) {
|
||||||
|
purpose_input.setAttribute('disabled', 'disabled');
|
||||||
|
purpose_input.closest('.session-purpose-toggle').classList.add('text-muted');
|
||||||
|
} else {
|
||||||
|
purpose_input.removeAttribute('disabled');
|
||||||
|
purpose_input.closest('.session-purpose-toggle').classList.remove('text-muted');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide timeslot toggles for hidden timeslots
|
||||||
|
*/
|
||||||
|
function updateTimeSlotOptions() {
|
||||||
|
timeSlotGroupInputs.each((_, timeslot_input) => {
|
||||||
|
if (timeslots
|
||||||
|
.filter('.' + timeslot_input.value)
|
||||||
|
.not('.hidden-timeslot-type')
|
||||||
|
.length === 0) {
|
||||||
|
timeslot_input.setAttribute('disabled', 'disabled');
|
||||||
|
} else {
|
||||||
|
timeslot_input.removeAttribute('disabled');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make timeslots visible/invisible/hidden
|
||||||
|
*
|
||||||
|
* Responsible for final determination of whether a timeslot is visible, invisible, or hidden.
|
||||||
|
*/
|
||||||
|
function updateTimeSlotVisibility() {
|
||||||
|
timeslots.not(classes_to_hide).removeClass('hidden');
|
||||||
|
timeslots.filter(classes_to_hide).addClass('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sessions visible/invisible/hidden
|
||||||
|
*
|
||||||
|
* Responsible for final determination of whether a session is visible or hidden.
|
||||||
|
*/
|
||||||
|
function updateSessionVisibility() {
|
||||||
|
sessions.not(classes_to_hide).removeClass('hidden');
|
||||||
|
sessions.filter(classes_to_hide).addClass('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make day / time headers visible / hidden to match visible grid contents
|
||||||
|
*/
|
||||||
|
function updateHeaderVisibility() {
|
||||||
|
days.each(function () {
|
||||||
|
jQuery(this).toggle(jQuery(this).find(".timeslot").not(".hidden").length > 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
const rgs = content.find('.day-flow .room-group');
|
||||||
|
rgs.each(function (index, roomGroup) {
|
||||||
|
const headerLabels = jQuery(roomGroup).find('.time-header .time-label');
|
||||||
|
const rgTimeslots = jQuery(roomGroup).find('.timeslot');
|
||||||
|
headerLabels.each(function(index, label) {
|
||||||
|
jQuery(label).toggle(
|
||||||
|
rgTimeslots
|
||||||
|
.filter('[data-start="' + label.dataset.start + '"][data-end="' + label.dataset.end + '"]')
|
||||||
|
.not('.hidden')
|
||||||
|
.length > 0
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update visibility of room rows
|
||||||
|
*/
|
||||||
|
function updateRoomVisibility() {
|
||||||
|
const tsContainers = { toShow: [], toHide: [] };
|
||||||
|
const roomGroups = { toShow: [], toHide: [] };
|
||||||
|
// roomsWithVisibleSlots is an array of room IDs that have at least one visible timeslot
|
||||||
|
let roomsWithVisibleSlots = content.find('.day-flow .timeslots')
|
||||||
|
.has('.timeslot:not(.hidden)')
|
||||||
|
.map((_, e) => e.dataset.roomId).get();
|
||||||
|
roomsWithVisibleSlots = [...new Set(roomsWithVisibleSlots)]; // unique-ify by converting to Set and back
|
||||||
|
|
||||||
|
/* The "timeslots" class identifies elements (now and probably always <div>s) that are containers (i.e.,
|
||||||
|
* parents) of timeslots (elements with the "timeslot" class). Sort these containers based on whether
|
||||||
|
* their room has at least one timeslot visible - if so, we will show it, if not it will be hidden.
|
||||||
|
* This will hide containers both in the day-flow and room label sections, so it will hide the room
|
||||||
|
* labels for rooms with no visible timeslots. */
|
||||||
|
content.find('.timeslots').each((_, e) => {
|
||||||
|
if (roomsWithVisibleSlots.indexOf(e.dataset.roomId) === -1) {
|
||||||
|
tsContainers.toHide.push(e);
|
||||||
|
} else {
|
||||||
|
tsContainers.toShow.push(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Now check whether each room group has any rooms not being hidden. If not, entirely hide the
|
||||||
|
* room group so that all its headers, etc, do not take up space. */
|
||||||
|
content.find('.room-group').each((_, e) => {
|
||||||
|
if (jQuery(e).has(tsContainers.toShow).length > 0) {
|
||||||
|
roomGroups.toShow.push(e);
|
||||||
|
} else {
|
||||||
|
roomGroups.toHide.push(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
jQuery(roomGroups.toShow).show();
|
||||||
|
jQuery(roomGroups.toHide).hide();
|
||||||
|
jQuery(tsContainers.toShow).show();
|
||||||
|
jQuery(tsContainers.toHide).hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update visibility of UI elements
|
||||||
|
*
|
||||||
|
* Call this after changing 'hidden-*' classes on timeslots
|
||||||
|
*/
|
||||||
|
function updateGridVisibility() {
|
||||||
|
updateTimeSlotVisibility();
|
||||||
|
updateSessionVisibility();
|
||||||
|
updateHeaderVisibility();
|
||||||
|
updateRoomVisibility();
|
||||||
|
updateTimeSlotOptions();
|
||||||
|
updateSessionPurposeOptions();
|
||||||
|
content.find('div.edit-grid').removeClass('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
timeSlotGroupInputs.on("click change", updateTimeSlotGroupToggling);
|
timeSlotGroupInputs.on("click change", updateTimeSlotGroupToggling);
|
||||||
content.find('#timeslot-group-toggles-modal .timeslot-group-buttons .select-all').get(0).addEventListener(
|
content.find('#timeslot-group-toggles-modal .timeslot-group-buttons .select-all').get(0).addEventListener(
|
||||||
'click',
|
'click',
|
||||||
function() {
|
function() {
|
||||||
timeSlotGroupInputs.prop('checked', true);
|
timeSlotGroupInputs.not(':disabled').prop('checked', true);
|
||||||
updateTimeSlotGroupToggling();
|
updateTimeSlotGroupToggling();
|
||||||
});
|
});
|
||||||
content.find('#timeslot-group-toggles-modal .timeslot-group-buttons .clear-all').get(0).addEventListener(
|
content.find('#timeslot-group-toggles-modal .timeslot-group-buttons .clear-all').get(0).addEventListener(
|
||||||
'click',
|
'click',
|
||||||
function() {
|
function() {
|
||||||
timeSlotGroupInputs.prop('checked', false);
|
timeSlotGroupInputs.not(':disabled').prop('checked', false);
|
||||||
updateTimeSlotGroupToggling();
|
updateTimeSlotGroupToggling();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -817,9 +970,9 @@ jQuery(document).ready(function () {
|
||||||
setInterval(updatePastTimeslots, 10 * 1000 /* ms */);
|
setInterval(updatePastTimeslots, 10 * 1000 /* ms */);
|
||||||
|
|
||||||
// session info
|
// session info
|
||||||
content.find(".session-info-container").on("mouseover", ".other-session", function (event) {
|
content.find(".session-info-container").on("mouseover", ".other-session", function () {
|
||||||
sessions.filter("#session" + this.dataset.othersessionid).addClass("highlight");
|
sessions.filter("#session" + this.dataset.othersessionid).addClass("highlight");
|
||||||
}).on("mouseleave", ".other-session", function (event) {
|
}).on("mouseleave", ".other-session", function () {
|
||||||
sessions.filter("#session" + this.dataset.othersessionid).removeClass("highlight");
|
sessions.filter("#session" + this.dataset.othersessionid).removeClass("highlight");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright The IETF Trust 2021, All Rights Reserved
|
/* Copyright The IETF Trust 2021-2022, All Rights Reserved
|
||||||
*
|
*
|
||||||
* JS support for the SessionDetailsForm
|
* JS support for the SessionDetailsForm
|
||||||
* */
|
* */
|
||||||
|
@ -37,30 +37,27 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update visibility of 'type' select so it is only shown when multiple options are available */
|
/* Update visibility of 'type' select so it is only shown when multiple options are available */
|
||||||
function update_widget_visibility(elt, purpose, allowed_types) {
|
function update_type_field_visibility(elt, purpose, allowed_types) {
|
||||||
const valid_types = allowed_types[purpose] || [];
|
const valid_types = allowed_types[purpose] || [];
|
||||||
if (valid_types.length > 1) {
|
if (valid_types.length > 1) {
|
||||||
elt.removeAttribute('hidden'); // make visible
|
elt.classList.remove('hidden'); // make visible
|
||||||
} else {
|
} else {
|
||||||
elt.setAttribute('hidden', 'hidden'); // make invisible
|
elt.classList.add('hidden'); // make invisible
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update the 'type' select to reflect a change in the selected purpose */
|
/* Update the 'type' select to reflect a change in the selected purpose */
|
||||||
function update_type_element(type_elt, purpose, type_options, allowed_types) {
|
function update_type_element(type_elt, purpose, type_options, allowed_types) {
|
||||||
update_widget_visibility(type_elt, purpose, allowed_types);
|
update_type_field_visibility(type_elt.closest('.form-group'), purpose, allowed_types);
|
||||||
update_type_option_visibility(type_options, purpose, allowed_types);
|
update_type_option_visibility(type_options, purpose, allowed_types);
|
||||||
set_valid_type(type_elt, purpose, allowed_types);
|
set_valid_type(type_elt, purpose, allowed_types);
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_name_field_visibility(name_elt, purpose) {
|
function update_name_field_visibility(name_elts, purpose) {
|
||||||
const row = name_elt.closest('tr');
|
if (!purpose || purpose === 'regular') {
|
||||||
if (row) {
|
name_elts.forEach(e => e.closest('.form-group').classList.add('hidden'));
|
||||||
if (purpose === 'regular') {
|
} else {
|
||||||
row.setAttribute('hidden', 'hidden');
|
name_elts.forEach(e => e.closest('.form-group').classList.remove('hidden'));
|
||||||
} else {
|
|
||||||
row.removeAttribute('hidden');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +76,10 @@
|
||||||
if (purpose_elt.type === 'hidden') {
|
if (purpose_elt.type === 'hidden') {
|
||||||
return; // element is hidden, so nothing to do
|
return; // element is hidden, so nothing to do
|
||||||
}
|
}
|
||||||
const name_elt = document.getElementById(id_prefix + 'name');
|
const name_elts = [
|
||||||
|
document.getElementById(id_prefix + 'name'),
|
||||||
|
document.getElementById(id_prefix + 'short'),
|
||||||
|
];
|
||||||
const type_elt = document.getElementById(id_prefix + 'type');
|
const type_elt = document.getElementById(id_prefix + 'type');
|
||||||
const type_options = type_elt.getElementsByTagName('option');
|
const type_options = type_elt.getElementsByTagName('option');
|
||||||
const allowed_types = (type_elt.dataset.allowedOptions) ?
|
const allowed_types = (type_elt.dataset.allowedOptions) ?
|
||||||
|
@ -88,17 +88,17 @@
|
||||||
// update on future changes
|
// update on future changes
|
||||||
purpose_elt.addEventListener(
|
purpose_elt.addEventListener(
|
||||||
'change',
|
'change',
|
||||||
purpose_change_handler(name_elt, type_elt, type_options, allowed_types)
|
purpose_change_handler(name_elts, type_elt, type_options, allowed_types)
|
||||||
);
|
);
|
||||||
|
|
||||||
// update immediately
|
// update immediately
|
||||||
update_type_element(type_elt, purpose_elt.value, type_options, allowed_types);
|
update_type_element(type_elt, purpose_elt.value, type_options, allowed_types);
|
||||||
update_name_field_visibility(name_elt, purpose_elt.value);
|
update_name_field_visibility(name_elts, purpose_elt.value);
|
||||||
|
|
||||||
// hide the purpose selector if only one option
|
// hide the purpose selector if only one option
|
||||||
const purpose_options = purpose_elt.querySelectorAll('option:not([value=""])');
|
const purpose_options = purpose_elt.querySelectorAll('option:not([value=""])');
|
||||||
if (purpose_options.length < 2) {
|
if (purpose_options.length < 2) {
|
||||||
purpose_elt.closest('tr').setAttribute('hidden', 'hidden');
|
purpose_elt.closest('.form-group').classList.add('hidden');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,12 +19,9 @@
|
||||||
{# style off-agenda sessions to indicate this #}
|
{# style off-agenda sessions to indicate this #}
|
||||||
.edit-meeting-schedule .session.off-agenda { filter: brightness(0.9); }
|
.edit-meeting-schedule .session.off-agenda { filter: brightness(0.9); }
|
||||||
{# type and purpose styling #}
|
{# type and purpose styling #}
|
||||||
.edit-meeting-schedule .edit-grid .timeslot.wrong-timeslot-type,
|
.edit-meeting-schedule .edit-grid .timeslot.wrong-timeslot-type { background-color: transparent; ); }
|
||||||
.edit-meeting-schedule .edit-grid .timeslot.hidden-timeslot-type { background-color: transparent; ); }
|
.edit-meeting-schedule .edit-grid .timeslot.wrong-timeslot-type .time-label { color: transparent; ); }
|
||||||
.edit-meeting-schedule .edit-grid .timeslot.wrong-timeslot-type .time-label,
|
.edit-meeting-schedule .session.hidden-purpose { filter: blur(3px); }
|
||||||
.edit-meeting-schedule .edit-grid .timeslot.hidden-timeslot-type .time-label { color: transparent; ); }
|
|
||||||
.edit-meeting-schedule .session.hidden-purpose,
|
|
||||||
.edit-meeting-schedule .session.hidden-timeslot-type { filter: blur(3px); }
|
|
||||||
{% endblock morecss %}
|
{% endblock morecss %}
|
||||||
|
|
||||||
{% block title %}{{ schedule.name }}: IETF {{ meeting.number }} meeting agenda{% endblock %}
|
{% block title %}{{ schedule.name }}: IETF {{ meeting.number }} meeting agenda{% endblock %}
|
||||||
|
@ -45,14 +42,17 @@
|
||||||
<p class="pull-right">
|
<p class="pull-right">
|
||||||
{% if can_edit_properties %}
|
{% if can_edit_properties %}
|
||||||
<a href="{% url "ietf.meeting.views.edit_schedule_properties" schedule.meeting.number schedule.owner_email schedule.name %}">Edit properties</a>
|
<a href="{% url "ietf.meeting.views.edit_schedule_properties" schedule.meeting.number schedule.owner_email schedule.name %}">Edit properties</a>
|
||||||
|
·
|
||||||
|
{% endif %}{% if user|has_role:"Secretariat" %}
|
||||||
|
<a href="{% url "ietf.meeting.views.edit_timeslots" num=meeting.number %}">
|
||||||
|
Edit timeslots
|
||||||
|
</a>
|
||||||
·
|
·
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<a href="{% url "ietf.meeting.views.new_meeting_schedule" num=meeting.number owner=schedule.owner_email name=schedule.name %}">Copy agenda</a>
|
<a href="{% url "ietf.meeting.views.new_meeting_schedule" num=meeting.number owner=schedule.owner_email name=schedule.name %}">Copy agenda</a>
|
||||||
·
|
·
|
||||||
|
|
||||||
<a href="{% url "ietf.meeting.views.list_schedules" num=meeting.number %}">Other Agendas</a>
|
<a href="{% url "ietf.meeting.views.list_schedules" num=meeting.number %}">Other agendas</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -85,7 +85,7 @@
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="edit-grid {% if not can_edit %}read-only{% endif %}">
|
<div class="edit-grid hidden {% if not can_edit %}read-only{% endif %}">
|
||||||
{# using the same markup in both room labels and the actual days ensures they are aligned #}
|
{# using the same markup in both room labels and the actual days ensures they are aligned #}
|
||||||
<div class="room-label-column">
|
<div class="room-label-column">
|
||||||
{% for day_data in days.values %}
|
{% for day_data in days.values %}
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
<div class="room-group">
|
<div class="room-group">
|
||||||
<div class="time-header"><div class="time-label"></div></div>
|
<div class="time-header"><div class="time-label"></div></div>
|
||||||
{% for room_data in rgroup %}{% with room_data.room as room %}
|
{% for room_data in rgroup %}{% with room_data.room as room %}
|
||||||
<div class="timeslots">
|
<div class="timeslots" data-room-id="{{ room.pk }}">
|
||||||
<div class="room-name">
|
<div class="room-name">
|
||||||
<strong>{{ room.name }}</strong><br>
|
<strong>{{ room.name }}</strong><br>
|
||||||
{% if room.capacity %}{{ room.capacity }} <i class="fa fa-user-o"></i>{% endif %}
|
{% if room.capacity %}{{ room.capacity }} <i class="fa fa-user-o"></i>{% endif %}
|
||||||
|
@ -131,19 +131,23 @@
|
||||||
<div class="time-header">
|
<div class="time-header">
|
||||||
{# All rooms in a group have same timeslots; grab the first for the labels #}
|
{# All rooms in a group have same timeslots; grab the first for the labels #}
|
||||||
{% for t in rgroup.0.timeslots %}
|
{% for t in rgroup.0.timeslots %}
|
||||||
<div class="time-label" style="width: {{ t.layout_width }}rem">
|
<div class="time-label"
|
||||||
|
style="width: {{ t.layout_width }}rem"
|
||||||
|
data-start="{{ t.utc_start_time.isoformat }}"
|
||||||
|
data-end="{{ t.utc_end_time.isoformat }}">
|
||||||
<span>
|
<span>
|
||||||
{{ t.time|date:"G:i" }} - {{ t.end_time|date:"G:i" }}
|
{{ t.time|date:"G:i" }} - {{ t.end_time|date:"G:i" }}
|
||||||
<i class="fa fa-exchange swap-timeslot-col"
|
<i class="fa fa-exchange swap-timeslot-col"
|
||||||
data-origin-label="{{ day|date:"l, N j" }}, {{ t.time|date:"G:i" }}-{{ t.end_time|date:"G:i" }}"
|
data-origin-label="{{ day|date:"l, N j" }}, {{ t.time|date:"G:i" }}-{{ t.end_time|date:"G:i" }}"
|
||||||
data-start="{{ t.utc_start_time.isoformat }}"
|
|
||||||
data-timeslot-pk="{{ t.pk }}"></i>
|
data-timeslot-pk="{{ t.pk }}"></i>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% for room_data in rgroup %}{% with room_data.room as room %}
|
{% for room_data in rgroup %}{% with room_data.room as room %}
|
||||||
<div class="timeslots" data-roomcapacity="{{ room.capacity }}">
|
<div class="timeslots"
|
||||||
|
data-room-id="{{ room.pk }}"
|
||||||
|
data-roomcapacity="{{ room.capacity }}">
|
||||||
{% for t in room_data.timeslots %}
|
{% for t in room_data.timeslots %}
|
||||||
<div id="timeslot{{ t.pk }}"
|
<div id="timeslot{{ t.pk }}"
|
||||||
class="timeslot {{ t.start_end_group }}"
|
class="timeslot {{ t.start_end_group }}"
|
||||||
|
@ -204,10 +208,26 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{% if session_purposes|length > 1 %}
|
<button id="session-toggle-modal-open"
|
||||||
<button id="session-toggle-modal-open" class="btn btn-default" data-toggle="modal" data-target="#session-toggles-modal"><input type="checkbox" checked="checked" disabled> Sessions</button>
|
class="btn btn-default"
|
||||||
{% endif %}
|
data-toggle="modal"
|
||||||
<button id="timeslot-toggle-modal-open" class="btn btn-default" data-toggle="modal" data-target="#timeslot-group-toggles-modal"><input type="checkbox" checked="checked" disabled> Timeslots</button>
|
data-target="#session-toggles-modal">
|
||||||
|
<span class="fa fa-check-square"></span> Session Purposes
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button id="timeslot-toggle-modal-open"
|
||||||
|
class="btn btn-default"
|
||||||
|
data-toggle="modal"
|
||||||
|
data-target="#timeslot-group-toggles-modal">
|
||||||
|
<span class="fa fa-check-square"></span> Timeslots
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button id="timeslot-type-modal-open"
|
||||||
|
class="btn btn-default"
|
||||||
|
data-toggle="modal"
|
||||||
|
data-target="#timeslot-type-toggles-modal">
|
||||||
|
<span class="fa fa-check-square"></span> Timeslot Types
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -225,39 +245,65 @@
|
||||||
<h4 class="modal-title" id="timeslot-group-toggles-modal-title">Displayed timeslots</h4>
|
<h4 class="modal-title" id="timeslot-group-toggles-modal-title">Displayed timeslots</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="timeslot-group-buttons">
|
<div class="individual-timeslots">
|
||||||
<button type="button" class="btn btn-default select-all">Select all times</button>
|
|
||||||
<button type="button" class="btn btn-default clear-all">Clear times</button>
|
|
||||||
</div>
|
|
||||||
<div class="individual-timeslots">
|
|
||||||
|
|
||||||
{% for day, t_groups in timeslot_groups %}
|
{% for day, t_groups in timeslot_groups %}
|
||||||
<div>
|
<div>
|
||||||
<div><strong>{{ day|date:"M. d" }}</strong></div>
|
<div><strong>{{ day|date:"M. d" }}</strong></div>
|
||||||
{% for start, end, key in t_groups %}
|
{% for start, end, key in t_groups %}
|
||||||
<label><input type="checkbox" name="timeslot-group" value="{{ key }}" checked="checked"> {{ start|date:"H:i" }} - {{ end|date:"H:i" }}</label>
|
<label><input type="checkbox" name="timeslot-group" value="{{ key }}" checked="checked"> {{ start|date:"H:i" }} - {{ end|date:"H:i" }}</label>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="timeslot-group-buttons">
|
||||||
|
<button type="button" class="btn btn-default select-all">Select all</button>
|
||||||
|
<button type="button" class="btn btn-default clear-all">Clear all</button>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="timeslots-by-type timeslot-type-toggles">
|
|
||||||
Type:
|
|
||||||
{% for type in timeslot_types %}
|
|
||||||
<label class="timeslot-type-{{ type.slug }}"><input type="checkbox" checked value="{{ type.slug }}"> {{ type }}</label>
|
|
||||||
{% endfor %}
|
|
||||||
<button type="button" class="btn btn-default select-all">Select all types</button>
|
|
||||||
<button type="button" class="btn btn-default clear-all">Clear types</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="timeslot-type-toggles-modal" class="modal" role="dialog" aria-labelledby="timeslot-type-toggles-modal-title">
|
||||||
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
<span class="sr-only">Close</span>
|
||||||
|
</button>
|
||||||
|
<h4 class="modal-title" id="timeslot-type-toggles-modal-title">Displayed types</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="timeslots-by-type timeslot-type-toggles">
|
||||||
|
{% for type in timeslot_types %}
|
||||||
|
<div class="timeslot-type-toggle">
|
||||||
|
<label class="timeslot-type-{{ type.slug }}">
|
||||||
|
<input type="checkbox"
|
||||||
|
value="{{ type.slug }}" {% if type.slug == 'regular' %}checked{% endif %}>
|
||||||
|
{{ type }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<button type="button" class="btn btn-default select-all">Select all</button>
|
||||||
|
<button type="button" class="btn btn-default clear-all">Clear all</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="session-toggles-modal" class="modal" role="dialog" aria-labelledby="session-toggles-modal-title">
|
<div id="session-toggles-modal" class="modal" role="dialog" aria-labelledby="session-toggles-modal-title">
|
||||||
<div class="modal-dialog modal-lg" role="document">
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
@ -272,7 +318,7 @@
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="session-purpose-toggles">
|
<div class="session-purpose-toggles">
|
||||||
{% for purpose in session_purposes %}
|
{% for purpose in session_purposes %}
|
||||||
<div>
|
<div class="session-purpose-toggle">
|
||||||
<label class="purpose-{{ purpose.slug }}"><input type="checkbox" checked value="{% firstof purpose.slug 'none' %}"> {{ purpose }}</label>
|
<label class="purpose-{{ purpose.slug }}"><input type="checkbox" checked value="{% firstof purpose.slug 'none' %}"> {{ purpose }}</label>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
{% else %} {# secr app room view requires a schedule - show something for now (should try to avoid this possibility) #}
|
{% else %} {# secr app room view requires a schedule - show something for now (should try to avoid this possibility) #}
|
||||||
<span title="Must create meeting schedule to edit rooms">Edit rooms</span>
|
<span title="Must create meeting schedule to edit rooms">Edit rooms</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
·
|
||||||
|
<a href="{% url "ietf.meeting.views.list_schedules" num=meeting.number %}">Agenda list</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p> IETF {{ meeting.number }} Meeting Agenda: Timeslots and Room Availability</p>
|
<p> IETF {{ meeting.number }} Meeting Agenda: Timeslots and Room Availability</p>
|
||||||
|
|
Loading…
Reference in a new issue