feat(admin): open ticket creation in a dialog on the list page
Move "代客提交工单" back into a native <dialog> on the ticket list while the ticket workspace stays a full page. Closing asks before discarding input, ignores backdrop clicks, resets the form and returns focus; an uncertain create result keeps the form locked behind "重新加载". Remove the ?view=new create page route and its styles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d86a85e6a6
commit
54cc5301c3
9 files changed
+169
-65
No files matched your search
@@ -7,7 +7,7 @@ const test = require('node:test');
|
||||
const vm = require('node:vm');
|
||||
|
||||
const scriptPath = path.resolve(__dirname, '../../assets/js/admin-tickets.js');
|
||||
const strings = { saving: 'Saving', saveFailed: 'Save failed', uncertain: 'Unknown result', required: 'Required', tooLong: 'Too long' };
|
||||
const strings = { saving: 'Saving', saveFailed: 'Save failed', uncertain: 'Unknown result', required: 'Required', tooLong: 'Too long', discard: 'Discard?' };
|
||||
const REST = 'https://site.test/wp-json/aksrt/v1/admin/tickets';
|
||||
|
||||
function makeRuntime({ href = 'https://site.test/wp-admin/users.php?page=aksrt_tickets&id=41', page = 'detail', storage = {} } = {}) {
|
||||
@@ -161,12 +161,19 @@ function makeRuntime({ href = 'https://site.test/wp-admin/users.php?page=aksrt_t
|
||||
nodes.noteText = el('textarea', { id: 'aksrt-ticket-note-41', name: 'content', required: '', maxlength: '24', 'data-aksrt-ticket-editor': '' }, note);
|
||||
el('button', { type: 'submit' }, note);
|
||||
} else {
|
||||
const create = nodes.create = el('form', { action: REST, 'data-aksrt-ticket-form': 'create' }, root);
|
||||
nodes.opener = el('button', { type: 'button', 'data-aksrt-dialog-open': 'aksrt-ticket-create-dialog' }, root);
|
||||
const dialog = nodes.dialog = el('dialog', { id: 'aksrt-ticket-create-dialog' }, root);
|
||||
dialog.open = false;
|
||||
dialog.showModal = () => { dialog.open = true; };
|
||||
dialog.close = () => { if (!dialog.open) return; dialog.open = false; dialog.fire('close'); };
|
||||
const create = nodes.create = el('form', { action: REST, 'data-aksrt-ticket-form': 'create' }, dialog);
|
||||
nodes.closeButton = el('button', { type: 'button', 'data-aksrt-dialog-close': '' }, create);
|
||||
el('input', { type: 'number', name: 'user_id', required: '' }, create);
|
||||
select(create, { name: 'ticket_type_id', required: '' }, [[''], ['3']], '');
|
||||
el('input', { type: 'text', name: 'title', required: '' }, create);
|
||||
el('textarea', { id: 'aksrt-ticket-create-content', name: 'content', required: '', maxlength: '500', 'data-aksrt-ticket-editor': '' }, create);
|
||||
el('button', { type: 'submit' }, create);
|
||||
create.reset = () => create.querySelectorAll('input, select, textarea').forEach((control) => { control.value = ''; });
|
||||
}
|
||||
|
||||
document.querySelector = (selector) => (selector === '.aksrt-tickets-admin' ? root : null);
|
||||
@@ -179,6 +186,9 @@ function makeRuntime({ href = 'https://site.test/wp-admin/users.php?page=aksrt_t
|
||||
location: { href, assigned: null, reloads: 0, assign(url) { this.assigned = url; }, reload() { this.reloads += 1; } },
|
||||
history: { state: null, replaceState(_state, _title, url) { window.location.href = url; } },
|
||||
sessionStorage: { getItem: (key) => store.get(key) ?? null, setItem: (key, value) => store.set(key, String(value)), removeItem: (key) => store.delete(key) },
|
||||
confirmAnswer: false,
|
||||
confirmCalls: [],
|
||||
confirm(message) { this.confirmCalls.push(message); return this.confirmAnswer; },
|
||||
});
|
||||
|
||||
const fetchCalls = [];
|
||||
@@ -232,8 +242,38 @@ test('after the reload, stashed drafts are restored once and the result code is
|
||||
assert.equal(unload(rt).defaultPrevented, true, 'a restored draft still counts as unsaved');
|
||||
});
|
||||
|
||||
test('create dialog opens from the header button, guards dirty close, resets and returns focus', () => {
|
||||
const rt = makeRuntime({ page: 'create', href: 'https://site.test/wp-admin/users.php?page=aksrt_tickets' });
|
||||
rt.opener.isConnected = true;
|
||||
rt.root.fire('click', { target: rt.opener });
|
||||
assert.equal(rt.dialog.open, true);
|
||||
|
||||
const title = rt.create.querySelector('[name="title"]');
|
||||
type(rt, title, 'Unsaved');
|
||||
rt.root.fire('click', { target: rt.closeButton });
|
||||
assert.equal(rt.dialog.open, true, 'declined discard keeps the dialog open');
|
||||
assert.deepEqual(rt.window.confirmCalls, [strings.discard]);
|
||||
|
||||
const esc = rt.dialog.fire('cancel');
|
||||
assert.equal(esc.defaultPrevented, true, 'Esc goes through the same guard');
|
||||
assert.equal(rt.dialog.open, true);
|
||||
|
||||
rt.window.confirmAnswer = true;
|
||||
rt.root.fire('click', { target: rt.closeButton });
|
||||
assert.equal(rt.dialog.open, false);
|
||||
assert.equal(title.value, '', 'closing resets the form');
|
||||
assert.equal(rt.document.activeElement, rt.opener);
|
||||
|
||||
rt.window.confirmCalls.length = 0;
|
||||
rt.root.fire('click', { target: rt.opener });
|
||||
rt.root.fire('click', { target: rt.closeButton });
|
||||
assert.equal(rt.dialog.open, false, 'a clean dialog closes without asking');
|
||||
assert.equal(rt.window.confirmCalls.length, 0);
|
||||
});
|
||||
|
||||
test('create posts to the collection route and opens the new ticket workspace with the list context kept', async () => {
|
||||
const rt = makeRuntime({ page: 'create', href: 'https://site.test/wp-admin/users.php?page=aksrt_tickets&view=new&status=pending&paged=2' });
|
||||
const rt = makeRuntime({ page: 'create', href: 'https://site.test/wp-admin/users.php?page=aksrt_tickets&status=pending&paged=2' });
|
||||
rt.root.fire('click', { target: rt.opener });
|
||||
const form = rt.create;
|
||||
form.querySelector('[name="user_id"]').value = '12';
|
||||
form.querySelector('[name="ticket_type_id"]').value = '3';
|
||||
|
||||
Reference in new issue
Block a user