on-demand release 3.8dev+
[moodle.git] / mod / forum / report / summary / amd / src / filters.js
CommitLineData
b29de56d
MH
1// This file is part of Moodle - http://moodle.org/
2//
3// Moodle is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// Moodle is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
15
16/**
17 * Module responsible for handling forum summary report filters.
18 *
19 * @module forumreport_summary/filters
20 * @package forumreport_summary
21 * @copyright 2019 Michael Hawkins <michaelh@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25import $ from 'jquery';
26import Popper from 'core/popper';
ce8a2d11
JP
27import CustomEvents from 'core/custom_interaction_events';
28import Selectors from 'forumreport_summary/selectors';
a9496531
MH
29import Y from 'core/yui';
30import Ajax from 'core/ajax';
b29de56d
MH
31
32export const init = (root) => {
ce8a2d11 33 let jqRoot = $(root);
b29de56d
MH
34
35 // Hide loading spinner and show report once page is ready.
36 // This ensures filters can be applied when sorting by columns.
37 $(document).ready(function() {
38 $('.loading-icon').hide();
39 $('#summaryreport').removeClass('hidden');
40 });
41
42 // Generic filter handlers.
43
b29de56d 44 // Called to override click event to trigger a proper generate request with filtering.
a9496531
MH
45 const generateWithFilters = (event) => {
46 let newLink = $('#filtersform').attr('action');
b29de56d
MH
47
48 if (event) {
49 event.preventDefault();
50
51 let filterParams = event.target.search.substr(1);
52 newLink += '&' + filterParams;
53 }
54
55 $('#filtersform').attr('action', newLink);
56 $('#filtersform').submit();
57 };
58
59 // Override 'reset table preferences' so it generates with filters.
60 $('.resettable').on("click", "a", function(event) {
61 generateWithFilters(event);
62 });
63
64 // Override table heading sort links so they generate with filters.
65 $('thead').on("click", "a", function(event) {
66 generateWithFilters(event);
67 });
68
69 // Override pagination page links so they generate with filters.
70 $('.pagination').on("click", "a", function(event) {
71 generateWithFilters(event);
72 });
73
cc15134c 74 // Submit report via filter
a9496531 75 const submitWithFilter = (containerelement) => {
cc15134c
MH
76 // Close the container (eg popover).
77 $(containerelement).addClass('hidden');
78
79 // Submit the filter values and re-generate report.
80 generateWithFilters(false);
81 };
82
a9496531
MH
83 // Use popper to override date mform calendar position.
84 const updateCalendarPosition = (referenceid) => {
85 let referenceElement = document.querySelector(referenceid),
86 popperContent = document.querySelector(Selectors.filters.date.calendar);
87
88 popperContent.style.removeProperty("z-index");
89 new Popper(referenceElement, popperContent, {placement: 'bottom'});
90 };
91
92 // Call when opening filter to ensure only one can be activated.
93 const canOpenFilter = (event) => {
94 if (document.querySelector('[data-openfilter="true"]')) {
95 return false;
96 }
97
98 event.target.setAttribute('data-openfilter', "true");
99 return true;
100 };
101
b29de56d
MH
102 // Groups filter specific handlers.
103
cc15134c 104 // Event handler for clicking select all groups.
ce8a2d11
JP
105 jqRoot.on(CustomEvents.events.activate, Selectors.filters.group.selectall, function() {
106 let deselected = root.querySelectorAll(Selectors.filters.group.checkbox + ':not(:checked)');
107 deselected.forEach(function(checkbox) {
108 checkbox.checked = true;
109 });
110 });
111
112 // Event handler for clearing filter by clicking option.
113 jqRoot.on(CustomEvents.events.activate, Selectors.filters.group.clear, function() {
114 // Clear checkboxes.
115 let selected = root.querySelectorAll(Selectors.filters.group.checkbox + ':checked');
116 selected.forEach(function(checkbox) {
117 checkbox.checked = false;
118 });
b29de56d
MH
119 });
120
121 // Event handler for showing groups filter popover.
a9496531
MH
122 jqRoot.on(CustomEvents.events.activate, Selectors.filters.group.trigger, function(event) {
123 if (!canOpenFilter(event)) {
124 return false;
125 }
126
b29de56d 127 // Create popover.
a9496531 128 let referenceElement = root.querySelector(Selectors.filters.group.trigger),
ce8a2d11 129 popperContent = root.querySelector(Selectors.filters.group.popover);
b29de56d
MH
130
131 new Popper(referenceElement, popperContent, {placement: 'bottom'});
132
ce8a2d11
JP
133 // Show popover.
134 popperContent.classList.remove('hidden');
135
136 // Change to outlined button.
137 referenceElement.classList.add('btn-outline-primary');
138 referenceElement.classList.remove('btn-primary');
139
140 // Let screen readers know that it's now expanded.
141 referenceElement.setAttribute('aria-expanded', true);
a9496531 142 return true;
b29de56d
MH
143 });
144
cc15134c 145 // Event handler to click save groups filter.
ce8a2d11 146 jqRoot.on(CustomEvents.events.activate, Selectors.filters.group.save, function() {
cc15134c
MH
147 submitWithFilter('#filter-groups-popover');
148 });
a9496531
MH
149
150 // Dates filter specific handlers.
151
152 // Event handler for showing dates filter popover.
153 jqRoot.on(CustomEvents.events.activate, Selectors.filters.date.trigger, function(event) {
154 if (!canOpenFilter(event)) {
155 return false;
156 }
157
158 // Create popover.
159 let referenceElement = root.querySelector(Selectors.filters.date.trigger),
160 popperContent = root.querySelector(Selectors.filters.date.popover);
161
162 new Popper(referenceElement, popperContent, {placement: 'bottom'});
163
164 // Show popover and move focus.
165 popperContent.classList.remove('hidden');
166 popperContent.querySelector('[name="filterdatefrompopover[enabled]"]').focus();
167
168 // Change to outlined button.
169 referenceElement.classList.add('btn-outline-primary');
170 referenceElement.classList.remove('btn-primary');
171
172 // Let screen readers know that it's now expanded.
173 referenceElement.setAttribute('aria-expanded', true);
174 return true;
175 });
176
177 // Event handler to save dates filter.
178 jqRoot.on(CustomEvents.events.activate, Selectors.filters.date.save, function() {
179 // Populate the hidden form inputs to submit the data.
180 let filtersForm = document.forms.filtersform;
181 const datesPopover = root.querySelector(Selectors.filters.date.popover);
182 const fromEnabled = datesPopover.querySelector('[name="filterdatefrompopover[enabled]"]').checked ? 1 : 0;
183 const toEnabled = datesPopover.querySelector('[name="filterdatetopopover[enabled]"]').checked ? 1 : 0;
184
185 // Disable the mform checker to prevent unsubmitted form warning to the user when closing the popover.
186 Y.use('moodle-core-formchangechecker', function() {
187 M.core_formchangechecker.reset_form_dirty_state();
188 });
189
190 if (!fromEnabled && !toEnabled) {
191 // Update the elements in the filter form.
192 filtersForm.elements['datefrom[timestamp]'].value = 0;
193 filtersForm.elements['datefrom[enabled]'].value = fromEnabled;
194 filtersForm.elements['dateto[timestamp]'].value = 0;
195 filtersForm.elements['dateto[enabled]'].value = toEnabled;
196
197 // Submit the filter values and re-generate report.
198 submitWithFilter('#filter-dates-popover');
199 } else {
200 let args = {data: []};
201
202 if (fromEnabled) {
203 args.data.push({
204 'key': 'from',
205 'year': datesPopover.querySelector('[name="filterdatefrompopover[year]"]').value,
206 'month': datesPopover.querySelector('[name="filterdatefrompopover[month]"]').value,
207 'day': datesPopover.querySelector('[name="filterdatefrompopover[day]"]').value,
208 'hour': 0,
209 'minute': 0
210 });
211 }
212
213 if (toEnabled) {
214 args.data.push({
215 'key': 'to',
216 'year': datesPopover.querySelector('[name="filterdatetopopover[year]"]').value,
217 'month': datesPopover.querySelector('[name="filterdatetopopover[month]"]').value,
218 'day': datesPopover.querySelector('[name="filterdatetopopover[day]"]').value,
219 'hour': 23,
220 'minute': 59
221 });
222 }
223
224 const request = {
225 methodname: 'core_calendar_get_timestamps',
226 args: args
227 };
228
229 Ajax.call([request])[0].done(function(result) {
230 let fromTimestamp = 0,
231 toTimestamp = 0;
232
233 result['timestamps'].forEach(function(data){
234 if (data.key === 'from') {
235 fromTimestamp = data.timestamp;
236 } else if (data.key === 'to') {
237 toTimestamp = data.timestamp;
238 }
239 });
240
241 // Display an error if the from date is later than the do date.
242 if (toTimestamp > 0 && fromTimestamp > toTimestamp) {
243 const warningdiv = document.getElementById('dates-filter-warning');
244 warningdiv.classList.remove('hidden');
245 warningdiv.classList.add('d-block');
246 } else {
247 filtersForm.elements['datefrom[timestamp]'].value = fromTimestamp;
248 filtersForm.elements['datefrom[enabled]'].value = fromEnabled;
249 filtersForm.elements['dateto[timestamp]'].value = toTimestamp;
250 filtersForm.elements['dateto[enabled]'].value = toEnabled;
251
252 // Submit the filter values and re-generate report.
253 submitWithFilter('#filter-dates-popover');
254 }
255 });
256 }
257 });
258
259 jqRoot.on(CustomEvents.events.activate, Selectors.filters.date.calendariconfrom, function() {
260 updateCalendarPosition(Selectors.filters.date.calendariconfrom);
261 });
262
263 jqRoot.on(CustomEvents.events.activate, Selectors.filters.date.calendariconto, function() {
264 updateCalendarPosition(Selectors.filters.date.calendariconto);
265 });
b29de56d 266};