function createSearchWeekPlan({week,claim,accept=item=>item,identity,maxItems=5}={}) { let running=null; const eligible=detail=>Boolean(detail&&detail.kind==='issue'&&detail.state==='open'&& (detail.claimable||detail.assigned_to_me)); const loadDay=date=>{ const value=week.day(date),estimates=value.estimates||{}; return {...value,planned_minutes:(value.ids||[]).reduce((total,id)=>total+(Number(estimates[id])||0),0)}; }; async function preview(detail) { if(!eligible(detail))return {eligible:false,days:[]}; await week.load(); const id=identity(detail),existing=week.placement(id); return {eligible:true,existing,days:week.dates().map(item=>({...item,...loadDay(item.date)}))}; } function plan(detail,options={}) { if(running)return running; const perform=async()=>{ if(!eligible(detail))return {status:'ineligible'}; const estimate=Number(options.estimate); if(!Number.isFinite(estimate)||estimate<=0)return {status:'estimate-required'}; await week.load(); if(!week.dates().some(item=>item.date===options.date))return {status:'date-required'}; const originalId=identity(detail),existing=week.placement(originalId); if(existing&&existing.date!==options.date&&!options.move) return {status:'already-planned',date:existing.date,estimate:existing.estimate}; const destination=loadDay(options.date); const alreadyThere=(destination.ids||[]).includes(originalId); if(!alreadyThere&&(destination.ids||[]).length>=maxItems)return {status:'day-full',limit:maxItems}; const previous=alreadyThere?(Number(destination.estimates?.[originalId])||0):0; const plannedMinutes=destination.planned_minutes-previous+estimate; if(Number(destination.capacity_minutes)>0&&plannedMinutes>Number(destination.capacity_minutes)&&!options.confirmOverload) return {status:'overload-confirmation-required',planned_minutes:plannedMinutes, capacity_minutes:Number(destination.capacity_minutes)}; let item=detail,assigned=false; if(detail.claimable){item=await claim(detail);assigned=true;} item=accept(item)||item; const id=identity(item); if(!week.place(id,options.date,estimate,{move:Boolean(options.move)})) return {status:assigned?'assigned-not-planned':'not-planned',item}; week.rememberPendingItem?.(id,item); try { await week.flush(); return {status:'planned',date:options.date,estimate,item}; } catch(_error) { return {status:'planned-pending',date:options.date,estimate}; } }; running=perform().finally(()=>{running=null;}); return running; } return {eligible,preview,plan,pending:()=>Boolean(running)}; } function createSearchWeekPlanUI({planner,document,window,getDetail,onPlanned=()=>{},announce=()=>{},escapeHtml,escapeAttribute}={}) { const qs=selector=>document.querySelector(selector); let trigger=null,detail=null,preview=null,overload=false; function close(navigate=false) { if(navigate&&window.history.state?.searchWeekPlan){window.history.back();return;} qs('#search-week-plan-sheet').hidden=true; detail=null;preview=null;overload=false;trigger?.focus?.();trigger=null; } function selectDate(date) { qs('#search-week-plan-days').querySelectorAll('[data-search-week-date]').forEach(button=> button.setAttribute('aria-pressed',String(button.dataset.searchWeekDate===date))); qs('#search-week-plan-days').dataset.selected=date; overload=false;qs('#confirm-search-week-plan').textContent='Plan'; } async function open(button) { const selected=getDetail(); if(!selected||planner.pending())return false; trigger=button;detail=selected;overload=false; const sheet=qs('#search-week-plan-sheet'),status=qs('#search-week-plan-status'); sheet.hidden=false;status.textContent='Loading Week Ahead…';qs('#confirm-search-week-plan').disabled=true; window.history.pushState({...window.history.state,searchWeekPlan:true},'',window.location.href); qs('#search-week-plan-copy').textContent=detail.title||'Untitled issue'; try { preview=await planner.preview(detail); const existing=preview.existing; qs('#search-week-plan-days').innerHTML=preview.days.map((day,index)=> '').join(''); qs('#search-week-plan-days').querySelectorAll('[data-search-week-date]').forEach(dayButton=> dayButton.addEventListener('click',()=>selectDate(dayButton.dataset.searchWeekDate))); selectDate(existing?.date||preview.days[0].date); qs('#search-week-plan-estimate').value=existing?.estimate||''; status.textContent=existing?'Already planned for '+existing.date+'. Choose another day to move it.':'Choose a day and add an estimate.'; qs('#confirm-search-week-plan').disabled=false;qs('#search-week-plan-estimate').focus();return true; } catch(error) {status.textContent=(error.message||'Week Ahead is unavailable.')+' Retry when connected.';return false;} } async function confirm() { if(!detail||planner.pending())return; const date=qs('#search-week-plan-days').dataset.selected,estimate=Number(qs('#search-week-plan-estimate').value); const existing=preview?.existing,move=Boolean(existing&&existing.date!==date); const button=qs('#confirm-search-week-plan'),status=qs('#search-week-plan-status');button.disabled=true; const outcome=await planner.plan(detail,{date,estimate,move,confirmOverload:overload}); if(outcome.status==='overload-confirmation-required'){ overload=true;button.textContent='Confirm over capacity'; status.textContent=outcome.planned_minutes+' min exceeds '+outcome.capacity_minutes+' min capacity. Confirm to plan anyway.'; } else if(outcome.status==='estimate-required')status.textContent='Enter an estimate in minutes.'; else if(outcome.status==='day-full')status.textContent='That day already has '+outcome.limit+' items. Choose another day.'; else if(outcome.status==='already-planned')status.textContent='Already planned for '+outcome.date+'. Choose Move to change the day.'; else if(outcome.status==='assigned-not-planned')status.textContent='Assigned, not planned. Find it in My Work and retry.'; else if(outcome.status==='planned-pending'){announce('Planned for '+date+' · sync pending.');onPlanned(detail,true);close(true);} else if(outcome.status==='planned'){announce('Planned for '+date+'.');onPlanned(detail,false);close(true);} else status.textContent='Could not plan this issue. Retry.'; button.disabled=false; } qs('#plan-search-result').addEventListener('click',event=>open(event.currentTarget)); qs('#cancel-search-week-plan').addEventListener('click',()=>close(true)); qs('#confirm-search-week-plan').addEventListener('click',confirm); window.addEventListener('popstate',()=>{if(!qs('#search-week-plan-sheet').hidden)close();}); document.addEventListener('keydown',event=>{ if(event.key==='Escape'&&!qs('#search-week-plan-sheet').hidden){event.preventDefault();close(true);} }); return {open,close,confirm}; } if(typeof module!=='undefined'&&module.exports){module.exports=createSearchWeekPlan;module.exports.UI=createSearchWeekPlanUI;}