Upload via GUI (69 Dateien)
This commit is contained in:
+11
-2
@@ -13,8 +13,16 @@ const routes = {
|
||||
'me.php': () => ({ staff }), 'metadata.php': () => ({ staff, departments: [{ id: 1, name: 'Technik' }], priorities: prio, agents: [{ id: 1, name: 'Max Muster' }, { id: 2, name: 'Eva Beispiel' }], statuses, tags: [], liveChatEnabled: true }),
|
||||
'activity.php': () => ({ newReplies: 0, newUnassigned: 0, serverTime: '2026-10-12 10:00:00', health: window.__health || [] }),
|
||||
'dashboard.php': () => ({ myOpen: 3, overdue: 2, unassigned: 1, unanswered: 4, resolvedToday: 5, slaPercent: 92.4, recent: tickets, departments: [{ name: 'Technik', open: 8, overdue: 2 }, { name: 'Vertrieb', open: 3, overdue: 0 }] }),
|
||||
'web_pages.php': () => ({ pages: [{ key: 'settings', label: 'Einstellungen', path: '/staff/settings.php', group: 'Verwaltung' }, { key: 'backup', label: 'Backup', path: '/staff/backup.php', group: 'System' }] }),
|
||||
'assistant.php': (req) => {
|
||||
if (req.method === 'POST') { (window.__assistantPosts = window.__assistantPosts || []).push(req.body); return req.body.action === 'test' ? { found: true, result: { source: 'custom', title: 'Versand', text: 'Zwei bis drei Werktage.', others: [] } } : req.body.action === 'save_synonyms' ? { ok: true, groups: 2 } : { ok: true, item: { id: 9 } }; }
|
||||
const sec = (req.query || {}).section;
|
||||
if (sec === 'answers') return { items: [{ id: 1, title: 'Wie lange dauert der Versand?', keywords: 'Lieferzeit', answer: 'Zwei bis drei Werktage.', isActive: true, hits: 3, updatedAt: '2026-10-20 10:00:00' }] };
|
||||
if (sec === 'log') return { filters: { custom: 'Meine Antwort gefunden', kb: 'Wissensdatenbank', none: 'Keine Antwort', helpful: 'Hat geholfen' }, stats: { custom: 4, kb: 2, none: 1, helpful: 3 }, top: [{ question: 'Wie kündige ich?', n: 2, last: '2026-10-22 09:00:00' }], total: 1, pages: 1, page: 1, items: [{ id: 1, question: 'Wie kündige ich?', result: 'none', matchedTitle: null, ticketNumber: null, helpful: null, createdAt: '2026-10-22 09:00:00' }] };
|
||||
return { dbReady: true, settings: { enabled: true, mode: 'offline_only', name: '', greeting: '', noAnswer: '', departmentId: 0, priorityId: 0, logDays: 90, synonyms: 'Zugang, Einladung, beitreten' }, defaults: { name: 'Digitaler Assistent', greeting: 'Hallo!', noAnswer: 'Keine Antwort.' }, state: { activeNow: true, liveChatEnabled: false, kbEnabled: true, helpdeskOffline: false }, departments: [{ id: 1, name: 'Technik' }], priorities: [{ id: 1, name: 'Normal' }] };
|
||||
},
|
||||
'tickets.php': () => ({ items: tickets, page: 1, perPage: 30, total: 4, hasMore: false }),
|
||||
'ticket.php': () => ({ ticket: { ...tickets[0], source: 'web', firstResponseDueAt: null, resolutionDueAt: '2026-10-12 12:00:00', lockedByOtherName: null, lockedBy: window.__lockedBy || null, canUnlock: !!window.__lockedBy, customFields: [{ label: 'Titel der Geschichte', type: 'text', value: 'Der kleine Bär' }, { label: 'Nutzerbedingungen', type: 'checkbox', value: 'Ja' }], replySignatures: { default: 'own', own: { available: true, text: 'Viele Grüße\nMax persönlich', html: '<p>Viele Grüße</p><div>Max persönlich</div>', hasImage: false }, department: { available: true, name: 'Technik', text: 'Technik-Team Support', html: '<p>Technik-Team Support</p><img src="https://example.org/logo.png" width="120" onerror="alert(1)" style="width:999px"><a href="javascript:alert(1)">Link</a><scr' + 'ipt>alert(2)</scr' + 'ipt>', hasImage: true } }, threads: [
|
||||
'ticket.php': () => ({ ticket: { ...tickets[0], source: 'web', firstResponseDueAt: null, resolutionDueAt: '2026-10-12 12:00:00', similar: { articles: [{ title: 'Drucker einrichten', url: '/kb_article.php?slug=drucker' }], tickets: [{ number: 'T-77', subject: 'Alter Druckerfall' }] }, lockedByOtherName: null, lockedBy: window.__lockedBy || null, canUnlock: !!window.__lockedBy, customFields: [{ label: 'Titel der Geschichte', type: 'text', value: 'Der kleine Bär' }, { label: 'Nutzerbedingungen', type: 'checkbox', value: 'Ja' }], replySignatures: { default: 'own', own: { available: true, text: 'Viele Grüße\nMax persönlich', html: '<p>Viele Grüße</p><div>Max persönlich</div>', hasImage: false }, department: { available: true, name: 'Technik', text: 'Technik-Team Support', html: '<p>Technik-Team Support</p><img src="https://example.org/logo.png" width="120" onerror="alert(1)" style="width:999px"><a href="javascript:alert(1)">Link</a><scr' + 'ipt>alert(2)</scr' + 'ipt>', hasImage: true } }, threads: [
|
||||
{ id: 1, authorType: 'user', authorName: 'Kunde 1', message: 'Mein Drucker druckt nichts mehr.\nBitte helft.', isInternalNote: false, createdAt: '2026-10-12 08:00:00', attachments: [{ id: 1, name: 'foto.png', size: 20480 }], comments: [] },
|
||||
{ id: 2, authorType: 'staff', authorName: 'Max Muster', message: 'Wir schauen uns das an.', isInternalNote: false, createdAt: '2026-10-12 08:30:00', attachments: [], comments: [] },
|
||||
{ id: 3, authorType: 'staff', authorName: 'Max Muster', message: 'Treiber prüfen', isInternalNote: true, createdAt: '2026-10-12 08:35:00', attachments: [], comments: [] }] } }),
|
||||
@@ -39,7 +47,8 @@ window.deskhub = {
|
||||
settings: async () => ({ theme: window.__theme || 'dark', autostart: false, closeToTray: true, notifications: true, pollSeconds: 60, version: '1.1.0', packaged: false }),
|
||||
setSettings: async (p) => { if (p.theme) window.__theme = p.theme; return window.deskhub.settings(); },
|
||||
login: async () => ({ staff }), logout: async () => true, notify: async () => {}, badge: async () => {},
|
||||
api: async (req) => { window.__calls.push(req.path); if (req.body) (window.__bodies = window.__bodies || {})[req.path] = req.body; window.__bodies = window.__bodies || {}; const f = routes[req.path]; return f ? { data: f() } : { error: { message: 'Mock: ' + req.path + ' nicht vorhanden' } }; },
|
||||
openWeb: async (path) => { window.__web = path; return { data: true }; },
|
||||
api: async (req) => { window.__calls.push(req.path); if (req.body) (window.__bodies = window.__bodies || {})[req.path] = req.body; window.__bodies = window.__bodies || {}; const f = routes[req.path]; return f ? { data: f(req) } : { error: { message: 'Mock: ' + req.path + ' nicht vorhanden' } }; },
|
||||
onSessionExpired() {}, onNavigate() {},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -39,11 +39,20 @@ app.whenReady().then(async () => {
|
||||
|
||||
await run(`go('tickets')`);
|
||||
check('Ticketliste zeigt alle Tickets', await waitFor(`['Drucker druckt nicht','Passwort zurücksetzen','Rechnung fehlt','VPN Verbindung bricht ab'].every(t => document.getElementById('main').innerText.includes(t))`));
|
||||
check('Reiter „Wiedervorlage“, „Pausiert“ und „Beobachtet“ sind da', await run(`(() => { const t = [...document.querySelectorAll('#main .tabs button')].map((b) => b.innerText).join('|'); return t.includes('Wiedervorlage') && t.includes('Pausiert') && t.includes('Beobachtet'); })()`));
|
||||
check('Gespeicherte Ansicht erscheint als Chip', await run(`document.getElementById('main').innerText.includes('Meine überfälligen')`));
|
||||
|
||||
await run(`go('ticket', 1)`);
|
||||
check('Ticketdetail zeigt Betreff und Verlauf', await waitFor(`['Drucker druckt nicht', 'Mein Drucker druckt nichts mehr', 'Treiber prüfen'].every(x => document.getElementById('main').innerText.includes(x))`));
|
||||
check('Ticketdetail zeigt Eigenschaften und Aktionen', await run(`['eigenschaften', 'schließen'].every(x => document.getElementById('main').innerText.toLowerCase().includes(x))`));
|
||||
check('Ticketdetail: Wiedervorlage- und Beobachten-Knopf sind da', await run(`(() => { const t = document.getElementById('main').innerText; return t.includes('Auf später') && t.includes('Beobachten'); })()`));
|
||||
check('Ticketdetail: „Dazu passend“ zeigt Artikel und ähnliches Ticket', await run(`(() => { const t = document.getElementById('main').innerText; return t.toLowerCase().includes('dazu passend') && t.includes('Drucker einrichten') && t.includes('T-77'); })()`));
|
||||
await run(`window.__bodies = {}; [...document.querySelectorAll('#main button')].find((b) => b.innerText.includes('Beobachten') && !b.innerText.includes('Nicht')).click()`);
|
||||
await run(`window.__web = ''; [...document.querySelectorAll('#main button')].find((b) => b.innerText.includes('Im Browser')).click()`);
|
||||
check('Ticket: „Im Browser“ öffnet das Ticket in der Weboberfläche', await waitFor(`String(window.__web).startsWith('/staff/ticket_view.php?n=')`));
|
||||
check('Beobachten sendet following=true an den Server', await waitFor(`(window.__bodies['ticket_update.php'] || {}).following === true`));
|
||||
await run(`go('ticket', 1)`);
|
||||
await waitFor(`document.getElementById('main').innerText.includes('Drucker druckt nicht')`);
|
||||
check('Interne Notiz ist gekennzeichnet', await run(`!!document.querySelector('.thread.note')`));
|
||||
// Textbausteine nur für die Abteilung des Tickets, Signatur-Auswahl mit Vorschau
|
||||
await waitFor(`!!document.querySelector('.composer select')`);
|
||||
@@ -78,7 +87,36 @@ app.whenReady().then(async () => {
|
||||
check('Verwaltung „' + key + '“ rendert ohne Fehler', ok, await run(`(document.querySelector('#main .warnbox')||{}).innerText || ''`));
|
||||
}
|
||||
|
||||
// Digitaler Assistent: Reiter, Einstellungen, Antworten, Fragen-Log, Synonyme, Ausprobieren
|
||||
await run(`go('admin', 'assistant')`);
|
||||
const txt = () => run(`document.getElementById('main').innerText`);
|
||||
const tabBtn = (label) => run(`[...document.querySelectorAll('#main .tabs button')].find((b) => b.innerText.includes(${JSON.stringify(label)})).click()`);
|
||||
check('Assistent: Einstellungen zeigen Zustand und Felder', await waitFor(`(() => { const t = document.getElementById('main').innerText; return t.includes('Zustand jetzt') && t.includes('aktiv') && t.includes('Aufbewahrung'); })()`));
|
||||
await run(`window.__assistantPosts = []; [...document.querySelectorAll('#main button')].find((b) => b.innerText === 'Speichern').click()`);
|
||||
check('Assistent: Speichern sendet die Einstellungen', await waitFor(`(window.__assistantPosts[0] || {}).action === 'save_settings' && window.__assistantPosts[0].logDays === 90`));
|
||||
await tabBtn('Zusätzliche Antworten');
|
||||
check('Assistent: Zusätzliche Antworten werden aufgelistet', await waitFor(`document.getElementById('main').innerText.includes('Wie lange dauert der Versand?') && document.getElementById('main').innerText.includes('3×')`));
|
||||
await tabBtn('Fragen-Log');
|
||||
check('Assistent: Fragen-Log zeigt Kennzahlen, Frage und häufige Fragen ohne Antwort', await waitFor(`(() => { const t = document.getElementById('main').innerText; return t.includes('Wie kündige ich?') && t.includes('Häufige Fragen ohne Antwort') && t.includes('Hat geholfen'); })()`));
|
||||
await tabBtn('Gleichbedeutende Wörter');
|
||||
check('Assistent: Synonyme sind sichtbar und speicherbar', await waitFor(`!!document.querySelector('#main textarea') && document.querySelector('#main textarea').value.includes('Einladung')`));
|
||||
await run(`window.__assistantPosts = []; [...document.querySelectorAll('#main button')].find((b) => b.innerText === 'Speichern').click()`);
|
||||
check('Assistent: Synonyme senden save_synonyms', await waitFor(`(window.__assistantPosts[0] || {}).action === 'save_synonyms'`));
|
||||
await tabBtn('Frage ausprobieren');
|
||||
await run(`window.__assistantPosts = []; document.querySelector('#main input').value = 'Wann kommt das Paket?'; [...document.querySelectorAll('#main button')].find((b) => b.innerText === 'Testen').click()`);
|
||||
check('Assistent: Ausprobieren zeigt die Antwort', await waitFor(`document.getElementById('main').innerText.includes('Zwei bis drei Werktage.')`));
|
||||
// Rechte: ohne das Recht ist der Bereich nicht in der Verwaltung
|
||||
await run(`state.staff.role = 'agent'; state.staff.permissions = {}`);
|
||||
check('Assistent: ohne Recht nicht in den Verwaltungsbereichen', await run(`!adminSections().some((s) => s.key === 'assistant')`));
|
||||
await run(`state.staff.permissions = { manage_assistant: true }`);
|
||||
check('Assistent: mit dem Recht „manage_assistant“ ist er da, andere Bereiche nicht', await run(`adminSections().some((s) => s.key === 'assistant') && !adminSections().some((s) => s.key === 'departments')`));
|
||||
await run(`state.staff.role = 'admin'`);
|
||||
|
||||
// Abteilung: automatische Zuweisung neuer Tickets einstellen
|
||||
await run(`go('admin')`);
|
||||
check('Verwaltung zeigt die Weboberfläche-Seiten, die der Server freigibt', await waitFor(`(() => { const t = document.getElementById('main').innerText; return t.includes('Backup') && t.includes('Einstellungen') && t.includes('im Browser'); })()`));
|
||||
await run(`[...document.querySelectorAll('#main .tile')].find((x) => x.innerText.includes('Backup')).click()`);
|
||||
check('Klick öffnet die Seite über die einmalige Anmeldung (nur /staff/-Pfad)', await waitFor(`window.__web === '/staff/backup.php'`));
|
||||
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()`);
|
||||
|
||||
Reference in New Issue
Block a user