Skip to content

Alex/delete merge activists#359

Merged
alexsapps merged 6 commits intomainfrom
alex/delete-merge-activists
May 3, 2026
Merged

Alex/delete merge activists#359
alexsapps merged 6 commits intomainfrom
alex/delete-merge-activists

Conversation

@alexsapps
Copy link
Copy Markdown
Collaborator

@alexsapps alexsapps commented May 3, 2026

Summary by CodeRabbit

  • New Features

    • Hide activist: "Hide" button + confirmation dialog with progress state, success/error toasts, and redirect to activists list.
    • Merge activist: "Merge" button + dialog with target typeahead, validation to prevent invalid/self-merge, progress state, success/error toasts, and redirect to activists list.
  • Bug Fixes

    • Enforced chapter-level access checks on hide/merge actions.
  • Tests

    • Updated server tests covering merge semantics and hide/update flows.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 3, 2026

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: c58221f3-bcf8-48ae-911b-c6265ae7e639

📥 Commits

Reviewing files that changed from the base of the PR and between eec6eec and be064bb.

📒 Files selected for processing (8)
  • frontend-v2/src/app/(authed)/activists/[id]/activist-detail.tsx
  • frontend-v2/src/app/(authed)/activists/[id]/hide-activist-dialog.tsx
  • frontend-v2/src/app/(authed)/activists/[id]/merge-activist-dialog.tsx
  • frontend-v2/src/lib/api.ts
  • server/src/main.go
  • server/src/model/activist.go
  • server/src/model/activist_test.go
  • server/src/model/adb_auth.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend-v2/src/app/(authed)/activists/[id]/activist-detail.tsx
  • frontend-v2/src/lib/api.ts

📝 Walkthrough

Walkthrough

This PR adds frontend "Merge" and "Hide" dialog flows to the activist detail page, new API client methods and endpoint paths, and backend and model changes to require authenticated user chapter checks and to support a merge_name flag controlling whether name fields are merged. Tests are updated to exercise the new signatures and merge semantics.

Changes

Activist Merge and Hide Features

Layer / File(s) Summary
API Contract
frontend-v2/src/lib/api.ts
Adds ACTIVIST_HIDE and ACTIVIST_MERGE endpoint constants and a shared SuccessResp schema for success-only responses.
API Client Methods
frontend-v2/src/lib/api.ts
Adds ApiClient.hideActivist(activistId, signal?) and ApiClient.mergeActivist(currentActivistId, targetActivistName, signal?) that POST to the new endpoints, call throwIfApiError, and return SuccessResp. Updates deleteEvent to parse SuccessResp.
Frontend Integration
frontend-v2/src/app/(authed)/activists/[id]/activist-detail.tsx
Adds local state isHideDialogOpen/isMergeDialogOpen, imports HideActivistDialog/MergeActivistDialog, and updates the header to show "Merge" and "Hide" action buttons wired to those dialogs.
Frontend Dialog Components
frontend-v2/src/app/(authed)/activists/[id]/hide-activist-dialog.tsx, .../merge-activist-dialog.tsx
New HideActivistDialog and MergeActivistDialog components with React Query mutations invoking apiClient.hideActivist / apiClient.mergeActivist, toast notifications, query invalidation, navigation to /activists, pending-state guards, and (for merge) suggestion/typeahead and validation logic.
Backend Handlers & Routing
server/src/main.go
ActivistHideHandler now requires the authenticated user and uses model.HideActivist(c.db, user, activistID). /api/activist/hide route middleware switched to apiOrganizerAccessAuthMiddleware. ActivistMergeHandler parses merge_name, derives chapter context from the authenticated user, checks chapter access, and passes merge_name into model.MergeActivist. ActivistSaveHandler now calls model.UserUpdateActivist(..., user) with the authenticated user object.
Backend Model Logic
server/src/model/activist.go
UserUpdateActivist, HideActivist, and MergeActivist signatures changed to accept authedUser and/or mergeName as appropriate. MergeActivist fetches both original and target activists inside the transaction, enforces same-chapter constraints, and delegates to updateMergedActivistDataDetails(..., mergeName). getMergeActivistWinner and updateMergedActivistDataDetails were updated to conditionally merge Name/NameUpdated only when mergeName is true.
Auth Helpers
server/src/model/adb_auth.go
Adds CheckChapterAccess(user ADBUser, chapterID int) error to enforce chapter-level access (admins bypass).
Tests
server/src/model/activist_test.go
Updated tests to pass ADBUser into UserUpdateActivist/HideActivist; TestMergeActivist updated to call MergeActivist(..., false) for non-name merges and adds Name subtests exercising MergeActivist(..., true) assertions, plus other call-site adjustments.
sequenceDiagram
    participant User as User
    participant UI as Activist Detail UI
    participant Dialog as Merge/Hide Dialog
    participant API as API Client
    participant Server as Server
    participant DB as Database
    participant Toast as Toast/Navigation

    User->>UI: Click Merge or Hide action
    UI->>Dialog: Open appropriate dialog
    User->>Dialog: Provide target name (merge) or confirm (hide)
    Dialog->>API: POST mergeActivist(...) or hideActivist(...)
    API->>Server: HTTP POST /api/activist/merge or /api/activist/hide
    Server->>DB: Begin transaction / fetch activist(s) / check chapter access
    DB->>DB: Update/merge/hide records, commit
    Server->>API: Return { status: "success" }
    API->>Dialog: Resolve mutation
    Dialog->>Dialog: Invalidate queries, close dialog
    Dialog->>Toast: Show success toast
    Toast->>User: Navigate to /activists
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

Suggested Reviewers

  • jakehobbs

Poem

