Skip to content

20 add bulk delete contacts view - #24

Merged
yamcodes merged 9 commits into
springfrom
20-add-bulk-delete-contacts-view
Mar 16, 2026
Merged

20 add bulk delete contacts view#24
yamcodes merged 9 commits into
springfrom
20-add-bulk-delete-contacts-view

Conversation

@yamcodes

@yamcodes yamcodes commented Mar 14, 2026

Copy link
Copy Markdown
Owner

Closes #20

Summary by CodeRabbit

  • New Features

    • Bulk deletion UI: checkboxes and a "Delete Selected" button with confirmation for removing multiple contacts.
  • Bug Fixes / Improvements

    • Bulk delete now safely ignores empty selections to prevent unintended actions.
  • Style

    • Template and code formatting refinements for consistency; layout stylesheet link removed.
  • Tests

    • Expanded sample dataset used for demo/test data.

- Improve SQL `INSERT INTO` formatting for clarity.
- Reorganize and reformat HTML templates for better consistency and readability.
- Adjust Thymeleaf directives in fragments and templates.
- Simplify annotation formatting in `Contact.java`.
- Rename `slugs` parameter to `selected_contact_slugs` in `deleteManyContacts` for clarity.
- Update `.idea` settings to remove obsolete biome options.
- Add `page` parameter to `contacts` endpoint in `ContactController`.
- Pass `page` as a model attribute for rendering.
- Update HTML template to support dynamic content loading with HTMX.
- Wrap contact list in a form with `hx-delete` for bulk deletion functionality.
- Replace contact list rows with a Thymeleaf fragment for better modularity.
- Add a "Delete Selected" button for bulk actions.
- Insert multiple new contacts into `data.sql` for extended dataset.
- Refactor `contact-list-rows` Thymeleaf fragment to include `th:fragment` for reusability.
- Adjust "Delete Selected" button to include HTMX attributes for confirmation and deletion logic.
- Remove unused `page` parameter from `ContactController` and related HTML templates.
… list template for correct Thymeleaf usage
…tor `contact-list-rows` for streamlined Thymeleaf rendering
@yamcodes

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 14, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

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 commented Mar 14, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d126bb06-565d-41a4-8ff2-e8de449a0295

📥 Commits

Reviewing files that changed from the base of the PR and between 55edafc and ac338c4.

📒 Files selected for processing (1)
  • src/main/java/codes/yam/contacts/ContactController.java

📝 Walkthrough

Walkthrough

Adds UI and controller support for bulk-deleting contacts via an optional selected_contact_slugs request parameter; introduces a reusable fragment for list rows with checkboxes and HTMX delete wiring. Multiple templates and small formatting/config edits are included; sample data.sql expanded.

Changes

Cohort / File(s) Summary
Controller (bulk delete)
src/main/java/codes/yam/contacts/ContactController.java
Signature changed to accept @RequestParam(name="selected_contact_slugs", required=false) List<String> selectedContactSlugs; added null/empty guard and conditional call to contactService.deleteMany(...).
UI: list rows fragment
src/main/resources/templates/fragments/contact-list-rows.html
New Thymeleaf fragment rows that renders table rows with checkboxes, fields, Edit/View links, and HTMX-enabled per-row Delete links.
UI: contacts list
src/main/resources/templates/contacts/list.html
Reworked to conditionally render a form when contacts exist, embed the rows fragment, add a "Delete Selected" HTMX button, and adjust header/count placement.
UI: contact fields fragment
src/main/resources/templates/fragments/contact-fields.html
Consolidated duplicated label/input blocks into single grouped inputs for Email, First, Last, Phone; preserves conditional hx-get for email when slug exists.
Templates & layout adjustments
src/main/resources/templates/...
src/main/resources/templates/contacts/edit.html, .../new.html, .../view.html, error.html, error/404.html, layout.html
Mostly formatting, attribute reordering, minor structural tweaks; layout.html removes the stylesheet link.
Data
src/main/resources/data.sql
Replaced separate INSERTs with a single multi-row INSERT and added additional sample contact rows.
Model / Repo / Config formatting
src/main/java/codes/yam/contacts/Contact.java, .../ContactRepository.java, .../WebConfig.java
Formatting-only changes (annotation placement, string/line breaks).
IDE / tooling
.idea/biome.xml
Removed three save-time Biome settings: applySafeFixesOnSave, formatOnSave, sortImportOnSave.
Tests
src/test/java/codes/yam/contacts/ContactsApplicationTests.java
Formatting-only change to an empty test method.

