Skip to content

improvement: Autosave settings on change - #544

Merged
Blaumaus merged 6 commits into
mainfrom
improvement/settings-autosave
May 23, 2026
Merged

Blaumaus merged 6 commits into
mainfrom
improvement/settings-autosave

Conversation

@Blaumaus

@Blaumaus Blaumaus commented May 23, 2026 •

Copy link
Copy Markdown
Member

Changes

todo:

  • better endpoints? (PATCH, for specific fields only; not PUT)

If applicable, please describe what changes were made in this pull request.

Community Edition support

  • Your feature is implemented for the Swetrix Community Edition
  • This PR only updates the Cloud (Enterprise) Edition code (e.g. Paddle webhooks, blog, payouts, etc.)

Database migrations

  • Clickhouse / MySQL migrations added for this PR
  • No table schemas changed in this PR

Documentation

  • You have updated the documentation according to your PR
  • This PR did not change any publicly documented endpoints

Summary by CodeRabbit

  • New Features

    • Expanded autosave across project and user settings with blur/debounce behavior and immediate updates for many controls; added autosave user messages.
  • Bug Fixes

    • Deduplicated server-response handling to prevent duplicate processing and repeated success/error toasts across settings and admin pages.
  • Chores

    • Wired autosave into settings UI, removed several explicit Save buttons, simplified header authentication state handling, and refined various UI update flows.

Review Change Stack

@Blaumaus Blaumaus self-assigned this May 23, 2026
@coderabbitai

coderabbitai Bot commented May 23, 2026 •

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2b1ad8dc-d69e-44d3-ab69-50e990513156

📥 Commits

Reviewing files that changed from the base of the PR and between 67f391e and 4fa5139.

📒 Files selected for processing (5)
  • web/app/pages/Organisations/Settings/People.tsx
  • web/app/pages/Organisations/Settings/Projects.tsx
  • web/app/pages/Project/Settings/tabs/Revenue.tsx
  • web/app/pages/UserSettings/UserSettings.tsx
  • web/app/routes/projects.settings.$id.tsx

📝 Walkthrough

Walkthrough

Input-level autosave is added across Project and User settings; a shared useDeduplicateFetcherResponse hook prevents duplicate fetcher handling. Tabs receive blur callbacks or delegated toggle handlers; several list and settings pages mirror local lists and use dedupe guards.

Changes

Input-level Autosave Settings Implementation

