Skip to content

Add delete collection with confirmation - #29

Merged
ebulgakov merged 2 commits into
mainfrom
feature/delete-collection
Aug 1, 2026
Merged

Add delete collection with confirmation#29
ebulgakov merged 2 commits into
mainfrom
feature/delete-collection

Conversation

@ebulgakov

@ebulgakov ebulgakov commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add a "Delete collection" action (with confirmation dialog) to the collections list page, next to the existing Edit link
  • New DELETE /api/collections/[id] route: verifies ownership, deletes the collection (cascades collection_items via the existing DB FK), and cleans up now-orphaned urls cache rows
  • New useDeleteCollection composable manages the shared confirmation dialog for the list, mirroring the existing useCollectionLinks pattern
  • i18n strings added in both en.json and ru.json

Test plan

  • pnpm type-check, pnpm lint, pnpm format — clean
  • pnpm test — 188/188 passing (added 13 new tests for useDeleteCollection)
  • Manual: created a collection with a link, deleted it via the confirm dialog, verified the list updates without a reload
  • Manual: verified in the DB that collections, collection_items, and the now-orphaned urls row are all cleaned up after delete
  • Manual: confirmed an empty collection (no links) also deletes cleanly

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added collection deletion with a confirmation dialog.
    • Added loading states, retry support, and clear error messages for deletion and refresh failures.
    • Added English and Russian translations for deletion actions and confirmations.
    • Successfully deleted collections now refresh the collection list automatically.
  • Bug Fixes

    • Prevented duplicate deletion requests during concurrent confirmations.
    • Preserved the confirmation dialog when deletion fails.

Deleting a collection now removes it and all links inside it, gated by a
confirmation dialog on the collections list page. The DB cascade already
handled collection_items; the new DELETE route also cleans up now-orphaned
urls rows, mirroring the existing single-link delete route.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
linkfolio Ready Ready Preview Aug 1, 2026 9:07pm

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ebulgakov, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3996d014-a6a2-4b1b-b872-7322a9b231dc

📥 Commits

Reviewing files that changed from the base of the PR and between 54b8077 and 281924d.

📒 Files selected for processing (3)
  • app/features/collections/model/__tests__/use-delete-collection.test.ts
  • app/features/collections/model/use-delete-collection.ts
  • app/features/collections/ui/collections-list.vue
📝 Walkthrough

Walkthrough

Changes

Collection deletion

Layer / File(s) Summary
Delete endpoint and cleanup
server/api/collections/[id].delete.ts
Adds authenticated, ownership-checked collection deletion. The endpoint removes unreferenced URLs and returns HTTP 204.
Client API and test mocks
app/shared/api/collections.ts, app/shared/testing/mocks/collections-api.ts
Adds the deleteCollection request helper and its configurable test mock.
Deletion composable
app/features/collections/model/use-delete-collection.ts, app/features/collections/model/__tests__/use-delete-collection.test.ts, app/features/collections/index.ts
Adds and exports useDeleteCollection. Tests cover dialog state, failures, retries, refresh errors, stale errors, and duplicate confirmations.
Collections list integration
app/features/collections/ui/collections-list.vue, i18n/locales/en.json, i18n/locales/ru.json
Adds collection delete actions, confirmation UI, pending and error states, refresh-error handling, and translations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CollectionsList
  participant UseDeleteCollection
  participant CollectionsApi
  participant DeleteEndpoint
  CollectionsList->>UseDeleteCollection: Confirm collection deletion
  UseDeleteCollection->>CollectionsApi: deleteCollection(id)
  CollectionsApi->>DeleteEndpoint: DELETE /api/collections/:id
  DeleteEndpoint-->>CollectionsApi: Return 204
  CollectionsApi-->>UseDeleteCollection: Resolve deletion
  UseDeleteCollection->>CollectionsList: Refresh collections
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding collection deletion with a confirmation dialog.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/delete-collection

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ebulgakov

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/features/collections/ui/collections-list.vue (1)

19-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the refresh result-handling logic into a model composable.

refreshCollections inspects error.value and conditionally re-throws, which is result-handling logic embedded directly in this presentational component's script. Move this behavior into useCollections (for example, expose a refresh that already throws on failure) so this .vue file stays a thin consumer of composable state.

As per coding guidelines, "Keep feature UI components presentational; place state, business logic, result handling, and API calls in the corresponding model/ composables."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/features/collections/ui/collections-list.vue` around lines 19 - 26, Move
the error-aware refresh behavior from the component-level refreshCollections
function into the useCollections composable, exposing a refresh operation that
awaits the underlying refresh and rethrows error.value on failure. Update the
component to consume that composable method while preserving
useDeleteCollection’s failure propagation through refreshError.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/features/collections/model/use-delete-collection.ts`:
- Around line 27-42: Prevent dismissal during an in-flight deletion by making
cancelDelete no-op when deletePending.value is true; this also guards the
isDialogOpen setter. In app/features/collections/model/use-delete-collection.ts
lines 27-42, update cancelDelete accordingly. In
app/features/collections/ui/collections-list.vue lines 106-123, disable the
Cancel button with deletePending to match the Confirm button’s loading state.

---

Nitpick comments:
In `@app/features/collections/ui/collections-list.vue`:
- Around line 19-26: Move the error-aware refresh behavior from the
component-level refreshCollections function into the useCollections composable,
exposing a refresh operation that awaits the underlying refresh and rethrows
error.value on failure. Update the component to consume that composable method
while preserving useDeleteCollection’s failure propagation through refreshError.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 77fecac8-0b85-41fd-b606-1065763860d8

📥 Commits

Reviewing files that changed from the base of the PR and between 208c99c and 54b8077.

📒 Files selected for processing (9)
  • app/features/collections/index.ts
  • app/features/collections/model/__tests__/use-delete-collection.test.ts
  • app/features/collections/model/use-delete-collection.ts
  • app/features/collections/ui/collections-list.vue
  • app/shared/api/collections.ts
  • app/shared/testing/mocks/collections-api.ts
  • i18n/locales/en.json
  • i18n/locales/ru.json
  • server/api/collections/[id].delete.ts

Comment thread app/features/collections/model/use-delete-collection.ts
cancelDelete (and thus the isDialogOpen v-model setter) no-oped while a
delete is in flight; the Cancel button is disabled to match. Without this,
dismissing the dialog for one collection while its delete was still pending
let the stale confirmDelete continuation null out whatever collection a
newly re-opened dialog was targeting once it resolved.

Addresses a CodeRabbit review comment on PR #29.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ebulgakov

Copy link
Copy Markdown
Owner Author

Addressed the review:

  • Major (dialog dismiss race): fixed in 281924dcancelDelete no-ops while deletePending is true, Cancel button disabled to match, regression test added.
  • Nitpick (move refreshCollections into useCollections): skipping this one — it mirrors the existing refreshLinks wrapper in app/pages/collections/[id]/index.vue, which does the identical error-aware re-throw at the call site rather than inside the shared useCollection/data-fetching composable. Moving it into useCollections here would diverge from that established precedent rather than align with it, since useCollections is a thin generic data composable and shouldn't assume a throw-on-failure contract for every caller.

@ebulgakov
ebulgakov merged commit 90cdbd1 into main Aug 1, 2026
7 checks passed
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.

1 participant