Sequence Diagram

sequenceDiagram
    actor User
    participant UI as "Browser / Contact List"
    participant HTMX as "HTMX"
    participant Controller as "ContactController"
    participant Service as "ContactService"
    participant DB as "Database"

    User->>UI: select checkboxes
    User->>UI: click "Delete Selected"
    UI->>HTMX: DELETE /contacts?selected_contact_slugs=...
    HTMX->>Controller: HTTP DELETE request
    Controller->>Controller: if selected_contact_slugs null/empty -> return 204
    alt non-empty
        Controller->>Service: deleteMany(selected_contact_slugs)
        Service->>DB: delete rows by slug list
        DB-->>Service: confirm deletions
        Service-->>Controller: success
        Controller-->>HTMX: 204 No Content / redirect
    end
    HTMX->>UI: swap/remove deleted rows
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • 4 add bulk delete contacts #19: Modifies the bulk-delete endpoint and request parameter handling in ContactController; directly related to this PR's controller and UI changes.

Poem

🐰 I nudge the checkboxes in tidy rows,
A click, a hum — the HTMX wind blows.
Fragments hop in, slugs take flight,
Templates trimmed, the list grows light.
A rabbit cheers for bulk-delete tonight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically refers to the main change: adding bulk delete functionality to the contacts view, which aligns with the primary objectives.
Linked Issues check ✅ Passed All requirements from issue #20 are met: HTTP DELETE endpoint with selected_contact_slugs parameter [#20], checkboxes for multi-contact selection in UI [#20], and bulk delete button added [#20].
Out of Scope Changes check ✅ Passed Changes include formatting/refactoring beyond the stated requirements (Biome config, field formatting, method reformatting), but these support the core feature and improve code maintainability without conflicting with objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 20-add-bulk-delete-contacts-view
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Tip

CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.

OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required.

@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: 4

🧹 Nitpick comments (1)
src/test/java/codes/yam/contacts/ContactsApplicationTests.java (1)

9-10: Add tests for bulk delete behavior.

The endpoint at ContactController.deleteManyContacts() (lines 75–77) handles the bulk delete contract with @RequestParam List<String> selected_contact_slugs, but this flow has no test coverage. Add tests to cover parameter binding and the multi-delete logic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/codes/yam/contacts/ContactsApplicationTests.java` around lines
9 - 10, Add unit tests to cover the bulk-delete flow for
ContactController.deleteManyContacts: write tests in ContactsApplicationTests
that use MockMvc (or WebTestClient) to POST/DELETE to the endpoint with multiple
selected_contact_slugs request parameters, assert that parameter binding works
(controller receives both slugs) and that the multi-delete logic calls the
repository/service delete method for each slug and returns the expected status;
specifically, create tests that (1) send multiple selected_contact_slugs and
verify ContactRepository.deleteBySlug or ContactService.deleteContact is invoked
for each slug, and (2) test the empty/none case to ensure no deletes occur and
an appropriate response is returned. Ensure you reference
ContactController.deleteManyContacts, the request param name
selected_contact_slugs, and the repository/service methods used to perform
deletions when wiring mocks and verifications.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/codes/yam/contacts/ContactController.java`:
- Around line 75-77: Make the request param optional and guard against a
null/empty selection in deleteManyContacts: change the `@RequestParam` on
selected_contact_slugs to be optional (e.g. required=false or
Optional<List<String>>) and then check if the list is null or empty before
calling contactService.deleteMany(selected_contact_slugs); if empty, return an
early ResponseEntity (e.g. noContent()) instead of invoking the service. Ensure
you only call contactService.deleteMany(...) when the list has elements to avoid
MissingServletRequestParameterException and unnecessary service calls.

