feat: implement bulk feature verification and enhance selection mode

- Added functionality for bulk verifying features in the BoardView, allowing users to mark multiple features as verified at once.
- Introduced a selection target mechanism to differentiate between 'backlog' and 'waiting_approval' features during selection mode.
- Updated the KanbanCard and SelectionActionBar components to support the new selection target logic, improving user experience for bulk actions.
- Enhanced the UI to provide appropriate actions based on the current selection target, including verification options for waiting approval features.
This commit is contained in:
webdevcody
2026-01-15 13:14:15 -05:00
parent 241fd0b252
commit 06b047cfcb
10 changed files with 348 additions and 73 deletions

View File

@@ -187,6 +187,7 @@ export function BoardView() {
// Selection mode hook for mass editing
const {
isSelectionMode,
selectionTarget,
selectedFeatureIds,
selectedCount,
toggleSelectionMode,
@@ -684,6 +685,67 @@ export function BoardView() {
isPrimaryWorktreeBranch,
]);
// Get waiting_approval feature IDs in current branch for "Select All"
const allSelectableWaitingApprovalFeatureIds = useMemo(() => {
return hookFeatures
.filter((f) => {
// Only waiting_approval features
if (f.status !== 'waiting_approval') return false;
// Filter by current worktree branch
const featureBranch = f.branchName;
if (!featureBranch) {
// No branch assigned - only selectable on primary worktree
return currentWorktreePath === null;
}
if (currentWorktreeBranch === null) {
// Viewing main but branch hasn't been initialized
return currentProject?.path
? isPrimaryWorktreeBranch(currentProject.path, featureBranch)
: false;
}
// Match by branch name
return featureBranch === currentWorktreeBranch;
})
.map((f) => f.id);
}, [
hookFeatures,
currentWorktreePath,
currentWorktreeBranch,
currentProject?.path,
isPrimaryWorktreeBranch,
]);
// Handler for bulk verifying multiple features
const handleBulkVerify = useCallback(async () => {
if (!currentProject || selectedFeatureIds.size === 0) return;
try {
const api = getHttpApiClient();
const featureIds = Array.from(selectedFeatureIds);
const updates = { status: 'verified' as const };
// Use bulk update API for efficient batch processing
const result = await api.features.bulkUpdate(currentProject.path, featureIds, updates);
if (result.success) {
// Update local state for all features
featureIds.forEach((featureId) => {
updateFeature(featureId, updates);
});
toast.success(`Verified ${result.updatedCount} features`);
exitSelectionMode();
} else {
toast.error('Failed to verify some features', {
description: `${result.failedCount} features failed to verify`,
});
}
} catch (error) {
logger.error('Bulk verify failed:', error);
toast.error('Failed to verify features');
}
}, [currentProject, selectedFeatureIds, updateFeature, exitSelectionMode]);
// Handler for addressing PR comments - creates a feature and starts it automatically
const handleAddressPRComments = useCallback(
async (worktree: WorktreeInfo, prInfo: PRInfo) => {
@@ -1448,6 +1510,7 @@ export function BoardView() {
pipelineConfig={pipelineConfig}
onOpenPipelineSettings={() => setShowPipelineSettings(true)}
isSelectionMode={isSelectionMode}
selectionTarget={selectionTarget}
selectedFeatureIds={selectedFeatureIds}
onToggleFeatureSelection={toggleFeatureSelection}
onToggleSelectionMode={toggleSelectionMode}
@@ -1463,11 +1526,23 @@ export function BoardView() {
{isSelectionMode && (
<SelectionActionBar
selectedCount={selectedCount}
totalCount={allSelectableFeatureIds.length}
onEdit={() => setShowMassEditDialog(true)}
onDelete={handleBulkDelete}
totalCount={
selectionTarget === 'waiting_approval'
? allSelectableWaitingApprovalFeatureIds.length
: allSelectableFeatureIds.length
}
onEdit={selectionTarget === 'backlog' ? () => setShowMassEditDialog(true) : undefined}
onDelete={selectionTarget === 'backlog' ? handleBulkDelete : undefined}
onVerify={selectionTarget === 'waiting_approval' ? handleBulkVerify : undefined}
onClear={clearSelection}
onSelectAll={() => selectAll(allSelectableFeatureIds)}
onSelectAll={() =>
selectAll(
selectionTarget === 'waiting_approval'
? allSelectableWaitingApprovalFeatureIds
: allSelectableFeatureIds
)
}
mode={selectionTarget === 'waiting_approval' ? 'waiting_approval' : 'backlog'}
/>
)}