64 lines
4.4 KiB
JavaScript
64 lines
4.4 KiB
JavaScript
// Aufgaben: ansehen, abhaken, neue anlegen.
|
|
import { h, clear, icon, toast, errText } from '../lib/dom.js';
|
|
import { apiCall, parseServerTime } from '../lib/core.js';
|
|
import { spinner, showError } from '../lib/ui.js';
|
|
|
|
const FILTERS = [['my', 'Meine'], ['open', 'Alle offenen'], ['done', 'Erledigt']];
|
|
const memory = { filter: 'my' };
|
|
|
|
const dueText = (s) => { const d = parseServerTime(s); return d ? d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' }) + ' ' + d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' }) : ''; };
|
|
|
|
export async function renderTasks(host, ctx) {
|
|
const list = h('div');
|
|
const chips = h('div', { class: 'chips' });
|
|
const form = h('div');
|
|
|
|
const drawChips = () => chips.replaceChildren(...FILTERS.map(([k, l]) => h('button', { type: 'button', class: `chip${memory.filter === k ? ' on' : ''}`, onclick: () => { memory.filter = k; drawChips(); load(); } }, l)));
|
|
|
|
async function load() {
|
|
clear(list).append(spinner());
|
|
try {
|
|
const { data } = await apiCall('tasks.php', { query: { filter: memory.filter } });
|
|
ctx.taskStaff = data.staffOptions || [];
|
|
clear(list);
|
|
if (!data.items.length) return list.append(h('div', { class: 'empty' }, memory.filter === 'done' ? 'Keine erledigten Aufgaben.' : 'Keine Aufgaben. Schön!'));
|
|
list.append(h('div', { class: 'stack' }, data.items.map((it) => {
|
|
const done = it.status === 'done';
|
|
const box = h('input', { type: 'checkbox', checked: done, 'aria-label': 'Erledigt', onchange: async () => {
|
|
try { await apiCall('tasks.php', { method: 'POST', body: { action: 'update_status', id: it.id, status: box.checked ? 'done' : 'open' } }); toast(box.checked ? 'Erledigt' : 'Wieder geöffnet'); load(); }
|
|
catch (e) { box.checked = !box.checked; toast(errText(e), 'err'); }
|
|
} });
|
|
return h('div', { class: `card task${done ? ' done' : ''}` }, box, h('div', { class: 'grow' },
|
|
h('div', { class: 'tt', style: { fontWeight: 650 } }, it.title),
|
|
it.description ? h('div', { class: 'muted' }, it.description) : null,
|
|
h('div', { class: 'muted', style: { marginTop: '3px' } }, [it.dueAt ? `fällig ${dueText(it.dueAt)}` : null, it.assignedName ? `für ${it.assignedName}` : null].filter(Boolean).join(' · ')),
|
|
it.ticketNumber ? h('button', { class: 'btn small', style: { marginTop: '6px' }, onclick: () => ctx.go('ticket', { number: it.ticketNumber }) }, `#${it.ticketNumber}`) : null),
|
|
h('button', { class: 'iconbtn', title: 'Aufgabe löschen', onclick: async () => {
|
|
if (!confirm('Aufgabe löschen?')) return;
|
|
try { await apiCall('tasks.php', { method: 'POST', body: { action: 'delete', id: it.id } }); load(); } catch (e) { toast(errText(e), 'err'); }
|
|
} }, icon('x', 16)));
|
|
})));
|
|
} catch (e) { showError(ctx, list, e); }
|
|
}
|
|
|
|
function drawForm(open) {
|
|
if (!open) { form.replaceChildren(h('button', { class: 'btn', onclick: () => drawForm(true) }, icon('plus', 15), 'Neue Aufgabe')); return; }
|
|
const title = h('input', { type: 'text', required: true, maxlength: '200', placeholder: 'Was ist zu tun?' });
|
|
const due = h('input', { type: 'datetime-local' });
|
|
const ticket = h('input', { type: 'text', placeholder: 'Ticketnummer (optional)' });
|
|
const who = h('select', {}, h('option', { value: '' }, 'Niemand'), (ctx.taskStaff || []).map((s) => h('option', { value: s.id, selected: s.id === ctx.session.staff?.id ? '' : undefined }, s.name)));
|
|
form.replaceChildren(h('form', { class: 'sheet', onsubmit: async (ev) => {
|
|
ev.preventDefault();
|
|
try {
|
|
await apiCall('tasks.php', { method: 'POST', body: { action: 'create', title: title.value.trim(), dueAt: due.value ? due.value.replace('T', ' ') + ':00' : '', ticketNumber: ticket.value.trim(), assignedToId: who.value ? Number(who.value) : null } });
|
|
toast('Aufgabe angelegt'); drawForm(false); load();
|
|
} catch (e) { toast(errText(e), 'err'); }
|
|
} }, h('label', {}, 'Titel', title), h('div', { class: 'row2' }, h('label', {}, 'Fällig', due), h('label', {}, 'Für', who)), h('label', {}, 'Ticket', ticket),
|
|
h('div', { class: 'actions' }, h('button', { class: 'btn primary', type: 'submit' }, 'Anlegen'), h('button', { class: 'btn', type: 'button', onclick: () => drawForm(false) }, 'Abbrechen'))));
|
|
title.focus();
|
|
}
|
|
|
|
host.append(h('div', { class: 'stack' }, form, chips, list));
|
|
drawChips(); drawForm(false); load();
|
|
}
|