In `@src/main/resources/templates/fragments/contact-fields.html`:
- Around line 10-12: The hx-get URL in the contact-fields fragment is built from
contact.slug so in ContactController.newContact (create flow) this yields
/contacts/null/email and breaks ContactController.validateEmail; fix by only
emitting the hx-get attribute when contact.slug is non-null: update the input in
contact-fields.html to conditionally set hx-get (e.g. use a Thymeleaf
conditional expression or th:if/th:attr to add
hx-get=@{/contacts/{slug}/email(slug=${contact.slug})} only when ${contact.slug}
!= null), leaving the input without hx-get for new contacts (or use a data
attribute fallback) so email validation requests only target the validateEmail
endpoint when a valid slug exists.

In `@src/main/resources/templates/fragments/contact-list-rows.html`:
- Around line 9-13: The checkboxes rendered by the input element
(name="selected_contact_slugs", th:value="*{slug}") lack an accessible name;
update the markup so each checkbox has an explicit accessible label by either
wrapping the input in a <label> that uses the contact display field (e.g. the
contact's name) or by adding an aria-label attribute bound to the contact name
(use Thymeleaf binding like th:attr="aria-label=*{name}" or give each input an
id bound to *{slug} and a corresponding <label
th:for="*{slug}">*{name}</label>); ensure the identifier uses th:value="*{slug}"
so ids remain unique and screen readers receive the contact name for each
checkbox.
- Around line 25-27: The fragment currently uses hx-swap="outerHTML swap:1s" on
the row element with hx-target="closest tr" and th:attr="hx-delete=...", but
ContactController returns an HTTP 303 redirect which causes HTMX to follow the
redirect and insert the full page into the targeted tr; change the hx-swap
attribute on that element from "outerHTML swap:1s" to "delete swap:1s" so HTMX
removes the row instead of swapping a full-page response into it.

---

Nitpick comments:
In `@src/test/java/codes/yam/contacts/ContactsApplicationTests.java`:
- Around line 9-10: Add unit tests to cover the bulk-delete flow for
ContactController.deleteManyContacts: write tests in ContactsApplicationTests
that use MockMvc (or WebTestClient) to POST/DELETE to the endpoint with multiple
selected_contact_slugs request parameters, assert that parameter binding works
(controller receives both slugs) and that the multi-delete logic calls the
repository/service delete method for each slug and returns the expected status;
specifically, create tests that (1) send multiple selected_contact_slugs and
verify ContactRepository.deleteBySlug or ContactService.deleteContact is invoked
for each slug, and (2) test the empty/none case to ensure no deletes occur and
an appropriate response is returned. Ensure you reference
ContactController.deleteManyContacts, the request param name
selected_contact_slugs, and the repository/service methods used to perform
deletions when wiring mocks and verifications.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5f67dcc8-dfd2-4ca4-824a-3fb6b0e83932

📥 Commits

Reviewing files that changed from the base of the PR and between 5db1e9b and ac697ef.

📒 Files selected for processing (16)
  • .idea/biome.xml
  • src/main/java/codes/yam/contacts/Contact.java
  • src/main/java/codes/yam/contacts/ContactController.java
  • src/main/java/codes/yam/contacts/ContactRepository.java
  • src/main/java/codes/yam/contacts/WebConfig.java
  • src/main/resources/data.sql
  • src/main/resources/templates/contacts/edit.html
  • src/main/resources/templates/contacts/list.html
  • src/main/resources/templates/contacts/new.html
  • src/main/resources/templates/contacts/view.html
  • src/main/resources/templates/error.html
  • src/main/resources/templates/error/404.html
  • src/main/resources/templates/fragments/contact-fields.html
  • src/main/resources/templates/fragments/contact-list-rows.html
  • src/main/resources/templates/layout.html
  • src/test/java/codes/yam/contacts/ContactsApplicationTests.java
💤 Files with no reviewable changes (1)
  • .idea/biome.xml

Comment thread src/main/java/codes/yam/contacts/ContactController.java Outdated
Comment thread src/main/resources/templates/fragments/contact-fields.html
Comment thread src/main/resources/templates/fragments/contact-list-rows.html
Comment thread src/main/resources/templates/fragments/contact-list-rows.html Outdated
…mplate

- Add `aria-label` for checkboxes in `contact-list-rows` for better accessibility.
- Change `hx-swap` action from `outerHTML` to `delete` in delete actions.
- Update `deleteManyContacts` to handle empty or null selections gracefully.
- Enhance `hx-get` logic in contact fields to handle null slugs conditionally.
@yamcodes

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 14, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

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)
src/main/java/codes/yam/contacts/ContactController.java (1)

