bmc_hub/static/js/case-template-admin.js

40 lines
5.1 KiB
JavaScript
Raw Permalink Normal View History

(() => {
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);
})();