85 lines
3.4 KiB
JavaScript
85 lines
3.4 KiB
JavaScript
function createCreateAndStart({ todayWork, todaySync, refresh, warm, start, announce }) {
|
|
const completed = new Set();
|
|
|
|
function available() {
|
|
return todayWork.read().length < todayWork.limit;
|
|
}
|
|
|
|
function capacity(value) {
|
|
if (String(value ?? '').trim() === '') return { valid: false, reason: 'required' };
|
|
const minutes = Number(value);
|
|
if (!Number.isInteger(minutes) || minutes < 5 || minutes > 1440) {
|
|
return { valid: false, reason: 'range' };
|
|
}
|
|
const plan = todayWork.planning();
|
|
const capacityMinutes = plan.capacity_minutes;
|
|
if (!Number.isInteger(capacityMinutes)) {
|
|
return { valid: true, minutes, capacityMinutes: null, plannedMinutes: null,
|
|
remainingMinutes: null, projectedMinutes: null, fits: true };
|
|
}
|
|
const plannedMinutes = todayWork.read().reduce(
|
|
(total, identity) => total + (Number.isInteger(plan.estimates[identity]) ? plan.estimates[identity] : 0), 0
|
|
);
|
|
const remainingMinutes = capacityMinutes - plannedMinutes;
|
|
const projectedMinutes = remainingMinutes - minutes;
|
|
return { valid: true, minutes, capacityMinutes, plannedMinutes, remainingMinutes,
|
|
projectedMinutes, fits: projectedMinutes >= 0 };
|
|
}
|
|
|
|
function capacityMessage(result) {
|
|
if (!result.valid) return result.reason === 'required'
|
|
? 'Required only for Create & start.'
|
|
: 'Use a whole number from 5 to 1440 minutes.';
|
|
if (result.capacityMinutes === null) {
|
|
return result.minutes + ' min · Set a Today budget to see remaining time.';
|
|
}
|
|
return result.fits
|
|
? result.minutes + ' min · ' + result.projectedMinutes + ' min will remain in Today.'
|
|
: result.minutes + ' min · ' + Math.abs(result.projectedMinutes) + ' min over Today capacity.';
|
|
}
|
|
|
|
function complete(issue, estimateMinutes) {
|
|
const identity = todayWork.identity(issue);
|
|
if (!identity || completed.has(identity)) return 'exists';
|
|
const added = todayWork.add(issue);
|
|
if (added === 'full') {
|
|
announce('Created, but Today changed—remove an item, then add this issue.');
|
|
return 'full';
|
|
}
|
|
if (added !== 'added' && added !== 'exists') {
|
|
announce('Created, but Today could not be saved on this device.');
|
|
return 'unavailable';
|
|
}
|
|
let estimatedPlan = null;
|
|
if (Number.isInteger(estimateMinutes)) {
|
|
estimatedPlan = todayWork.planning();
|
|
estimatedPlan.estimates[identity] = estimateMinutes;
|
|
if (!todayWork.replacePlanning(estimatedPlan)) {
|
|
announce('Created and added to Today, but its estimate could not be saved on this device.');
|
|
return 'estimate-unavailable';
|
|
}
|
|
}
|
|
if (!todaySync.enqueue('add', identity)) {
|
|
announce('Created and saved to Today on this device, but account sync is unavailable.');
|
|
return 'sync-unavailable';
|
|
}
|
|
if (estimatedPlan) {
|
|
if (!todaySync.enqueueConfiguration(estimatedPlan.capacity_minutes, estimatedPlan.estimates)) {
|
|
announce('Created and estimated on this device, but Today plan sync is unavailable.');
|
|
return 'sync-unavailable';
|
|
}
|
|
}
|
|
completed.add(identity);
|
|
refresh();
|
|
todaySync.flush();
|
|
warm();
|
|
start(issue);
|
|
announce('Created, added to Today, and ready to work.');
|
|
return 'started';
|
|
}
|
|
|
|
return { available, capacity, capacityMessage, complete };
|
|
}
|
|
|
|
if (typeof module !== 'undefined' && module.exports) module.exports = createCreateAndStart;
|