Layer / File(s) Summary
Header - Remove unused user import
web/app/components/Header/index.tsx
The Header component now destructures only isAuthenticated and logout from the auth hook, removing the unused user value.
Tabs — Access, General, Shields prop changes
web/app/pages/Project/Settings/tabs/AccessSettings.tsx, web/app/pages/Project/Settings/tabs/General.tsx, web/app/pages/Project/Settings/tabs/Shields.tsx
Adds blur/handler props; Access delegates public/isPasswordProtected changes to parent autosave callbacks; General and Shields wire handleBlur to inputs.
DangerZone — Save button removal
web/app/pages/Project/Settings/tabs/DangerZone.tsx
Removes isSaving prop and the Save button from the DangerZone UI.
Localization - Autosave confirmation strings
web/public/locales/en.json
Adds profileSettings.autosave and project-level autosave entries used by autosave success toasts.
NotificationChannels — dedupe upgrade
web/app/components/NotificationChannels/*
Replaces manual/ref-based dedupe with the shared useDeduplicateFetcherResponse for subscribe and mutate flows.
New hook: useDeduplicateFetcherResponse
web/app/hooks/useDeduplicateFetcherResponse.ts
Adds useDeduplicateFetcherResponse which returns a stable callback used to decide whether a fetcher response should be handled (supports key-based or comparator-based deduplication).
Organisation settings — dedupe guard
web/app/pages/Organisations/Settings/index.tsx
Applies the dedupe hook to early-return when fetcher responses were already processed; updates effect dependencies.
Project Alerts / View / Annotations — dedupe guards
web/app/pages/Project/Settings/Alerts/*, web/app/pages/Project/Settings/Annotations.tsx
Replace ref-based last-handled logic with the shared dedupe hook and update effects to early-return on duplicates.
Project Emails — dedupe for multiple flows
web/app/pages/Project/Settings/Emails/Emails.tsx
Adds dedupe checkers for load/add/remove subscriber flows and requires idle state for remove-subscriber handling.
ProjectSettings — autosave infra & helpers
web/app/pages/Project/Settings/ProjectSettings.tsx
Adds AUTOSAVE_DEBOUNCE_MS, memoized helpers to map/normalize/serialize project form updates, registers autosaveFetcher, and imports needed hooks.
ProjectSettings — state, validation, lifecycle
web/app/pages/Project/Settings/ProjectSettings.tsx
Introduces autosave lifecycle refs (last-saved snapshot, active/pending autosaves, debounce timer, captcha secret flag), memoized validation, queuing/flushing autosaves with debounce, and autosave response handling (dedupe, merge state, apply field errors, chain pending autosaves).
ProjectSettings — input handlers & wiring
web/app/pages/Project/Settings/ProjectSettings.tsx, web/app/pages/Project/Settings/tabs/*
Implements handleInput (debounced autosave), handleInputBlur (immediate autosave), and handleFieldAutosave (local update + queue). Passes blur/handlers into General, Shields, and Access tabs; submit and password-protection flows flush/queue autosaves rather than sending full submit payloads.
ProjectSettings special fields
web/app/pages/Project/Settings/ProjectSettings.tsx, web/app/pages/Project/Settings/tabs/Revenue.tsx, web/app/pages/Project/Settings/tabs/DangerZone.tsx
GSC property selection, captcha difficulty, DangerZone active toggle, and Revenue currency selection now persist immediately via autosave and correlate responses to pending refs.
Routes — extended action response payloads
web/app/routes/organisations.$id.tsx, web/app/routes/projects.settings.$id.tsx
Action response types extended to include memberId, projectId, shareId, role, and revenueCurrencyRequestId where applicable for precise client-side list updates.
Organisations / Projects / People pages — local lists + dedupe
web/app/pages/Organisations/Settings/*, web/app/pages/Organisations/Settings/Projects.tsx, web/app/pages/Project/Settings/People/*
Introduce local members/projects state mirrors, dedupe guards, submitted-payload refs for add flows, and id-based removal/role updates without full revalidation.
UserSettings — autosave infra & handlers
web/app/pages/UserSettings/UserSettings.tsx, web/app/pages/UserSettings/components/*
Adds profileAutosaveFetcher, buffers pending/active profile autosaves with per-request toast keys, uses dedupe guard for main fetcher, implements submitProfileAutosave/flushProfileAutosave, and wires timezone/time format/report frequency UI to autosave immediately; email/password flows route through autosave with intent-specific toast keys.
UserSettings components — dedupe guards
web/app/pages/UserSettings/components/*
Applies useDeduplicateFetcherResponse to Organisation invites, ProjectList accept/reject, Socialisations unlink, and TwoFA flows to avoid duplicate handling and update dependencies.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 "I nibble fields and save with flair,

A hop, a blur — your changes cared for.
Toasts hum softly, dedupe clears the way,
Autosave sings: your settings stay."

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Description check ❓ Inconclusive The description follows the template structure but the main 'Changes' section contains only a TODO comment rather than substantive details about what was implemented. Expand the 'Changes' section with concrete details about which settings now autosave and how the autosave mechanism works across the affected components and pages.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'improvement: Autosave settings on change' clearly and concisely summarizes the main objective of the PR—implementing autosave functionality for settings across multiple pages and components.
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 improvement/settings-autosave

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.

@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.

🧹 Nitpick comments (1)
web/app/pages/UserSettings/UserSettings.tsx (1)

576-617: 💤 Low value

Consider reverting local state on autosave failure.

When profileAutosaveFetcher returns an error (lines 605-613), the local state (e.g., timezone, reportFrequency, form.timeFormat) remains set to the new value while the server retains the old value. The user sees an error toast but the UI remains inconsistent with server state.

This is acceptable for a "chill" review since users can manually re-select the correct value, but for a more robust UX, you could track the previous value and revert on error.

🤖 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 `@web/app/pages/UserSettings/UserSettings.tsx` around lines 576 - 617, When
profile autosave fails, the UI remains optimistically updated; capture and
restore previous local values by saving the prior snapshot before applying
optimistic changes and reverting it when profileAutosaveFetcher.data.error is
truthy. Specifically, in the flow that triggers autosave (where
activeProfileAutosave, pendingProfileAutosave, and flushProfileAutosave are
used), store previous values for the mutated fields (e.g., timezone,
reportFrequency, form.timeFormat) keyed to the autosave intent, then in the
useEffect branch handling profileAutosaveFetcher.data.error restore those saved
values (or call mergeUser with the server-side user if present) and clear
activeProfileAutosave/pendingProfileAutosave as currently done; ensure
lastHandledProfileAutosaveData is still updated to avoid double-handling.
🤖 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.

Nitpick comments:
In `@web/app/pages/UserSettings/UserSettings.tsx`:
- Around line 576-617: When profile autosave fails, the UI remains
optimistically updated; capture and restore previous local values by saving the
prior snapshot before applying optimistic changes and reverting it when
profileAutosaveFetcher.data.error is truthy. Specifically, in the flow that
triggers autosave (where activeProfileAutosave, pendingProfileAutosave, and
flushProfileAutosave are used), store previous values for the mutated fields
(e.g., timezone, reportFrequency, form.timeFormat) keyed to the autosave intent,
then in the useEffect branch handling profileAutosaveFetcher.data.error restore
those saved values (or call mergeUser with the server-side user if present) and
clear activeProfileAutosave/pendingProfileAutosave as currently done; ensure
lastHandledProfileAutosaveData is still updated to avoid double-handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c9dc1dd0-cfcb-4304-b7b8-bbcfd10a683b

📥 Commits

Reviewing files that changed from the base of the PR and between ddb1533 and ac6aab1.

📒 Files selected for processing (9)
  • web/app/components/Header/index.tsx
  • web/app/pages/Project/Settings/ProjectSettings.tsx
  • web/app/pages/Project/Settings/tabs/AccessSettings.tsx
  • web/app/pages/Project/Settings/tabs/DangerZone.tsx
  • web/app/pages/Project/Settings/tabs/General.tsx
  • web/app/pages/Project/Settings/tabs/Revenue.tsx
  • web/app/pages/Project/Settings/tabs/Shields.tsx
  • web/app/pages/UserSettings/UserSettings.tsx
  • web/public/locales/en.json
💤 Files with no reviewable changes (1)
  • web/app/pages/Project/Settings/tabs/DangerZone.tsx

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
web/app/pages/Project/Settings/ProjectSettings.tsx (2)

1168-1176: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Rollback failed autosaves for non-text settings.

handleFieldAutosave commits the new value to local state immediately, but the autosave error path only reports the failure. When a request is rejected, controls like public, active, or CAPTCHA difficulty can stay in the new position even though the server kept the old value.

Also applies to: 1208-1218


1558-1569: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t keep the new GSC property selected after a failed save.

This writes gscPropertyUri into local state before gsc-set-property succeeds, and the error path never restores the previous value. A failed request will therefore leave the dropdown showing a property that is not actually connected.

🤖 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 `@web/app/pages/Project/Settings/ProjectSettings.tsx` around lines 1558 - 1569,
The onSelect handler currently writes gscPropertyUri into local state before the
POST completes, so a failed gsc-set-property leaves the dropdown showing an
unsaved value; change the flow so you do NOT call setForm immediately in the
onSelect; instead call gscFetcher.submit({ intent: 'gsc-set-property',
propertyUri: item.key }, { method: 'post' }) first, and update
form.gscPropertyUri only after you observe a successful response (e.g. in an
effect watching gscFetcher.data or gscFetcher.state for the gsc-set-property
result); leave the previous gscPropertyUri intact on failure (no state change)
so the dropdown reflects the actual persisted value.
🧹 Nitpick comments (1)
web/app/pages/Organisations/Settings/index.tsx (1)

68-68: 🏗️ Heavy lift

Extract the fetcher-response dedupe into a shared hook.

This useRef + early-return pattern now exists across several settings screens. Centralizing it would make future autosave-side-effect fixes easier to apply consistently and reduce drift between pages.

Also applies to: 92-94

🤖 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 `@web/app/pages/Organisations/Settings/index.tsx` at line 68, The repeated
useRef-based dedupe (see lastHandledData: useRef<OrganisationSettingsActionData
| null>) and early-return pattern used to ignore duplicate fetcher responses
should be extracted into a reusable hook (e.g., useDeduplicateFetcherResponse)
and used across settings pages; implement a hook that accepts the fetcher (or
fetcher.data) and a comparator/key extractor, keeps the last handled value
internally (via ref), returns a boolean or the new data only when it differs
from the last value, then replace the inline lastHandledData checks in
Organisation Settings (and the other screens referenced) with calls to this new
hook to centralize the dedupe logic and avoid duplicated early-return logic.
🤖 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 `@web/app/pages/Project/Settings/tabs/Revenue.tsx`:
- Around line 148-156: When handling the 'update-revenue-currency' autosave
failure, restore the UI dropdown to the prior value instead of only nulling
pendingCurrency: on the error branch where you currently do
pendingCurrency.current = null and toast.error(error), also reset
selectedCurrency (or the state that drives the dropdown) back to the previous
value stored in pendingCurrency.current before it was cleared, so the displayed
currency matches saved status; apply the same fix to the analogous error
handling block at the other occurrence (lines ~354-357) to ensure both autosave
failures revert the UI to the previous currency.

---

Outside diff comments:
In `@web/app/pages/Project/Settings/ProjectSettings.tsx`:
- Around line 1558-1569: The onSelect handler currently writes gscPropertyUri
into local state before the POST completes, so a failed gsc-set-property leaves
the dropdown showing an unsaved value; change the flow so you do NOT call
setForm immediately in the onSelect; instead call gscFetcher.submit({ intent:
'gsc-set-property', propertyUri: item.key }, { method: 'post' }) first, and
update form.gscPropertyUri only after you observe a successful response (e.g. in
an effect watching gscFetcher.data or gscFetcher.state for the gsc-set-property
result); leave the previous gscPropertyUri intact on failure (no state change)
so the dropdown reflects the actual persisted value.

---

Nitpick comments:
In `@web/app/pages/Organisations/Settings/index.tsx`:
- Line 68: The repeated useRef-based dedupe (see lastHandledData:
useRef<OrganisationSettingsActionData | null>) and early-return pattern used to
ignore duplicate fetcher responses should be extracted into a reusable hook
(e.g., useDeduplicateFetcherResponse) and used across settings pages; implement
a hook that accepts the fetcher (or fetcher.data) and a comparator/key
extractor, keeps the last handled value internally (via ref), returns a boolean
or the new data only when it differs from the last value, then replace the
inline lastHandledData checks in Organisation Settings (and the other screens
referenced) with calls to this new hook to centralize the dedupe logic and avoid
duplicated early-return logic.
🪄 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

Run ID: 6a7ca194-9886-4d26-8131-c7f72aebf34d

📥 Commits

Reviewing files that changed from the base of the PR and between ac6aab1 and d7bc8f0.

📒 Files selected for processing (11)
  • web/app/pages/Organisations/Settings/People.tsx
  • web/app/pages/Organisations/Settings/Projects.tsx
  • web/app/pages/Organisations/Settings/index.tsx
  • web/app/pages/Project/Settings/Emails/Emails.tsx
  • web/app/pages/Project/Settings/People/People.tsx
  • web/app/pages/Project/Settings/ProjectSettings.tsx
  • web/app/pages/Project/Settings/tabs/Revenue.tsx
  • web/app/pages/UserSettings/components/Organisations.tsx
  • web/app/pages/UserSettings/components/ProjectList.tsx
  • web/app/pages/UserSettings/components/Socialisations.tsx
  • web/app/pages/UserSettings/components/TwoFA.tsx

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
web/app/pages/Project/Settings/tabs/Revenue.tsx (1)

206-214: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent overlapping currency autosaves from corrupting rollback state.

pendingCurrency tracks only one {previous,next} pair. If the user changes currency again before the first request resolves, this ref gets overwritten and success/error handlers can commit or rollback to the wrong value.

💡 Minimal fix
   const handleCurrencyChange = (
     currency: RevenueCurrency,
     previousCurrency: RevenueCurrency,
   ) => {
+    if (fetcher.state !== 'idle' || pendingCurrency.current) return
     pendingCurrency.current = { previous: previousCurrency, next: currency }
     fetcher.submit(
       { intent: 'update-revenue-currency', currency },
       { method: 'POST', action: `/projects/settings/${projectId}` },
     )
   }

   ...
   <Select<CurrencyOption>
     ...
     onSelect={(item) => {
+      if (fetcher.state !== 'idle' || pendingCurrency.current) return
       if (item.code === selectedCurrency) return

       const previousCurrency = isRevenueCurrency(status?.currency)
         ? status.currency
         : selectedCurrency
       setSelectedCurrency(item.code)
       handleCurrencyChange(item.code, previousCurrency)
     }}
   />

Also applies to: 370-378

🤖 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 `@web/app/pages/Project/Settings/tabs/Revenue.tsx` around lines 206 - 214,
pendingCurrency currently holds a single {previous,next} and gets overwritten if
the user changes currency before a prior request resolves, causing handlers to
commit/rollback the wrong value; fix by associating each submission with a
unique request id (e.g., const requestId = Symbol() or a counter) inside
handleCurrencyChange, store that id alongside the {previous,next} in
pendingCurrency.current before calling fetcher.submit, and then in the success
and error handlers only apply commit/rollback when the handler's captured
requestId matches pendingCurrency.current.requestId (otherwise ignore), so stale
responses cannot corrupt the rollback state; update any other currency-change
call sites (lines similar to 370-378) to use the same pattern.
web/app/pages/UserSettings/UserSettings.tsx (1)

502-523: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t silently swallow server-side fieldErrors.

This branch handles fieldErrors, but if there is no top-level error it returns on Line 523 without showing any feedback. That makes backend validation failures look like a no-op to the user.

At minimum, surface a fallback toast from the first field error before returning; ideally, plumb these errors into the relevant inline form state.

🤖 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 `@web/app/pages/UserSettings/UserSettings.tsx` around lines 502 - 523, The
branch that handles fetcher.data?.fieldErrors currently returns early when
there's no top-level fetcher.data.error, silently swallowing validation
feedback; update the block that checks fetcher.data?.error ||
fetcher.data?.fieldErrors so that when fetcher.data.fieldErrors exists (even if
fetcher.data.error is falsy) you surface user feedback — e.g., extract the first
field error from fetcher.data.fieldErrors and show a fallback toast or map the
field errors into the form's inline error state before returning; keep the
existing rollback logic using pendingToggles.current, mergeUser,
pendingPasswordChangeRef/passwordChangedRef and setIsPasswordChangeModalOpened,
but do not return without first displaying the field error(s).
🤖 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 `@web/app/pages/Organisations/Settings/People.tsx`:
- Around line 286-291: The removal logic is racey because it falls back to
memberToRemove (cleared when the modal closes); change the fallback to a stable
source by capturing the id before the modal closes or reading it from the
fetcher submission instead of memberToRemove: use fetcher.data.memberId first,
then fallback to fetcher.submission?.formData.get('memberId') or a captured
variable (e.g., memberIdToRemove) that you set when opening the modal; update
the block around removedMemberId, setMembers, and the modal close flow so
memberToRemove is not relied on post-submit.

In `@web/app/pages/Organisations/Settings/Projects.tsx`:
- Around line 241-255: The success path currently reads the UI state variable
selectedProject when appending to setProjects, which can drift if the selection
changes while the request is in flight; capture the project you actually
submitted into a local const (e.g., submittedProject) before starting the async
request and then use submittedProject inside the success branch instead of
selectedProject (update the block that calls setProjects/current.some(...) and
the object literal to reference submittedProject.id, submittedProject.name, and
submittedProject.admin) so the list reflects the submitted item regardless of
later UI changes.

---

Outside diff comments:
In `@web/app/pages/Project/Settings/tabs/Revenue.tsx`:
- Around line 206-214: pendingCurrency currently holds a single {previous,next}
and gets overwritten if the user changes currency before a prior request
resolves, causing handlers to commit/rollback the wrong value; fix by
associating each submission with a unique request id (e.g., const requestId =
Symbol() or a counter) inside handleCurrencyChange, store that id alongside the
{previous,next} in pendingCurrency.current before calling fetcher.submit, and
then in the success and error handlers only apply commit/rollback when the
handler's captured requestId matches pendingCurrency.current.requestId
(otherwise ignore), so stale responses cannot corrupt the rollback state; update
any other currency-change call sites (lines similar to 370-378) to use the same
pattern.

In `@web/app/pages/UserSettings/UserSettings.tsx`:
- Around line 502-523: The branch that handles fetcher.data?.fieldErrors
currently returns early when there's no top-level fetcher.data.error, silently
swallowing validation feedback; update the block that checks fetcher.data?.error
|| fetcher.data?.fieldErrors so that when fetcher.data.fieldErrors exists (even
if fetcher.data.error is falsy) you surface user feedback — e.g., extract the
first field error from fetcher.data.fieldErrors and show a fallback toast or map
the field errors into the form's inline error state before returning; keep the
existing rollback logic using pendingToggles.current, mergeUser,
pendingPasswordChangeRef/passwordChangedRef and setIsPasswordChangeModalOpened,
but do not return without first displaying the field error(s).
🪄 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

Run ID: 42ed434e-a3a0-47d3-82c5-5f4b9860fa78

📥 Commits

Reviewing files that changed from the base of the PR and between d7bc8f0 and 67f391e.

📒 Files selected for processing (20)
  • web/app/components/NotificationChannels/EnableWebPushButton.tsx
  • web/app/components/NotificationChannels/NotificationChannels.tsx
  • web/app/hooks/useDeduplicateFetcherResponse.ts
  • web/app/pages/Organisations/Settings/People.tsx
  • web/app/pages/Organisations/Settings/Projects.tsx
  • web/app/pages/Organisations/Settings/index.tsx
  • web/app/pages/Project/Settings/Alerts/ProjectAlertsSettings.tsx
  • web/app/pages/Project/Settings/Alerts/ProjectAlertsView.tsx
  • web/app/pages/Project/Settings/Annotations.tsx
  • web/app/pages/Project/Settings/Emails/Emails.tsx
  • web/app/pages/Project/Settings/People/People.tsx
  • web/app/pages/Project/Settings/ProjectSettings.tsx
  • web/app/pages/Project/Settings/tabs/Revenue.tsx
  • web/app/pages/UserSettings/UserSettings.tsx
  • web/app/pages/UserSettings/components/Organisations.tsx
  • web/app/pages/UserSettings/components/ProjectList.tsx
  • web/app/pages/UserSettings/components/Socialisations.tsx
  • web/app/pages/UserSettings/components/TwoFA.tsx
  • web/app/routes/organisations.$id.tsx
  • web/app/routes/projects.settings.$id.tsx

@Blaumaus
Blaumaus merged commit d575ee0 into main May 23, 2026
10 of 11 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jun 4, 2026
3 of 6 tasks
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