Files
wordpress-theme-aksrt/assets/js/admin-user-auth.js
T
kate522andClaude Fable 5 bc26d62602 feat(admin): redesign identity review with dialogs
Replace inline identity review forms with focused detail and review dialogs, improve list filters and caching, and align evidence permissions and deletion feedback. Add contract and runtime regression coverage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-30 20:05:21 +08:00

256 lines
8.4 KiB
JavaScript

/** 认证记录审核详情与删除确认。仅在认证 native 后台页入队。 */
(function () {
'use strict';
var root = document.querySelector('.aksrt-verify-page');
var dialog = document.getElementById('aksrt-verify-dialog');
if (!root || !dialog || typeof dialog.showModal !== 'function') return;
var opener = null;
var activeForm = null;
var submitting = false;
var pointerStartedOutside = false;
function prevent(event) {
if (event && typeof event.preventDefault === 'function') event.preventDefault();
}
function valueOf(element, name, fallback) {
var value = element ? element.getAttribute(name) : null;
return value === null ? (fallback || '') : value;
}
function lockScroll() {
document.documentElement.classList.toggle('aksrt-verify-modal-open', !!dialog.open);
}
function closestAttribute(target, attribute, boundary) {
var node = target;
while (node) {
if (node.nodeType === 1 && node.hasAttribute(attribute)) return node;
if (node === boundary) break;
node = node.parentElement;
}
return null;
}
function isOutside(event) {
var rect = dialog.getBoundingClientRect();
return event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom;
}
function selectedStatus(form) {
var radios = form.querySelectorAll('input[name="status"]');
for (var index = 0; index < radios.length; index += 1) {
if (radios[index].checked) return radios[index].value;
}
return '';
}
function getProfileName(profile) {
return profile ? profile.querySelector('input[name="name"]') : null;
}
function setTrimValidity(field, active) {
if (!field || typeof field.setCustomValidity !== 'function') return;
if (!active || String(field.value || '').trim() !== '') {
field.setCustomValidity('');
return;
}
field.setCustomValidity(valueOf(field, 'data-required-error', '必填项不能为空'));
}
function syncReview(form) {
var decision = selectedStatus(form);
var profile = form.querySelector('[data-aksrt-verify-profile]');
var name = getProfileName(profile);
var note = form.querySelector('textarea[name="note"]');
var noteLabel = form.querySelector('[data-aksrt-verify-note-label]');
var submit = form.querySelector('[data-aksrt-verify-submit]');
var rejected = decision === 'rejected';
var approved = decision === 'verified';
if (profile) {
profile.hidden = !approved;
profile.disabled = !approved;
}
if (note) {
note.required = rejected;
setTrimValidity(note, rejected);
}
setTrimValidity(name, approved);
if (noteLabel) {
noteLabel.textContent = valueOf(noteLabel, rejected ? 'data-required' : 'data-optional');
}
if (submit) {
var idleLabel = valueOf(submit, 'data-idle-label');
submit.disabled = !decision;
submit.textContent = approved
? valueOf(submit, 'data-approve-label', idleLabel)
: rejected
? valueOf(submit, 'data-reject-label', idleLabel)
: idleLabel;
submit.classList.toggle('is-danger', rejected);
}
}
function resetReview(form) {
var radios = form.querySelectorAll('input[name="status"]');
radios.forEach(function (radio) { radio.checked = false; });
var note = form.querySelector('textarea[name="note"]');
if (note) note.value = '';
syncReview(form);
}
function actionButtons() {
var buttons = [];
var submitButtons = dialog.querySelectorAll('[data-aksrt-verify-submit]');
var closeButtons = dialog.querySelectorAll('[data-aksrt-dialog-close]');
submitButtons.forEach(function (button) { buttons.push(button); });
closeButtons.forEach(function (button) {
if (buttons.indexOf(button) === -1) buttons.push(button);
});
return buttons;
}
function setActionsDisabled(disabled) {
actionButtons().forEach(function (button) { button.disabled = disabled; });
}
function setBusyLabel(form) {
var submit = form.querySelector('[data-aksrt-verify-submit]');
if (submit) submit.textContent = valueOf(submit, 'data-busy-label', submit.textContent);
}
function syncReviewIfIdle(form) {
if (!submitting && form.dataset.submitting !== '1') syncReview(form);
}
function setupForm(form) {
var review = form.hasAttribute('data-aksrt-verify-review');
var deletion = form.hasAttribute('data-aksrt-verify-delete-form');
var submit = form.querySelector('[data-aksrt-verify-submit]');
if (!review && !deletion) return;
if (review) {
resetReview(form);
form.addEventListener('change', function () { syncReviewIfIdle(form); });
form.addEventListener('input', function () { syncReviewIfIdle(form); });
} else if (submit) {
submit.disabled = false;
submit.textContent = valueOf(submit, 'data-idle-label', submit.textContent);
submit.classList.add('is-danger');
}
form.addEventListener('submit', function (event) {
if (submitting || form.dataset.submitting === '1') {
prevent(event);
return;
}
if (review) syncReview(form);
var valid = typeof form.reportValidity !== 'function' || form.reportValidity();
if (!valid || (review && !selectedStatus(form))) {
prevent(event);
return;
}
submitting = true;
form.dataset.submitting = '1';
setActionsDisabled(true);
setBusyLabel(form);
lockScroll();
});
}
function openTemplate(button, prefix) {
if (submitting || dialog.open) return;
var id = button.getAttribute(prefix);
if (id === null || id === '') return;
var template = document.getElementById('aksrt-verify-' + (prefix === 'data-aksrt-verify-view' ? 'detail-' : 'delete-') + id);
if (!template || !template.content || typeof template.content.cloneNode !== 'function') return;
pointerStartedOutside = false;
dialog.replaceChildren(template.content.cloneNode(true));
opener = button;
activeForm = dialog.querySelector('form[data-aksrt-verify-review]')
|| dialog.querySelector('form[data-aksrt-verify-delete-form]');
if (activeForm) setupForm(activeForm);
dialog.showModal();
lockScroll();
}
root.querySelectorAll('[data-aksrt-verify-view]').forEach(function (button) {
button.addEventListener('click', function (event) {
prevent(event);
openTemplate(button, 'data-aksrt-verify-view');
});
});
root.querySelectorAll('[data-aksrt-verify-delete]').forEach(function (button) {
button.addEventListener('click', function (event) {
prevent(event);
openTemplate(button, 'data-aksrt-verify-delete');
});
});
dialog.addEventListener('click', function (event) {
var closeButton = closestAttribute(event.target, 'data-aksrt-dialog-close', dialog);
if (closeButton) {
prevent(event);
if (!submitting) dialog.close();
pointerStartedOutside = false;
return;
}
if (pointerStartedOutside && event.target === dialog && isOutside(event) && !submitting) dialog.close();
pointerStartedOutside = false;
});
dialog.addEventListener('pointerdown', function (event) {
pointerStartedOutside = event.target === dialog && isOutside(event);
});
dialog.addEventListener('cancel', function (event) {
prevent(event);
if (!submitting) dialog.close();
});
dialog.addEventListener('keydown', function (event) {
if (event.key === 'Escape' && submitting) {
prevent(event);
if (typeof event.stopPropagation === 'function') event.stopPropagation();
}
});
dialog.addEventListener('close', function () {
dialog.replaceChildren();
activeForm = null;
submitting = false;
pointerStartedOutside = false;
lockScroll();
var previousOpener = opener;
opener = null;
if (previousOpener && previousOpener.isConnected) previousOpener.focus();
});
window.addEventListener('pageshow', function () {
submitting = false;
if (activeForm) {
delete activeForm.dataset.submitting;
setActionsDisabled(false);
if (activeForm.hasAttribute('data-aksrt-verify-review')) {
syncReview(activeForm);
} else {
var submit = activeForm.querySelector('[data-aksrt-verify-submit]');
if (submit) {
submit.disabled = false;
submit.textContent = valueOf(submit, 'data-idle-label', submit.textContent);
submit.classList.add('is-danger');
}
}
}
lockScroll();
});
})();