feat: confirm capacity before Week-to-Today pulls (Closes #1244)
CI / lint (pull_request) Successful in 3m11s
CI / build-release (pull_request) Successful in 6s
CI / browser-journey (pull_request) Successful in 5m18s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-22 01:33:04 +00:00
1 parent d01f502e70
commit 666671ee59
6 files changed
+132 -9

No files matched your search

+12 -4
View File
@@ -464,11 +464,12 @@ function createWeekPlan({fetchJson,localDate,timeZone,storage,getLogin,now=()=>D
plan_date:due.plan_date,today_revision:todayRevision,
})});
}
async function pullItem(identity,today,operationId) {
async function pullItem(identity,today,operationId,allowOverCapacity=false) {
if(offlineSnapshot||pending()||conflict()||!identity||!operationId||
!Number.isInteger(today?.revision)||!(today.ids||[]).length)return false;
const result=await fetchJson('api/v1/week/pull-item',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({
operation_id:operationId,identity,today_revision:today.revision,week_revision:week.revision,
allow_over_capacity:Boolean(allowOverCapacity),
})});
adoptConfirmed(result.week,{...confirmedItems,...pendingItems});
return result;
@@ -702,8 +703,11 @@ function createWeekPlanWorkflow({controller,qs,getItem=()=>null,openItem,openPla
const pull=id=>{
const inToday=(today?.ids||[]).includes(id),full=(today?.ids||[]).length>=5;
if(!overviewing||readOnly||pending||!Number.isInteger(today?.revision)||!(today?.ids||[]).length||inToday)return '';
const todayMinutes=(today.ids||[]).reduce((total,itemId)=>total+(Number(today.estimates?.[itemId])||0),0);
const projected=todayMinutes+(Number(day.estimates?.[id])||0),capacity=Number(today.capacity_minutes)||0;
const overload=capacity&&projected>capacity;
return '<button type="button" class="week-review-pull" data-week-pull-item="'+escapeAttribute(id)+'"'+(full?' disabled':'')+'>'+
(full?'Today is full':'Add to Today')+'</button>';
(full?'Today is full':(overload?'Review Today · '+projected+' of '+capacity+' min':'Add to Today'))+'</button>';
};
const items=day.ids.length?'<ul>'+day.ids.map(id=>'<li>'+itemMarkup(id,day)+move(id)+pull(id)+unplan(id)+'</li>').join('')+'</ul>':
'<p class="small muted">Nothing planned.</p>';
@@ -751,15 +755,19 @@ function createWeekPlanWorkflow({controller,qs,getItem=()=>null,openItem,openPla
root.querySelectorAll('[data-week-pull-item]').forEach(button=>button.addEventListener('click',async event=>{
const id=event.currentTarget.dataset.weekPullItem,current=getTodayPlan?.();
if(!id||!Number.isInteger(current?.revision)||!(current.ids||[]).length||current.ids.length>=5)return;
const source=value.days.find(day=>day.ids.includes(id)),estimate=Number(source?.estimates?.[id])||0;
const todayMinutes=current.ids.reduce((total,itemId)=>total+(Number(current.estimates?.[itemId])||0),0);
const projected=todayMinutes+estimate,capacity=Number(current.capacity_minutes)||0;
const overCapacity=Boolean(capacity&&projected>capacity),item=getItem(id)||controller.item?.(id);
if(overCapacity&&!confirmEarly('Add '+String(item?.title||'work')+' to Today?\n\nToday will be '+projected+' of '+capacity+' min · '+(projected-capacity)+' min over capacity.'))return;
event.currentTarget.disabled=true;
try{
const result=await controller.pullItem?.(id,current,operationId());
const result=await controller.pullItem?.(id,current,operationId(),overCapacity);
if(!result)return;
getTodayPlan?.(result.today);todayWork.replace(result.today.ids);
todayWork.replacePlanning({capacity_minutes:result.today.capacity_minutes??null,estimates:result.today.estimates||{}});
refresh();warm();renderReview();
qs('#mobile-week-summary').textContent=controller.summary();
const item=getItem(id)||controller.item?.(id);
qs('#week-review-status').textContent=String(item?.title||'Work')+' added to Today.';
}catch(error){
renderReview();