77 lines
13 KiB
HTML
77 lines
13 KiB
HTML
<!doctype html>
|
||
<html lang="de"><head><meta charset="utf-8"><title>DeskHub Mock</title>
|
||
<link rel="stylesheet" href="../src/styles.css"></head>
|
||
<body><div id="app"></div><div id="toast" class="toast hidden"></div>
|
||
<script>
|
||
// Test-Attrappe für die Electron-Brücke (window.deskhub) – nur für lokale UI-Tests ohne Server
|
||
const staff = { id: 1, name: 'Max Muster', email: 'max@example.de', role: 'agent', permissions: { view_reports: true, reply_ticket: true, close_ticket: true, post_note: true, assign_ticket: true, create_ticket: true, manage_teams: true, manage_roles: true, manage_business_hours: true, fetch_mail: true, share_views: true, manage_banlist: true } };
|
||
const prio = [{ id: 1, name: 'Hoch', color: '#ef4444' }, { id: 2, name: 'Normal', color: '#3b82f6' }];
|
||
const statuses = [{ value: 'open', label: 'Offen', color: '#3b82f6' }, { value: 'answered', label: 'Beantwortet', color: '#22c55e' }, { value: 'closed', label: 'Geschlossen', color: '#64748b' }, { value: 'resolved', label: 'Gelöst', color: '#16a34a' }];
|
||
const tk = (n, s, st, over) => ({ id: n, number: 'T-' + (1000 + n), subject: s, requesterName: 'Kunde ' + n, requesterEmail: `k${n}@mail.de`, status: st, priority: prio[n % 2], department: { id: 1, name: 'Technik' }, assignedStaff: n % 3 ? { id: 1, name: 'Max Muster' } : null, isOverdue: !!over, dueLabel: over ? 'vor 2 Std (überfällig)' : 'in 5 Std', isSpam: false, createdAt: '2026-10-12 08:00:00', updatedAt: '2026-10-12 09:1' + n + ':00' });
|
||
const tickets = [tk(1, 'Drucker druckt nicht', 'open', true), tk(2, 'Passwort zurücksetzen', 'answered'), tk(3, 'Rechnung fehlt', 'open'), tk(4, 'VPN Verbindung bricht ab', 'open', true)];
|
||
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, ...(window.__attention ? { attention: window.__attention } : {}), 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], collaborators: window.__collabs || [{ id: 1, email: 'cc@firma.de', name: 'Cc Person' }], followers: [{ id: 2, name: 'Eva Beispiel' }], csat: { rating: 4, comment: 'Schnell und freundlich', sentAt: '2026-10-12 09:00:00', answeredAt: '2026-10-12 09:30:00' }, source: window.__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: [] }] } }),
|
||
'mail_fetch.php': (req) => { (window.__mailFetchPosts = window.__mailFetchPosts || []).push(req.body); return { ok: true, level: 'success', message: 'Abruf fertig: 2 E-Mail(s) verarbeitet.', processed: 2, newTickets: 2, replies: 0, errors: [], available: true }; },
|
||
'ticket_mail_reload.php': (req) => { (window.__mailReloadPosts = window.__mailReloadPosts || []).push(req.body); return { added: 1, inlineImages: 1, formatting: true, skipped: 0, alreadyThere: 0, failed: [] }; },
|
||
'ticket_collaborators.php': (req) => {
|
||
window.__collabs = window.__collabs || [{ id: 1, email: 'cc@firma.de', name: 'Cc Person' }];
|
||
const b = req.body || {};
|
||
if (b.action === 'add') window.__collabs.push({ id: 10 + window.__collabs.length, email: b.email, name: '' });
|
||
if (b.action === 'remove') window.__collabs = window.__collabs.filter((c) => c.id !== b.id);
|
||
return { collaborators: window.__collabs };
|
||
},
|
||
'ticket_templates.php': () => ({ templates: [{ id: 1, name: 'Neuer Mitarbeiter', description: '', subject: 'Neuer Mitarbeiter: ', body: 'Name:\nStartdatum:', departmentId: 1, helpTopicId: null, priorityId: 1, isPublic: true }] }),
|
||
'csat.php': () => ({ enabled: true, days: 30, stats: { sent: 10, answered: 6, avg: 4.2, rate: 60, dist: [1, 2, 3, 4, 5].map((r) => ({ rating: r, count: r === 5 ? 3 : r === 4 ? 2 : r === 1 ? 1 : 0 })), byStaff: [{ id: 1, name: 'Max Muster', count: 4, avg: 4.5 }], recent: [{ rating: 5, comment: 'Top', answeredAt: '2026-10-12 09:30:00', number: 'T-1001', subject: 'Drucker druckt nicht' }] } }),
|
||
'status.php': (req) => {
|
||
window.__status = window.__status || { enabled: false, title: 'Systemstatus', url: 'https://demo.example.de/status.php', overall: { key: 'operational', label: 'Alle Systeme laufen', color: '#157a45' },
|
||
componentStates: [{ key: 'operational', label: 'In Betrieb' }, { key: 'maintenance', label: 'Wartung' }, { key: 'degraded', label: 'Eingeschränkt' }, { key: 'outage', label: 'Ausfall' }],
|
||
incidentStates: [{ key: 'investigating', label: 'Wird untersucht' }, { key: 'resolved', label: 'Behoben' }],
|
||
components: [{ id: 1, name: 'Webseite', description: 'Kundenportal', status: 'operational', statusLabel: 'In Betrieb' }], incidents: [] };
|
||
if (req.method === 'POST') { (window.__statusPosts = window.__statusPosts || []).push(req.body); const b = req.body; if (b.action === 'set_enabled') window.__status.enabled = b.enabled; }
|
||
return { status: window.__status, canManage: true };
|
||
},
|
||
'ticket_unlock.php': () => { window.__lockedBy = null; return { ok: true, unlocked: true }; },
|
||
'departments.php': () => ({ staffOptions: [{ id: 1, name: 'Max Muster' }, { id: 2, name: 'Eva Beispiel' }], emailOptions: [], slaPlans: [], items: [{ id: 1, name: 'Technik', email: 'technik@example.de', isDefault: true, signature: '', isActive: true, slaPlanId: null, managerId: 1, managerName: 'Max Muster', autoAssignMode: 'staff', autoAssignStaffId: 2, notes: '', members: [{ id: 1, name: 'Max Muster', isPrimary: true }] }] }),
|
||
'queues.php': () => ({ items: [{ id: 1, name: 'Meine überfälligen', isShared: false, mine: true, ownerName: 'Max Muster', filters: { status: 'open,pending', assigned: 'me', sla: 'overdue' } }, { id: 2, name: 'Team-Überblick', isShared: true, mine: false, ownerName: 'Eva Beispiel', filters: { status: 'open,pending,answered' } }] }),
|
||
'report_builder.php': () => ({ config: { dimension: 'department', metrics: ['total', 'open', 'resolved', 'sla_resolve'], from: '2026-09-15', to: '2026-10-15', sort: 'label', desc: false, filters: {} },
|
||
columns: [{ key: 'label', label: 'Abteilung', unit: '' }, { key: 'total', label: 'Tickets gesamt', unit: '' }, { key: 'open', label: 'Offen', unit: '' }, { key: 'resolved', label: 'Gelöst/Geschlossen', unit: '' }, { key: 'sla_resolve', label: 'SLA Lösung', unit: '%' }],
|
||
rows: [{ label: 'Technik', total: 42, open: 8, resolved: 34, sla_resolve: 91.2 }, { label: 'Vertrieb', total: 17, open: 3, resolved: 14, sla_resolve: 97.5 }], totals: { total: 59, open: 11, resolved: 48, sla_resolve: 94.4 },
|
||
dimensions: [{ key: 'department', label: 'Abteilung' }, { key: 'month', label: 'Monat' }, { key: 'agent', label: 'Mitarbeiter' }],
|
||
metrics: [{ key: 'total', label: 'Tickets gesamt', unit: '' }, { key: 'open', label: 'Offen', unit: '' }, { key: 'resolved', label: 'Gelöst/Geschlossen', unit: '' }, { key: 'sla_resolve', label: 'SLA Lösung', unit: '%' }],
|
||
saved: [{ id: 1, name: 'Monatsbericht Support', isShared: true, mine: true, ownerName: 'Max Muster' }] }),
|
||
'canned_responses.php': () => ({ items: [{ id: 1, title: 'Gruß', body: 'Hallo,<br>danke für Ihre Nachricht.', departmentId: null }, { id: 2, title: 'Nur Technik', body: 'Technik-Text', departmentId: 1 }, { id: 3, title: 'Nur Vertrieb', body: 'Vertrieb-Text', departmentId: 2 }] }),
|
||
'customer.php': () => ({ customer: { name: 'Kunde 1', email: 'k1@mail.de' }, tickets }),
|
||
'teams.php': () => ({ staffOptions: [{ id: 1, name: 'Max Muster' }, { id: 2, name: 'Eva Beispiel' }], items: [{ id: 1, name: 'Support', leadId: 1, leadName: 'Max Muster', notes: '', isActive: true, memberCount: 2, memberIds: [1, 2] }] }),
|
||
'roles.php': () => ({ permissionKeys: ['reply_ticket', 'close_ticket', 'manage_teams', 'merge_split_ticket'], permissionLabels: { reply_ticket: 'Tickets beantworten', close_ticket: 'Tickets schließen/lösen', manage_teams: 'Teams verwalten', merge_split_ticket: 'Tickets zusammenführen und teilen' }, permissionGroups: [{ title: 'Tickets bearbeiten', keys: ['reply_ticket', 'close_ticket', 'merge_split_ticket'] }, { title: 'Team und Sicherheit', keys: ['manage_teams'] }], defaultAgentPermissions: { reply_ticket: true, merge_split_ticket: true }, items: [{ id: 1, name: 'Teamleitung', permissions: { reply_ticket: true }, effectivePermissions: { reply_ticket: true, close_ticket: false, manage_teams: false, merge_split_ticket: true }, staffCount: 2 }] }),
|
||
'business_hours.php': () => ({ enabled: true, hours: [1, 2, 3, 4, 5, 6, 0].map((w) => ({ weekday: w, isOpen: w > 0 && w < 6, openTime: '09:00', closeTime: '17:00' })), holidays: [{ id: 1, date: '2026-12-25', name: 'Weihnachten' }] }),
|
||
};
|
||
window.__calls = [];
|
||
window.deskhub = {
|
||
session: async () => ({ serverUrl: 'https://demo.example.de', staff }),
|
||
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 () => {},
|
||
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() {}, onUpdateProgress() {},
|
||
updateCheck: async () => ({ data: window.__update || { available: false, packaged: true, current: '1.1.0' } }), updateInstall: async () => ({ data: true }),
|
||
};
|
||
</script>
|
||
<script src="../src/lang-en.js"></script><script src="../src/i18n.js"></script><script src="../src/app.js"></script><script src="../src/admin-extra.js"></script><script src="../src/admin-status.js"></script>
|
||
</body></html>
|