74-82: Optional: Consider using camelCase for Java variable with explicit name attribute.

The parameter name selected_contact_slugs uses snake_case to match the HTML form field, but Java convention prefers camelCase. You could use @RequestParam(name = "selected_contact_slugs", required = false) List<String> selectedContactSlugs to satisfy both conventions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/codes/yam/contacts/ContactController.java` around lines 74 -
82, In deleteManyContacts in ContactController, change the snake_case parameter
to camelCase and bind it to the incoming request param by using
`@RequestParam`(name = "selected_contact_slugs", required = false) List<String>
selectedContactSlugs; update the method signature and any uses (e.g., the
contactService.deleteMany call) to use selectedContactSlugs so Java naming
conventions are followed while still matching the HTML form field.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/codes/yam/contacts/ContactController.java`:
- Around line 77-79: When selected_contact_slugs is null or empty in the
ContactController delete action, it currently returns ResponseEntity.noContent()
which HTMX treats as silent/no-op; change that branch to return the same
redirect response used by the successful delete path (i.e., the same HTTP
redirect or HX-Redirect header and status) so the client refreshes/redirects
consistently. Locate the check for selected_contact_slugs in the
ContactController method handling deletions and replace the noContent() return
with the success-path redirect/headers so both branches produce identical
HX-Redirect behavior.

---

Nitpick comments:
In `@src/main/java/codes/yam/contacts/ContactController.java`:
- Around line 74-82: In deleteManyContacts in ContactController, change the
snake_case parameter to camelCase and bind it to the incoming request param by
using `@RequestParam`(name = "selected_contact_slugs", required = false)
List<String> selectedContactSlugs; update the method signature and any uses
(e.g., the contactService.deleteMany call) to use selectedContactSlugs so Java
naming conventions are followed while still matching the HTML form field.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e597c77f-c111-498e-b99b-868e14430dda

📥 Commits

Reviewing files that changed from the base of the PR and between ac697ef and 55edafc.

📒 Files selected for processing (3)
  • src/main/java/codes/yam/contacts/ContactController.java
  • src/main/resources/templates/fragments/contact-fields.html
  • src/main/resources/templates/fragments/contact-list-rows.html
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/resources/templates/fragments/contact-list-rows.html

Comment thread src/main/java/codes/yam/contacts/ContactController.java Outdated
@yamcodes

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 16, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

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.

@yamcodes
yamcodes merged commit 6e4376d into spring Mar 16, 2026
1 check passed
@yamcodes
yamcodes deleted the 20-add-bulk-delete-contacts-view branch March 16, 2026 17:18
This was referenced Mar 16, 2026
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