feat(ticket): redesign native ticket admin page UI/UX

- Fix empty status/department selects in the detail panel (printf leak)
- Replace dead 代客提交 button with a working collapsible create form
- Error-aware notices; redirect keeps filters and the open ticket
- Status views with counts, incl. 未关闭 default and 已关闭 view
- Two-column detail: conversation + attachments + prereplies | info,
  status/department/assignee select, internal notes; closed = read-only
- Split detail panel into UsersMenusTicketsDetail trait; page-scoped
  tickets.css / admin-tickets.js replace inline styles
- AdminListPagination accepts parent file so users.php keeps menu highlight
- Drop native loading="lazy" per lazyload contract

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Fable 5 committed 2026-10-27 14:04:48 +08:00
1 parent b927b9ec7c
commit b74381cfa2
9 files changed
+885 -278

No files matched your search

@@ -42,16 +42,21 @@ test('write actions are nonce-protected and dispatched by action name', () => {
assert.ok(center.includes(`case '${action}':`), `dispatch case ${action} missing`);
}
// nonce 以 action + id 为单位,行内表单各自带自己的 nonce(渲染在 List trait 中)。
// nonce 以 action + id 为单位,行内表单各自带自己的 nonce:
// 代客提交在 List trait,回复 / 状态 / 备注在 Detail trait。
// 删除类操作已迁至 SPA(n-dialog 二次确认),本页不再有 onsubmit=confirm 的小表单。
const list = read('src/Admin/Native/UsersMenusTicketsList.php');
const detail = read('src/Admin/Native/UsersMenusTicketsDetail.php');
assert.ok(list.includes("wp_nonce_field('aksrt_ticket_create_0')"), 'create nonce missing');
for (const hook of [
'aksrt_ticket_reply_',
'aksrt_ticket_status_',
'aksrt_ticket_note_',
]) {
assert.ok(list.includes(hook), `nonce prefix ${hook} missing`);
assert.ok(detail.includes(hook), `nonce prefix ${hook} missing`);
}
assert.ok(center.includes('use UsersMenusTicketsDetail;'), 'detail trait not composed');
assert.ok(!detail.includes('ticket_tab'), 'ticket_tab hidden input must be gone');
// Tab 已移除,三个行内表单不再输出 ticket_tab 隐藏域。
assert.ok(!list.includes('ticket_tab'), 'ticket_tab hidden input must be gone');
assert.ok(!center.includes('onsubmit="return confirm('), 'config delete forms must be gone');
@@ -97,8 +102,11 @@ test('the ticket list paginates through the shared helper', () => {
});
test('detail panel renders replies, quote blocks and internal notes', () => {
const list = read('src/Admin/Native/UsersMenusTicketsList.php');
const body = list.slice(list.indexOf('private function render_ticket_detail_panel'));
const detail = read('src/Admin/Native/UsersMenusTicketsDetail.php');
const body = detail.slice(detail.indexOf('private function render_ticket_detail_panel'));
assert.ok(detail.includes('private function render_ticket_detail_panel'), 'detail renderer missing');
// 选项必须拼进返回值而不是直接 echo(历史 bug:printf 导致下拉框为空)。
assert.ok(!/\bprintf\(/.test(detail), 'detail panel must not echo via printf');
assert.match(body, /TicketNoteService::for_ticket/);
assert.match(body, /repo->replies\(\$id\)/);