Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughRefactors the activist detail page into section-driven read / edit UI, adds per-section edit form and unsaved-change protection, extends column metadata for edit types, introduces a PATCH API/schema and client method, and updates server query projections for extra updated timestamps and chapter name. ChangesActivist Edit and Sectionize Feature
Sequence DiagramsequenceDiagram
actor User
participant UI as Activist Detail UI
participant Form as ActivistSectionForm
participant Cache as Query Cache (TanStack)
participant API as ApiClient.patchActivist
participant Server as Server PATCH /api/activists/{id}
User->>UI: Click Edit (section)
UI->>Form: Render with initialValues (activist)
Form->>Form: Optionally fetch users
User->>Form: Modify fields
Form->>UI: Emit onDirtyChange(true)
User->>Form: Click Save
Form->>Form: Build dirty-only ActivistPatchInput
Form->>API: patchActivist(activistId, patch)
API->>Server: PATCH /api/activists/{id}
Server-->>API: Return updated ActivistJSON
API->>Cache: Update activist cache & invalidate search
API-->>Form: Success
Form->>UI: Call onSaved()
UI->>User: Close edit mode, re-render read-only
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
90bafb8 to
20e24ae
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 278-281: The code treats restored zero/false values as missing by
setting isZeroValue when rawValue === def.blankValue; remove that logic and stop
using def.blankValue as an “empty” marker. Update the block around
isFormattedBlank/isZeroValue/isEmpty so isEmpty only reflects the formatted
absence (e.g., const isEmpty = isFormattedBlank) and remove references to
isZeroValue; adjust any downstream logic that relied on isZeroValue to instead
handle explicit undefined/null raw values if needed (refer to variables
isFormattedBlank, isZeroValue, isEmpty, def.blankValue, rawValue, formatted).
- Around line 100-119: The current protection only handles full unloads
(useEffect handler) and a single link (handleBackClick), so client-side Next.js
navigation can still discard edits; add a global client-side navigation guard in
a shared parent (e.g., the layout) that watches Next.js router events and blocks
navigation when isFormDirty: subscribe to Router.events.on('routeChangeStart',
handler) and call confirmDiscard() inside handler, and if the user cancels abort
the navigation by emitting a routeChangeError and throwing (pattern used to stop
route changes), and also register Router.beforePopState to intercept browser
back/pop navigation; keep using the existing isFormDirty and confirmDiscard
symbols and remove duplicate logic from individual links like handleBackClick.
In `@frontend-v2/src/app/`(authed)/activists/[id]/section-form.tsx:
- Around line 261-295: The assignee Select becomes active when the usersQuery
fails; update the disabled prop on the Select (and any interactive children if
needed) to also check usersQuery.isError so it remains non-interactive on fetch
failure (e.g., change disabled={isSaving || usersQuery.isLoading} to
disabled={isSaving || usersQuery.isLoading || usersQuery.isError}), referencing
the Select component, usersQuery, isSaving and UNASSIGNED_USER_ID to ensure the
picker cannot be changed when the user list failed to load.
🪄 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: 48e510eb-03b6-4d6f-879b-f4f1ffe17a2f
📒 Files selected for processing (7)
frontend-v2/src/app/(authed)/activists/[id]/activist-detail.tsxfrontend-v2/src/app/(authed)/activists/[id]/section-form.tsxfrontend-v2/src/app/(authed)/activists/column-definitions.tsfrontend-v2/src/app/(authed)/activists/column-selector.tsxfrontend-v2/src/app/(authed)/activists/field-description-popover.tsxfrontend-v2/src/lib/api.tsfrontend-v2/src/lib/api/activists.ts
20e24ae to
320770c
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
frontend-v2/src/lib/api.ts (1)
415-430: ⚡ Quick winAdd
AbortSignalsupport topatchActivistfor cancellation parity.Line 415 introduces a request method that can’t be aborted, unlike most other API methods in this client. Adding
signalhelps prevent stale in-flight PATCH requests during unmount/navigation flows.♻️ Proposed change
- patchActivist = async (activistId: number, patch: ActivistPatchInput) => { + patchActivist = async ( + activistId: number, + patch: ActivistPatchInput, + signal?: AbortSignal, + ) => { try { const csrfToken = this.getCsrfToken() const resp = await this.client .patch(`${API_PATH.ACTIVIST_GET}/${activistId}`, { json: patch, + signal, headers: { 'X-CSRF-Token': csrfToken }, }) .json() const activist = ActivistGetResp.parse(resp).activist fillActivistBlankFields(activist) return activist } catch (err) { return this.handleKyError(err) } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend-v2/src/lib/api.ts` around lines 415 - 430, The patchActivist method currently cannot be canceled; update its signature to accept an optional AbortSignal (e.g., patchActivist(activistId: number, patch: ActivistPatchInput, signal?: AbortSignal)) and pass that signal into the this.client.patch call options so the underlying request can be aborted; ensure the call still sends json and headers as before and update any callers to pass a signal where appropriate (or omit it).
🤖 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/hooks/use-navigation-guard.ts`:
- Around line 44-52: The code currently cancels a rejected popstate by calling
originalPushState(null, '', savedHref), which mutates the history stack and
leaves the rejected entry orphaned; instead undo the traversal itself by calling
window.history.go(1) to move the index back to the guarded page (or
history.go(-1) if your saved traversal direction was forward), replacing the
originalPushState call in the popstate rejection branch. Update the logic around
confirmRef.current(), savedState/savedHref and allowNextPopState so you only
call window.history.go(...) to reverse the traversal (and remove the
originalPushState usage) and ensure any flag like allowNextPopState still
prevents re-entrancy when you programmatically step the history.
---
Nitpick comments:
In `@frontend-v2/src/lib/api.ts`:
- Around line 415-430: The patchActivist method currently cannot be canceled;
update its signature to accept an optional AbortSignal (e.g.,
patchActivist(activistId: number, patch: ActivistPatchInput, signal?:
AbortSignal)) and pass that signal into the this.client.patch call options so
the underlying request can be aborted; ensure the call still sends json and
headers as before and update any callers to pass a signal where appropriate (or
omit it).
🪄 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: 6e55fc73-ee14-40b1-9729-caa24930079c
📒 Files selected for processing (9)
frontend-v2/src/app/(authed)/activists/[id]/activist-detail.tsxfrontend-v2/src/app/(authed)/activists/[id]/section-form.tsxfrontend-v2/src/app/(authed)/activists/column-definitions.tsfrontend-v2/src/app/(authed)/activists/column-selector.tsxfrontend-v2/src/app/(authed)/activists/field-description-popover.tsxfrontend-v2/src/hooks/use-navigation-guard.tsfrontend-v2/src/lib/api.tsfrontend-v2/src/lib/api/activists.tsserver/src/model/activist.go
✅ Files skipped from review due to trivial changes (1)
- frontend-v2/src/app/(authed)/activists/field-description-popover.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
- frontend-v2/src/app/(authed)/activists/column-selector.tsx
- frontend-v2/src/lib/api/activists.ts
- frontend-v2/src/app/(authed)/activists/[id]/section-form.tsx
- frontend-v2/src/app/(authed)/activists/column-definitions.ts
3d762b5 to
7adfe07
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/lib/api/activists.ts`:
- Around line 76-118: The ActivistPatchInput schema includes fields past
cm_approval_email that the server handler (which uses DisallowUnknownFields and
whose accepted shape ends at cm_approval_email) does not accept; update
ActivistPatchInput to only include fields up through cm_approval_email (remove
all properties added after cm_approval_email such as prospect_organizer,
prospect_chapter_member, referral_*, interest_date, notes, vision_wall,
voting_agreement, street_address, city, state, assigned_to, followup_date,
etc.), or alternatively implement matching server-side PATCH support for those
fields so isEditableActivistField (which is derived from
ActivistPatchInput.shape) stays consistent with the backend contract.
🪄 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: edce37a9-6b6d-4bc8-9561-1ef534c54108
📒 Files selected for processing (7)
frontend-v2/src/app/(authed)/activists/[id]/activist-detail.tsxfrontend-v2/src/app/(authed)/activists/[id]/section-form.tsxfrontend-v2/src/app/(authed)/activists/column-definitions.tsfrontend-v2/src/app/(authed)/activists/column-selector.tsxfrontend-v2/src/app/(authed)/activists/field-description-popover.tsxfrontend-v2/src/lib/api.tsfrontend-v2/src/lib/api/activists.ts
✅ Files skipped from review due to trivial changes (2)
- frontend-v2/src/lib/api.ts
- frontend-v2/src/app/(authed)/activists/column-definitions.ts
| // Fields accepted by PATCH /api/activists/{id}. | ||
| // Mirrors ActivistPatchInput in server/src/transport/activists.go — keep in sync. | ||
| export const ActivistPatchInput = z.object({ | ||
| email: z.string().optional(), | ||
| facebook: z.string().optional(), | ||
| name: z.string().optional(), | ||
| preferred_name: z.string().optional(), | ||
| phone: z.string().optional(), | ||
| pronouns: z.string().optional(), | ||
| language: z.string().optional(), | ||
| accessibility: z.string().optional(), | ||
| dob: z.string().optional(), | ||
| location: z.string().optional(), | ||
| activist_level: z.string().optional(), | ||
| source: z.string().optional(), | ||
| hiatus: z.boolean().optional(), | ||
| connector: z.string().optional(), | ||
| training0: z.string().optional(), | ||
| training1: z.string().optional(), | ||
| training4: z.string().optional(), | ||
| training5: z.string().optional(), | ||
| training6: z.string().optional(), | ||
| consent_quiz: z.string().optional(), | ||
| training_protest: z.string().optional(), | ||
| dev_quiz: z.string().optional(), | ||
| dev_interest: z.string().optional(), | ||
| cm_first_email: z.string().optional(), | ||
| cm_approval_email: z.string().optional(), | ||
| prospect_organizer: z.boolean().optional(), | ||
| prospect_chapter_member: z.boolean().optional(), | ||
| referral_friends: z.string().optional(), | ||
| referral_apply: z.string().optional(), | ||
| referral_outlet: z.string().optional(), | ||
| interest_date: z.string().optional(), | ||
| notes: z.string().optional(), | ||
| vision_wall: z.string().optional(), | ||
| voting_agreement: z.boolean().optional(), | ||
| street_address: z.string().optional(), | ||
| city: z.string().optional(), | ||
| state: z.string().optional(), | ||
| assigned_to: z.number().optional(), | ||
| followup_date: z.string().optional(), | ||
| }) |
There was a problem hiding this comment.
Keep the frontend PATCH schema aligned with the server contract.
server/src/transport/activists.go:27-60 still only accepts fields through cm_approval_email, but this schema adds a larger editable set after that. Since isEditableActivistField() is derived from ActivistPatchInput.shape, the detail form will surface editors for fields the handler rejects with DisallowUnknownFields(). Please remove the unsupported keys here, or land the matching backend PATCH support first.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend-v2/src/lib/api/activists.ts` around lines 76 - 118, The
ActivistPatchInput schema includes fields past cm_approval_email that the server
handler (which uses DisallowUnknownFields and whose accepted shape ends at
cm_approval_email) does not accept; update ActivistPatchInput to only include
fields up through cm_approval_email (remove all properties added after
cm_approval_email such as prospect_organizer, prospect_chapter_member,
referral_*, interest_date, notes, vision_wall, voting_agreement, street_address,
city, state, assigned_to, followup_date, etc.), or alternatively implement
matching server-side PATCH support for those fields so isEditableActivistField
(which is derived from ActivistPatchInput.shape) stays consistent with the
backend contract.
7adfe07 to
2f011c3
Compare
Summary by CodeRabbit
New Features
Improvements