Playwright Tests / test (push) Canceled after 0s
Wires the previously-unused fis_admin_* user/group stored procs into the Admin module (fds_sys-gated), adds a users grid with a per-row ribbon (reset password, change email, manage group membership), and always emails a password reset result to the user rather than showing it to the admin. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
357 lines
16 KiB
JavaScript
357 lines
16 KiB
JavaScript
let $adm = {
|
|
init2: function () {
|
|
// No external libraries needed — go straight to render.
|
|
$ocms.getScript([], function () { $adm.init3(); });
|
|
},
|
|
init3: function () {
|
|
$fis.cf(true);
|
|
$fis.lf(true);
|
|
$('#activemodule').text($adt.mdl);
|
|
$adm.topbar();
|
|
let frm = $$.dc('admfrm', $('#contentframe')).ldng(1);
|
|
$adm.load(frm);
|
|
},
|
|
topbar: function () {
|
|
return $('#topbar').ocmsmenu([
|
|
{ lbl: $adt.refreshAll, id: 'adm_refresh_all', ico: 'glyphicon glyphicon-refresh', fnc: function () { $adm.init3(); } }
|
|
]);
|
|
},
|
|
/* Per-row ribbon shown while a user row is selected (mirrors the Banking module's iMn/eM swap). */
|
|
uMn: function (rw) {
|
|
return $('#topbar').ocmsmenu([
|
|
{ lbl: $adt.u_pwdreset, fnc: function () { $adm.pwdResetDlg(rw); } },
|
|
{ lbl: $adt.u_email, fnc: function () { $adm.emailDlg(rw); } },
|
|
{ lbl: $adt.u_groups, fnc: function () { $adm.groupsDlg(rw); } }
|
|
]);
|
|
},
|
|
load: function (frm) {
|
|
$ocms.postXT({
|
|
url: $ocms.url('admin/status'), success: function (r) {
|
|
frm.empty();
|
|
$adm.render(frm, r.info || {}, r.probes || []);
|
|
}, error: function () {
|
|
frm.empty();
|
|
$$.dc('adm_err', frm).text($adt.loadfail);
|
|
}, complete: function () { frm.ldng(0); }
|
|
});
|
|
},
|
|
render: function (frm, info, probes) {
|
|
let pmap = {};
|
|
$.each(probes, function (i, p) { pmap[p.component] = p; });
|
|
|
|
$$.dc('adm_sec', frm).text($adt.sub_info);
|
|
$adm.sysCard(frm, info);
|
|
$adm.startupCard(frm, info.startupChecks);
|
|
|
|
$$.dc('adm_sec', frm).text($adt.sub_status);
|
|
let grid = $$.dc('adm_grid', frm);
|
|
$adm.dbCard(grid, info.database || {}, pmap.database);
|
|
$adm.mailCard(grid, info.email || {});
|
|
$adm.blobCard(grid, info.blob || {}, pmap.blob);
|
|
$adm.mfrCard(grid, info.mfr || {}, pmap.mfr);
|
|
$adm.kvCard(grid, info.keyVault || {}, pmap.keyvault);
|
|
|
|
$$.dc('adm_sec', frm).text($adt.sub_users);
|
|
$adm.usersCard(frm);
|
|
},
|
|
|
|
/* ── user management ─────────────────────────────────────────────────── */
|
|
usersCard: function (frm) {
|
|
let card = $$.dc('adm_card adm_card_wide', frm);
|
|
let hd = $$.dc('adm_card_hd', card);
|
|
$$.s($adt.u_title).appendTo(hd);
|
|
let bd = $$.dc('adm_card_bd adm_users_bd', card);
|
|
$adm.renderUsers(bd);
|
|
},
|
|
renderUsers: function (bd) {
|
|
bd.empty();
|
|
let ts = $$.tblset({}, bd);
|
|
let thr = $$.tr(ts.hd);
|
|
$.each([$adt.u_col_name, $adt.u_col_email, $adt.u_col_status], function (i, lbl) {
|
|
$$.th(thr).text(lbl);
|
|
});
|
|
$ocms.postXT({
|
|
url: $ocms.url('admin/users'), success: function (r) {
|
|
$.each(r.users || [], function (ri, rw) {
|
|
let tr = $$.tr(ts.bdy, { id: 'adu' + rw.useraccount_id });
|
|
$$.td(tr).text(`${rw.firstname || ''} ${rw.name || ''}`.trim());
|
|
$$.td(tr).text(rw.email || '');
|
|
$$.td(tr).text(bool(rw.disabled, false) === true ? $adt.u_disabled : $adt.u_active);
|
|
tr.click(function () {
|
|
tr.toggleClass('selected').siblings().rC('selected');
|
|
if (tr.is('.selected') === true) { $adm.uMn(rw); } else { $adm.topbar(); }
|
|
});
|
|
});
|
|
}, error: function () {
|
|
$$.dc('adm_err', bd).text($adt.loadfail);
|
|
}
|
|
});
|
|
},
|
|
pwdResetDlg: function (rw) {
|
|
$ocms.dlgform([
|
|
{ name: 'useraccount_id', type: 'hidden', value: rw.useraccount_id },
|
|
{ name: 'newpassword', label: $adt.u_pwd_field, type: 'string', note: $adt.u_pwd_hint }
|
|
], {
|
|
title: $adt.u_pwdreset,
|
|
button: $adt.u_pwdreset_btn,
|
|
submit: function (e) {
|
|
let c = $(this).ldng(1);
|
|
let qs = c.serializeObject(true, { typedvalues: true });
|
|
$ocms.postXT({
|
|
url: $ocms.url('admin/userpwdreset'), data: qs, success: function (response) {
|
|
alert(response.emailed === true ? $adt.u_pwdreset_sent : $adt.u_pwdreset_noemail);
|
|
c.trigger('modal_close');
|
|
}, error: function (jqXHR) {
|
|
let code = ((jqXHR || {}).responseJSON || {}).error;
|
|
alert($adt.u_err[code] || $adt.u_err_generic);
|
|
}, complete: function () { c.ldng(0); $adm.renderUsers($('.adm_users_bd')); $adm.topbar(); }
|
|
});
|
|
}
|
|
});
|
|
},
|
|
emailDlg: function (rw) {
|
|
$ocms.dlgform([
|
|
{ name: 'useraccount_id', type: 'hidden', value: rw.useraccount_id },
|
|
{ name: 'email', label: $adt.u_email_field, type: 'email', required: true, value: rw.email }
|
|
], {
|
|
title: $adt.u_email,
|
|
submit: function (e) {
|
|
let c = $(this).ldng(1);
|
|
let qs = c.serializeObject(true, { typedvalues: true });
|
|
$ocms.postXT({
|
|
url: $ocms.url('admin/useremail'), data: qs, success: function () {
|
|
c.trigger('modal_close');
|
|
}, error: function (jqXHR) {
|
|
let code = ((jqXHR || {}).responseJSON || {}).error;
|
|
alert($adt.u_err[code] || $adt.u_err_generic);
|
|
}, complete: function () { c.ldng(0); $adm.renderUsers($('.adm_users_bd')); $adm.topbar(); }
|
|
});
|
|
}
|
|
});
|
|
},
|
|
groupsDlg: function (rw) {
|
|
let f = $$.dc('adm_groups_dlg');
|
|
$ocms.postXT({
|
|
url: $ocms.url('admin/usergroups'), data: { useraccount_id: rw.useraccount_id }, success: function (r) {
|
|
$.each(r.groups || [], function (gi, g) {
|
|
let row = $$.dc('adm_group_row', f);
|
|
let cid = 'adg' + 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('admin/usergroupset'),
|
|
data: { useraccount_id: rw.useraccount_id, usergroup_id: g.usergroup_id, member: checked },
|
|
error: function () {
|
|
cb.prop('checked', !checked);
|
|
alert($adt.u_err_generic);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
});
|
|
$ocms.dlg(f, { title: string($adt.u_groups_title, [`${rw.firstname || ''} ${rw.name || ''}`.trim()]), size: [420, 380] });
|
|
},
|
|
|
|
/* ── generic building blocks ─────────────────────────────────────────── */
|
|
kvTable: function (parent, rows) {
|
|
let t = $$.tbl().addClass('adm_kv');
|
|
$.each(rows || [], function (i, r) {
|
|
if (!r) { return true; }
|
|
let tr = $$.tr(t);
|
|
$$.td(tr).addClass('k').text(r[0] == null ? '' : String(r[0]));
|
|
let v = $$.td(tr).addClass('v');
|
|
if (r[1] instanceof jQuery) { v.append(r[1]); } else { v.text(r[1] == null ? '' : String(r[1])); }
|
|
});
|
|
if (parent instanceof jQuery) { parent.append(t); }
|
|
return t;
|
|
},
|
|
yesNo: function (b) {
|
|
return $$.sc('adm_bool adm_bool_' + (b ? 'y' : 'n'), b ? $adt.yes : $adt.no);
|
|
},
|
|
setPill: function (pill, probe) {
|
|
probe = probe || {};
|
|
let st = probe.status || 'unconfigured';
|
|
let lbl = {
|
|
ok: $adt.st_ok, error: $adt.st_error, disabled: $adt.st_disabled,
|
|
unconfigured: $adt.st_unconfigured, checking: $adt.st_checking
|
|
}[st] || st;
|
|
pill.attr('class', 'adm_pill adm_pill_' + st).text(lbl);
|
|
return pill;
|
|
},
|
|
|
|
/* Connectivity card with a live status pill + per-card refresh button. */
|
|
probeCard: function (grid, title, component, cfgRows, probe) {
|
|
let card = $$.dc('adm_card', grid);
|
|
let hd = $$.dc('adm_card_hd', card);
|
|
$$.s(title).appendTo(hd);
|
|
let pill = $$.sc('adm_pill').appendTo(hd);
|
|
let rbtn = $$.bbtn($adt.refresh, 'adm_btn').appendTo(hd);
|
|
let bd = $$.dc('adm_card_bd', card);
|
|
$adm.kvTable(bd, cfgRows);
|
|
|
|
let pf = $$.dc('adm_probe', bd);
|
|
let msg = $$.dc('adm_probe_msg', pf);
|
|
let met = $$.dc('adm_probe_metrics', pf);
|
|
let det = $$.dc('adm_probe_det', pf);
|
|
let dur = $$.dc('adm_probe_dur', pf);
|
|
let apply = function (p) {
|
|
p = p || {};
|
|
$adm.setPill(pill, p);
|
|
msg.text(p.message || '');
|
|
met.empty();
|
|
$.each(p.metrics || [], function (i, m) {
|
|
let row = $$.dc('adm_metric', met);
|
|
$$.sc('adm_metric_k').text(m.label || '').appendTo(row);
|
|
$$.sc('adm_metric_v').text(m.value == null ? '' : String(m.value)).appendTo(row);
|
|
});
|
|
det.text(p.detail || '');
|
|
dur.text(p.durationMs != null ? (p.durationMs + ' ms') : '');
|
|
};
|
|
apply(probe);
|
|
|
|
rbtn.click(function () {
|
|
$adm.setPill(pill, { status: 'checking' });
|
|
msg.text(''); met.empty(); det.text(''); dur.text('');
|
|
$ocms.postXT({
|
|
url: $ocms.url('admin/probe/' + component), success: function (r) {
|
|
apply(r.probe);
|
|
}, error: function () {
|
|
$adm.setPill(pill, { status: 'error' });
|
|
msg.text($adt.st_error);
|
|
}
|
|
});
|
|
});
|
|
return card;
|
|
},
|
|
|
|
/* ── individual cards ────────────────────────────────────────────────── */
|
|
sysCard: function (frm, info) {
|
|
let card = $$.dc('adm_card adm_card_wide', frm);
|
|
let hd = $$.dc('adm_card_hd', card);
|
|
$$.s($adt.host).appendTo(hd);
|
|
let env = $$.sc('adm_env').text(info.environment || '');
|
|
if (info.isTestDeployment === true) { env.aC('adm_env_test'); }
|
|
env.appendTo(hd);
|
|
let bd = $$.dc('adm_card_bd', card);
|
|
$adm.kvTable(bd, [
|
|
[$adt.machine, info.machineName],
|
|
[$adt.os, info.osDescription],
|
|
[$adt.framework, info.frameworkDescription],
|
|
[$adt.env, info.environment],
|
|
[$adt.testdep, $adm.yesNo(info.isTestDeployment === true)],
|
|
[$adt.pid, info.processId],
|
|
[$adt.since, info.processStartUtc ? fdt(info.processStartUtc) : ''],
|
|
[$adt.uptime, (info.uptimeHours != null ? (info.uptimeHours + ' ' + $adt.hours) : '')]
|
|
]);
|
|
},
|
|
/* Non-refreshable widget: the one-shot startup self-test result captured at boot. */
|
|
startupCard: function (frm, sc) {
|
|
let card = $$.dc('adm_card adm_card_wide', frm);
|
|
let hd = $$.dc('adm_card_hd', card);
|
|
$$.s($adt.startup).appendTo(hd);
|
|
let bd = $$.dc('adm_card_bd', card);
|
|
|
|
if (!sc || sc.ran !== true) {
|
|
$adm.setPill($$.sc('adm_pill').appendTo(hd), { status: 'disabled' });
|
|
$$.dc('adm_note', bd).text($adt.startup_notrun);
|
|
return;
|
|
}
|
|
|
|
let items = sc.items || [];
|
|
let anyFail = false;
|
|
$.each(items, function (i, it) { if (it.enabled === true && it.ok !== true) { anyFail = true; } });
|
|
$adm.setPill($$.sc('adm_pill').appendTo(hd), { status: anyFail ? 'error' : 'ok' });
|
|
|
|
$adm.kvTable(bd, [
|
|
[$adt.startup_on, sc.machineName],
|
|
[$adt.startup_at, sc.completedUtc ? fdt(sc.completedUtc) : '']
|
|
]);
|
|
|
|
let list = $$.dc('adm_checks', bd);
|
|
$.each(items, function (i, it) {
|
|
let row = $$.dc('adm_check', list);
|
|
$$.sc('adm_check_k').text($adt['chk_' + it.name] || it.name).appendTo(row);
|
|
let stt = it.enabled !== true ? 'skipped' : (it.ok === true ? 'ok' : 'fail');
|
|
let lbl = { ok: $adt.chk_ok, fail: $adt.chk_fail, skipped: $adt.chk_skipped }[stt];
|
|
$$.sc('adm_check_v adm_check_' + stt).text(lbl).appendTo(row);
|
|
});
|
|
},
|
|
dbCard: function (grid, d, probe) {
|
|
$adm.probeCard(grid, $adt.db, 'database', [
|
|
[$adt.configured, $adm.yesNo(d.configured === true)],
|
|
[$adt.server, d.server],
|
|
[$adt.catalog, d.catalog],
|
|
[$adt.login, d.userId]
|
|
], probe);
|
|
},
|
|
mailCard: function (grid, e) {
|
|
let card = $$.dc('adm_card', grid);
|
|
let hd = $$.dc('adm_card_hd', card);
|
|
$$.s($adt.mail).appendTo(hd);
|
|
$adm.setPill($$.sc('adm_pill').appendTo(hd), { status: e.mailerEnabled === true ? 'ok' : 'disabled' });
|
|
let bd = $$.dc('adm_card_bd', card);
|
|
let rows = [
|
|
[$adt.mailer, $adm.yesNo(e.mailerEnabled === true)],
|
|
[$adt.baseurl, e.baseUrl],
|
|
[$adt.account, e.accountId],
|
|
[$adt.serverid, e.serverId],
|
|
[$adt.token, $adm.yesNo(e.tokenConfigured === true)],
|
|
[$adt.overrideActive, $adm.yesNo(e.overrideActive === true)]
|
|
];
|
|
if (e.overrideActive === true) { rows.push([$adt.override, e.overrideRecipient]); }
|
|
$adm.kvTable(bd, rows);
|
|
let ft = $$.dc('adm_card_ft', card);
|
|
$$.bbtn($adt.testmail_btn, 'adm_btn adm_btn_primary').appendTo(ft).click($adm.testMailDlg);
|
|
},
|
|
blobCard: function (grid, b, probe) {
|
|
$adm.probeCard(grid, $adt.blob, 'blob', [
|
|
[$adt.enabled, $adm.yesNo(b.enabled === true)],
|
|
[$adt.configured, $adm.yesNo(b.configured === true)],
|
|
[$adt.invContainer, b.invoiceContainer],
|
|
[$adt.remContainer, b.reminderContainer]
|
|
], probe);
|
|
},
|
|
mfrCard: function (grid, m, probe) {
|
|
$adm.probeCard(grid, $adt.mfr, 'mfr', [
|
|
[$adt.host_l, m.host],
|
|
[$adt.creds, $adm.yesNo(m.credentialsConfigured === true)],
|
|
[$adt.sync, $adm.yesNo(m.syncEnabled === true)]
|
|
], probe);
|
|
},
|
|
kvCard: function (grid, k, probe) {
|
|
$adm.probeCard(grid, $adt.kv, 'keyvault', [
|
|
[$adt.vault, k.vaultUri],
|
|
[$adt.appname, k.appName],
|
|
[$adt.seccount, k.managedSecretCount],
|
|
[$adt.clientreg, $adm.yesNo(k.clientRegistered === true)]
|
|
], probe);
|
|
},
|
|
|
|
/* ── test email ──────────────────────────────────────────────────────── */
|
|
testMailDlg: function () {
|
|
$ocms.dlgform([
|
|
{ name: 'to', label: $adt.tm_to, type: 'email', required: true, value: ($ocms.auth.email || '') },
|
|
{ name: 'subject', label: $adt.tm_subject, type: 'string', required: true, value: $adt.tm_default_subject },
|
|
{ name: 'body', label: $adt.tm_body, type: 'text', required: true, value: $adt.tm_default_body }
|
|
], {
|
|
title: $adt.testmail,
|
|
button: $adt.testmail_btn,
|
|
size: [420, 560],
|
|
addcontent: $$.dc('adm_note').text($adt.tm_hint),
|
|
url: $ocms.url('admin/testmail'),
|
|
typedvalues: true,
|
|
/* Runs inside jQuery's success dispatch — it must never throw, or postXT's
|
|
`complete` (which clears the page loading indicator) is skipped. */
|
|
success: function (r) {
|
|
let res = (r && r.result) || {};
|
|
alert(res.message || (res.sent === true ? $adt.tm_sent : $adt.tm_failed));
|
|
}
|
|
});
|
|
}
|
|
};
|
|
let $$adm = { init2: $adm.init2, auth: {} };
|
|
export default $$adm;
|