feat(admin): dock ticket reply composer in conversation and move notes to a dialog
Put the reply composer at the bottom of the conversation card so the timeline and composer form one viewport-height panel; the send button is visible on load and Ctrl/Cmd+Enter submits. Internal notes now open in a dialog instead of expanding inline, so the sidebar no longer grows. Remove the shared ticket_composer helper and the inline note expansion CSS; both dialogs share ticket_dialog_head. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
54cc5301c3
commit
b9000e1b93
8 files changed
+178
-73
No files matched your search
@@ -44,7 +44,18 @@ test('workspace loads by ID independent of list filters and keeps the list conte
|
||||
assert.match(closed, /aksrt-ticket-detail__closed/);
|
||||
assert.match(closed, /data-aksrt-ticket-form="note"/);
|
||||
assert.doesNotMatch(closed, /data-aksrt-ticket-form="(?:reply|status)"/);
|
||||
for (const html of [open, closed]) assert.doesNotMatch(html, /<dialog|data-aksrt-dialog-close/);
|
||||
// 回复框是会话卡片的底部,不是独立卡片;工作区唯一的弹窗是内部备注。
|
||||
const conversation = open.match(/<section class="aksrt-ticket-conversation"[^]*?<\/section>/)?.[0] || '';
|
||||
assert.match(conversation, /aksrt-ticket-conversation__scroll[^]*data-aksrt-ticket-form="reply"/);
|
||||
assert.match(conversation, /data-aksrt-ticket-send-shortcut/);
|
||||
assert.match(conversation, /<button type="submit" class="button button-primary">[^]*发送回复<\/button>/);
|
||||
for (const html of [open, closed]) {
|
||||
assert.equal((html.match(/<dialog /g) || []).length, 1);
|
||||
assert.match(html, /<dialog id="aksrt-ticket-note-dialog"[^]*data-aksrt-ticket-form="note"[^]*<\/dialog>/);
|
||||
assert.match(html, /data-aksrt-dialog-open="aksrt-ticket-note-dialog"/);
|
||||
}
|
||||
const sidebar = open.match(/<aside\b[^]*?<\/aside>/)?.[0] || '';
|
||||
assert.doesNotMatch(sidebar, /<textarea|data-aksrt-ticket-form="note"/, 'note input lives only in the dialog');
|
||||
});
|
||||
|
||||
test('result codes render as one native notice and unknown codes are ignored', () => {
|
||||
|
||||
@@ -133,6 +133,14 @@ function makeRuntime({ href = 'https://site.test/wp-admin/users.php?page=aksrt_t
|
||||
return node;
|
||||
};
|
||||
|
||||
function makeDialog(id) {
|
||||
const dialog = el('dialog', { id, class: 'aksrt-ticket-dialog' }, root);
|
||||
dialog.open = false;
|
||||
dialog.showModal = () => { dialog.open = true; };
|
||||
dialog.close = () => { if (!dialog.open) return; dialog.open = false; dialog.fire('close'); };
|
||||
return dialog;
|
||||
}
|
||||
|
||||
const root = new Element('div', { class: 'aksrt-tickets-admin' });
|
||||
const feedback = el('div', { 'data-aksrt-ticket-feedback': '', hidden: '' }, root);
|
||||
el('p', {}, feedback);
|
||||
@@ -157,15 +165,21 @@ function makeRuntime({ href = 'https://site.test/wp-admin/users.php?page=aksrt_t
|
||||
nodes.warning = el('div', { 'data-aksrt-ticket-close-warning': '', hidden: '' }, status);
|
||||
nodes.confirm = el('input', { type: 'checkbox', 'data-aksrt-ticket-close-confirm': '' }, nodes.warning);
|
||||
nodes.statusSubmit = el('button', { type: 'submit' }, status);
|
||||
const note = nodes.note = el('form', { action: `${REST}/41/notes`, 'data-aksrt-ticket-form': 'note' }, detail);
|
||||
nodes.noteText = el('textarea', { id: 'aksrt-ticket-note-41', name: 'content', required: '', maxlength: '24', 'data-aksrt-ticket-editor': '' }, note);
|
||||
nodes.noteOpener = el('button', { type: 'button', 'data-aksrt-dialog-open': 'aksrt-ticket-note-dialog' }, detail);
|
||||
const noteDialog = nodes.noteDialog = makeDialog('aksrt-ticket-note-dialog');
|
||||
const note = nodes.note = el('form', { action: `${REST}/41/notes`, 'data-aksrt-ticket-form': 'note' }, noteDialog);
|
||||
nodes.noteClose = el('button', { type: 'button', 'data-aksrt-dialog-close': '' }, note);
|
||||
nodes.noteFeedback = el('div', { 'data-aksrt-ticket-feedback': '', hidden: '' }, note);
|
||||
el('p', {}, nodes.noteFeedback);
|
||||
el('button', { type: 'button', 'data-aksrt-ticket-reload': '', hidden: '' }, nodes.noteFeedback);
|
||||
nodes.noteText = el('textarea', { id: 'aksrt-ticket-note-41', name: 'content', required: '', autofocus: '', maxlength: '24', 'data-aksrt-ticket-editor': '' }, note);
|
||||
el('button', { type: 'submit' }, note);
|
||||
note.reset = () => { nodes.noteText.value = ''; };
|
||||
nodes.replyText.attrs['data-aksrt-ticket-send-shortcut'] = '';
|
||||
reply.requestSubmit = () => reply.fire('submit') && root.fire('submit', { target: reply });
|
||||
} else {
|
||||
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 dialog = nodes.dialog = makeDialog('aksrt-ticket-create-dialog');
|
||||
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);
|
||||
|
||||
Reference in new issue
Block a user