/* Creation uses the existing command parser and case command endpoints. */ (function(root,factory){ const api=factory(); if(typeof module==='object'&&module.exports)module.exports=api; if(root && root.document) api.init(root); })(typeof window!=='undefined'?window:null,function(){ 'use strict'; class CommandRun { constructor(request,uuid){this.request=request;this.uuid=uuid;this.pending=null;} attach(caseId,commands){ if(!commands.length)return; this.pending={caseId,body:{request_id:this.uuid(),comment:'',internal:true,billing_mode:'automatic',commands}}; } async execute(){ if(!this.pending)return; const {caseId,body}=this.pending; await this.request(`/api/v1/sag/${caseId}/command-execute`,body); this.pending=null; return caseId; } } function init(root){ const doc=root.document, input=doc.getElementById('beskrivelse'); if(!input || !root.CaseCommands || !root.CaseCommandSuggestions)return; const config=JSON.parse(doc.getElementById('caseCreateCommandData').textContent); const values={users:(config.users||[]).map(user=>({...user,full_name:user.full_name,username:user.username||user.display_name})),groups:config.groups||[]}; const panel=doc.createElement('section');panel.className='cc-command-tools'; const toolbar=doc.createElement('div');toolbar.className='cc-command-toolbar'; const hint=doc.createElement('small');hint.textContent='Skriv / på en ny linje for handlinger.'; const help=doc.createElement('details'),summary=doc.createElement('summary');summary.textContent='/ Hjælp'; const helpList=doc.createElement('div');helpList.className='cc-command-help';help.append(summary,helpList); const suggestions=doc.createElement('div');suggestions.className='case-command-suggestions'; const preview=doc.createElement('div');preview.className='cc-command-preview';preview.setAttribute('aria-live','polite'); const recovery=doc.createElement('div');recovery.className='cc-command-recovery';recovery.hidden=true; toolbar.append(hint,help);panel.append(toolbar,suggestions,preview,recovery);input.after(panel); let active=-1, options=[],storageKey=null,prepared=null; const request=async(url,body)=>{ const response=await root.fetch(url,{method:'POST',credentials:'include',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}); const data=await response.json(); if(!response.ok){ const error=new Error(typeof data.detail==='string'?data.detail:'Kommandoerne kunne ikke udføres.'); error.definite=[400,403,404,409,422].includes(response.status); throw error; } return data; }; const run=new CommandRun(request,()=>root.crypto.randomUUID()); const ready=(async()=>{ try { const response=await root.fetch('/api/v1/auth/me',{credentials:'include'}); if(!response.ok)return; const actor=await response.json(); if(!actor.id && !actor.user_id)return; storageKey=`bmc:create-command-run:${actor.id||actor.user_id}`; const stored=root.sessionStorage.getItem(storageKey); if(stored){run.pending=JSON.parse(stored);if(run.pending.draftText)input.value=run.pending.draftText;} } catch(_) { /* The current page still supports an in-memory retry. */ } showRecovery(); })(); function persist(){try{if(storageKey){if(run.pending)root.sessionStorage.setItem(storageKey,JSON.stringify(run.pending));else root.sessionStorage.removeItem(storageKey);}}catch(_){}} function showRecovery(){ recovery.replaceChildren();recovery.hidden=!run.pending; if(!run.pending)return; input.readOnly=!run.pending.canEdit; const link=doc.createElement('a');link.href=`/sag/${run.pending.caseId}/v5`;link.textContent=`Åbn den oprettede sag #${run.pending.caseId}`; recovery.append(doc.createTextNode('Sagen er oprettet. Kommandoerne afventer. '),link); doc.getElementById('submitBtn').textContent=run.pending.canEdit?'Gem rettede kommandoer':'Prøv kommandoerne igen'; } function line(){const start=input.value.lastIndexOf('\n',input.selectionStart-1)+1;const end=input.value.indexOf('\n',input.selectionStart);return {start,text:input.value.slice(start,end<0?input.value.length:end)};} function choose(option){ if(input.readOnly)return; const current=line(); input.setRangeText(option.value,current.start+option.from,current.start+option.to,'end'); if(option.caret!==undefined){const caret=current.start+option.from+option.caret;input.setSelectionRange(caret,caret);} input.focus();update();input.dispatchEvent(new root.Event('input',{bubbles:true})); } function renderSuggestions(){ suggestions.replaceChildren();active=-1; const current=line();options=root.CaseCommandSuggestions.suggest(current.text,input.selectionStart-current.start,values); options.forEach(option=>{ const button=doc.createElement('button');button.type='button'; const title=doc.createElement('strong');title.textContent=option.label; const detail=doc.createElement('small');detail.textContent=option.hint; button.append(title,detail);button.addEventListener('click',()=>choose(option));suggestions.append(button); }); } function update(){ if(run.pending){showRecovery();if(!run.pending.canEdit)return;} const current=line(),cursor=input.selectionStart-current.start; if(cursor===current.text.length && /\s$/.test(current.text)){ const option=root.CaseCommandSuggestions.suggest(current.text,cursor,values).find(row=>row.value==='"" '&&row.from===cursor); if(option){input.setRangeText('""',input.selectionStart,input.selectionStart,'end');const caret=current.start+cursor+1;input.setSelectionRange(caret,caret);} } const parsed=root.CaseCommands.parse(input.value); if(parsed.commands.some(command=>command.type==='help'))help.open=true; preview.replaceChildren(); parsed.commands.forEach(command=>{ const chip=doc.createElement('span');chip.className='cc-command-chip'; const target=command.scope==='main'?'Ny sag':`Undersag ${command.scope+1}`; const name=root.CaseCommands.names.find(name=>({tid:'time',tildel:'assign',forventet:'estimate',undersag:'subcase',mig:'self',afventer:'waiting',løsning:'solution',udsat:'defer',fil:'file',hjælp:'help',prioritet:'priority',gruppe:'group'}[name]||name)===command.type)||command.type; chip.textContent=`/${name} · ${command.title||command.assignee||command.minutes&&command.minutes+' min'||command.date||command.action||target} · ${target}`; preview.append(chip); }); if(parsed.errors.length){const error=doc.createElement('small');error.className='text-danger';error.textContent=parsed.errors.map(error=>`Linje ${error.line}: ${error.message}`).join(' · ');preview.append(error);} renderSuggestions(); } root.CaseCommands.names.forEach(name=>{ const button=doc.createElement('button');button.type='button';button.textContent='/'+name; button.addEventListener('click',()=>{ if(input.readOnly)return; const prefix=input.value&&!input.value.endsWith('\n')?'\n':''; input.value+=prefix+'/'+name+' ';input.setSelectionRange(input.value.length,input.value.length);input.focus();update();input.dispatchEvent(new root.Event('input',{bubbles:true})); });helpList.append(button); }); input.addEventListener('input',update);input.addEventListener('click',renderSuggestions); input.addEventListener('keydown',event=>{ if(event.isComposing||event.ctrlKey||event.metaKey||event.altKey||event.shiftKey)return; if(event.key==='Escape'){suggestions.replaceChildren();options=[];active=-1;return;} if(event.key==='ArrowDown'||event.key==='ArrowUp'){ if(!options.length)return;event.preventDefault();active=(active+(event.key==='ArrowDown'?1:-1)+options.length)%options.length; [...suggestions.children].forEach((button,index)=>button.classList.toggle('is-active',index===active));return; } if(event.key!=='Enter'||input.selectionStart!==input.selectionEnd)return; const current=line(),cursor=input.selectionStart-current.start; if(active<0&&/^\s*\/\/?\S+/.test(current.text)){ let opening=-1; for(let index=0;index=0&&['"','\\'].includes(current.text[index+1])){index++;continue;} if(current.text[index]!=='"')continue; if(opening<0)opening=index; else {if(cursor>opening&&cursor<=index){event.preventDefault();const end=current.start+current.text.length;input.setRangeText('\n',end,end,'end');update();input.dispatchEvent(new root.Event('input',{bubbles:true}));return;}opening=-1;} } } if(options.length){event.preventDefault();choose(options[active>=0?active:0]);} }); root.caseCreateCommands={ ready, prepare(){ const parsed=root.CaseCommands.parse(input.value); if(parsed.errors.length)throw new Error(parsed.errors.map(error=>`Linje ${error.line}: ${error.message}`).join(' · ')); if(!parsed.comment)throw new Error('Skriv også en beskrivelse af sagen over kommandoerne.'); if(parsed.commands.some(command=>command.type==='file'))throw new Error('Vedhæft filer på sagen efter oprettelse. Fjern /fil før du opretter.'); prepared=parsed;return parsed; }, hasPending:()=>Boolean(run.pending), async created(result){run.attach(result.id,prepared?.commands||[]);if(run.pending)run.pending.draftText=input.value;persist();showRecovery();await this.retry();}, async retry(){ if(!run.pending)return; if(run.pending.canEdit){ const caseId=run.pending.caseId; const parsed=this.prepare(); run.attach(caseId,parsed.commands); // An empty command list explicitly finishes the remaining step. if(!parsed.commands.length){run.pending=null;persist();return;} run.pending.draftText=input.value;persist(); } input.readOnly=true; try {await run.execute();persist();showRecovery();} catch(error){run.pending.canEdit=Boolean(error.definite);persist();showRecovery();throw new Error(`Sag #${run.pending.caseId} er oprettet, men kommandoerne afventer: ${error.message}`);} finally {input.readOnly=Boolean(run.pending&&!run.pending.canEdit);} }, setCreating:busy=>{input.readOnly=busy||Boolean(run.pending&&!run.pending.canEdit);}, pendingCaseId:()=>run.pending?.caseId }; update(); } return {CommandRun,init}; });