Skip to content

feat(buckets): bulk delete buckets by host (closes #662)#821

Merged
ErikBjare merged 5 commits into
ActivityWatch:masterfrom
TimeToBuildBob:bob/issue-662-delete-buckets-by-host
May 19, 2026
Merged

feat(buckets): bulk delete buckets by host (closes #662)#821
ErikBjare merged 5 commits into
ActivityWatch:masterfrom
TimeToBuildBob:bob/issue-662-delete-buckets-by-host

Conversation

@TimeToBuildBob

Copy link
Copy Markdown
Contributor

Summary

  • Add a "Delete all buckets for this host" button to each device card on the Buckets view
  • New confirmation modal lists the affected bucket IDs before deletion
  • New deleteBucketsByHost({ hostname }) store action deletes all buckets for a host and reloads once at the end (one round-trip instead of N)

Closes #662.

Why

When migrating machines or renaming hosts, deleting buckets one-by-one through the existing per-bucket dropdown is tedious. The Buckets view already groups buckets by hostname, so adding a host-level bulk action is a small UI addition with no new server-side dependencies — it reuses the existing DELETE /api/0/buckets/{id} endpoint.

Validation

  • ./node_modules/.bin/eslint --ext .vue,.js,.ts src/stores/buckets.ts src/views/Buckets.vue — clean
  • ./node_modules/.bin/tsc --noEmit — clean
  • Existing single-bucket delete flow is untouched (confirmed: only the device-card header and a new modal were added; per-bucket dropdown actions are unchanged)

Notes

  • The confirmation modal includes the count and a scrollable list of bucket IDs that will be deleted, so users see exactly what's about to happen before confirming.
  • Deletes are sequential (not parallel) to avoid hammering the server; the existing single-bucket delete returns quickly, so a typical host with a handful of buckets completes in well under a second.
  • vue-cli-service lint fails to start on this checkout due to a pre-existing webpack config mismatch (CopyPlugin Invalid Options against the installed copy-webpack-plugin), which is unrelated to this change. CI uses its own clean install and should run lint without issue.

Add a "Delete all buckets for this host" button to each device card on the
Buckets view, with a confirmation modal that lists the affected bucket IDs.

- New store action `deleteBucketsByHost({ hostname })` deletes all buckets
  matching the hostname and reloads once at the end (one round-trip instead
  of N).
- Existing single-bucket delete flow is unchanged.

Useful when migrating machines or renaming hosts and you want to drop all
buckets attributed to the old hostname in one step.
@TimeToBuildBob TimeToBuildBob marked this pull request as draft May 11, 2026 14:10
@codecov

codecov Bot commented May 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 30.59%. Comparing base (8d9a7f8) to head (a4b43d8).

Files with missing lines Patch % Lines
src/stores/buckets.ts 0.00% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #821      +/-   ##
==========================================
- Coverage   30.71%   30.59%   -0.13%     
==========================================
  Files          33       33              
  Lines        1976     1984       +8     
  Branches      353      370      +17     
==========================================
  Hits          607      607              
+ Misses       1348     1298      -50     
- Partials       21       79      +58     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@greptile-apps

greptile-apps Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a "Delete all buckets for this host" button to each device card on the Buckets view, backed by a new deleteBucketsByHost Pinia action that deletes bucket IDs captured at modal-open time and always reloads the bucket list via try/finally.

  • Store action (src/stores/buckets.ts): deleteBucketsByHost accepts a pre-captured bucketIds array, deletes sequentially, and wraps loadBuckets() in a .catch() so a transient reload failure cannot mask a successful deletion.
  • Confirmation modal (src/views/Buckets.vue): lists the exact bucket IDs that will be deleted (scrollable, max-height: 200px), disables the Confirm button while the operation is in-flight, and surfaces any partial-failure error inline rather than silently closing.
  • All issues raised in prior review rounds (snapshot divergence, silent partial failure, loadBuckets() error masking, modal flicker, missing scroll constraint) are addressed in this revision.

Confidence Score: 5/5

Safe to merge — the bulk-delete flow correctly scopes deletions to the snapshot captured at modal-open, surfaces partial failures in-modal, and always refreshes the bucket list regardless of outcome.

Both changed files handle the full success, partial-failure, and reload-failure scenarios correctly. The store's try/finally with a swallowed loadBuckets() error prevents a transient network hiccup from masking a completed deletion, and the view's catch block keeps the modal open with an error message rather than silently closing on failure. No functional defects were found in the new code paths.

No files require special attention.

Important Files Changed

Filename Overview
src/stores/buckets.ts Adds deleteBucketsByHost action that iterates over a caller-supplied bucket ID list, uses try/finally with a swallowed loadBuckets() error to guarantee a UI refresh after any outcome, and returns the deleted IDs — all previously-raised concerns addressed.
src/views/Buckets.vue Adds host-level "Delete all buckets" button to each device card, a confirmation modal with scrollable bucket-ID list and inline error alert, and a deleteBucketsForSelectedHost method that shows errors in-modal on partial failure; previously-flagged snapshot-divergence, flicker, and silent-failure issues are all resolved.

Sequence Diagram

sequenceDiagram
    actor User
    participant BucketsView as Buckets.vue
    participant Modal as delete-host-modal
    participant Store as buckets store
    participant API as AW REST API

    User->>BucketsView: Click Delete all buckets for this host
    BucketsView->>BucketsView: openDeleteHostModal snapshots hostname and bucketIds
    BucketsView->>Modal: bv::show::modal

    User->>Modal: Click Confirm
    Modal->>BucketsView: deleteBucketsForSelectedHost
    BucketsView->>Store: deleteBucketsByHost with bucketIds array

    loop for each bucketId sequential
        Store->>API: DELETE /api/0/buckets/id
        API-->>Store: 200 OK or error
    end

    Note over Store: finally block always runs
    Store->>API: GET /api/0/buckets loadBuckets
    API-->>Store: updated bucket list
    Store-->>BucketsView: resolves or throws

    alt Success
        BucketsView->>Modal: bv::hide::modal
        Modal-->>BucketsView: hidden event clears delete_host_selected
    else Partial or full failure
        BucketsView->>Modal: set delete_host_error shows alert
    end
Loading

Reviews (5): Last reviewed commit: "fix(buckets): swallow loadBuckets error ..." | Re-trigger Greptile

Comment thread src/stores/buckets.ts Outdated
Comment thread src/views/Buckets.vue
Comment thread src/views/Buckets.vue Outdated
Comment thread src/stores/buckets.ts Outdated
… flicker

- Wrap delete loop in try/finally so loadBuckets() is always called even
  if a mid-loop network error occurs (store was left stale on partial failure)
- Use b-modal @hidden event to clear delete_host_selected after the hide
  animation, instead of nulling it immediately in the finally block (which
  caused the modal content to vanish before the animation completed)
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Addressed the two issues from the Greptile review:

Store staleness on partial delete (correctness): wrapped the delete loop in try/finally so loadBuckets() is always called even if a mid-loop network error occurs. Previously a failed delete would leave the store showing buckets that no longer exist.

Modal content flicker (cosmetic): moved delete_host_selected = null to the modal's @hidden event instead of nulling it immediately in the finally block. This ensures the confirmation content stays visible during the hide animation.

… list

- deleteBucketsByHost now accepts bucketIds[] directly instead of
  re-reading from this.buckets at confirm-time; prevents silently
  deleting buckets created between modal-open and confirm
- Add max-height/overflow-y to confirmation modal bucket list so it
  stays within viewport for hosts with many buckets
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment thread src/views/Buckets.vue
… silently closing

On partial or full deletion failure, keep the modal open and show the
error message via a b-alert. Only close the modal on success. The
@hidden event also now clears delete_host_error to avoid stale state
on re-open.
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Addressed the remaining Greptile finding (silent failure on partial delete):

Error surfacing on deletion failure: moved hide::modal out of the finally block and into the try block (success path only). Added a catch that stores the error in delete_host_error and shows it as a b-alert inside the modal. The modal now stays open on failure so the user can see what went wrong. The error is cleared on modal close via the @hidden event.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Addressed the remaining Greptile finding (confidence 4/5):

Silent failure on partial/total deletion error: when deleteBucketsByHost throws (e.g. a network error mid-loop), the modal now stays open and shows an error alert instead of closing silently. The finally block only resets the spinner; the modal hides only on success in the try branch.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment thread src/stores/buckets.ts
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Addressed the remaining store bug from Greptile (confidence 4/5):

loadBuckets error-masking in finally block (commit a4b43d8): wrapped loadBuckets() in the finally block with .catch() so a reload failure after a successful deletion doesn't cause the function to reject. Previously, a transient network hiccup after all buckets were deleted would propagate as a rejection, the view would show 'Deletion failed', and a retry would attempt to delete already-deleted buckets.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@TimeToBuildBob TimeToBuildBob marked this pull request as ready for review May 11, 2026 15:03
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

PR is ready to merge. All CI checks green, Greptile 5/5 (safe to merge). All review feedback from prior rounds addressed — try/finally for store refresh, error surfacing in-modal, snapshot capture at open time, scrollable bucket list, modal flicker fix. Needs maintainer merge.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Tried to merge this on 2026-05-15 after rechecking the latest PR state: CI is still green, the PR is CLEAN/MERGEABLE, and all review threads are resolved. TimeToBuildBob does not have upstream merge or review-request permissions on ActivityWatch/aw-webui, so this needs a maintainer merge.

@ErikBjare ErikBjare merged commit b8ec3c0 into ActivityWatch:master May 19, 2026
8 checks passed
TimeToBuildBob added a commit to TimeToBuildBob/aw-webui that referenced this pull request Jul 3, 2026
…ActivityWatch#821)

* feat(buckets): add bulk delete by host (closes ActivityWatch#662)

Add a "Delete all buckets for this host" button to each device card on the
Buckets view, with a confirmation modal that lists the affected bucket IDs.

- New store action `deleteBucketsByHost({ hostname })` deletes all buckets
  matching the hostname and reloads once at the end (one round-trip instead
  of N).
- Existing single-bucket delete flow is unchanged.

Useful when migrating machines or renaming hosts and you want to drop all
buckets attributed to the old hostname in one step.

* fix(buckets): ensure loadBuckets runs after partial delete, fix modal flicker

- Wrap delete loop in try/finally so loadBuckets() is always called even
  if a mid-loop network error occurs (store was left stale on partial failure)
- Use b-modal @hidden event to clear delete_host_selected after the hide
  animation, instead of nulling it immediately in the finally block (which
  caused the modal content to vanish before the animation completed)

* fix(buckets): pass captured bucketIds to store, add scrollable bucket list

- deleteBucketsByHost now accepts bucketIds[] directly instead of
  re-reading from this.buckets at confirm-time; prevents silently
  deleting buckets created between modal-open and confirm
- Add max-height/overflow-y to confirmation modal bucket list so it
  stays within viewport for hosts with many buckets

* fix(buckets): surface deletion errors in host-delete modal instead of silently closing

On partial or full deletion failure, keep the modal open and show the
error message via a b-alert. Only close the modal on success. The
@hidden event also now clears delete_host_error to avoid stale state
on re-open.

* fix(buckets): swallow loadBuckets error in finally to avoid false deletion failure
TimeToBuildBob added a commit to TimeToBuildBob/aw-webui that referenced this pull request Jul 3, 2026
…ActivityWatch#821)

* feat(buckets): add bulk delete by host (closes ActivityWatch#662)

Add a "Delete all buckets for this host" button to each device card on the
Buckets view, with a confirmation modal that lists the affected bucket IDs.

- New store action `deleteBucketsByHost({ hostname })` deletes all buckets
  matching the hostname and reloads once at the end (one round-trip instead
  of N).
- Existing single-bucket delete flow is unchanged.

Useful when migrating machines or renaming hosts and you want to drop all
buckets attributed to the old hostname in one step.

* fix(buckets): ensure loadBuckets runs after partial delete, fix modal flicker

- Wrap delete loop in try/finally so loadBuckets() is always called even
  if a mid-loop network error occurs (store was left stale on partial failure)
- Use b-modal @hidden event to clear delete_host_selected after the hide
  animation, instead of nulling it immediately in the finally block (which
  caused the modal content to vanish before the animation completed)

* fix(buckets): pass captured bucketIds to store, add scrollable bucket list

- deleteBucketsByHost now accepts bucketIds[] directly instead of
  re-reading from this.buckets at confirm-time; prevents silently
  deleting buckets created between modal-open and confirm
- Add max-height/overflow-y to confirmation modal bucket list so it
  stays within viewport for hosts with many buckets

* fix(buckets): surface deletion errors in host-delete modal instead of silently closing

On partial or full deletion failure, keep the modal open and show the
error message via a b-alert. Only close the modal on success. The
@hidden event also now clears delete_host_error to avoid stale state
on re-open.

* fix(buckets): swallow loadBuckets error in finally to avoid false deletion failure
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Idea: bulk delete buckets by host

2 participants