feat: pull Week Ahead work into Today (Closes #1242)
This commit is contained in:
1 parent
6afdad6fe4
commit
3fd5983df6
7 files changed
+314
-13
No files matched your search
@@ -347,6 +347,8 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
.week-review-item-open:hover { background:#173453; }
|
||||
.week-review-item-open:focus-visible { outline:3px solid #93c5fd; outline-offset:2px; }
|
||||
.week-review-unplan { width:100%; min-height:44px; border-color:#6b87a6; background:transparent; color:#d7e5f5; }
|
||||
.week-review-pull { grid-column:1/-1; width:100%; min-height:44px; border-color:#60a5fa; background:#1d4f7a; color:#eff6ff; font-weight:800; }
|
||||
.week-review-unplan { grid-column:1/-1; }
|
||||
.week-unplan-receipt { margin:12px 0 6px; padding:10px 12px; border:1px solid #60a5fa; border-radius:10px; background:#112d4d; color:#dbeafe; }
|
||||
#undo-week-unplan { width:100%; min-height:44px; margin-bottom:8px; border-color:#60a5fa; background:#1d4f7a; color:#eff6ff; font-weight:800; }
|
||||
.week-start-early { display:block; width:100%; min-height:44px; margin-top:12px; border-color:#60a5fa; background:#1d4f7a; color:#eff6ff; font-weight:800; }
|
||||
|
||||
+38
-3
@@ -464,6 +464,15 @@ function createWeekPlan({fetchJson,localDate,timeZone,storage,getLogin,now=()=>D
|
||||
plan_date:due.plan_date,today_revision:todayRevision,
|
||||
})});
|
||||
}
|
||||
async function pullItem(identity,today,operationId) {
|
||||
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,
|
||||
})});
|
||||
adoptConfirmed(result.week,{...confirmedItems,...pendingItems});
|
||||
return result;
|
||||
}
|
||||
async function startEarly(planDate,todayRevision) {
|
||||
if(offlineSnapshot||pending()||conflict()||!Number.isInteger(todayRevision))return false;
|
||||
const day=week.days.find(item=>item.plan_date===planDate&&item.ids.length);
|
||||
@@ -494,13 +503,14 @@ function createWeekPlan({fetchJson,localDate,timeZone,storage,getLogin,now=()=>D
|
||||
return label+(pending()?' · sync pending':'');
|
||||
}
|
||||
return {adopt,state,dates,day,pass,load,saveDay,stageDay,stageCapacities,review,previewReflow,applyReflow,move,retire,unplan,restore,placement,place,pending,flush,conflict,chooseDay,saveMerged,
|
||||
keepLocal,useRemote,promote,startEarly,reconcile:reconcilePromotion,summary,rememberItems,rememberPendingItem,
|
||||
keepLocal,useRemote,promote,pullItem,startEarly,reconcile:reconcilePromotion,summary,rememberItems,rememberPendingItem,
|
||||
item:id=>pendingItems[id]||confirmedItems[id]||null,
|
||||
offline:()=>offlineSnapshot,request:fetchJson,reschedule:()=>({storage,getLogin})};
|
||||
}
|
||||
function createWeekPlanWorkflow({controller,qs,getItem=()=>null,openItem,openPlanner,setReviewMode=()=>{},escapeHtml,escapeAttribute,
|
||||
todayWork,refresh:r=()=>{},warm:w=()=>{},today:t=()=>null,r:refresh=r,w:warm=w,t:getTodayPlan=t,
|
||||
confirmEarly=message=>globalThis.confirm?.(message)??false,setTimer=globalThis.setTimeout,clearTimer=globalThis.clearTimeout,x=null}={}) {
|
||||
confirmEarly=message=>globalThis.confirm?.(message)??false,setTimer=globalThis.setTimeout,clearTimer=globalThis.clearTimeout,
|
||||
operationId=()=>globalThis.crypto?.randomUUID?.()||('pull-'+Date.now()+'-'+Math.random().toString(16).slice(2)),x=null}={}) {
|
||||
let selectedDate=null;
|
||||
let reviewing=false;
|
||||
let overviewing=false;
|
||||
@@ -689,7 +699,13 @@ function createWeekPlanWorkflow({controller,qs,getItem=()=>null,openItem,openPla
|
||||
const move=id=>overviewing?'':'<div class="week-review-move"><label>Move to <select data-week-move-destination="'+escapeAttribute(id)+'">'+destinations+
|
||||
'</select></label><button type="button" data-week-move="'+escapeAttribute(id)+'">Move</button></div>';
|
||||
const unplan=id=>overviewing&&!readOnly?'<button type="button" class="week-review-unplan" data-week-unplan="'+escapeAttribute(id)+'">Remove from week</button>':'';
|
||||
const items=day.ids.length?'<ul>'+day.ids.map(id=>'<li>'+itemMarkup(id,day)+move(id)+unplan(id)+'</li>').join('')+'</ul>':
|
||||
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 '';
|
||||
return '<button type="button" class="week-review-pull" data-week-pull-item="'+escapeAttribute(id)+'"'+(full?' disabled':'')+'>'+
|
||||
(full?'Today is full':'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>';
|
||||
const edit=readOnly?'':'<button type="button" data-week-edit-day="'+escapeAttribute(day.plan_date)+'" aria-label="Edit '+escapeAttribute(day.label)+'">Edit day</button>';
|
||||
const start=day===nextUp&&canStartEarly?'<button type="button" class="week-start-early" data-week-start-early="'+escapeAttribute(day.plan_date)+'">Start this day early</button>':'';
|
||||
@@ -732,6 +748,25 @@ function createWeekPlanWorkflow({controller,qs,getItem=()=>null,openItem,openPla
|
||||
qs('#week-review-status').textContent=(error.message||'Week Ahead sync is unavailable.')+' Removal remains saved on this phone.';
|
||||
}
|
||||
}));
|
||||
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;
|
||||
event.currentTarget.disabled=true;
|
||||
try{
|
||||
const result=await controller.pullItem?.(id,current,operationId());
|
||||
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();
|
||||
qs('#week-review-status').textContent=error?.code==='today_full'?'Today is full. Finish or move work before adding more.':
|
||||
((error.message||'Today or Week Ahead changed.')+' Reopen Week Ahead and retry.');
|
||||
}
|
||||
}));
|
||||
root.querySelectorAll('[data-week-open-item]').forEach(button=>button.addEventListener('click',event=>{
|
||||
const id=button.dataset.weekOpenItem,item=getItem(id)||controller.item?.(id);
|
||||
if(item)openItem?.(item,event.currentTarget);
|
||||
|
||||
Reference in new issue
Block a user