🐰 I hopped to your branch with joyful cheer,
Merge and Hide dialogs now appear.
Names and chapters checked with care,
Buttons, toasts, and tests all there.
Hop on, reviewers—this feature's near! 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.58% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'Alex/delete merge activists' is vague and doesn't clearly describe the main changes. The changeset implements hide and merge functionality with dialog flows and API endpoints, but the title is unclear about what 'delete merge' means and doesn't convey the actual scope of work. Consider revising the title to be more descriptive, such as 'Add hide and merge activist functionality with dialogs' or 'Implement activist hide and merge operations' to clearly summarize the primary changes.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.

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

✨ 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 alex/delete-merge-activists

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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

@alexsapps alexsapps marked this pull request as ready for review May 3, 2026 20:21
@alexsapps alexsapps requested a review from jakehobbs as a code owner May 3, 2026 20:21
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
server/src/model/activist.go (1)

1383-1390: ⚡ Quick win

Use standard wrapping instead of adding new pkg/errors calls.

These changed blocks introduce new errors.Wrapf(...) usages. Please switch them to fmt.Errorf(...: %w) so new backend code stays on the standard library path.

♻️ Proposed fix
-		return errors.Wrapf(err, "failed to get target activist with id %d", targetActivistID)
+		return fmt.Errorf("failed to get target activist with id %d: %w", targetActivistID, err)
As per coding guidelines, `server/**/*.go`: Do not use `github.com/pkg/errors` for new code in the Go backend.

Also applies to: 1673-1676

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

In `@server/src/model/activist.go` around lines 1383 - 1390, Replace the
pkg/errors Wrapf calls with standard library wrapping using fmt.Errorf and %w in
the blocks that fetch activists: where tx.Get is called for originalActivist
(variable originalActivist, id originalActivistID) and targetActivist (variable
targetActivist, id targetActivistID); keep the tx.Rollback() behavior but change
the return values to use fmt.Errorf("failed to get original activist with id %d:
%w", originalActivistID, err) and similarly for targetActivist, and update
imports to remove github.com/pkg/errors and import "fmt" instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend-v2/src/app/`(authed)/activists/[id]/activist-detail.tsx:
- Around line 160-178: The Hide button is visible to all users but the backend
endpoint /activist/hide is protected by apiSFBayOrganizerAuthMiddleware; update
the UI to gate or disable the Hide action using the same access rule: check the
current user's organizer status (e.g., an isSFBayOrganizer flag from your
session/user context or hook) before rendering the Button that calls
setIsHideDialogOpen(true) (or render it disabled with a tooltip), so only users
who satisfy the apiSFBayOrganizerAuthMiddleware can see/activate the Hide
dialog.

In `@frontend-v2/src/app/`(authed)/activists/[id]/merge-activist-dialog.tsx:
- Around line 45-46: The merge dialog currently filters suggestions and blocks
selection by comparing names (activistName) and rejecting selectedValue ===
activistName, which prevents merging two different activists with the same
display name; change the flow to track and pass activist IDs instead of names:
update the suggestion items and selection state to hold targetActivistId (not
just name), modify the filter to exclude only the entry whose id equals
activistId (leave identical names allowed), and change mutationFn from
apiClient.mergeActivist(activistId, targetName) to call a merge API that accepts
the target ID (e.g., apiClient.mergeActivist(activistId, targetActivistId));
make the same adjustments in the other block referenced (lines ~70-76) so
selection, filtering, and the mutation use IDs rather than names.

In `@server/src/model/activist.go`:
- Around line 1378-1391: The plain snapshot reads using tx.Get with
selectActivistExtraBaseQuery do not lock rows; change the reads that populate
originalActivist and targetActivist to locking SELECTs (e.g., append " FOR
UPDATE" or use the DB's row-locking clause) so both activist rows are locked in
this transaction before computing mergedActivist and performing UPDATEs; update
the two tx.Get calls (the ones filling originalActivist and targetActivist from
selectActivistExtraBaseQuery) to use the locking query so the merge winner is
derived from stable, locked rows.

---

Nitpick comments:
In `@server/src/model/activist.go`:
- Around line 1383-1390: Replace the pkg/errors Wrapf calls with standard
library wrapping using fmt.Errorf and %w in the blocks that fetch activists:
where tx.Get is called for originalActivist (variable originalActivist, id
originalActivistID) and targetActivist (variable targetActivist, id
targetActivistID); keep the tx.Rollback() behavior but change the return values
to use fmt.Errorf("failed to get original activist with id %d: %w",
originalActivistID, err) and similarly for targetActivist, and update imports to
remove github.com/pkg/errors and import "fmt" instead.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d7d007fd-956b-46d3-8402-ec286c89b7ca

📥 Commits

Reviewing files that changed from the base of the PR and between 915ce5b and eec6eec.

📒 Files selected for processing (7)
  • frontend-v2/src/app/(authed)/activists/[id]/activist-detail.tsx
  • frontend-v2/src/app/(authed)/activists/[id]/hide-activist-dialog.tsx
  • frontend-v2/src/app/(authed)/activists/[id]/merge-activist-dialog.tsx
  • frontend-v2/src/lib/api.ts
  • server/src/main.go
  • server/src/model/activist.go
  • server/src/model/activist_test.go

Comment thread frontend-v2/src/app/(authed)/activists/[id]/activist-detail.tsx
Comment thread server/src/model/activist.go
@alexsapps alexsapps force-pushed the alex/delete-merge-activists branch from eec6eec to be064bb Compare May 3, 2026 22:46
@alexsapps alexsapps merged commit 5381e22 into main May 3, 2026
2 checks passed
@alexsapps alexsapps deleted the alex/delete-merge-activists branch May 3, 2026 23:20
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