mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-05-05 17:35:30 +02:00
302 lines
8.1 KiB
JavaScript
302 lines
8.1 KiB
JavaScript
'use strict';
|
|
|
|
define('forum/flags/list', [
|
|
'components', 'Chart', 'categoryFilter',
|
|
'autocomplete', 'api', 'alerts',
|
|
'userFilter',
|
|
], function (
|
|
components, Chart, categoryFilter,
|
|
autocomplete, api, alerts,
|
|
userFilter
|
|
) {
|
|
const Flags = {};
|
|
const selected = new Map([
|
|
['cids', []],
|
|
['assignee', []],
|
|
['targetUid', []],
|
|
['reporterId', []],
|
|
]);
|
|
|
|
Flags.init = function () {
|
|
Flags.enableFilterForm();
|
|
Flags.enableCheckboxes();
|
|
Flags.handleBulkActions();
|
|
|
|
if (ajaxify.data.filters.hasOwnProperty('cid')) {
|
|
selected.set('cids', Array.isArray(ajaxify.data.filters.cid) ?
|
|
ajaxify.data.filters.cid : [ajaxify.data.filters.cid]);
|
|
}
|
|
|
|
categoryFilter.init($('[component="category/dropdown"]'), {
|
|
privilege: 'moderate',
|
|
selectedCids: selected.get('cids'),
|
|
updateButton: function ({ selectedCids: cids }) {
|
|
selected.set('cids', cids);
|
|
applyFilters();
|
|
},
|
|
});
|
|
|
|
['assignee', 'targetUid', 'reporterId'].forEach((filter) => {
|
|
if (ajaxify.data.filters.hasOwnProperty('filter')) {
|
|
selected.set(filter, ajaxify.data.selected[filter]);
|
|
}
|
|
const filterEl = $(`[component="flags/filter/${filter}"]`);
|
|
userFilter.init(filterEl, {
|
|
selectedUsers: selected.get(filter),
|
|
template: 'partials/flags/filters',
|
|
selectedBlock: 'selected.assignee',
|
|
onSelect: function (_selectedUsers) {
|
|
selected.set(filter, _selectedUsers);
|
|
},
|
|
onHidden: function () {
|
|
applyFilters();
|
|
},
|
|
});
|
|
});
|
|
|
|
components.get('flags/list')
|
|
.on('click', '[data-flag-id]', function (e) {
|
|
if (['BUTTON', 'A'].includes(e.target.nodeName)) {
|
|
return;
|
|
}
|
|
|
|
const flagId = this.getAttribute('data-flag-id');
|
|
ajaxify.go('flags/' + flagId);
|
|
});
|
|
|
|
$('#flags-daily-wrapper').one('shown.bs.collapse', function () {
|
|
Flags.handleGraphs();
|
|
});
|
|
|
|
autocomplete.user($('#filter-assignee, #filter-targetUid, #filter-reporterId'), (ev, ui) => {
|
|
setTimeout(() => { ev.target.value = ui.item.user.uid; });
|
|
});
|
|
};
|
|
|
|
Flags.enableFilterForm = function () {
|
|
const $filtersEl = components.get('flags/filters');
|
|
if ($filtersEl && $filtersEl.get(0).nodeName !== 'FORM') {
|
|
// Harmony; update hidden form and submit on change
|
|
const filtersEl = $filtersEl.get(0);
|
|
const formEl = filtersEl.querySelector('form');
|
|
|
|
filtersEl.addEventListener('click', (e) => {
|
|
const subselector = e.target.closest('[data-value]');
|
|
if (!subselector) {
|
|
return;
|
|
}
|
|
|
|
const name = subselector.getAttribute('data-name');
|
|
const value = subselector.getAttribute('data-value');
|
|
|
|
formEl[name].value = value;
|
|
|
|
applyFilters();
|
|
});
|
|
} else {
|
|
// Persona; parse ajaxify data to set form values to reflect current filters
|
|
for (const filter in ajaxify.data.filters) {
|
|
if (ajaxify.data.filters.hasOwnProperty(filter)) {
|
|
$filtersEl.find('[name="' + filter + '"]').val(ajaxify.data.filters[filter]);
|
|
}
|
|
}
|
|
$filtersEl.find('[name="sort"]').val(ajaxify.data.sort);
|
|
|
|
document.getElementById('apply-filters').addEventListener('click', function () {
|
|
applyFilters();
|
|
});
|
|
|
|
$filtersEl.find('button[data-target="#more-filters"]').click((ev) => {
|
|
const textVariant = ev.target.getAttribute('data-text-variant');
|
|
if (!textVariant) {
|
|
return;
|
|
}
|
|
ev.target.setAttribute('data-text-variant', ev.target.textContent);
|
|
ev.target.firstChild.textContent = textVariant;
|
|
});
|
|
}
|
|
};
|
|
|
|
function applyFilters() {
|
|
let formEl = components.get('flags/filters').get(0);
|
|
if (!formEl) {
|
|
return;
|
|
}
|
|
if (formEl.nodeName !== 'FORM') {
|
|
formEl = formEl.querySelector('form');
|
|
}
|
|
|
|
const payload = new FormData(formEl);
|
|
|
|
// cid is special comes from categoryFilter module
|
|
selected.get('cids').forEach(function (cid) {
|
|
payload.append('cid', cid);
|
|
});
|
|
|
|
// these three fields are special; comes from userFilter module
|
|
['assignee', 'targetUid', 'reporterId'].forEach((filter) => {
|
|
selected.get(filter).forEach(({ uid }) => {
|
|
payload.append(filter, uid);
|
|
});
|
|
});
|
|
|
|
const length = Array.from(payload.values()).filter(Boolean);
|
|
const qs = new URLSearchParams(payload).toString();
|
|
|
|
ajaxify.go('flags?' + (length ? qs : 'reset=1'));
|
|
}
|
|
|
|
Flags.enableCheckboxes = function () {
|
|
const flagsList = document.querySelector('[component="flags/list"]');
|
|
const checkboxes = flagsList.querySelectorAll('[data-flag-id] input[type="checkbox"]');
|
|
const bulkEl = document.querySelector('[component="flags/bulk-actions"] button');
|
|
let lastClicked;
|
|
|
|
document.querySelector('[data-action="toggle-all"]').addEventListener('click', function () {
|
|
const state = this.checked;
|
|
|
|
checkboxes.forEach(function (el) {
|
|
el.checked = state;
|
|
});
|
|
bulkEl.disabled = !state;
|
|
});
|
|
|
|
flagsList.addEventListener('click', function (e) {
|
|
const subselector = e.target.closest('input[type="checkbox"]');
|
|
if (subselector) {
|
|
// Stop checkbox clicks from going into the flag details
|
|
e.stopImmediatePropagation();
|
|
|
|
if (lastClicked && e.shiftKey && lastClicked !== subselector) {
|
|
// Select all the checkboxes in between
|
|
const state = subselector.checked;
|
|
let started = false;
|
|
|
|
checkboxes.forEach(function (el) {
|
|
if ([subselector, lastClicked].some(function (ref) {
|
|
return ref === el;
|
|
})) {
|
|
started = !started;
|
|
}
|
|
|
|
if (started) {
|
|
el.checked = state;
|
|
}
|
|
});
|
|
}
|
|
|
|
// (De)activate bulk actions button based on checkboxes' state
|
|
bulkEl.disabled = !Array.prototype.some.call(checkboxes, function (el) {
|
|
return el.checked;
|
|
});
|
|
|
|
lastClicked = subselector;
|
|
}
|
|
|
|
// If you miss the checkbox, don't descend into the flag details, either
|
|
if (e.target.querySelector('input[type="checkbox"]')) {
|
|
e.stopImmediatePropagation();
|
|
}
|
|
});
|
|
};
|
|
|
|
Flags.handleBulkActions = function () {
|
|
document.querySelector('[component="flags/bulk-actions"]').addEventListener('click', function (e) {
|
|
const subselector = e.target.closest('[data-action]');
|
|
if (subselector) {
|
|
const action = subselector.getAttribute('data-action');
|
|
const flagIds = Flags.getSelected();
|
|
const promises = flagIds.map((flagId) => {
|
|
const data = {};
|
|
if (action === 'bulk-assign') {
|
|
data.assignee = app.user.uid;
|
|
} else if (action === 'bulk-mark-resolved') {
|
|
data.state = 'resolved';
|
|
}
|
|
return api.put(`/flags/${flagId}`, data);
|
|
});
|
|
|
|
Promise.allSettled(promises).then(function (results) {
|
|
const fulfilled = results.filter(function (res) {
|
|
return res.status === 'fulfilled';
|
|
}).length;
|
|
const errors = results.filter(function (res) {
|
|
return res.status === 'rejected';
|
|
});
|
|
if (fulfilled) {
|
|
alerts.success('[[flags:bulk-success, ' + fulfilled + ']]');
|
|
ajaxify.refresh();
|
|
}
|
|
|
|
errors.forEach(function (res) {
|
|
alerts.error(res.reason);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
Flags.getSelected = function () {
|
|
const checkboxes = document.querySelectorAll('[component="flags/list"] [data-flag-id] input[type="checkbox"]');
|
|
const payload = [];
|
|
checkboxes.forEach(function (el) {
|
|
if (el.checked) {
|
|
payload.push(el.closest('[data-flag-id]').getAttribute('data-flag-id'));
|
|
}
|
|
});
|
|
|
|
return payload;
|
|
};
|
|
|
|
Flags.handleGraphs = function () {
|
|
const dailyCanvas = document.getElementById('flags:daily');
|
|
const dailyLabels = utils.getDaysArray().map(function (text, idx) {
|
|
return idx % 3 ? '' : text;
|
|
});
|
|
|
|
if (utils.isMobile()) {
|
|
Chart.defaults.global.tooltips.enabled = false;
|
|
}
|
|
const data = {
|
|
'flags:daily': {
|
|
labels: dailyLabels,
|
|
datasets: [
|
|
{
|
|
label: '',
|
|
backgroundColor: 'rgba(151,187,205,0.2)',
|
|
borderColor: 'rgba(151,187,205,1)',
|
|
pointBackgroundColor: 'rgba(151,187,205,1)',
|
|
pointHoverBackgroundColor: '#fff',
|
|
pointBorderColor: '#fff',
|
|
pointHoverBorderColor: 'rgba(151,187,205,1)',
|
|
data: ajaxify.data.analytics,
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
dailyCanvas.width = $(dailyCanvas).parent().width();
|
|
new Chart(dailyCanvas.getContext('2d'), {
|
|
type: 'line',
|
|
data: data['flags:daily'],
|
|
options: {
|
|
responsive: true,
|
|
animation: false,
|
|
legend: {
|
|
display: false,
|
|
},
|
|
scales: {
|
|
yAxes: [{
|
|
ticks: {
|
|
beginAtZero: true,
|
|
precision: 0,
|
|
},
|
|
}],
|
|
},
|
|
},
|
|
});
|
|
};
|
|
|
|
return Flags;
|
|
});
|