bmc_hub/static/js/case-template-admin.js
2026-09-08 01:55:47 +02:00

40 lines
5.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(() => {
const $ = id => document.getElementById(`cta-${id}`);
const esc = v => String(v ?? '').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
let rows = [], tags = [], selected = [];
async function api(path, options) { const r = await fetch(`/api/v1/${path}`, {credentials:'include',...options}); const data = await r.json(); if (!r.ok) throw new Error(typeof data.detail === 'string' ? data.detail : 'Kontrollér skabelonens felter'); return data; }
function message(text, error=false) { $('message').textContent=text; $('message').className=error?'alert alert-danger':'alert alert-success'; }
function renderTags() { $('tags').innerHTML=selected.map(id=>`<button type="button" class="btn btn-sm btn-outline-secondary me-1" data-tag="${id}">${esc(tags.find(t=>t.id===id)?.name || `Tag #${id}`)} ×</button>`).join(''); }
function edit(row) {
$('form').reset(); const v=row?.values || {};
for (const [key,value] of Object.entries({id:row?.id || '',name:row?.name || '',icon:row?.icon || 'bi-lightning',sort:row?.sort_order || 0,type:v.type || 'ticket',status:v.status || 'åben',group:v.assigned_group_id || '',title:v.titel || '',description:v.beskrivelse || '',stage:v.pipeline?.stage_id || '',amount:v.pipeline?.amount ?? '',probability:v.pipeline?.probability ?? '', 'pipeline-description':v.pipeline?.description || ''})) $(key).value=value;
$('active').checked=row?.is_active ?? true; selected=[...(v.tag_ids || [])]; renderTags();
}
async function load() { rows=await api('case-create/templates/admin'); $('list').innerHTML=rows.map(r=>`<div class="list-group-item d-flex align-items-center gap-2"><button type="button" class="btn btn-link flex-grow-1 text-start" data-edit="${r.id}"><i class="bi ${esc(r.icon)} me-2"></i>${esc(r.name)} · ${r.is_active?'Aktiv':'Inaktiv'} · ${r.sort_order}</button><button type="button" class="btn btn-sm btn-outline-danger" data-delete="${r.id}">Slet</button></div>`).join('') || '<p class="text-muted">Ingen skabeloner endnu.</p>'; }
async function init() {
try {
const [types, groups, stages, tagRows] = await Promise.all([api('settings/case_types'),api('case-create/template-options'),api('pipeline/stages'),api('tags?is_active=true')]);
let typeKeys; try { typeKeys=JSON.parse(types.value || '[]'); } catch { typeKeys=[]; }
if (!Array.isArray(typeKeys) || !typeKeys.length) typeKeys=['ticket','pipeline','ordre','opgave','projekt','service','abonnement'];
$('type').innerHTML=[...new Set([...typeKeys,'pipeline','abonnement'])].map(t=>`<option value="${esc(t)}">${esc(t)}</option>`).join('');
$('group').insertAdjacentHTML('beforeend',groups.map(g=>`<option value="${g.id}">${esc(g.name)}</option>`).join(''));
$('stage').insertAdjacentHTML('beforeend',stages.map(s=>`<option value="${s.id}">${esc(s.name)}</option>`).join(''));
tags=tagRows; await load(); edit(null);
const setting = await api('settings/case_statuses');
const statuses = JSON.parse(setting.value || '[]');
if (Array.isArray(statuses)) statuses.forEach(s => { const value=typeof s==='string'?s:s.value; if(value && !Array.from($('status').options).some(o=>o.value===value)) $('status').add(new Option(typeof s==='string'?s:(s.label || value),value)); });
} catch(e) { message(e.message,true); }
$('new').onclick=()=>edit(null);
$('tags').onclick=e=>{ const b=e.target.closest('[data-tag]'); if(b){selected=selected.filter(id=>id!==Number(b.dataset.tag));renderTags();} };
$('add-tag').onclick=()=>window.tagPicker.showSelection(t=>{if(!selected.includes(t.id))selected.push(t.id);renderTags();});
$('list').onclick=async e=>{try{const b=e.target.closest('[data-edit],[data-delete]');if(!b)return;if(b.dataset.edit)edit(rows.find(r=>r.id===Number(b.dataset.edit)));else if(confirm('Slet denne fælles skabelon?')){await api(`case-create/templates/${b.dataset.delete}`,{method:'DELETE'});await load();edit(null);}}catch(err){message(err.message,true);}};
$('form').onsubmit=async e=>{e.preventDefault();const button=$('form').querySelector('[type=submit]');button.disabled=true;try{
const number=k=>$(k).value===''?null:Number($(k).value);
const pipeline={stage_id:number('stage'),amount:number('amount'),probability:number('probability'),description:$('pipeline-description').value || null};
const body={name:$('name').value.trim(),icon:$('icon').value,sort_order:number('sort') || 0,is_active:$('active').checked,values:{type:$('type').value,titel:$('title').value,beskrivelse:$('description').value,status:$('status').value,assigned_group_id:number('group'),tag_ids:selected,pipeline:Object.values(pipeline).some(v=>v!==null)?pipeline:null}};
const id=$('id').value;const saved=await api(`case-create/templates${id?`/${id}`:''}`,{method:id?'PUT':'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});await load();edit(saved);message('Skabelon gemt');
}catch(err){message(err.message,true);}finally{button.disabled=false;}};
}
document.addEventListener('DOMContentLoaded',init);
})();