Files
Fuchs_Intranet/Fuchs/wwwroot/web/fis.usr.de.js
T
Stefan 38e0d55b09
Playwright Tests / test (push) Canceled after 0s
Improve refreshRow to handle overlapping AJAX responses
Refactored refreshRow in fis.usr.de.js to look up the row node by id inside the AJAX response handler, ensuring correct replacement even with overlapping calls (e.g., rapid checkbox toggles). This prevents errors when a previous call detaches the node before a later response arrives. Enhanced fallback to full reload for robustness. Updated fis.usr.de.min.js accordingly. Reformatted tools.js with no content changes.
2026-09-16 09:45:09 +02:00

251 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
let $uct = {
mdl: 'Benutzerverwaltung'
, rel: 'Neu Laden'
, col_name: 'Name'
, col_email: 'E-Mail'
, col_groups: 'Gruppen'
, col_status: 'Status'
, active: 'Aktiv'
, disabled: 'Deaktiviert'
, loadfail: 'Die Benutzerliste konnte nicht geladen werden.'
, empty: 'Es sind keine Benutzerkonten vorhanden.'
, newuser: 'Neuer Benutzer'
, newuser_btn: 'Neuer Benutzer'
, firstname_field: 'Vorname'
, name_field: 'Nachname'
, title_field: 'Titel'
, gender_field: 'Anrede'
, gender_m: 'Herr'
, gender_f: 'Frau'
, mobile_field: 'Mobil'
, create_sent: 'Das Benutzerkonto wurde angelegt, die Zugangsdaten wurden per E-Mail versendet.'
, create_noemail: 'Das Benutzerkonto wurde angelegt, die Zugangsdaten konnten aber nicht per E-Mail zugestellt werden.'
, delete: 'Benutzer löschen'
, delete_confirm: 'Soll der Benutzer "{0}" wirklich gelöscht werden?'
, pwdreset: 'Passwort zurücksetzen'
, pwdreset_btn: 'Zurücksetzen'
, pwd_field: 'Neues Passwort'
, pwd_hint: 'Leer lassen, um automatisch ein Passwort zu generieren. In jedem Fall wird das neue Passwort per E-Mail an den Benutzer gesendet.'
, pwdreset_sent: 'Das neue Passwort wurde per E-Mail an den Benutzer gesendet.'
, pwdreset_noemail: 'Das Passwort wurde zurückgesetzt, konnte aber nicht per E-Mail zugestellt werden.'
, email: 'E-Mail-Adresse ändern'
, email_field: 'E-Mail-Adresse'
, groups: 'Gruppenzugehörigkeit'
, groups_title: 'Gruppen – {0}'
, err: {
requirements: 'Das Passwort erfüllt nicht die Mindestanforderungen (mind. 6 Zeichen).',
duplicate_email: 'Diese E-Mail-Adresse ist bereits einem anderen Benutzerkonto zugeordnet.',
reset_failed: 'Das Passwort konnte nicht zurückgesetzt werden.',
change_failed: 'Die Änderung konnte nicht gespeichert werden.',
create_failed: 'Das Benutzerkonto konnte nicht angelegt werden.',
delete_failed: 'Der Benutzer konnte nicht gelöscht werden.',
cannot_delete_self: 'Das eigene Benutzerkonto kann nicht gelöscht werden.'
}
, err_generic: 'Die Änderung konnte nicht gespeichert werden.'
};
let $usr = {
init2: function () {
// No external libraries needed — go straight to render.
$ocms.getScript([], function () { $usr.init3(); });
},
init3: function () {
$fis.cf(true);
$fis.lf(true);
$('#activemodule').text($uct.mdl);
$usr.eM();
$usr.renderLst();
},
/* Default ribbon (nothing selected). */
eM: function (r) {
let m = [{ lbl: $uct.newuser_btn, fnc: $usr.createDlg }];
if (bool(r, false) === true) { m.push({ lbl: $uct.rel, fnc: $usr.renderLst }); }
return $('#topbar').ocmsmenu(m);
},
/* Per-row ribbon shown while a user row is selected (mirrors the Banking module's iMn/eM swap). */
iMn: function (rw) {
return $('#topbar').ocmsmenu([
{ lbl: $uct.pwdreset, fnc: function () { $usr.pwdResetDlg(rw); } },
{ lbl: $uct.email, fnc: function () { $usr.emailDlg(rw); } },
{ lbl: $uct.groups, fnc: function () { $usr.groupsDlg(rw); } },
{ lbl: $uct.delete, fnc: function () { $usr.deleteUser(rw); } }
]);
},
/* initials for the avatar badge, e.g. "Nadine Beckmann" -> "NB" */
initials: function (firstname, name) {
let f = (firstname || '').trim().charAt(0), n = (name || '').trim().charAt(0);
return (f + n).toUpperCase() || '?';
},
/* Builds one (detached) grid row for a user record; shared by renderLst and refreshRow. */
buildRow: function (rw) {
let disabled = bool(rw.disabled, false) === true;
let row = $$.dc('usr_row', { id: 'usr' + rw.useraccount_id });
$$.dc('usr_avatar', row).text($usr.initials(rw.firstname, rw.name));
$$.dc('usr_col_name', row).text(`${rw.firstname || ''} ${rw.name || ''}`.trim());
$$.dc('usr_col_email', row).text(rw.email || '');
$$.dc('usr_col_groups', row).text(rw.groups || '').attr('title', rw.groups || '');
$$.dc('usr_col_status', row).append(
$$.dc(disabled ? 'usr_pill usr_pill_off' : 'usr_pill usr_pill_on').text(disabled ? $uct.disabled : $uct.active)
);
row.click(function () {
row.toggleClass('selected').siblings().rC('selected');
if (row.is('.selected') === true) { $usr.iMn(rw); } else { $usr.eM(true); }
});
return row;
},
/* Re-fetches a single user and swaps its grid row in place, keeping selection + ribbon
(used after changes that only affect that one row: email, group membership). Falls back
to a full reload when the row is no longer in the grid. Looks up the current row node by
id inside the response handler (not before the request) so two overlapping calls — e.g.
two quick checkbox toggles in groupsDlg — each replace whatever row is live at the time
their own response arrives, instead of the second call operating on a node the first
already detached via replaceWith. */
refreshRow: function (rw) {
if ($('#usr' + rw.useraccount_id).length === 0) { $usr.renderLst(); return; }
$ocms.postXT({
url: $ocms.url('usr/list'), data: { useraccount_id: rw.useraccount_id }, success: function (r) {
let fresh = (r.users || [])[0];
let old = $('#usr' + rw.useraccount_id);
if (!fresh || old.length === 0) { $usr.renderLst(); return; }
let selected = old.is('.selected');
let row = $usr.buildRow(fresh).toggleClass('selected', selected);
old.replaceWith(row);
if (selected) { $usr.iMn(fresh); }
}, error: function () { $usr.renderLst(); }
});
},
renderLst: function () {
let usrlst = $fis.frm_list(true, true).ldng(1), usrfrm = $$.dc('usrfrm', usrlst);
$usr.eM(true);
$ocms.postXT({
url: $ocms.url('usr/list'), success: function (r) {
usrfrm.empty();
let grid = $$.dc('usr_grid', usrfrm);
let hdr = $$.dc('usr_row usr_row_hd', grid);
$$.dc('usr_col_avatar', hdr);
$$.dc('usr_col_name', hdr).text($uct.col_name);
$$.dc('usr_col_email', hdr).text($uct.col_email);
$$.dc('usr_col_groups', hdr).text($uct.col_groups);
$$.dc('usr_col_status', hdr).text($uct.col_status);
if ((r.users || []).length === 0) {
$$.dc('usr_empty', grid).text($uct.empty);
return;
}
$.each(r.users || [], function (ri, rw) { grid.append($usr.buildRow(rw)); });
}, error: function () {
usrfrm.empty();
$$.dc('usr_err', usrfrm).text($uct.loadfail);
}, complete: function () { usrlst.ldng(0); }
});
},
createDlg: function () {
$ocms.dlgform([
{ name: 'firstname', label: $uct.firstname_field, type: 'string', required: true },
{ name: 'name', label: $uct.name_field, type: 'string', required: true },
{ name: 'email', label: $uct.email_field, type: 'email', required: true },
{ name: 'title', label: $uct.title_field, type: 'string' },
{ name: 'gender', label: $uct.gender_field, type: 'select', url: [['', ''], ['m', $uct.gender_m], ['f', $uct.gender_f]] },
{ name: 'mobile', label: $uct.mobile_field, type: 'string' }
], {
title: $uct.newuser,
button: $uct.newuser_btn,
submit: function (e) {
let c = $(this).ldng(1);
let qs = c.serializeObject(true, { typedvalues: true });
$ocms.postXT({
url: $ocms.url('usr/create'), data: qs, success: function (response) {
alert(response.emailed === true ? $uct.create_sent : $uct.create_noemail);
c.trigger('modal_close');
}, error: function (jqXHR) {
let code = ((jqXHR || {}).responseJSON || {}).error;
alert($uct.err[code] || $uct.err_generic);
}, complete: function () { c.ldng(0); $usr.renderLst(); }
});
}
});
},
deleteUser: function (rw) {
let displayName = `${rw.firstname || ''} ${rw.name || ''}`.trim();
if (confirm(string($uct.delete_confirm, [displayName]))) {
$ocms.postXT({
url: $ocms.url('usr/delete'), data: { useraccount_id: rw.useraccount_id }, success: function () {
$usr.eM(true);
}, error: function (jqXHR) {
let code = ((jqXHR || {}).responseJSON || {}).error;
alert($uct.err[code] || $uct.err_generic);
}, complete: function () { $usr.renderLst(); }
});
}
},
pwdResetDlg: function (rw) {
$ocms.dlgform([
{ name: 'useraccount_id', type: 'hidden', value: rw.useraccount_id },
{ name: 'newpassword', label: $uct.pwd_field, type: 'string', note: $uct.pwd_hint }
], {
title: $uct.pwdreset,
button: $uct.pwdreset_btn,
submit: function (e) {
let c = $(this).ldng(1);
let qs = c.serializeObject(true, { typedvalues: true });
$ocms.postXT({
url: $ocms.url('usr/pwdreset'), data: qs, success: function (response) {
alert(response.emailed === true ? $uct.pwdreset_sent : $uct.pwdreset_noemail);
c.trigger('modal_close');
}, error: function (jqXHR) {
let code = ((jqXHR || {}).responseJSON || {}).error;
alert($uct.err[code] || $uct.err_generic);
}, complete: function () { c.ldng(0); $usr.renderLst(); }
});
}
});
},
emailDlg: function (rw) {
$ocms.dlgform([
{ name: 'useraccount_id', type: 'hidden', value: rw.useraccount_id },
{ name: 'email', label: $uct.email_field, type: 'email', required: true, value: rw.email }
], {
title: $uct.email,
submit: function (e) {
let c = $(this).ldng(1);
let qs = c.serializeObject(true, { typedvalues: true });
$ocms.postXT({
url: $ocms.url('usr/email'), data: qs, success: function () {
c.trigger('modal_close');
}, error: function (jqXHR) {
let code = ((jqXHR || {}).responseJSON || {}).error;
alert($uct.err[code] || $uct.err_generic);
}, complete: function () { c.ldng(0); $usr.refreshRow(rw); }
});
}
});
},
groupsDlg: function (rw) {
let f = $$.dc('usr_groups_dlg');
$ocms.postXT({
url: $ocms.url('usr/groups'), data: { useraccount_id: rw.useraccount_id }, success: function (r) {
$.each(r.groups || [], function (gi, g) {
let row = $$.dc('usr_group_row', f);
let cid = 'usrg' + g.usergroup_id;
let cb = $$.i({ type: 'checkbox', id: cid }).prop('checked', bool(g.member, false)).appendTo(row);
$$.lbl(g.name, { for: cid }).appendTo(row);
cb.change(function () {
let checked = cb.prop('checked');
$ocms.postXT({
url: $ocms.url('usr/groupset'),
data: { useraccount_id: rw.useraccount_id, usergroup_id: g.usergroup_id, member: checked },
success: function () { $usr.refreshRow(rw); }, /* keep the groups csv in the grid current per toggle */
error: function () {
cb.prop('checked', !checked);
alert($uct.err_generic);
}
});
});
});
}
});
$ocms.dlg(f, { title: string($uct.groups_title, [`${rw.firstname || ''} ${rw.name || ''}`.trim()]), size: [420, 380] });
}
};
let $$usr = { init2: $usr.init2, auth: {} };
export default $$usr;