Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
 * Report builder filters editor
18
 *
19
 * @module      core_reportbuilder/local/editor/filters
20
 * @copyright   2021 David Matamoros <davidmc@moodle.com>
21
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
 
24
"use strict";
25
 
26
import AutoComplete from 'core/form-autocomplete';
27
import 'core/inplace_editable';
28
import Notification from 'core/notification';
29
import Pending from 'core/pending';
30
import {prefetchStrings} from 'core/prefetch';
31
import SortableList from 'core/sortable_list';
32
import {getString} from 'core/str';
33
import Templates from 'core/templates';
34
import {add as addToast} from 'core/toast';
35
import * as reportSelectors from 'core_reportbuilder/local/selectors';
36
import {addFilter, deleteFilter, reorderFilter} from 'core_reportbuilder/local/repository/filters';
37
 
38
/**
39
 * Reload filters settings region
40
 *
41
 * @param {Element} reportElement
42
 * @param {Object} templateContext
43
 * @return {Promise}
44
 */
45
const reloadSettingsFiltersRegion = (reportElement, templateContext) => {
46
    const pendingPromise = new Pending('core_reportbuilder/filters:reload');
47
    const settingsFiltersRegion = reportElement.querySelector(reportSelectors.regions.settingsFilters);
48
 
49
    return Templates.renderForPromise('core_reportbuilder/local/settings/filters', {filters: templateContext})
50
        .then(({html, js}) => {
51
            Templates.replaceNode(settingsFiltersRegion, html, js);
52
 
53
            initFiltersForm();
54
 
55
            // Re-focus the add filter element after reloading the region.
56
            const reportAddFilter = reportElement.querySelector(reportSelectors.actions.reportAddFilter);
57
            reportAddFilter?.focus();
58
 
59
            return pendingPromise.resolve();
60
        });
61
};
62
 
63
/**
64
 * Initialise filters form, must be called on each init because the form container is re-created when switching editor modes
65
 */
66
const initFiltersForm = () => {
67
    const reportElement = document.querySelector(reportSelectors.regions.report);
68
 
69
    // Enhance filter selector.
70
    const reportAddFilter = reportElement.querySelector(reportSelectors.actions.reportAddFilter);
71
    AutoComplete.enhanceField(reportAddFilter, false, '', getString('selectafilter', 'core_reportbuilder'))
72
        .catch(Notification.exception);
73
};
74
 
75
/**
76
 * Initialise module, prefetch all required strings
77
 *
78
 * @param {Boolean} initialized Ensure we only add our listeners once
79
 */
80
export const init = initialized => {
81
    prefetchStrings('core_reportbuilder', [
82
        'deletefilter',
83
        'deletefilterconfirm',
84
        'filteradded',
85
        'filterdeleted',
86
        'filtermoved',
87
        'selectafilter',
88
    ]);
89
 
90
    prefetchStrings('core', [
91
        'delete',
92
    ]);
93
 
94
    initFiltersForm();
95
    if (initialized) {
96
        return;
97
    }
98
 
99
    // Add filter to report.
100
    document.addEventListener('change', event => {
101
        const reportAddFilter = event.target.closest(reportSelectors.actions.reportAddFilter);
102
        if (reportAddFilter) {
103
            event.preventDefault();
104
 
105
            // Check if dropdown is closed with no filter selected.
106
            if (reportAddFilter.value === "" || reportAddFilter.value === "0") {
107
                return;
108
            }
109
 
110
            const reportElement = reportAddFilter.closest(reportSelectors.regions.report);
111
            const pendingPromise = new Pending('core_reportbuilder/filters:add');
112
 
113
            addFilter(reportElement.dataset.reportId, reportAddFilter.value)
114
                .then(data => reloadSettingsFiltersRegion(reportElement, data))
115
                .then(() => getString('filteradded', 'core_reportbuilder',
116
                    reportAddFilter.options[reportAddFilter.selectedIndex].text))
117
                .then(addToast)
118
                .then(() => pendingPromise.resolve())
119
                .catch(Notification.exception);
120
        }
121
    });
122
 
123
    document.addEventListener('click', event => {
124
 
125
        // Remove filter from report.
126
        const reportRemoveFilter = event.target.closest(reportSelectors.actions.reportRemoveFilter);
127
        if (reportRemoveFilter) {
128
            event.preventDefault();
129
 
130
            const reportElement = reportRemoveFilter.closest(reportSelectors.regions.report);
131
            const filterContainer = reportRemoveFilter.closest(reportSelectors.regions.activeFilter);
132
            const filterName = filterContainer.dataset.filterName;
133
 
134
            Notification.saveCancelPromise(
135
                getString('deletefilter', 'core_reportbuilder', filterName),
136
                getString('deletefilterconfirm', 'core_reportbuilder', filterName),
137
                getString('delete', 'core'),
138
                {triggerElement: reportRemoveFilter}
139
            ).then(() => {
140
                const pendingPromise = new Pending('core_reportbuilder/filters:remove');
141
 
142
                return deleteFilter(reportElement.dataset.reportId, filterContainer.dataset.filterId)
143
                    .then(data => reloadSettingsFiltersRegion(reportElement, data))
144
                    .then(() => addToast(getString('filterdeleted', 'core_reportbuilder', filterName)))
145
                    .then(() => pendingPromise.resolve())
146
                    .catch(Notification.exception);
147
            }).catch(() => {
148
                return;
149
            });
150
        }
151
    });
152
 
1441 ariadna 153
    // Initialize sortable list to handle active filters moving.
154
    const activeFiltersSelector = `${reportSelectors.regions.activeFilters} ul`;
155
    const activeFiltersSortableList = new SortableList(activeFiltersSelector, {isHorizontal: false});
1 efrain 156
    activeFiltersSortableList.getElementName = element => Promise.resolve(element.data('filterName'));
157
 
1441 ariadna 158
    document.addEventListener(SortableList.EVENTS.elementDrop, event => {
159
        const reportOrderFilter = event.target.closest(`${activeFiltersSelector} ${reportSelectors.regions.activeFilter}`);
160
        if (reportOrderFilter && event.detail.positionChanged) {
1 efrain 161
            const pendingPromise = new Pending('core_reportbuilder/filters:reorder');
162
 
1441 ariadna 163
            const reportElement = reportOrderFilter.closest(reportSelectors.regions.report);
164
            const {filterId, filterPosition, filterName} = reportOrderFilter.dataset;
165
 
1 efrain 166
            // Select target position, if moving to the end then count number of element siblings.
1441 ariadna 167
            let targetFilterPosition = event.detail.targetNextElement.data('filterPosition')
168
                || event.detail.element.siblings().length + 2;
1 efrain 169
            if (targetFilterPosition > filterPosition) {
170
                targetFilterPosition--;
171
            }
172
 
173
            // Re-order filter, giving drop event transition time to finish.
174
            const reorderPromise = reorderFilter(reportElement.dataset.reportId, filterId, targetFilterPosition);
175
            Promise.all([reorderPromise, new Promise(resolve => setTimeout(resolve, 1000))])
176
                .then(([data]) => reloadSettingsFiltersRegion(reportElement, data))
1441 ariadna 177
                .then(() => getString('filtermoved', 'core_reportbuilder', filterName))
1 efrain 178
                .then(addToast)
179
                .then(() => pendingPromise.resolve())
180
                .catch(Notification.exception);
181
        }
182
    });
183
};