Files
SDS-CRM/public/js/core/ticket-events.js
2026-03-23 02:09:14 +01:00

221 lines
8.4 KiB
JavaScript

import { apiGet, apiUrl } from '../api.js';
import { esc, formatDateTime, formatRemoteDurationDe, telHref } from './utils.js';
function formatFileSizeDe(n) {
if (n == null || typeof n !== 'number' || n < 0) return '';
if (n < 1024) return `${n} B`;
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
}
/** Zwischenspeicher für GET /integrations/teamviewer/connections */
let tvSessionsCache = null;
/** HTML für die Inhaltsspalte (nur server-/formularbekannte Typen) */
export function eventInhaltHtml(ev) {
const t = ev.type;
if (t === 'CALL') {
let h = `<div class="event-inhalt-block"><p class="event-inhalt-label">Beschreibung</p><div class="event-inhalt-text">${esc(ev.description)}</div>`;
if (ev.callbackNumber) {
const th = telHref(ev.callbackNumber);
const numHtml = th
? `<a href="${esc(th)}">${esc(ev.callbackNumber)}</a>`
: esc(ev.callbackNumber);
h += `<p class="event-inhalt-meta"><strong>Rückrufnummer:</strong> ${numHtml}</p>`;
}
return `${h}</div>`;
}
if (t === 'REMOTE') {
let h = `<div class="event-inhalt-block"><p class="event-inhalt-label">Beschreibung</p><div class="event-inhalt-text">${esc(ev.description)}</div>`;
if (ev.teamviewerId) {
h += `<p class="event-inhalt-meta"><strong>Gerät-ID (TeamViewer):</strong> <code>${esc(ev.teamviewerId)}</code></p>`;
}
if (ev.remoteDurationSeconds != null) {
h += `<p class="event-inhalt-meta"><strong>Remote-Dauer:</strong> ${esc(formatRemoteDurationDe(ev.remoteDurationSeconds))}</p>`;
}
if (ev.teamviewerNotes && String(ev.teamviewerNotes).trim()) {
h += `<p class="event-inhalt-meta"><strong>Notizen:</strong> <span class="event-tv-notes" style="white-space:pre-wrap">${esc(String(ev.teamviewerNotes).trim())}</span></p>`;
}
return `${h}</div>`;
}
if (t === 'PART') {
let h = `<div class="event-inhalt-block"><p class="event-inhalt-meta"><strong>Artikelnummer:</strong> <code class="event-artnr">${esc(ev.articleNumber || '')}</code></p>`;
if (ev.description && String(ev.description).trim()) {
h += `<p class="event-inhalt-label">Bemerkung</p><div class="event-inhalt-text">${esc(ev.description)}</div>`;
}
return `${h}</div>`;
}
if (t === 'ATTACHMENT') {
let h = '';
if (ev.description && String(ev.description).trim()) {
h += `<div class="event-inhalt-block"><p class="event-inhalt-label">Beschreibung</p><div class="event-inhalt-text">${esc(ev.description)}</div></div>`;
}
const atts = ev.attachments || [];
if (atts.length === 0) {
h += '<p class="muted">Keine Dateien.</p>';
} else {
h += '<ul class="event-attachment-list">';
for (const a of atts) {
const href = apiUrl(a.url);
const timeParen =
a.createdAt != null
? ` <span class="muted">(${esc(formatDateTime(a.createdAt))})</span>`
: '';
h += `<li><a href="${esc(href)}" class="js-attachment-preview" data-mime="${esc(a.mimeType || '')}" data-name="${esc(a.originalName)}">${esc(a.originalName)}</a>${timeParen}`;
if (a.sizeBytes != null) {
h += ` <span class="muted">· ${esc(formatFileSizeDe(a.sizeBytes))}</span>`;
}
h += '</li>';
}
h += '</ul>';
}
return h;
}
return `<div class="event-inhalt-text">${esc(ev.description)}</div>`;
}
export function fillTvDeviceSelect() {
const userSel = document.getElementById('tv-user-select');
const devSel = document.getElementById('tv-conn-select');
if (!devSel) return;
const ukey = userSel?.value ?? '';
devSel.innerHTML =
'<option value="">— Gerät / Session wählen —</option>';
if (!ukey || !tvSessionsCache) {
devSel.disabled = true;
return;
}
const u = (tvSessionsCache.users || []).find((x) => x.userKey === ukey);
const devices = u?.devices || [];
if (devices.length === 0) {
devSel.disabled = true;
return;
}
devSel.disabled = false;
devSel.innerHTML +=
devices
.map(
(d) =>
`<option value="${esc(d.deviceid)}" data-devicename="${esc(d.label)}" data-start-date="${esc(d.startDate || '')}" data-end-date="${esc(d.endDate || '')}" data-notes="${esc(d.notes || '')}">${esc(d.label)}</option>`,
)
.join('');
}
export async function loadTeamViewerConnectionsIntoSelect() {
const userSel = document.getElementById('tv-user-select');
const devSel = document.getElementById('tv-conn-select');
const hint = document.getElementById('tv-conn-hint');
if (!userSel || !devSel) return;
userSel.innerHTML = '<option value="">… lädt …</option>';
devSel.innerHTML = '<option value="">…</option>';
devSel.disabled = true;
if (hint) hint.textContent = '';
try {
const data = await apiGet('/integrations/teamviewer/connections');
tvSessionsCache = data;
const users = data.users || [];
userSel.innerHTML =
'<option value="">— Benutzer wählen (letzte 7 Tage) —</option>' +
users
.map((u) => {
const label =
u.username && u.username !== '_unbekannt'
? u.username
: u.userid
? `Benutzer ${u.userid}`
: 'Unbekannt (Benutzer)';
return `<option value="${esc(u.userKey)}">${esc(label)}</option>`;
})
.join('');
devSel.innerHTML =
'<option value="">— zuerst Benutzer wählen —</option>';
devSel.disabled = true;
userSel.onchange = () => fillTvDeviceSelect();
if (hint) {
const ndev = users.reduce((n, u) => n + (u.devices?.length || 0), 0);
if (users.length) {
hint.textContent = `${users.length} Benutzer, ${ndev} Gerät(e)/Session(s).`;
} else {
hint.textContent =
data.meta?.recordCount === 0
? 'Keine Verbindungen in den letzten 7 Tagen.'
: 'Keine gruppierten Einträge (TeamViewer-Antwort prüfen).';
}
}
} catch (e) {
tvSessionsCache = null;
userSel.innerHTML = '<option value="">— Laden fehlgeschlagen —</option>';
devSel.innerHTML = '<option value="">—</option>';
devSel.disabled = true;
if (hint) hint.textContent = e.message || 'Fehler';
}
}
export function syncEventFormFieldGroups(form) {
const sel = form.querySelector('#ev-type-sel');
if (!sel) return;
const v = sel.value;
form.querySelectorAll('.ev-field-group').forEach((el) => {
const show = el.getAttribute('data-ev-type') === v;
el.hidden = !show;
el.querySelectorAll('input, textarea').forEach((inp) => {
const name = inp.getAttribute('name');
let req = false;
if (show) {
if (v === 'NOTE' && name === 'description_note') req = true;
if (v === 'CALL' && name === 'description_call') req = true;
if (v === 'REMOTE' && name === 'description_remote') req = false;
if (v === 'PART' && name === 'articleNumber') req = true;
if (v === 'ATTACHMENT') req = false;
}
inp.required = req;
});
});
if (v === 'REMOTE') {
loadTeamViewerConnectionsIntoSelect();
}
}
/** Neueste zuerst; ATTACHMENT-Blöcke bleiben unten (innerhalb ebenfalls neuer über älter). */
export function sortEventsChronologicalWithAttachmentsLast(events) {
const non = events
.filter((e) => e.type !== 'ATTACHMENT')
.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
const att = events
.filter((e) => e.type === 'ATTACHMENT')
.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
return [...non, ...att];
}
export function buildEventPostBody(ticketId, fd) {
const type = fd.get('type');
const base = { ticketId, type };
if (type === 'NOTE') {
return { ...base, description: fd.get('description_note') };
}
if (type === 'CALL') {
return {
...base,
description: fd.get('description_call'),
callbackNumber: fd.get('callbackNumber'),
};
}
if (type === 'REMOTE') {
const body = {
...base,
description: fd.get('description_remote'),
};
const tv = fd.get('teamviewerDevice');
if (tv && String(tv).trim()) body.teamviewerId = String(tv).trim();
return body;
}
if (type === 'PART') {
return {
...base,
articleNumber: fd.get('articleNumber'),
description: fd.get('description_part') || '',
};
}
return base;
}