diff --git a/frontend/dashboard.css b/frontend/dashboard.css index c000ce3..ce0daf8 100644 --- a/frontend/dashboard.css +++ b/frontend/dashboard.css @@ -539,6 +539,9 @@ textarea { resize: vertical; min-height: 120px; } #issue-sheet.read-only .detail-defer, #issue-sheet.read-only #issue-blockers, #issue-sheet.read-only #retry-issue-comment-actions { display:none; } +.filed-claim-actions { position:sticky; bottom:0; z-index:4; box-sizing:border-box; width:min(560px,100%); display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px; margin-top:14px; padding:10px 4px calc(10px + env(safe-area-inset-bottom)); background:rgba(11,21,38,.98); border-top:1px solid #4ade80; } +.filed-claim-actions[hidden] { display:none; } +.filed-claim-actions button { min-width:0; min-height:44px; } .issue-sheet-actions { position:sticky; bottom:0; z-index:3; display:grid; gap:8px; margin-top:14px; padding:10px 4px; padding-bottom:calc(10px + env(safe-area-inset-bottom)); background:rgba(11,21,38,.98); border-top:1px solid #2a496e; } .issue-sheet-actions button, .issue-sheet-actions a { min-height:44px; display:flex; align-items:center; justify-content:center; } .issue-sheet-actions a { border:1px solid #60a5fa; border-radius:10px; font-weight:700; } diff --git a/frontend/dashboard.js b/frontend/dashboard.js index f4231a2..e933f30 100644 --- a/frontend/dashboard.js +++ b/frontend/dashboard.js @@ -1799,6 +1799,35 @@ }, }); + const filedAssignAndStart = createAssignAndStart({ + available: createAndStart.available, + claim: item => findWorkController.claim(item), + start: confirmed => { + const claimed = acceptClaimedIssue(confirmed); + closeIssueSheet(false); + refreshMyWorkView(); + return createAndStart.complete(claimed); + }, + queue: confirmed => { + const claimed = acceptClaimedIssue(confirmed); + const outcome = queueToday(claimed); + closeIssueSheet(false); + refreshMyWorkView(); + openRoutedWork(claimed, qs('#my-work')); + return outcome; + }, + recover: confirmed => { + const claimed = acceptClaimedIssue(confirmed); + closeIssueSheet(false); + refreshMyWorkView(); + openRoutedWork(claimed, qs('#my-work')); + }, + announce: message => { + qs('#issue-sheet-status').textContent = message; + qs('#my-work-action-status').textContent = message; + }, + }); + const batchFindWork = createBatchFindWork({ capacity: () => Math.max(0, todayWork.limit - todayWork.read().length), owner:()=>planningOwnerLogin,timeBudget:()=>{ @@ -3373,6 +3402,9 @@ candidate.repository === item.repository && candidate.number === item.number ); qs('#completed-filed-actions').hidden = !item.is_completed; + qs('#filed-claim-actions').hidden = true; + qs('#queue-filed-issue').disabled = true; + qs('#start-filed-issue').disabled = true; qs('#completed-filed-progress').textContent = item.is_completed ? 'Completed Filed issue ' + (completedPosition + 1) + ' of ' + completedItems.length : ''; qs('#issue-sheet-body').textContent = ''; @@ -3432,6 +3464,10 @@ ).join(' '); qs('#issue-assignees').textContent = (detail.assignees || []).length ? 'Assigned to ' + detail.assignees.join(', ') : 'No assignee reported'; + const claimableFiling = !offlineDetail && filedClaimEligible(item, detail); + qs('#filed-claim-actions').hidden = !claimableFiling; + qs('#queue-filed-issue').disabled = !claimableFiling; + qs('#start-filed-issue').disabled = !claimableFiling; if (readOnly) paintIssueConversation(issueConversation.snapshot(), null); else renderIssueConversation(issueConversation.snapshot()); qs('#open-issue-gitea').href = detail.url || item.url || '#'; @@ -5481,6 +5517,25 @@ } }); qs('#close-issue-sheet').addEventListener('click', closeIssueSheet); + function runFiledClaim(destination) { + const item = selectedIssue; + if (!item || !selectedIssueDetail || !filedClaimEligible(item, selectedIssueDetail)) return; + qs('#queue-filed-issue').disabled = true; + qs('#start-filed-issue').disabled = true; + const request = destination === 'queue' ? + filedAssignAndStart.run(item, { destination:'queue' }) : filedAssignAndStart.run(item); + request.catch(error => { + if (selectedIssue !== item) return; + qs('#issue-sheet-status').textContent = 'Claim failed; this issue is still in Filed. ' + error.message + ' Retry.'; + }).finally(() => { + if (selectedIssue === item && filedClaimEligible(item, selectedIssueDetail)) { + qs('#queue-filed-issue').disabled = false; + qs('#start-filed-issue').disabled = false; + } + }); + } + qs('#queue-filed-issue').addEventListener('click', () => runFiledClaim('queue')); + qs('#start-filed-issue').addEventListener('click', () => runFiledClaim('start')); qs('#retry-issue-load').addEventListener('click', () => { if (selectedIssue) openIssueSheet(selectedIssue, issueTrigger); }); diff --git a/frontend/filed-claim.js b/frontend/filed-claim.js new file mode 100644 index 0000000..15846e0 --- /dev/null +++ b/frontend/filed-claim.js @@ -0,0 +1,9 @@ +function filedClaimEligible(item, detail) { + return Boolean( + item?.is_filed && !item?.is_assigned && !item?.is_completed && + item?.state === 'open' && detail?.state === 'open' && + Array.isArray(detail?.assignees) && detail.assignees.length === 0 + ); +} + +if (typeof module !== 'undefined' && module.exports) module.exports = filedClaimEligible; diff --git a/frontend/index.html b/frontend/index.html index db24aca..2701660 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -698,6 +698,10 @@