From b247ca42d2ea0a932e1bc25af2d29be845ff6023 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Fri, 23 Oct 2026 11:14:56 +0000 Subject: [PATCH] Upload via GUI (69 Dateien) --- desktop/src/app.js | 6 +-- desktop/test/mock.html | 93 +++++++++++++++++++++--------------------- desktop/test/run.js | 11 +++++ 3 files changed, 61 insertions(+), 49 deletions(-) diff --git a/desktop/src/app.js b/desktop/src/app.js index 8ef32a8c..9f29579e 100644 --- a/desktop/src/app.js +++ b/desktop/src/app.js @@ -275,13 +275,13 @@ function ticketTable(items, opts = {}) { const selected = opts.selected; const allBox = selected ? h('input', { type: 'checkbox', onchange: (e) => { items.forEach((t) => (e.target.checked ? selected.add(t.number) : selected.delete(t.number))); opts.onSelect?.(); for (const c of document.querySelectorAll('.rowcheck')) c.checked = e.target.checked; } }) : null; return h('div', { class: 'panel flush' }, h('table', { class: 'tickets' }, - h('tr', {}, selected ? h('th', { class: 'chk' }, allBox) : null, h('th', {}, 'Nr.'), h('th', {}, 'Betreff'), h('th', {}, 'Kunde'), h('th', {}, 'Status'), h('th', {}, 'Priorität'), h('th', {}, 'Zugewiesen'), h('th', {}, 'Fällig'), h('th', {}, 'Aktualisiert')), + h('tr', {}, selected ? h('th', { class: 'chk' }, allBox) : null, h('th', {}, 'Nr.'), h('th', {}, 'Betreff'), h('th', {}, 'Kunde'), h('th', {}, 'Status'), h('th', {}, 'Priorität'), h('th', {}, 'Zugewiesen'), h('th', {}, 'Eingang'), h('th', {}, 'Fällig'), h('th', {}, 'Aktualisiert')), ...items.map((t) => h('tr', { class: 'click' + (t.isOverdue ? ' overdue' : ''), onclick: () => go('ticket', t.number) }, selected ? h('td', { class: 'chk', onclick: (e) => e.stopPropagation() }, h('input', { type: 'checkbox', class: 'rowcheck', checked: selected.has(t.number), onchange: (e) => { e.target.checked ? selected.add(t.number) : selected.delete(t.number); e.target.closest('tr').classList.toggle('sel', e.target.checked); opts.onSelect?.(); } })) : null, h('td', { class: 'nowrap' }, t.number), h('td', {}, (t.isSpam ? '🚫 ' : '') + t.subject), h('td', {}, t.requesterName), h('td', { class: 'nowrap' }, h('span', { class: 'pill', color: statusInfo(t.status).color }, statusInfo(t.status).label)), h('td', { class: 'nowrap' }, h('span', { class: 'dot', color: t.priority.color }), t.priority.name), - h('td', {}, t.assignedStaff?.name || '—'), h('td', { class: dueClass(t) + ' nowrap' }, t.dueLabel || ''), h('td', { class: 'muted nowrap' }, fmtDate(t.updatedAt)))))); + h('td', {}, t.assignedStaff?.name || '—'), h('td', { class: 'muted nowrap' }, fmtDate(t.createdAt)), h('td', { class: dueClass(t) + ' nowrap' }, t.dueLabel || ''), h('td', { class: 'muted nowrap' }, fmtDate(t.updatedAt)))))); } async function vDashboard(main) { @@ -720,7 +720,7 @@ async function vAvailability(main) { const T = 'text', N = 'number', B = 'bool', A = 'area', H = 'heading', ES = 'emailsel', XD = 'extradepts'; const ADMIN = [ { key: 'departments', label: 'Abteilungen', perm: 'manage_departments', file: 'departments.php', cols: [['Name', 'name'], ['Status', (x) => x.isActive ? 'Aktiv' : 'Deaktiviert'], ['Mitglieder', (x) => (x.members || []).length], ['Abteilungsleiter', 'managerName'], ['E-Mail', 'email'], ['Aktualisiert', (x) => x.updatedAt ? fmtDate(x.updatedAt) : '']], - fields: [['_h1', 'Abteilungsinformationen', H], ['name', 'Name', T], ['isActive', 'Aktiv', B], ['managerId', 'Abteilungsleiter', 'staffOptions?'], ['slaPlanId', 'SLA-Plan (leer = Zeiten der Priorität)', 'slaPlans?'], ['email', 'Abteilungs-E-Mail (optional, sonst Standardadresse)', ES], ['isDefault', 'Standardabteilung', B], ['signature', 'Signatur', A], ['notes', 'Administratornotizen (intern)', A]], + fields: [['_h1', 'Abteilungsinformationen', H], ['name', 'Name', T], ['isActive', 'Aktiv', B], ['managerId', 'Abteilungsleiter', 'staffOptions?'], ['autoAssignMode', 'Neue Tickets automatisch zuweisen', [['none', 'Nicht automatisch zuweisen'], ['staff', 'Immer an einen bestimmten Mitarbeiter'], ['round_robin', 'Reihum an die Mitarbeiter der Abteilung']]], ['autoAssignStaffId', 'Mitarbeiter (nur bei „Immer an einen bestimmten Mitarbeiter“)', 'staffOptions?'], ['slaPlanId', 'SLA-Plan (leer = Zeiten der Priorität)', 'slaPlans?'], ['email', 'Abteilungs-E-Mail (optional, sonst Standardadresse)', ES], ['isDefault', 'Standardabteilung', B], ['signature', 'Signatur', A], ['notes', 'Administratornotizen (intern)', A]], info: (x) => x?.members?.length ? `Mitglieder: ${x.members.map((m) => m.name + (m.isPrimary ? '' : ' (zusätzlich)')).join(', ')}` : '' }, { key: 'priorities', label: 'Prioritäten / SLA', perm: 'manage_priorities', file: 'priorities.php', cols: [['Name', 'name'], ['Reaktion (h)', 'responseHours'], ['Lösung (h)', 'resolutionHours']], fields: [['name', 'Name', T], ['color', 'Farbe (#RRGGBB)', T], ['responseHours', 'Reaktionszeit (Std.)', N], ['resolutionHours', 'Lösungszeit (Std.)', N], ['sortOrder', 'Reihenfolge', N]] }, diff --git a/desktop/test/mock.html b/desktop/test/mock.html index 4fc5f7d9..30beee8f 100644 --- a/desktop/test/mock.html +++ b/desktop/test/mock.html @@ -1,46 +1,47 @@ - -DeskHub Mock - -
- - - + +DeskHub Mock + +
+ + + diff --git a/desktop/test/run.js b/desktop/test/run.js index cb323071..4fdf32af 100644 --- a/desktop/test/run.js +++ b/desktop/test/run.js @@ -78,6 +78,17 @@ app.whenReady().then(async () => { check('Verwaltung „' + key + '“ rendert ohne Fehler', ok, await run(`(document.querySelector('#main .warnbox')||{}).innerText || ''`)); } + // Abteilung: automatische Zuweisung neuer Tickets einstellen + await run(`go('admin', 'departments')`); + check('Abteilungen: Liste lädt', await waitFor(`document.getElementById('main').innerText.includes('Technik')`)); + await run(`document.querySelector('#main tr.click').click()`); + check('Abteilung bearbeiten: Einstellung „Neue Tickets automatisch zuweisen“ ist da und zeigt den gespeicherten Wert', await waitFor(`(() => { const l = [...document.querySelectorAll('.modal label')].find((x) => x.innerText.includes('Neue Tickets automatisch zuweisen')); return !!l && l.nextElementSibling.value === 'staff'; })()`)); + check('Abteilung bearbeiten: Mitarbeiter-Auswahl zeigt den gespeicherten Mitarbeiter', await run(`(() => { const l = [...document.querySelectorAll('.modal label')].find((x) => x.innerText.startsWith('Mitarbeiter (nur bei')); return !!l && l.nextElementSibling.value === '2'; })()`)); + await run(`(() => { const l = [...document.querySelectorAll('.modal label')].find((x) => x.innerText.includes('Neue Tickets automatisch zuweisen')); l.nextElementSibling.value = 'round_robin'; })()`); + await run(`[...document.querySelectorAll('.modal button')].find((b) => b.innerText === 'Speichern').click()`); + check('Abteilung speichern sendet die Zuweisungs-Einstellung an den Server', await waitFor(`!!(window.__bodies['departments.php'] && window.__bodies['departments.php'].action === 'save')`), JSON.stringify(await run(`window.__bodies['departments.php']`))); + check('… mit „Reihum“ und dem Mitarbeiter', await run(`(() => { const b = window.__bodies['departments.php'] || {}; return b.autoAssignMode === 'round_robin' && b.autoAssignStaffId === 2; })()`)); + console.log('Theme'); await run(`window.deskhub.setSettings({ theme: 'light' }).then(() => { state.prefs = { ...state.prefs, theme: 'light' }; applyTheme(); })`); check('Hell-Modus setzt data-theme', await waitFor(`document.documentElement.dataset.theme === 'light'`));