(function () { 'use strict'; const DEFAULT_ACTION_CONFIG = { default_category: null, allow_company_templates: true, allow_global_templates: true }; let modalElement = null; let modalInstance = null; let currentCaseId = null; let currentAction = null; let availableTemplates = []; let currentPreview = null; function notify(message, level) { if (typeof window.showNotification === 'function') { window.showNotification(message, level || 'info'); return; } if (level === 'error') { alert(message); } } function ensureModal() { if (modalElement) return; const html = ` `; document.body.insertAdjacentHTML('beforeend', html); modalElement = document.getElementById('taskTemplateSelectorModal'); modalInstance = new bootstrap.Modal(modalElement); document.getElementById('ttSource').addEventListener('change', refreshTemplates); document.getElementById('ttCategory').addEventListener('change', refreshTemplates); document.getElementById('ttTemplateSearch').addEventListener('input', renderTemplateList); document.getElementById('ttTemplate').addEventListener('change', onTemplateSelected); document.getElementById('ttAssigneeMode').addEventListener('change', onAssigneeModeChanged); document.getElementById('ttPreviewBtn').addEventListener('click', runPreview); document.getElementById('ttRunBtn').addEventListener('click', runTemplate); document.getElementById('ttRefreshRunsBtn').addEventListener('click', loadRunHistory); modalElement.addEventListener('shown.bs.modal', () => { document.getElementById('ttTemplateSearch').focus(); }); } function todayIso() { const now = new Date(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); return `${now.getFullYear()}-${month}-${day}`; } async function fetchCase(caseId) { const response = await fetch(`/api/v1/sag/${caseId}`, { credentials: 'include' }); if (!response.ok) { throw new Error('Kunne ikke hente sag'); } return response.json(); } function getActionConfig() { const cfg = (currentAction && currentAction.config) || {}; return { ...DEFAULT_ACTION_CONFIG, ...cfg }; } async function refreshTemplates() { const source = document.getElementById('ttSource').value; const category = document.getElementById('ttCategory').value; const params = new URLSearchParams(); if (source) params.set('source', source); if (category) params.set('category', category); try { const caseRow = await fetchCase(currentCaseId); if (caseRow && caseRow.customer_id) { params.set('company_id', String(caseRow.customer_id)); } const response = await fetch(`/api/v1/task-templates?${params.toString()}`, { credentials: 'include' }); if (!response.ok) { const payload = await response.json().catch(() => ({})); throw new Error(payload.detail || 'Kunne ikke hente templates'); } availableTemplates = await response.json(); renderTemplateList(); } catch (error) { console.error('Failed to load templates:', error); availableTemplates = []; renderTemplateList(); notify(error.message || 'Kunne ikke hente templates', 'error'); } } function renderTemplateList() { const search = String(document.getElementById('ttTemplateSearch').value || '').toLowerCase().trim(); const select = document.getElementById('ttTemplate'); const emptyState = document.getElementById('ttTemplateEmpty'); const filtered = availableTemplates.filter((template) => { if (!search) return true; const haystack = `${template.name || ''} ${template.description || ''} ${template.category || ''}`.toLowerCase(); return haystack.includes(search); }); select.innerHTML = filtered.map((template) => { const scopeLabel = template.template_type === 'company' ? 'Firma' : template.template_type === 'global' ? 'Faelles' : 'Intern'; const cat = template.category || 'andet'; return ``; }).join(''); emptyState.classList.toggle('d-none', filtered.length > 0); document.getElementById('ttRunBtn').disabled = true; currentPreview = null; document.getElementById('ttPreviewSummary').textContent = 'Ingen preview endnu.'; document.getElementById('ttPreviewList').innerHTML = ''; } function onTemplateSelected() { currentPreview = null; document.getElementById('ttRunBtn').disabled = true; document.getElementById('ttPreviewSummary').textContent = 'Template valgt. Klik "Opdater preview".'; document.getElementById('ttPreviewList').innerHTML = ''; } function onAssigneeModeChanged() { const mode = document.getElementById('ttAssigneeMode').value; const userInput = document.getElementById('ttAssigneeUserId'); const roleInput = document.getElementById('ttAssigneeRoleId'); userInput.classList.toggle('d-none', mode !== 'specific_user'); roleInput.classList.toggle('d-none', mode !== 'specific_role'); } function buildPayload() { const templateId = Number(document.getElementById('ttTemplate').value); if (!templateId) { throw new Error('Vaelg en template'); } const mode = document.getElementById('ttMode').value; const assigneeMode = document.getElementById('ttAssigneeMode').value; const startDate = document.getElementById('ttStartDate').value || todayIso(); const assigneeUserIdRaw = document.getElementById('ttAssigneeUserId').value; const assigneeRoleIdRaw = document.getElementById('ttAssigneeRoleId').value; const payload = { template_id: templateId, start_date: startDate, mode, assignee_mode: assigneeMode, }; if (assigneeMode === 'specific_user') { const parsed = Number(assigneeUserIdRaw); if (!parsed) throw new Error('Udfyld medarbejder ID'); payload.assignee_user_id = parsed; } if (assigneeMode === 'specific_role') { const parsed = Number(assigneeRoleIdRaw); if (!parsed) throw new Error('Udfyld rolle ID'); payload.assignee_role_id = parsed; } return payload; } function renderPreview(preview) { const summary = preview.summary || {}; const list = Array.isArray(preview.items) ? preview.items : []; document.getElementById('ttPreviewSummary').textContent = `Du er ved at oprette: ${summary.subcases || 0} undersager, ${summary.tasks || 0} opgaver, ${summary.assignments || 0} tildelinger, ${summary.deadlines || 0} deadlines.`; document.getElementById('ttPreviewList').innerHTML = list.map((item) => { const typeLabel = item.item_type === 'subcase' ? 'Undersag' : 'Task'; return `
${escapeHtml(item.title || '')}
${escapeHtml(item.description || '')}
${typeLabel}
${escapeHtml(item.planned_due_date || '-')}
`; }).join(''); } async function runPreview() { if (!currentCaseId) return; try { const payload = buildPayload(); const response = await fetch(`/api/v1/cases/${currentCaseId}/template-preview`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(payload) }); if (!response.ok) { const body = await response.json().catch(() => ({})); throw new Error(body.detail || 'Preview fejlede'); } const preview = await response.json(); currentPreview = preview; renderPreview(preview); document.getElementById('ttRunBtn').disabled = false; } catch (error) { console.error('Preview failed:', error); notify(error.message || 'Preview fejlede', 'error'); } } async function runTemplate() { if (!currentCaseId) return; if (!currentPreview) { notify('Koer preview foerst', 'error'); return; } const runButton = document.getElementById('ttRunBtn'); runButton.disabled = true; const originalText = runButton.textContent; runButton.textContent = 'Opretter...'; try { const payload = buildPayload(); const response = await fetch(`/api/v1/cases/${currentCaseId}/run-template`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(payload) }); if (!response.ok) { const body = await response.json().catch(() => ({})); throw new Error(body.detail || 'Koersel fejlede'); } const result = await response.json(); notify(`Template koert. Oprettet ${result.summary?.tasks || 0} tasks og ${result.summary?.subcases || 0} undersager.`, 'success'); if (typeof window.syncCaseTagsUi === 'function') { window.syncCaseTagsUi(); } if (typeof window.loadTodoSteps === 'function') { window.loadTodoSteps(); } await loadRunHistory(); document.getElementById('ttRunBtn').disabled = true; document.getElementById('ttPreviewSummary').textContent = 'Template koert. Vaelg en ny template eller opdater preview igen.'; } catch (error) { console.error('Run failed:', error); notify(error.message || 'Koersel fejlede', 'error'); } finally { runButton.disabled = false; runButton.textContent = originalText; } } function escapeHtml(value) { return String(value || '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function formatDateTime(value) { if (!value) return '-'; const parsed = new Date(value); if (Number.isNaN(parsed.getTime())) return String(value); return parsed.toLocaleString('da-DK', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); } async function loadRunHistory() { const container = document.getElementById('ttRunHistory'); if (!container || !currentCaseId) return; container.innerHTML = '
Indlaeser historik...
'; try { const response = await fetch(`/api/v1/cases/${currentCaseId}/template-runs?limit=10`, { credentials: 'include' }); if (!response.ok) { const body = await response.json().catch(() => ({})); throw new Error(body.detail || 'Kunne ikke hente historik'); } const rows = await response.json(); if (!Array.isArray(rows) || rows.length === 0) { container.innerHTML = '
Ingen template-koersler endnu.
'; return; } container.innerHTML = rows.map((run) => { const status = escapeHtml(run.status || 'ukendt'); const templateName = escapeHtml(run.template_name || `Template #${run.template_id || '-'}`); const startedAt = formatDateTime(run.started_at); const taskCount = Number(run.created_tasks || 0); const subcaseCount = Number(run.created_subcases || 0); const statusClass = status === 'completed' ? 'text-success' : (status === 'failed' ? 'text-danger' : 'text-muted'); return `
${templateName}
${escapeHtml(startedAt)}
${status}
Oprettet: ${taskCount} opgaver, ${subcaseCount} undersager
${run.template_id ? `
` : ''} ${run.error_message ? `
${escapeHtml(run.error_message)}
` : ''}
`; }).join(''); } catch (error) { container.innerHTML = `
${escapeHtml(error.message || 'Kunne ikke hente historik')}
`; } } async function reuseTemplateFromHistory(templateId) { const wantedId = Number(templateId || 0); if (!wantedId) { notify('Ugyldigt template-id', 'error'); return; } const sourceSelect = document.getElementById('ttSource'); const categorySelect = document.getElementById('ttCategory'); const searchInput = document.getElementById('ttTemplateSearch'); const templateSelect = document.getElementById('ttTemplate'); // Widen filters to make sure template is available in selector. sourceSelect.value = 'all'; categorySelect.value = ''; searchInput.value = ''; await refreshTemplates(); const hasOption = Array.from(templateSelect.options).some((opt) => Number(opt.value) === wantedId); if (!hasOption) { notify('Template findes ikke i nuvaerende scope eller er deaktiveret', 'error'); return; } templateSelect.value = String(wantedId); onTemplateSelected(); await runPreview(); } async function openTaskTemplateSelectorModal(detailOrCaseId) { ensureModal(); if (typeof detailOrCaseId === 'object' && detailOrCaseId !== null) { currentCaseId = Number(detailOrCaseId.entity_id || detailOrCaseId.case_id || 0); currentAction = detailOrCaseId.action || null; } else { currentCaseId = Number(detailOrCaseId || 0); currentAction = null; } if (!currentCaseId) { notify('Kunne ikke finde sag-id til template modal', 'error'); return; } document.getElementById('ttStartDate').value = todayIso(); document.getElementById('ttTemplateSearch').value = ''; document.getElementById('ttAssigneeMode').value = 'template_default'; document.getElementById('ttAssigneeUserId').value = ''; document.getElementById('ttAssigneeRoleId').value = ''; onAssigneeModeChanged(); const actionConfig = getActionConfig(); const sourceSelect = document.getElementById('ttSource'); sourceSelect.value = 'all'; if (!actionConfig.allow_company_templates && actionConfig.allow_global_templates) { sourceSelect.value = 'global'; } if (actionConfig.allow_company_templates && !actionConfig.allow_global_templates) { sourceSelect.value = 'company'; } const categorySelect = document.getElementById('ttCategory'); categorySelect.value = actionConfig.default_category || ''; await refreshTemplates(); await loadRunHistory(); modalInstance.show(); } window.reuseCaseTemplateFromHistory = reuseTemplateFromHistory; window.openTaskTemplateSelectorModal = openTaskTemplateSelectorModal; window.addEventListener('hub:tag-action', function (event) { const detail = event && event.detail ? event.detail : null; if (!detail || !detail.action || detail.action.type !== 'open_task_template_modal') { return; } // Auto-open is disabled. Users open the selector by clicking the template tag. }); })();