186 lines
9.4 KiB
JavaScript
186 lines
9.4 KiB
JavaScript
|
|
|
||
|
|
(function () {
|
||
|
|
const SAG_ID = {{ case.id }};
|
||
|
|
let _historyLoaded = false;
|
||
|
|
|
||
|
|
window.rewriteCaseDescriptionWithApproval = async function () {
|
||
|
|
const ta = document.getElementById('beskrivelse-textarea');
|
||
|
|
const rewriteBtn = document.getElementById('beskrivelse-rewrite-btn');
|
||
|
|
if (!ta) return;
|
||
|
|
|
||
|
|
const source = (ta.value || '').trim();
|
||
|
|
if (!source) {
|
||
|
|
if (typeof showNotification === 'function') showNotification('Skriv en beskrivelse først', 'warning');
|
||
|
|
else alert('Skriv en beskrivelse først');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const originalHtml = rewriteBtn?.innerHTML || '';
|
||
|
|
if (rewriteBtn) {
|
||
|
|
rewriteBtn.disabled = true;
|
||
|
|
rewriteBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Renskriver...';
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
const rewriteEndpoints = ['/api/v1/rewrite-text', '/api/v1/sag/rewrite-text', '/api/v1/emails/rewrite-text'];
|
||
|
|
let payload = null;
|
||
|
|
let lastError = null;
|
||
|
|
|
||
|
|
for (const endpoint of rewriteEndpoints) {
|
||
|
|
const response = await fetch(endpoint, {
|
||
|
|
method: 'POST',
|
||
|
|
credentials: 'include',
|
||
|
|
headers: { 'Content-Type': 'application/json' },
|
||
|
|
body: JSON.stringify({ text: source, context: 'case' })
|
||
|
|
});
|
||
|
|
|
||
|
|
if (response.ok) {
|
||
|
|
payload = await response.json();
|
||
|
|
lastError = null;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
let detail = `HTTP ${response.status}`;
|
||
|
|
try {
|
||
|
|
const err = await response.json();
|
||
|
|
if (err?.detail) detail = err.detail;
|
||
|
|
} catch (_) {}
|
||
|
|
|
||
|
|
lastError = new Error(detail);
|
||
|
|
|
||
|
|
// Retry next endpoint for common route mismatch cases.
|
||
|
|
if (![404, 405].includes(response.status)) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!payload) {
|
||
|
|
throw lastError || new Error('Kunne ikke hente renskrivningsforslag');
|
||
|
|
}
|
||
|
|
|
||
|
|
const rewrittenRaw = String(payload?.rewritten_text || '').trim();
|
||
|
|
const descMatch = rewrittenRaw.match(/(?:^|\n)Beskrivelse:\s*\n([\s\S]*)$/i);
|
||
|
|
const rewritten = descMatch?.[1] ? descMatch[1].trim() : rewrittenRaw;
|
||
|
|
|
||
|
|
openRewriteReviewModal({
|
||
|
|
title: 'Sagsbeskrivelse',
|
||
|
|
originalText: source,
|
||
|
|
rewrittenText: rewritten,
|
||
|
|
applyToTarget: (nextText) => {
|
||
|
|
ta.value = nextText;
|
||
|
|
bootstrap.Modal.getOrCreateInstance(document.getElementById('rewritePreviewModal')).hide();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} catch (e) {
|
||
|
|
console.error(e);
|
||
|
|
if (typeof showNotification === 'function') showNotification('Kunne ikke renskrive beskrivelse', 'error');
|
||
|
|
else alert(`Kunne ikke renskrive beskrivelse: ${e.message || 'Ukendt fejl'}`);
|
||
|
|
} finally {
|
||
|
|
if (rewriteBtn) {
|
||
|
|
rewriteBtn.disabled = false;
|
||
|
|
rewriteBtn.innerHTML = originalHtml;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
window.startBeskrivelsEdit = function () {
|
||
|
|
const current = document.getElementById('beskrivelse-text').innerText.trim();
|
||
|
|
document.getElementById('beskrivelse-textarea').value = current;
|
||
|
|
document.getElementById('beskrivelse-view').classList.add('d-none');
|
||
|
|
document.getElementById('beskrivelse-edit-btn')?.classList.add('d-none');
|
||
|
|
document.getElementById('beskrivelse-editor').classList.remove('d-none');
|
||
|
|
document.getElementById('beskrivelse-textarea').focus();
|
||
|
|
};
|
||
|
|
|
||
|
|
window.cancelBeskrivelsEdit = function () {
|
||
|
|
document.getElementById('beskrivelse-editor').classList.add('d-none');
|
||
|
|
document.getElementById('beskrivelse-view').classList.remove('d-none');
|
||
|
|
document.getElementById('beskrivelse-edit-btn')?.classList.remove('d-none');
|
||
|
|
};
|
||
|
|
|
||
|
|
window.saveBeskrivelsEdit = async function () {
|
||
|
|
const ta = document.getElementById('beskrivelse-textarea');
|
||
|
|
const saveBtn = document.getElementById('beskrivelse-save-btn');
|
||
|
|
const newVal = ta.value;
|
||
|
|
if (saveBtn) { saveBtn.disabled = true; saveBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Gemmer...'; }
|
||
|
|
try {
|
||
|
|
const res = await fetch(`/api/v1/sag/${SAG_ID}/beskrivelse`, {
|
||
|
|
method: 'PATCH',
|
||
|
|
credentials: 'include',
|
||
|
|
headers: { 'Content-Type': 'application/json' },
|
||
|
|
body: JSON.stringify({ beskrivelse: newVal })
|
||
|
|
});
|
||
|
|
if (!res.ok) throw new Error(await res.text());
|
||
|
|
const data = await res.json();
|
||
|
|
// Update view
|
||
|
|
const textEl = document.getElementById('beskrivelse-text');
|
||
|
|
textEl.innerText = data.beskrivelse || '';
|
||
|
|
const emptyEl = document.getElementById('beskrivelse-empty');
|
||
|
|
if (emptyEl) emptyEl.style.display = data.beskrivelse ? 'none' : '';
|
||
|
|
cancelBeskrivelsEdit();
|
||
|
|
// Show history and mark stale
|
||
|
|
document.getElementById('beskrivelse-history-wrap').classList.remove('d-none');
|
||
|
|
_historyLoaded = false;
|
||
|
|
if (typeof showNotification === 'function') showNotification('Beskrivelse gemt', 'success');
|
||
|
|
} catch (e) {
|
||
|
|
console.error(e);
|
||
|
|
if (typeof showNotification === 'function') showNotification('Kunne ikke gemme beskrivelse', 'error');
|
||
|
|
} finally {
|
||
|
|
if (saveBtn) { saveBtn.disabled = false; saveBtn.innerHTML = '<i class="bi bi-check2 me-1"></i>Gem'; }
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
window.loadBeskrivelsHistory = async function () {
|
||
|
|
if (_historyLoaded) return;
|
||
|
|
const list = document.getElementById('beskrivelse-history-list');
|
||
|
|
try {
|
||
|
|
const res = await fetch(`/api/v1/sag/${SAG_ID}/beskrivelse/history`, { credentials: 'include' });
|
||
|
|
if (!res.ok) throw new Error('failed');
|
||
|
|
const rows = await res.json();
|
||
|
|
_historyLoaded = true;
|
||
|
|
const label = document.getElementById('beskrivelse-history-label');
|
||
|
|
if (!rows.length) {
|
||
|
|
label.textContent = 'Historik (0)';
|
||
|
|
list.innerHTML = '<div class="list-group-item text-muted text-center py-2 small">Ingen historik endnu.</div>';
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
label.textContent = `Historik (${rows.length})`;
|
||
|
|
const esc = s => String(s || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||
|
|
const trunc = (s, n) => s && s.length > n ? s.substring(0, n) + '…' : (s || '');
|
||
|
|
list.innerHTML = rows.map(h => {
|
||
|
|
const d = new Date(h.changed_at);
|
||
|
|
const when = d.toLocaleDateString('da-DK', {day:'2-digit',month:'2-digit',year:'numeric'})
|
||
|
|
+ ' ' + d.toLocaleTimeString('da-DK', {hour:'2-digit',minute:'2-digit'});
|
||
|
|
const who = esc(h.changed_by_name || 'Ukendt');
|
||
|
|
const before = h.beskrivelse_before ? esc(trunc(h.beskrivelse_before, 150)) : '<em class="text-muted">tom</em>';
|
||
|
|
const after = h.beskrivelse_after ? esc(trunc(h.beskrivelse_after, 150)) : '<em class="text-muted">tom</em>';
|
||
|
|
return `<div class="list-group-item px-3 py-2">
|
||
|
|
<div class="d-flex justify-content-between mb-1">
|
||
|
|
<span class="fw-semibold small">${who}</span>
|
||
|
|
<span class="text-muted small">${when}</span>
|
||
|
|
</div>
|
||
|
|
<div class="d-flex gap-3" style="font-size:.85rem">
|
||
|
|
<div style="flex:1"><span class="badge text-bg-danger me-1" style="font-size:.7rem">Før</span>${before}</div>
|
||
|
|
<div style="flex:1"><span class="badge text-bg-success me-1" style="font-size:.7rem">Efter</span>${after}</div>
|
||
|
|
</div>
|
||
|
|
</div>`;
|
||
|
|
}).join('');
|
||
|
|
} catch (e) {
|
||
|
|
list.innerHTML = '<div class="list-group-item text-muted text-center py-2 small">Kunne ikke indlæse historik.</div>';
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Keyboard shortcuts
|
||
|
|
document.addEventListener('keydown', function (e) {
|
||
|
|
const editor = document.getElementById('beskrivelse-editor');
|
||
|
|
if (!editor || editor.classList.contains('d-none')) return;
|
||
|
|
if (e.ctrlKey && e.key === 'Enter') { e.preventDefault(); saveBeskrivelsEdit(); }
|
||
|
|
if (e.key === 'Escape') { e.preventDefault(); cancelBeskrivelsEdit(); }
|
||
|
|
});
|
||
|
|
|
||
|
|
// Show history toggle if description already exists on page load
|
||
|
|
if ((document.getElementById('beskrivelse-text').innerText || '').trim()) {
|
||
|
|
document.getElementById('beskrivelse-history-wrap').classList.remove('d-none');
|
||
|
|
}
|
||
|
|
})();
|
||
|
|
|