Files
2026-11-01 11:15:09 +00:00

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);
}