refactor(admin/users): migrate CSV export to core primitive

Replace the inline export implementation with the new generic primitive
(core/Service/Export + helpers/export + app-list-export.js). No more
hand-rolled fputcsv loop, header setup, or escape closure duplicated
here — one implementation, tested once, reused everywhere.

- export().php: uses exportRequireGetRequest, exportCapLimit,
  exportResolveFlavor, ExportColumn[], exportSendCsv. Phone/mobile/
  short_dial keep allowSignedNumeric=true so +49 numbers render
  untouched.
- export(none).phtml: deleted — headers + fputcsv now live in the
  core helper.
- index(default).phtml: inline <details class="dropdown"> "Export CSV"
  replaced by the reusable app-list-export-dropdown partial, exposing
  CSV and Excel flavors. exportUrl added to pageConfig.
- app-users-list.js: hand-rolled export-button binding removed in
  favor of initListExport({ gridConfig, exportUrl }).
- admin-users-index.js: forwards config.exportUrl into
  initUsersListPage.

Net effect: ~100 lines of duplicated export plumbing removed from the
users page; users gain the Excel flavor for free.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-21 21:57:31 +02:00
parent 12356e2266
commit 5ba086adee
5 changed files with 68 additions and 121 deletions

View File

@@ -171,6 +171,7 @@ createListPageModule({
appBase,
csrf,
labels,
exportUrl: config.exportUrl,
buildBulkUrl,
bulkDownloadForms: {
'access-pdf': '#users-access-pdf-bulk-form',

View File

@@ -1,6 +1,7 @@
import { confirmDialog } from '../core/app-confirm-dialog.js';
import { warnOnce } from '../core/app-dom.js';
import { postForm } from '../core/app-http.js';
import { initListExport } from '../core/app-list-export.js';
export function initUsersListPage(options = {}) {
const {
@@ -10,7 +11,7 @@ export function initUsersListPage(options = {}) {
labels = {},
selectors = {},
bulkDownloadForms = {},
exportPath = 'admin/users/export',
exportUrl,
listPath = 'admin/users',
resetOptions = {},
buildBulkUrl,
@@ -20,7 +21,6 @@ export function initUsersListPage(options = {}) {
if (!gridConfig) {return;}
const exportSelector = selectors.exportButton || '[data-users-export]';
const resetSelector = selectors.resetButton || '[data-users-reset]';
const bulkSelector = selectors.bulkButtons || '[data-users-bulk]';
@@ -33,19 +33,8 @@ export function initUsersListPage(options = {}) {
return postForm(url, body);
};
const exportButton = document.querySelector(exportSelector);
if (exportButton) {
exportButton.addEventListener('click', () => {
const exportUrl = new URL(exportPath, appBase);
const dataUrl = new URL(gridConfig.baseUrl());
dataUrl.searchParams.forEach((value, key) => exportUrl.searchParams.set(key, value));
const sortParams = gridConfig.getSortParams ? gridConfig.getSortParams() : null;
if (sortParams && sortParams.order) {
exportUrl.searchParams.set('order', sortParams.order);
exportUrl.searchParams.set('dir', sortParams.dir || 'asc');
}
window.location.href = exportUrl.toString();
});
if (exportUrl) {
initListExport({ gridConfig, exportUrl });
}
const resetButton = document.querySelector(resetSelector);