314 lines
11 KiB
JavaScript
314 lines
11 KiB
JavaScript
// auto-split module
|
|
|
|
|
|
async function tryLoad(urls) {
|
|
for (const u of urls) {
|
|
try {
|
|
await loadScript(u);
|
|
} catch (_) {}
|
|
if (window.jspdf && window.jspdf.jsPDF) return true;
|
|
}
|
|
return !!(window.jspdf && window.jspdf.jsPDF);
|
|
}
|
|
|
|
|
|
function go(){
|
|
try{ window.focus(); }catch(e){}
|
|
setTimeout(function(){ window.print(); }, 120);
|
|
}
|
|
|
|
|
|
|
|
function applyRun(run) {
|
|
template = {
|
|
name: run.name || '',
|
|
steps: (run.steps || []).map(s => {
|
|
const k = s.kind || s.type || 'step';
|
|
if (k === 'group') return {
|
|
kind: 'group',
|
|
title: s.title || ''
|
|
};
|
|
return {
|
|
kind: 'step',
|
|
id: s.id,
|
|
title: s.title,
|
|
expected: s.expected,
|
|
required: !!s.required,
|
|
status: s.status || '',
|
|
comment: s.comment || '',
|
|
evidence: s.evidence || ''
|
|
};
|
|
})
|
|
};
|
|
if (els.tplName) els.tplName.textContent = run.name || '—';
|
|
if (els.module) els.module.value = run.module || '';
|
|
if (els.moduleVersion) els.moduleVersion.value = run.module_version || '';
|
|
if (els.pbxVersion) els.pbxVersion.value = run.pbx_version || '';
|
|
if (els.tester) els.tester.value = run.tester || '';
|
|
if (els.docbeeUrl) els.docbeeUrl.value = run.docbee_url || '';
|
|
renderSteps(template.steps);
|
|
recomputeGroupStyles();
|
|
if (els.statusTag) els.statusTag.textContent = 'Lauf geladen';
|
|
updateAutosave();
|
|
}
|
|
|
|
|
|
|
|
function applyTemplateOnly(tplObj) {
|
|
template = {
|
|
name: tplObj.name || '',
|
|
steps: (tplObj.steps || []).map(s => {
|
|
const k = s.kind || s.type || 'step';
|
|
if (k === 'group') return {
|
|
kind: 'group',
|
|
title: s.title || ''
|
|
};
|
|
return {
|
|
kind: 'step',
|
|
id: s.id || '',
|
|
title: s.title || '',
|
|
expected: s.expected || '',
|
|
required: !!s.required,
|
|
status: '',
|
|
comment: '',
|
|
evidence: ''
|
|
};
|
|
})
|
|
};
|
|
if (els.tplName) els.tplName.textContent = template.name || '—';
|
|
if (els.module) els.module.value = tplObj.module || '';
|
|
if (els.moduleVersion) els.moduleVersion.value = tplObj.module_version || '';
|
|
if (els.pbxVersion) els.pbxVersion.value = tplObj.pbx_version || '';
|
|
renderSteps(template.steps);
|
|
recomputeGroupStyles();
|
|
if (els.statusTag) els.statusTag.textContent = 'Template geladen';
|
|
updateAutosave();
|
|
}
|
|
|
|
|
|
function collectTemplateFromDOM() {
|
|
if (!template) return {
|
|
name: '',
|
|
module: '',
|
|
module_version: '',
|
|
pbx_version: '',
|
|
olm_nummer: '',
|
|
steps: []
|
|
};
|
|
captureEditsIntoTemplate();
|
|
renumberSteps();
|
|
return {
|
|
name: template?.name || els.tplName?.textContent || '',
|
|
module: els.module.value,
|
|
module_version: els.moduleVersion.value,
|
|
pbx_version: els.pbxVersion.value,
|
|
olm_nummer: els.olmNummer ? els.olmNummer.value : '',
|
|
steps: template.steps.map(s => {
|
|
if ((s.kind || 'step') === 'group') return {
|
|
type: 'group',
|
|
title: s.title
|
|
};
|
|
return {
|
|
type: 'step',
|
|
id: s.id,
|
|
title: s.title,
|
|
expected: s.expected,
|
|
required: !!s.required
|
|
};
|
|
})
|
|
};
|
|
}
|
|
|
|
|
|
function collectRun() {
|
|
if (!template) return null;
|
|
captureEditsIntoTemplate();
|
|
return {
|
|
name: template?.name || '',
|
|
module: els.module.value,
|
|
module_version: els.moduleVersion.value,
|
|
pbx_version: els.pbxVersion.value,
|
|
olm_nummer: els.olmNummer ? els.olmNummer.value : '',
|
|
tester: els.tester.value,
|
|
docbee_url: els.docbeeUrl.value,
|
|
ts: new Date().toISOString(),
|
|
steps: [...template.steps]
|
|
};
|
|
}
|
|
|
|
|
|
|
|
function appendResultLink(createdJSON) {
|
|
let link = null;
|
|
try {
|
|
const j = JSON.parse(createdJSON || "{}");
|
|
link = j?.link || null;
|
|
} catch {}
|
|
|
|
// Ticket-ID aus dem Eingabefeld (falls vorhanden) extrahieren
|
|
const ticketId = extractTicketId(els.docbeeUrl?.value || '');
|
|
|
|
// Ziel-URL bestimmen:
|
|
// 1) Falls API bereits einen Pfad liefert (z. B. "ticket/show/123"), normieren.
|
|
// 2) Sonst UI-Link über bekannte Struktur /ticket/show/%ID% bauen.
|
|
let url = null;
|
|
if (link) {
|
|
if (/^https?:\/\//i.test(link)) {
|
|
url = link; // bereits absolute URL
|
|
} else {
|
|
url = DOCBEE_UI_BASE.replace(/\/+$/, '') + '/' + String(link).replace(/^\/+/, '');
|
|
}
|
|
} else if (ticketId) {
|
|
url = `${DOCBEE_UI_BASE.replace(/\/+$/,'')}/ticket/show/${ticketId}`;
|
|
}
|
|
if (!url) return;
|
|
|
|
const hint = document.createElement('div');
|
|
hint.className = 'docbee-hint';
|
|
hint.innerHTML = `✅ Angelegt: <a href="${url}" target="_blank" rel="noopener">${url}</a>`;
|
|
document.querySelector('.actions')?.appendChild(hint);
|
|
}
|
|
|
|
|
|
function formatDocBeeMessage(run) {
|
|
const pad = (s, n) => (s || '').length > n ? (s || '').slice(0, n - 1) + '…' : (s || '').padEnd(n, ' ');
|
|
const fmtDate = new Date(run.ts).toLocaleString('de-DE');
|
|
|
|
// Kurz-Summary (nur echte Steps zählen)
|
|
const counts = {
|
|
pass: 0,
|
|
fail: 0,
|
|
skip: 0,
|
|
blocked: 0
|
|
};
|
|
(run.steps || []).forEach(s => {
|
|
const k = s.kind || s.type || 'step';
|
|
if (k !== 'step') return;
|
|
if (counts[s.status] !== undefined) counts[s.status]++;
|
|
});
|
|
const summary = `✅ ${counts.pass} | ❌ ${counts.fail} | ⏭️ ${counts.skip} | ⛔ ${counts.blocked}`;
|
|
|
|
// Kopf + Metadaten (out VOR jeglicher Nutzung initialisieren)
|
|
let out = '';
|
|
out += `QA REPORT\n`;
|
|
out += `===========\n`;
|
|
out += `Modul: ${run.module || ''}\n`;
|
|
out += `Modul-Version:${run.module_version || ''}\n`;
|
|
out += `PBX-Version: ${run.pbx_version || ''}\n`;
|
|
out += `Tester: ${run.tester || ''}\n`;
|
|
if (run.docbee_url) out += `Ticket: ${run.docbee_url}\n`;
|
|
out += `Datum: ${fmtDate}\n\n`;
|
|
out += `Übersicht: ${summary}\n\n`;
|
|
|
|
// Tabelle (monospace-geeignet)
|
|
const SEP = '────────────────────────────────────────────────────────────────────────';
|
|
out += `${SEP}\n`;
|
|
out += `${pad('Schritt', 12)} ${pad('Status', 7)} ${pad('Titel', 52)}\n`;
|
|
out += `${SEP}\n`;
|
|
|
|
(run.steps || []).forEach(s => {
|
|
const k = s.kind || s.type || 'step';
|
|
if (k === 'group') {
|
|
out += `\n## ${s.title || ''}\n\n`;
|
|
return;
|
|
}
|
|
const st = (s.status || '').toUpperCase(); // PASS/FAIL/SKIP/BLOCKED/…
|
|
const stShort = st === 'BLOCKED' ? 'BLOCK' : st;
|
|
const SMAP = {
|
|
pass: '✅',
|
|
fail: '❌',
|
|
skip: '⏭️',
|
|
blocked: '⛔'
|
|
};
|
|
const stLabel = (SMAP[(s.status || '').toLowerCase()] ?
|
|
SMAP[(s.status || '').toLowerCase()] + ' ' :
|
|
'') + stShort;
|
|
const req = s.required ? '📌 ' : '';
|
|
out += `${pad(s.id || '', 12)} ${pad(stLabel, 9)} ${pad(req + (s.title || ''), 50)}\n`;
|
|
if (s.comment) out += ` • Kommentar: ${s.comment}\n`;
|
|
if (s.evidence) out += ` • Evidenz: ${s.evidence}\n`;
|
|
});
|
|
out += `${SEP}\n`;
|
|
out += `Legende: PASS=✅, FAIL=❌, SKIP=⏭️, BLOCK=⛔\n`;
|
|
return out;
|
|
}
|
|
|
|
|
|
|
|
|
|
async function postToDocBee() {
|
|
const run = collectRun();
|
|
if (!run) return;
|
|
if (!checkRequired(run)) return;
|
|
|
|
const ticketId = extractTicketId(els.docbeeUrl?.value || '');
|
|
if (!ticketId) {
|
|
alert("Keine Ticket-ID in der DocBee-URL gefunden.");
|
|
return;
|
|
}
|
|
if (!hasToken()) {
|
|
alert("Kein API-Token konfiguriert.");
|
|
return;
|
|
}
|
|
|
|
// Kommentarinhalt (Markdown)
|
|
const content = formatDocBeeMessage(run);
|
|
|
|
// alten Vorgangs-Status holen (Vorgangs-Status = ticketStatus)
|
|
const tGet = await getJSON(`${DOCBEE_BASEURL}/restApi/v1/ticket/${ticketId}?fields=ticketStatus`);
|
|
const prevStatusId = tGet?.json?.ticketStatus ?? null;
|
|
|
|
// Busy-State
|
|
els.btnPushDocBee?.setAttribute('disabled', '');
|
|
els.btnPushDocBee?.classList.add('is-busy');
|
|
|
|
try {
|
|
|
|
// *** Message posten (muss Message sein) ***
|
|
const url = `${DOCBEE_BASEURL}/restApi/v1/ticket/${ticketId}/message`;
|
|
const rMsg = await postJSON(url, {
|
|
content: content,
|
|
subject: `QA Report ${ticketId}`,
|
|
internal: true,
|
|
hidden: false
|
|
});
|
|
|
|
if (rMsg.ok) {
|
|
// kurze Wartezeit, dann Status ggf. zurücksetzen (gegen Auto-"Antwort erhalten")
|
|
await sleep(800);
|
|
if (prevStatusId != null) {
|
|
await restoreTicketStatus(ticketId, prevStatusId);
|
|
}
|
|
appendResultLink(rMsg.text);
|
|
alert("Nachricht im Ticket angelegt (Status beibehalten).");
|
|
return;
|
|
}
|
|
|
|
|
|
if (ENABLE_FALLBACK_NOTE) {
|
|
const rNote = await postJSON(`${DOCBEE_BASEURL}/restApi/v1/note`, {
|
|
note: {
|
|
ticket: {
|
|
id: Number(ticketId)
|
|
},
|
|
subject: `QA Report ${ticketId}`,
|
|
text: content,
|
|
internal: false
|
|
}
|
|
});
|
|
if (rNote.ok) {
|
|
appendResultLink(rNote.text);
|
|
alert("Notiz angelegt (Fallback).");
|
|
return;
|
|
}
|
|
alert(`Fehler: MSG ${rMsg.status}, NOTE ${rNote.status}`);
|
|
} else {
|
|
alert(`Nachricht fehlgeschlagen (Status ${rMsg.status}). Details siehe Konsole.`);
|
|
}
|
|
} catch (e) {
|
|
alert("DocBee-Request fehlgeschlagen: " + String(e));
|
|
} finally {
|
|
els.btnPushDocBee?.removeAttribute('disabled');
|
|
els.btnPushDocBee?.classList.remove('is-busy');
|
|
}
|
|
} |