diff --git a/ietf/meeting/forms.py b/ietf/meeting/forms.py index 1bbb1af62..17e023a49 100644 --- a/ietf/meeting/forms.py +++ b/ietf/meeting/forms.py @@ -662,11 +662,15 @@ class SessionDetailsForm(forms.ModelForm): def __init__(self, group, *args, **kwargs): session_purposes = group.features.session_purposes - kwargs.setdefault('initial', {}) - kwargs['initial'].setdefault( - 'purpose', - session_purposes[0] if len(session_purposes) > 0 else None, - ) + # Default to the first allowed session_purposes. Do not do this if we have an instance, + # though, because ModelForm will override instance data with initial data if it gets both. + # When we have an instance we want to keep its value. + 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) 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) if not group.features.acts_like_wg: self.fields['requested_duration'].durations = [datetime.timedelta(minutes=m) for m in range(30, 241, 30)] - + class Meta: model = Session fields = ( - 'name', 'short', 'purpose', 'type', 'requested_duration', + 'purpose', 'name', 'short', 'type', 'requested_duration', 'on_agenda', 'remote_instructions', 'attendees', 'comments', ) labels = {'requested_duration': 'Length'} diff --git a/ietf/meeting/tests_js.py b/ietf/meeting/tests_js.py index fa8ffe6d6..c388250ff 100644 --- a/ietf/meeting/tests_js.py +++ b/ietf/meeting/tests_js.py @@ -541,21 +541,21 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): past_swap_ts_buttons = self.driver.find_elements(By.CSS_SELECTOR, ','.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') future_swap_ts_buttons = self.driver.find_elements(By.CSS_SELECTOR, ','.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') now_swap_ts_buttons = self.driver.find_elements(By.CSS_SELECTOR, ','.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') diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index 8e521e918..572af8a8b 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -536,15 +536,17 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None): for s in sessions: s.requested_by_person = requested_by_lookup.get(s.requested_by) - s.scheduling_label = "???" s.purpose_label = None - if (s.purpose.slug in ('none', 'regular')) and s.group: - s.scheduling_label = s.group.acronym - s.purpose_label = 'BoF' if s.group.is_bof() else s.group.type.name + if s.group: + if (s.purpose.slug in ('none', 'regular')): + 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: - s.purpose_label = s.purpose.name - if s.name: - s.scheduling_label = s.name + s.scheduling_label = s.name if s.name else '???' + s.purpose_label = s.purpose.name s.requested_duration_in_hours = round(s.requested_duration.seconds / 60.0 / 60.0, 1) diff --git a/ietf/secr/meetings/forms.py b/ietf/secr/meetings/forms.py index 43cc790f8..50ccddbb7 100644 --- a/ietf/secr/meetings/forms.py +++ b/ietf/secr/meetings/forms.py @@ -190,7 +190,7 @@ class MiscSessionForm(TimeSlotForm): Plenary = IETF''', 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) def __init__(self,*args,**kwargs): diff --git a/ietf/static/ietf/js/edit-meeting-schedule.js b/ietf/static/ietf/js/edit-meeting-schedule.js index 60bce13ea..0e6305be0 100644 --- a/ietf/static/ietf/js/edit-meeting-schedule.js +++ b/ietf/static/ietf/js/edit-meeting-schedule.js @@ -1,5 +1,7 @@ /* globals alert, jQuery, moment */ jQuery(document).ready(function () { + 'use strict'; + let content = jQuery(".edit-meeting-schedule"); /* 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 @@ -12,8 +14,9 @@ jQuery(document).ready(function () { function reportServerError(xhr, textStatus, error) { let errorText = error || textStatus; - if (xhr && xhr.responseText) - errorText += "\n\n" + xhr.responseText; + if (xhr && xhr.responseText) { + errorText += '\n\n' + xhr.responseText; + } alert("Error: " + errorText); } @@ -33,9 +36,14 @@ jQuery(document).ready(function () { let swapTimeslotButtons = content.find('.swap-timeslot-col'); let days = content.find(".day-flow .day"); 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 - if (content.find(".scheduling-panel").css("position") != "sticky") { + if (content.find(".scheduling-panel").css("position") !== "sticky") { content.find(".scheduling-panel").css("position", "fixed"); content.css("padding-bottom", "14em"); } @@ -59,7 +67,7 @@ jQuery(document).ready(function () { let res = []; timeslots.each(function () { - var timeslot = jQuery(this); + const timeslot = jQuery(this); let start = startMoment(timeslot); let end = endMoment(timeslot); @@ -97,10 +105,11 @@ jQuery(document).ready(function () { let timeElement = jQuery(this).find(".time"); otherSessionElement.addClass("other-session-selected"); - if (scheduledAt) - timeElement.text(timeElement.data("scheduled").replace("{time}", scheduledAt)); - else - timeElement.text(timeElement.data("notscheduled")); + if (scheduledAt) { + timeElement.text(timeElement.data('scheduled').replace('{time}', scheduledAt)); + } else { + timeElement.text(timeElement.data('notscheduled')); + } }); } else { @@ -256,10 +265,10 @@ jQuery(document).ready(function () { // hide swap day/timeslot column buttons if (officialSchedule) { swapDaysButtons.filter( - (_, elt) => parseISOTimestamp(elt.dataset.start).isSameOrBefore(now, 'day') + (_, elt) => parseISOTimestamp(elt.closest('*[data-start]').dataset.start).isSameOrBefore(now, 'day') ).hide(); swapTimeslotButtons.filter( - (_, elt) => parseISOTimestamp(elt.dataset.start).isSameOrBefore(now, 'minute') + (_, elt) => parseISOTimestamp(elt.closest('*[data-start]').dataset.start).isSameOrBefore(now, 'minute') ).hide(); } } @@ -278,9 +287,12 @@ jQuery(document).ready(function () { } content.on("click", function (event) { - if (jQuery(event.target).is(".session-info-container") || jQuery(event.target).closest(".session-info-container").length > 0) - return; - selectSessionElement(null); + if (!( + jQuery(event.target).is('.session-info-container') || + jQuery(event.target).closest('.session-info-container').length > 0 + )) { + selectSessionElement(null); + } }); sessions.on("click", function (event) { @@ -418,12 +430,14 @@ jQuery(document).ready(function () { } dropElement.append(sessionElement); // move element - if (response.tombstone) + if (response.tombstone) { dragParent.append(response.tombstone); + } updateCurrentSchedulingHints(); - if (dropParent.hasClass("unassigned-sessions")) + if (dropParent.hasClass("unassigned-sessions")) { sortUnassigned(); + } } if (dropParent.hasClass("unassigned-sessions")) { @@ -436,8 +450,7 @@ jQuery(document).ready(function () { session: sessionElement.id.slice("session".length) } }).fail(failHandler).done(done); - } - else { + } else { jQuery.ajax({ url: window.location.href, method: "post", @@ -476,7 +489,7 @@ jQuery(document).ready(function () { // disable any that have passed const now=effectiveNow(); 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.prop('disabled', true); @@ -489,7 +502,7 @@ jQuery(document).ready(function () { let swapDaysLabels = swapDaysModal.find(".modal-body label"); let swapDaysRadios = swapDaysLabels.find('input[name=target_day]'); let updateSwapDaysSubmitButton = function () { - updateSwapSubmitButton(swapDaysModal, 'target_day') + updateSwapSubmitButton(swapDaysModal, 'target_day'); }; // handler to prep and open the modal content.find(".swap-days").on("click", function () { @@ -505,7 +518,7 @@ jQuery(document).ready(function () { updateSwapDaysSubmitButton(); 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 let swapTimeslotsModal = content.find('#swap-timeslot-col-modal'); @@ -534,7 +547,7 @@ jQuery(document).ready(function () { updateSwapTimeslotsSubmitButton(); swapTimeslotsModal.modal('show'); }); - swapTimeslotsRadios.on("change", function () {updateSwapTimeslotsSubmitButton()}); + swapTimeslotsRadios.on("change", function () {updateSwapTimeslotsSubmitButton();}); } // hints for the current schedule @@ -557,22 +570,44 @@ jQuery(document).ready(function () { }); scheduledSessions.sort(function (a, b) { - if (a.start < b.start) + if (a.start < b.start) { return -1; - if (a.start > b.start) + } + if (a.start > b.start) { return 1; + } return 0; }); let currentlyOpen = {}; 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) { let s = scheduledSessions[i]; // prune for (let sessionIdStr in currentlyOpen) { - if (currentlyOpen[sessionIdStr].end <= s.start) + if (currentlyOpen[sessionIdStr].end <= s.start) { delete currentlyOpen[sessionIdStr]; + } } // expand @@ -583,20 +618,7 @@ jQuery(document).ready(function () { } // check for violated constraints - s.element.find(".constraints > span").each(function () { - 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); - }); + markSessionConstraintViolations(s, currentlyOpen); } } @@ -614,8 +636,9 @@ jQuery(document).ready(function () { function updateAttendeesViolations() { sessions.each(function () { 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); + } }); } @@ -634,10 +657,12 @@ jQuery(document).ready(function () { let ai = a[i]; let bi = b[i]; - if (ai > bi) + if (ai > bi) { return 1; - else if (ai < bi) + } + else if (ai < bi) { return -1; + } } return 0; @@ -645,8 +670,9 @@ jQuery(document).ready(function () { let arrayWithSortKeys = array.map(function (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)); + } return res; }); @@ -693,8 +719,9 @@ jQuery(document).ready(function () { let unassignedSessionsContainer = content.find(".unassigned-sessions .drop-target"); 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]); + } } content.find("select[name=sort_unassigned]").on("change click", function () { @@ -704,7 +731,6 @@ jQuery(document).ready(function () { sortUnassigned(); // toggling visible sessions by session parents - let sessionParentInputs = content.find(".session-parent-toggles input"); function setSessionHiddenParent(sess, hide) { sess.toggleClass('hidden-parent', hide); @@ -725,7 +751,6 @@ jQuery(document).ready(function () { updateSessionParentToggling(); // Toggling timeslot types - let timeSlotTypeInputs = content.find('.timeslot-type-toggles input'); function updateTimeSlotTypeToggling() { let checked = []; timeSlotTypeInputs.filter(":checked").each(function () { @@ -736,27 +761,12 @@ jQuery(document).ready(function () { sessions.not(checked.join(",")).addClass('hidden-timeslot-type'); timeslots.filter(checked.join(",")).removeClass('hidden-timeslot-type'); timeslots.not(checked.join(",")).addClass('hidden-timeslot-type'); - } - 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(); - }); + updateGridVisibility(); } + // Toggling session purposes - let sessionPurposeInputs = content.find('.session-purpose-toggles input'); - function updateSessionPurposeToggling(evt) { + function updateSessionPurposeToggling() { let checked = []; sessionPurposeInputs.filter(":checked").each(function () { checked.push(".purpose-" + this.value); @@ -765,50 +775,193 @@ jQuery(document).ready(function () { sessions.filter(checked.join(",")).removeClass('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) { sessionPurposeInputs.on("change", updateSessionPurposeToggling); updateSessionPurposeToggling(); content.find('#session-toggles-modal .select-all').get(0).addEventListener( 'click', function() { - sessionPurposeInputs.prop('checked', true); + sessionPurposeInputs.not(':disabled').prop('checked', true); updateSessionPurposeToggling(); }); content.find('#session-toggles-modal .clear-all').get(0).addEventListener( 'click', function() { - sessionPurposeInputs.prop('checked', false); + sessionPurposeInputs.not(':disabled').prop('checked', false); updateSessionPurposeToggling(); }); } // toggling visible timeslots - let timeSlotGroupInputs = content.find("#timeslot-group-toggles-modal .modal-body .individual-timeslots input"); function updateTimeSlotGroupToggling() { let checked = []; timeSlotGroupInputs.filter(":checked").each(function () { checked.push("." + this.value); }); - timeslots.filter(checked.join(",")).removeClass("hidden"); - timeslots.not(checked.join(",")).addClass("hidden"); + timeslots.filter(checked.join(",")).removeClass("hidden-timeslot-group"); + timeslots.not(checked.join(",")).addClass("hidden-timeslot-group"); + updateGridVisibility(); + } - days.each(function () { - jQuery(this).toggle(jQuery(this).find(".timeslot:not(.hidden)").length > 0); + function updateSessionPurposeOptions() { + 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
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); content.find('#timeslot-group-toggles-modal .timeslot-group-buttons .select-all').get(0).addEventListener( 'click', function() { - timeSlotGroupInputs.prop('checked', true); + timeSlotGroupInputs.not(':disabled').prop('checked', true); updateTimeSlotGroupToggling(); }); content.find('#timeslot-group-toggles-modal .timeslot-group-buttons .clear-all').get(0).addEventListener( 'click', function() { - timeSlotGroupInputs.prop('checked', false); + timeSlotGroupInputs.not(':disabled').prop('checked', false); updateTimeSlotGroupToggling(); }); @@ -817,9 +970,9 @@ jQuery(document).ready(function () { setInterval(updatePastTimeslots, 10 * 1000 /* ms */); // 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"); - }).on("mouseleave", ".other-session", function (event) { + }).on("mouseleave", ".other-session", function () { sessions.filter("#session" + this.dataset.othersessionid).removeClass("highlight"); }); }); diff --git a/ietf/static/ietf/js/meeting/session_details_form.js b/ietf/static/ietf/js/meeting/session_details_form.js index 5016a0139..139310e4d 100644 --- a/ietf/static/ietf/js/meeting/session_details_form.js +++ b/ietf/static/ietf/js/meeting/session_details_form.js @@ -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 * */ @@ -37,30 +37,27 @@ } /* 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] || []; if (valid_types.length > 1) { - elt.removeAttribute('hidden'); // make visible + elt.classList.remove('hidden'); // make visible } 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 */ 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); set_valid_type(type_elt, purpose, allowed_types); } - function update_name_field_visibility(name_elt, purpose) { - const row = name_elt.closest('tr'); - if (row) { - if (purpose === 'regular') { - row.setAttribute('hidden', 'hidden'); - } else { - row.removeAttribute('hidden'); - } + function update_name_field_visibility(name_elts, purpose) { + if (!purpose || purpose === 'regular') { + name_elts.forEach(e => e.closest('.form-group').classList.add('hidden')); + } else { + name_elts.forEach(e => e.closest('.form-group').classList.remove('hidden')); } } @@ -79,7 +76,10 @@ if (purpose_elt.type === 'hidden') { 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_options = type_elt.getElementsByTagName('option'); const allowed_types = (type_elt.dataset.allowedOptions) ? @@ -88,17 +88,17 @@ // update on future changes purpose_elt.addEventListener( '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_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 const purpose_options = purpose_elt.querySelectorAll('option:not([value=""])'); if (purpose_options.length < 2) { - purpose_elt.closest('tr').setAttribute('hidden', 'hidden'); + purpose_elt.closest('.form-group').classList.add('hidden'); } } diff --git a/ietf/templates/meeting/edit_meeting_schedule.html b/ietf/templates/meeting/edit_meeting_schedule.html index 7603244d1..f6d9a4ed0 100644 --- a/ietf/templates/meeting/edit_meeting_schedule.html +++ b/ietf/templates/meeting/edit_meeting_schedule.html @@ -19,12 +19,9 @@ {# style off-agenda sessions to indicate this #} .edit-meeting-schedule .session.off-agenda { filter: brightness(0.9); } {# type and purpose styling #} - .edit-meeting-schedule .edit-grid .timeslot.wrong-timeslot-type, - .edit-meeting-schedule .edit-grid .timeslot.hidden-timeslot-type { background-color: transparent; ); } - .edit-meeting-schedule .edit-grid .timeslot.wrong-timeslot-type .time-label, - .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); } + .edit-meeting-schedule .edit-grid .timeslot.wrong-timeslot-type { background-color: transparent; ); } + .edit-meeting-schedule .edit-grid .timeslot.wrong-timeslot-type .time-label { color: transparent; ); } + .edit-meeting-schedule .session.hidden-purpose { filter: blur(3px); } {% endblock morecss %} {% block title %}{{ schedule.name }}: IETF {{ meeting.number }} meeting agenda{% endblock %} @@ -45,14 +42,17 @@

{% if can_edit_properties %} Edit properties - + · + {% endif %}{% if user|has_role:"Secretariat" %} + + Edit timeslots + · {% endif %} - Copy agenda · - Other Agendas + Other agendas

@@ -85,7 +85,7 @@

{% else %} -
+