bmc_hub/static/js/case-mail-templates.js

72 lines
9.2 KiB
JavaScript
Raw Permalink Normal View History

/* Case mail templates fill the established composer; sending remains explicit. */
(() => {
'use strict';
function init(){
const trigger=document.getElementById('caseMailTemplatesButton');if(!trigger)return;
const caseId=Number(location.pathname.match(/\/sag\/(\d+)/)?.[1]);if(!caseId)return;
let data=null,requestId=null;
const host=document.createElement('div');host.className='modal fade';host.id='caseMailTemplatesModal';host.tabIndex=-1;
host.setAttribute('aria-labelledby','caseMailTemplatesTitle');
host.innerHTML=`<div class="modal-dialog modal-lg modal-dialog-scrollable"><div class="modal-content"><div class="modal-header"><h5 id="caseMailTemplatesTitle" class="modal-title">Mailskabeloner</h5><button class="btn-close" type="button" data-bs-dismiss="modal" aria-label="Luk"></button></div><div class="modal-body"><p class="small text-muted" data-template-tags></p><div class="d-flex gap-2 mb-3"><input class="form-control form-control-sm" type="search" placeholder="Find skabelon…" aria-label="Find skabelon" data-template-search><button class="btn btn-sm btn-outline-primary text-nowrap" type="button" data-template-new>Ny skabelon</button></div><div data-template-list></div><form data-template-form hidden><div class="d-flex gap-2 mb-2"><button type="button" class="btn btn-sm btn-outline-secondary" data-template-preset="Oprettelsesskema">Oprettelsesskema</button><button type="button" class="btn btn-sm btn-outline-secondary" data-template-preset="Mobilportering">Mobilportering</button></div><label class="form-label small">Navn<input name="name" class="form-control form-control-sm" maxlength="255" required></label><label class="form-label small d-block">Tilgængelig for<select name="scope" class="form-select form-select-sm"><option value="customer">Kun denne kunde</option><option value="global">Alle kunder · global</option></select></label><label class="form-label small d-block">Foreslå ved tags<input name="tags" class="form-control form-control-sm" placeholder="Mobil, portering (adskil med komma)"></label><label class="form-label small d-block">Emne<input name="subject" class="form-control form-control-sm" maxlength="255" required></label><label class="form-label small d-block">Besked<textarea name="body" class="form-control form-control-sm" rows="6" required></textarea></label><p class="small text-muted">Felter: {kunde_navn}, {kontakt_navn}, {sag_id}, {sag_titel}</p><label class="form-label small d-block">Faste vedhæftninger<input name="files" class="form-control form-control-sm" type="file" multiple></label><p class="small text-muted">Vælg fx oprettelsesskema eller mobilporteringsark. Filerne gemmes med skabelonen.</p><button class="btn btn-sm btn-primary" type="submit">Gem skabelon</button> <button class="btn btn-sm btn-outline-secondary" type="button" data-template-cancel>Tilbage</button></form><div class="small mt-2" role="status" data-template-status></div></div></div></div>`;
document.body.append(host);
const find=s=>host.querySelector(s),form=find('[data-template-form]'),list=find('[data-template-list]'),status=find('[data-template-status]');
const assets=document.createElement('div');assets.className='small';assets.id='caseTemplateAttachments';
document.getElementById('caseEmailAttachmentIds')?.parentElement.append(assets);
function clearAttachments(){assets.replaceChildren();}
window.CaseMailTemplates={selectedAttachments:()=>[...assets.querySelectorAll('input:checked')].map(x=>Number(x.value)),clearAttachments};
const compose=document.getElementById('caseEmailComposeModal');
compose?.addEventListener('hidden.bs.modal',clearAttachments);
const modal=()=>bootstrap.Modal.getOrCreateInstance(host);
const message=(text,error=false)=>{status.textContent=text;status.className='small mt-2 '+(error?'text-danger':'text-muted');};
async function api(path,options){const res=await fetch(`/api/v1/sag/${caseId}/mail-templates${path}`,{credentials:'include',...options});const body=await res.json();if(!res.ok){const error=new Error(typeof body.detail==='string'?body.detail:'Skabelonen kunne ikke hentes eller gemmes');error.status=res.status;throw error;}return body;}
function render(){
list.replaceChildren();const query=find('[data-template-search]').value.toLowerCase();
const rows=(data?.templates||[]).filter(t=>`${t.name} ${t.subject} ${(t.match_tags||[]).join(' ')}`.toLowerCase().includes(query));
if(!rows.length){list.textContent='Ingen skabeloner fundet. Opret en skabelon med Ny skabelon.';return;}
rows.forEach(t=>{
const card=document.createElement('div');card.className='border rounded p-2 mb-2';
const title=document.createElement('strong');title.textContent=t.name;
const scope=document.createElement('span');scope.className='badge ms-2 '+(t.customer_id?'text-bg-info':'text-bg-secondary');scope.textContent=t.customer_id?'Kun denne kunde':'Global';
const detail=document.createElement('div');detail.className='small text-muted mt-1';detail.textContent=t.recommended?`Anbefalet · matcher ${t.matched_tags.join(', ')}`:'Ingen matchende tags';
const subject=document.createElement('div');subject.className='small';subject.textContent=t.subject;
const button=document.createElement('button');button.type='button';button.className='btn btn-sm btn-outline-primary mt-2';button.textContent=`Brug skabelon${t.attachment_count?` · ${t.attachment_count} filer`:''}`;
button.addEventListener('click',async()=>{
button.disabled=true;message('Henter skabelon…');
try{
const template=await api('/'+t.id);
host.addEventListener('hidden.bs.modal',()=>{
startNewEmailThread();clearAttachments();
document.getElementById('caseEmailSubject').value=template.subject;
document.getElementById('caseEmailBody').value=template.body;
if(/\{\{[^}]+\}\}/.test(template.subject+' '+template.body)){const warning=document.createElement('p');warning.className='text-warning';warning.textContent='Skabelonen indeholder felter, der skal udfyldes manuelt før afsendelse.';assets.append(warning);}
if(template.to)document.getElementById('caseEmailTo').value=template.to;
template.attachments.forEach(a=>{
const label=document.createElement('label');label.className='d-flex align-items-center gap-1 mt-1';
const box=document.createElement('input');box.type='checkbox';box.checked=true;box.value=a.id;
const name=document.createElement('span');name.textContent=a.filename;
const link=document.createElement('a');link.href=`/api/v1/sag/${caseId}/mail-template-files/${a.id}`;link.textContent='Hent';link.className='ms-auto';
label.append(box,name,link);assets.append(label);
});
},{once:true});modal().hide();message('');
}catch(error){message(error.message,true);}finally{button.disabled=false;}
});card.append(title,scope,detail,subject,button);list.append(card);
});
}
async function load(){data=await api('');find('[data-template-tags]').textContent=`Sagens tags: ${data.tags.join(', ')||'Ingen tags'}. Skabeloner med match vises først.`;render();}
trigger.addEventListener('click',async()=>{modal().show();form.hidden=true;list.hidden=false;message('Indlæser…');try{await load();message('');}catch(error){message(error.message,true);}});
find('[data-template-search]').addEventListener('input',render);
function showForm(){form.reset();requestId=crypto.randomUUID();form.hidden=false;list.hidden=true;form.elements.tags.value=(data?.tags||[]).join(', ');form.elements.subject.value=document.getElementById('caseEmailSubject').value;form.elements.body.value=document.getElementById('caseEmailBody').value;form.elements.scope.value=data?.customer_id?'customer':'global';form.elements.scope.options[0].disabled=!data?.customer_id;message('');}
find('[data-template-new]').addEventListener('click',showForm);
host.querySelectorAll('[data-template-preset]').forEach(button=>button.addEventListener('click',()=>{const name=button.dataset.templatePreset;form.elements.name.value=name;form.elements.subject.value=`${name} · {kunde_navn}`;form.elements.body.value='Hej {kontakt_navn},\n\n';form.elements.tags.value=name==='Mobilportering'?'mobil, portering':'oprettelse';}));
find('[data-template-cancel]').addEventListener('click',()=>{form.hidden=true;list.hidden=false;message('');});
form.addEventListener('submit',async event=>{
event.preventDefault();const save=form.querySelector('[type=submit]');message('Gemmer…');
const payload={request_id:requestId,name:form.elements.name.value,subject:form.elements.subject.value,body:form.elements.body.value,scope:form.elements.scope.value,match_tags:form.elements.tags.value.split(',').map(x=>x.trim()).filter(Boolean)};
const body=new FormData();body.append('data',JSON.stringify(payload));for(const file of form.elements.files.files)body.append('files',file);
const controls=[...form.querySelectorAll('input,textarea,select,button')];const disabled=controls.map(x=>x.disabled);controls.forEach(x=>x.disabled=true);
try{await api('',{method:'POST',body});await load();form.hidden=true;list.hidden=false;message('Skabelonen er gemt.');}catch(error){message(error.message,true);if(error.status && error.status<500)requestId=crypto.randomUUID();}finally{controls.forEach((x,i)=>x.disabled=disabled[i]);}
});
}
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init();
})();