MDL-73090 reportbuilder: confirmation before resetting conditions.
[moodle.git] / reportbuilder / amd / src / local / editor / conditions.js
blob0da8a3885face2dbf294c2d6cb09012e0cc02fa5
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.
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/>.
16 /**
17  * Report builder conditions editor
18  *
19  * @module      core_reportbuilder/local/editor/conditions
20  * @copyright   2021 Paul Holden <paulh@moodle.com>
21  * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22  */
24 "use strict";
26 import $ from 'jquery';
27 import {dispatchEvent} from 'core/event_dispatcher';
28 import 'core/inplace_editable';
29 import Notification from 'core/notification';
30 import Pending from 'core/pending';
31 import {prefetchStrings} from 'core/prefetch';
32 import SortableList from 'core/sortable_list';
33 import {get_string as getString, get_strings as getStrings} from 'core/str';
34 import Templates from 'core/templates';
35 import {add as addToast} from 'core/toast';
36 import DynamicForm from 'core_form/dynamicform';
37 import * as reportEvents from 'core_reportbuilder/local/events';
38 import * as reportSelectors from 'core_reportbuilder/local/selectors';
39 import {addCondition, deleteCondition, reorderCondition, resetConditions} from 'core_reportbuilder/local/repository/conditions';
41 /**
42  * Reload conditions settings region
43  *
44  * @param {Element} reportElement
45  * @param {Object} templateContext
46  * @return {Promise}
47  */
48 const reloadSettingsConditionsRegion = (reportElement, templateContext) => {
49     const pendingPromise = new Pending('core_reportbuilder/conditions:reload');
50     const settingsConditionsRegion = reportElement.querySelector(reportSelectors.regions.settingsConditions);
52     return Templates.renderForPromise('core_reportbuilder/local/settings/conditions', {conditions: templateContext})
53         .then(({html, js}) => {
54             Templates.replaceNode(settingsConditionsRegion, html, js + templateContext.javascript);
55             initConditionsForm(reportElement);
56             return pendingPromise.resolve();
57         });
60 /**
61  * Initialise conditions form, must be called on each init because the form container is re-created when switching editor modes
62  */
63 const initConditionsForm = () => {
64     // Handle dynamic conditions form.
65     const reportElement = document.querySelector(reportSelectors.regions.report);
66     const conditionFormContainer = reportElement.querySelector(reportSelectors.regions.settingsConditions);
67     if (!conditionFormContainer) {
68         return;
69     }
70     const conditionForm = new DynamicForm(conditionFormContainer, '\\core_reportbuilder\\form\\condition');
72     // Submit report conditions.
73     conditionForm.addEventListener(conditionForm.events.FORM_SUBMITTED, event => {
74         event.preventDefault();
76         getString('conditionsapplied', 'core_reportbuilder')
77             .then(addToast)
78             .catch(Notification.exception);
80         // After the form has been submitted, we should trigger report table reload.
81         dispatchEvent(reportEvents.tableReload, {}, reportElement);
82     });
84     // Reset report conditions.
85     conditionForm.addEventListener(conditionForm.events.NOSUBMIT_BUTTON_PRESSED, event => {
86         event.preventDefault();
88         getStrings([
89             {key: 'resetconditions', component: 'core_reportbuilder'},
90             {key: 'resetconditionsconfirm', component: 'core_reportbuilder'},
91             {key: 'resetall', component: 'core_reportbuilder'},
92         ]).then(([confirmTitle, confirmText, confirmButton]) => {
93             Notification.confirm(confirmTitle, confirmText, confirmButton, null, () => {
94                 const pendingPromise = new Pending('core_reportbuilder/conditions:reset');
96                 resetConditions(reportElement.dataset.reportId)
97                     .then(data => reloadSettingsConditionsRegion(reportElement, data))
98                     .then(() => getString('conditionsreset', 'core_reportbuilder'))
99                     .then(addToast)
100                     .then(() => {
101                         dispatchEvent(reportEvents.tableReload, {}, reportElement);
102                         return pendingPromise.resolve();
103                     })
104                     .catch(Notification.exception);
105             });
106             return;
107         }).catch(Notification.exception);
108     });
112  * Initialise module, prefetch all required strings
114  * @param {Boolean} initialized Ensure we only add our listeners once
115  */
116 export const init = initialized => {
117     prefetchStrings('core_reportbuilder', [
118         'conditionadded',
119         'conditiondeleted',
120         'conditionmoved',
121         'conditionsapplied',
122         'conditionsreset',
123         'deletecondition',
124         'deleteconditionconfirm',
125         'resetall',
126         'resetconditions',
127         'resetconditionsconfirm',
128     ]);
130     prefetchStrings('core', [
131         'delete',
132     ]);
134     initConditionsForm();
135     if (initialized) {
136         return;
137     }
139     document.addEventListener('click', event => {
141         // Add condition to report.
142         const reportAddCondition = event.target.closest(reportSelectors.actions.reportAddCondition);
143         if (reportAddCondition) {
144             event.preventDefault();
146             const reportElement = reportAddCondition.closest(reportSelectors.regions.report);
148             // Check if dropdown is closed with no condition selected.
149             if (reportAddCondition.value === '0') {
150                 return;
151             }
153             const pendingPromise = new Pending('core_reportbuilder/conditions:add');
155             addCondition(reportElement.dataset.reportId, reportAddCondition.value)
156                 .then(data => reloadSettingsConditionsRegion(reportElement, data))
157                 .then(() => getString('conditionadded', 'core_reportbuilder',
158                     reportAddCondition.options[reportAddCondition.selectedIndex].text))
159                 .then(addToast)
160                 .then(() => {
161                     dispatchEvent(reportEvents.tableReload, {}, reportElement);
162                     return pendingPromise.resolve();
163                 })
164                 .catch(Notification.exception);
165         }
167         // Remove condition from report.
168         const reportRemoveCondition = event.target.closest(reportSelectors.actions.reportRemoveCondition);
169         if (reportRemoveCondition) {
170             event.preventDefault();
172             const reportElement = reportRemoveCondition.closest(reportSelectors.regions.report);
173             const conditionContainer = reportRemoveCondition.closest(reportSelectors.regions.activeCondition);
174             const conditionName = conditionContainer.dataset.conditionName;
176             getStrings([
177                 {key: 'deletecondition', component: 'core_reportbuilder', param: conditionName},
178                 {key: 'deleteconditionconfirm', component: 'core_reportbuilder', param: conditionName},
179                 {key: 'delete', component: 'core'},
180             ]).then(([confirmTitle, confirmText, confirmButton]) => {
181                 Notification.confirm(confirmTitle, confirmText, confirmButton, null, () => {
182                     const pendingPromise = new Pending('core_reportbuilder/conditions:remove');
184                     deleteCondition(reportElement.dataset.reportId, conditionContainer.dataset.conditionId)
185                         .then(data => reloadSettingsConditionsRegion(reportElement, data))
186                         .then(() => getString('conditiondeleted', 'core_reportbuilder', conditionName))
187                         .then(addToast)
188                         .then(() => {
189                             dispatchEvent(reportEvents.tableReload, {}, reportElement);
190                             return pendingPromise.resolve();
191                         })
192                         .catch(Notification.exception);
193                 });
194                 return;
195             }).catch(Notification.exception);
196         }
197     });
199     // Initialize sortable list to handle active conditions moving (note JQuery dependency, see MDL-72293 for resolution).
200     var activeConditionsSortableList = new SortableList(`${reportSelectors.regions.activeConditions}`,
201         {isHorizontal: false});
202     activeConditionsSortableList.getElementName = element => Promise.resolve(element.data('conditionName'));
204     $(document).on(SortableList.EVENTS.DROP, reportSelectors.regions.activeCondition, (event, info) => {
205         if (info.positionChanged) {
206             const pendingPromise = new Pending('core_reportbuilder/conditions:reorder');
207             const reportElement = event.target.closest(reportSelectors.regions.report);
208             const conditionId = info.element.data('conditionId');
209             const conditionPosition = info.element.data('conditionPosition');
211             // Select target position, if moving to the end then count number of element siblings.
212             let targetConditionPosition = info.targetNextElement.data('conditionPosition') || info.element.siblings().length + 2;
213             if (targetConditionPosition > conditionPosition) {
214                 targetConditionPosition--;
215             }
217             reorderCondition(reportElement.dataset.reportId, conditionId, targetConditionPosition)
218                 .then(data => reloadSettingsConditionsRegion(reportElement, data))
219                 .then(() => getString('conditionmoved', 'core_reportbuilder', info.element.data('conditionName')))
220                 .then(addToast)
221                 .then(() => {
222                     dispatchEvent(reportEvents.tableReload, {}, reportElement);
223                     return pendingPromise.resolve();
224                 })
225                 .catch(Notification.exception);
226         }
227     });