40 lines
5.1 KiB
JavaScript
40 lines
5.1 KiB
JavaScript
|
|
(() => {
|
|||
|
|
const $ = id => document.getElementById(`cta-${id}`);
|
|||
|
|
const esc = v => String(v ?? '').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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);
|
|||
|
|
})();
|