54 lines
3.2 KiB
JavaScript
54 lines
3.2 KiB
JavaScript
// Ticketliste mit Filtern, Suche und „Mehr laden“.
|
|
import { h, clear, icon } from '../lib/dom.js';
|
|
import { apiCall } from '../lib/core.js';
|
|
import { spinner, ticketCard, showError } from '../lib/ui.js';
|
|
|
|
const OPEN = 'open,pending,answered,on_hold';
|
|
export const FILTERS = [
|
|
['mine', 'Meine', { assigned: 'me', status: OPEN }],
|
|
['unassigned', 'Nicht zugewiesen', { assigned: 'unassigned', status: OPEN }],
|
|
['overdue', 'Überfällig', { sla: 'overdue', status: OPEN }],
|
|
['open_status', 'Unbeantwortet', { status: 'open' }],
|
|
['following', 'Beobachtet', { following: 1, status: OPEN }],
|
|
['snoozed', 'Wiedervorlage', { snoozed: 1, status: OPEN }],
|
|
['all', 'Alle offenen', { status: OPEN }],
|
|
['done', 'Gelöst/Geschlossen', { status: 'resolved,closed' }],
|
|
];
|
|
|
|
const memory = { filter: 'mine', q: '' };
|
|
|
|
export async function renderTickets(host, ctx, params = {}) {
|
|
if (params.filter) { memory.filter = params.filter; memory.q = ''; }
|
|
if (params.q !== undefined) memory.q = params.q;
|
|
let page = 1;
|
|
const results = h('div');
|
|
const input = h('input', { type: 'search', placeholder: 'Betreff, Nummer, Name, Text …', 'aria-label': 'Suche', maxlength: '100', value: memory.q });
|
|
const chips = h('div', { class: 'chips', role: 'group' });
|
|
|
|
const drawChips = () => chips.replaceChildren(...FILTERS.map(([key, label]) => h('button', { class: `chip${!memory.q && memory.filter === key ? ' on' : ''}`, type: 'button', onclick: () => { memory.filter = key; memory.q = ''; input.value = ''; drawChips(); load(true); } }, label)));
|
|
|
|
async function load(reset) {
|
|
if (reset) { page = 1; clear(results).append(spinner()); }
|
|
const base = FILTERS.find((f) => f[0] === memory.filter)?.[2] || {};
|
|
const query = memory.q ? { q: memory.q, status: 'open,pending,answered,on_hold,resolved,closed' } : base;
|
|
try {
|
|
const { data } = await apiCall('tickets.php', { query: { ...query, page, perPage: 25 } });
|
|
if (reset) clear(results);
|
|
else results.querySelector('.more')?.remove();
|
|
if (reset) results.append(h('div', { class: 'muted', style: { margin: '2px 2px 8px' } }, `${data.total} Ticket${data.total === 1 ? '' : 's'}`));
|
|
const list = results.querySelector('ul.tickets') || results.appendChild(h('ul', { class: 'tickets' }));
|
|
if (!data.items.length && reset) { list.remove(); results.append(h('div', { class: 'empty' }, memory.q ? 'Keine Treffer.' : 'Keine Tickets in dieser Ansicht.')); }
|
|
for (const t of data.items) list.append(ticketCard(t, (x) => ctx.go('ticket', { number: x.number })));
|
|
if (data.hasMore) results.append(h('div', { class: 'more actions', style: { justifyContent: 'center', margin: '10px 0' } }, h('button', { class: 'btn', onclick: () => { page += 1; load(false); } }, 'Mehr laden')));
|
|
} catch (e) { showError(ctx, results, e); }
|
|
}
|
|
|
|
host.append(h('div', { class: 'stack' },
|
|
h('form', { onsubmit: (ev) => { ev.preventDefault(); const q = input.value.trim(); memory.q = q.length >= 2 ? q : ''; drawChips(); load(true); } },
|
|
h('div', { class: 'searchbox' }, icon('search', 16), input)),
|
|
chips, results));
|
|
drawChips();
|
|
chips.querySelector('.chip.on')?.scrollIntoView({ inline: 'center', block: 'nearest' });
|
|
load(true);
|
|
}
|