feat: select matching mobile work in one step (Closes #669)
CI / lint (pull_request) Successful in 1m27s
CI / build-release (pull_request) Successful in 5s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-12 18:23:34 +00:00
1 parent 0fbe047e03
commit 8edd542379
5 files changed
+110 -1

No files matched your search

+2
View File
@@ -241,6 +241,8 @@ textarea { resize: vertical; min-height: 120px; }
.my-work-card-title { display:block; margin:5px 0; font-weight:650; }
.update-selection-controls { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.update-selection-controls button { min-height:44px; }
.selection-scope-actions { display:flex; align-items:center; gap:8px; flex-wrap:wrap; max-width:100%; }
.selection-scope-actions button { min-height:44px; max-width:100%; }
.update-selector { min-height:44px; }
.work-selector { min-height:44px; }
.update-selector, .work-selector { display:flex; align-items:center; gap:10px; padding:6px; border:1px solid #31577f; border-radius:8px; cursor:pointer; }
+15
View File
@@ -2540,6 +2540,21 @@
selectionControls.hidden = !planningQueue && (selectedWorkFilter !== 'update' || updateIds.length === 0);
qs('#select-work').hidden = !planningQueue || workSelectionState.active || selection.active;
qs('#cancel-work-selection').hidden = !workSelectionState.active;
const selectionScopeActions = qs('#selection-scope-actions');
selectionScopeActions.hidden = !workSelectionState.active;
qs('#select-matching-work').disabled = visible.length === 0 || workSelectionState.count >= workSelection.limit;
qs('#clear-work-selection').disabled = workSelectionState.count === 0;
qs('#select-matching-work').onclick = () => {
const result = workSelection.selectMany(visible);
qs('#my-work-action-status').textContent = result.status === 'limit' ?
result.count + ' matches selected. Selection is capped at ' + result.limit + ' items.' :
result.count + ' matches selected from the current queue and search.';
};
qs('#clear-work-selection').onclick = () => {
workSelection.clear();
qs('#my-work-action-status').textContent = 'Selection cleared. Choose new matches or individual work.';
qs('#select-matching-work').focus();
};
qs('#select-updates').hidden = selectedWorkFilter !== 'update' || selection.active || workSelectionState.active;
qs('#cancel-update-selection').hidden = !selection.active;
qs('#update-selection-status').textContent = workSelectionState.active ?
+4
View File
@@ -169,6 +169,10 @@
<button id="cancel-update-selection" type="button" hidden>Cancel selection</button>
<span class="small" id="update-selection-status" role="status" aria-live="polite"></span>
</div>
<div class="selection-scope-actions" id="selection-scope-actions" hidden>
<button id="select-matching-work" type="button">Select all matches</button>
<button id="clear-work-selection" type="button">Clear selection</button>
</div>
<form class="queue-finder" id="queue-finder" role="search">
<label for="queue-find-input">Find in <span id="queue-find-label">All</span></label>
<div class="queue-finder-row">
+25 -1
View File
@@ -52,6 +52,30 @@ function createWorkSelection({ limit = 50, onChange = () => {} } = {}) {
return select(item);
}
function selectMany(items) {
if (!active) return { status: 'inactive', added: 0, count: selected.size, limit: maximum };
let added = 0;
let capped = false;
for (const item of items || []) {
const id = identity(item);
if (!id || selected.has(id)) continue;
if (selected.size >= maximum) {
capped = true;
break;
}
selected.add(id);
added += 1;
}
changed();
return { status: capped ? 'limit' : 'selected', added, count: selected.size, limit: maximum };
}
function clear() {
if (!active) return snapshot();
selected.clear();
return changed();
}
function retain(items) {
const retained = new Set((items || []).map(identity).filter(Boolean));
Array.from(selected).forEach(id => { if (!retained.has(id)) selected.delete(id); });
@@ -62,7 +86,7 @@ function createWorkSelection({ limit = 50, onChange = () => {} } = {}) {
return { active, count: selected.size, ids: Array.from(selected) };
}
return { identity, start, cancel, select, toggle, retain, snapshot, limit: maximum };
return { identity, start, cancel, select, selectMany, clear, toggle, retain, snapshot, limit: maximum };
}
if (typeof module !== 'undefined' && module.exports) module.exports = createWorkSelection;