Skip to content

Update state detection and error handling logic#486

Merged
SvenVw merged 4 commits into
developmentfrom
fix-double-delete-error
Mar 2, 2026
Merged

Update state detection and error handling logic#486
SvenVw merged 4 commits into
developmentfrom
fix-double-delete-error

Conversation

@SvenVw
Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw commented Mar 2, 2026

Summary by CodeRabbit

  • Bug Fixes
    • Buttons and forms remain disabled during both submission and revalidation phases.
    • Improved detection of permission-denied errors to avoid unnecessary error pages.
    • Loading state handling standardized across forms and lists so spinners appear and controls stay disabled for any active navigation/fetch activity, not only initial submissions.

SvenVw added 2 commits March 2, 2026 12:29
…ms remain disabled during both the "submitting" and "loading" (revalidation) phases by checking for `state !== "idle"
@SvenVw SvenVw self-assigned this Mar 2, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 2, 2026

📝 Walkthrough

Walkthrough

Replaces many exact "submitting" checks with broader "non-idle" checks (e.g., state !== "idle") across UI components and routes to widen busy-state detection; also improves permission-denied error detection to match nested/chained errors.

Changes

Cohort / File(s) Summary
Changeset Documentation
.changeset/famous-hotels-travel.md, .changeset/lucky-tigers-report.md
Added two changeset files documenting patch releases: one for keeping controls disabled during submitting/revalidation, and one for improved permission-denied error detection.
Fertilizer Applications
fdm-app/app/components/blocks/fertilizer-applications/card.tsx, .../columns.tsx, .../form.tsx, .../list.tsx
Switched busy/submitting checks to use non-idle conditions and introduced isBusy prop for list; updates disable logic, spinner rendering, and delete guards.
Cultivation & Atlas
fdm-app/app/components/blocks/cultivation/card-details.tsx, fdm-app/app/components/blocks/atlas/atlas-panels.tsx
Broadened submitting detection to any non-idle fetcher/navigation state for button/fieldset disabled and loading indicators.
Harvest / Field / Soil / Mijnpercelen
fdm-app/app/components/blocks/harvest/form.tsx, .../field/delete.tsx, .../soil/list.tsx, .../soil/form-upload.tsx, .../mijnpercelen/form-upload.tsx
Changed checks from explicit "submitting" to !== "idle"; added per-item deleting tracking in soil list and adjusted UI labeling/spinners accordingly.
Routes — Fertilizer / Rotation / Settings / Auth
fdm-app/app/routes/.../fertilizer._index.tsx, .../rotation_.fertilizer._index.tsx, .../rotation_.harvest._index.tsx, .../settings.delete.tsx, .../settings.organic-certification.tsx, app/routes/signin.*.tsx
Updated isSubmitting/isDeleting computations to consider any non-idle navigation state (often combined with presence of formData or method), affecting loading overlays and disabled states.
Error Handling
fdm-app/app/lib/error.ts
Replaced exact error.message equality checks with containsErrorMessage(...) to detect permission-denied errors even when nested; preserve original message handling when present.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

fdm-app, bug, branch:development

Suggested reviewers

  • BoraIneviNMI
  • gerardhros

Poem

🐰 I hopped through state checks, wide and spry,
From "submitting" small to "not-idle" sky,
Buttons rest their paws, spinners hum in tune,
Deep errors uncovered by the light of the moon,
A rabbit's nudge for smoother afternoons. 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed Title check skipped as CodeRabbit has written the PR title.

✏️ 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 fix-double-delete-error

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 changed the title @coderabbitai Update state detection and error handling logic Mar 2, 2026
Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (1)
fdm-app/app/components/blocks/soil/list.tsx (1)

97-99: Scope the loading label to the row being deleted.

At Line 111, every row shows "Verwijderen..." when one delete is in progress because the condition is global (fetcher.state !== "idle"). Keep global disabling if desired, but tie spinner/label to the active a_id to avoid confusing feedback.

Also applies to: 111-118

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

In `@fdm-app/app/components/blocks/soil/list.tsx` around lines 97 - 99, The delete
label is shown for every row because the loading check uses the global
fetcher.state; change the condition so the spinner/“Verwijderen...” label is
true only when the current row's id matches the active deletion request. Modify
the expression that currently uses fetcher.state !== "idle" (and
analysis.a_source === "nl-other-nmi") to also verify the active a_id from the
fetcher submission/formData (e.g. fetcher.submission?.formData?.get("a_id") ===
analysis.a_id) or use a local activeDeletingId state set when dispatching the
delete; apply this check where the label/spinner is rendered (reference:
fetcher, fetcher.state, fetcher.submission/formData and analysis.a_id).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.changeset/famous-hotels-travel.md:
- Line 5: The release note contains an unclosed inline code span around the
expression `state !== "idle"` which breaks markdown; edit
.changeset/famous-hotels-travel.md and close the inline code by adding the
missing backtick so the sentence reads with a properly fenced inline code span
(e.g., `state !== "idle"`), ensuring the markdown renders correctly.

In `@fdm-app/app/components/blocks/cultivation/card-details.tsx`:
- Around line 106-107: The checkbox is using a different busy-state check
(fetcher.state === "submitting") than the fieldset and button (fetcher.state !==
"idle"); update the checkbox's busy/disabled condition to use fetcher.state !==
"idle" so it aligns with the existing pattern (locate the checkbox input in the
CardDetails component and replace the fetching condition), and verify other
related controls use the same fetcher.state !== "idle" check for consistency.

In `@fdm-app/app/components/blocks/fertilizer-applications/list.tsx`:
- Around line 93-95: FertilizerApplicationsList is checking a local useFetcher
(fetcher.state !== "idle") but that fetcher never runs mutations so the busy
checks are ineffective; update the component to accept a boolean prop isBusy
(passed from parent FertilizerApplicationCard which actually runs
handleDelete/handleEdit) and replace all uses of fetcher.state !== "idle" in
FertilizerApplicationsList with the new isBusy prop, update the parent to
compute and pass isBusy (true while its fetcher is loading/submitting) and
remove or ignore the unused local useFetcher in FertilizerApplicationsList.

In `@fdm-app/app/components/blocks/harvest/form.tsx`:
- Around line 620-621: The submit controls and form wrappers in the harvest form
currently only check form.formState.isSubmitting and thus allow submits while a
delete fetcher is in flight; update every submit button and surrounding form
container in this component (where you currently reference
form.formState.isSubmitting) to also consider fetcher.state by disabling when
(form.formState.isSubmitting || fetcher.state !== "idle"), and add a disabled
attribute to the inline submit button so it obeys the same gating; reference the
existing fetcher variable and form.formState.isSubmitting in Harvest form JSX
and apply the combined check to the submit buttons and form containers.

In `@fdm-app/app/lib/error.ts`:
- Around line 248-251: handleLoaderError currently checks for permission issues
using exact equality which can miss wrapped errors; update handleLoaderError to
use the same fuzzy check as handleActionError by calling
containsErrorMessage(error, "Principal does not have permission to perform this
action") (or equivalent helper) so permission-related errors caught in loaders
map to 403 instead of falling through to 500; reference containsErrorMessage,
handleLoaderError, handleActionError and the permission message when making the
change.

In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.field.fertilizer._index.tsx:
- Line 327: The overlay shows fertilizer-specific submit messages because
isSubmitting is set from navigation.state !== "idle", which also flips true on
loader revalidations (e.g., handleSelectionChange updating search params);
change isSubmitting to only true for real form submissions (e.g.,
navigation.state === "submitting" && Boolean(navigation.formData)) or inspect a
known form field in navigation.formData (such as the fertilizer form's submit
name) so loader-triggered navigations don't show the fertilizer messages; update
the conditional used to render the overlay (the isSubmitting variable and the
JSX at the fertilizer overlay render around the current overlay text) to rely on
the new check so only genuine fertilizer form submissions display "Bemesting
wordt gewijzigd..." / "Bemesting wordt toegevoegd...".

In
`@fdm-app/app/routes/farm`.$b_id_farm.$calendar.rotation_.fertilizer._index.tsx:
- Line 308: Change the isSubmitting check to only treat real form (mutation)
submissions as submitting: replace the current navigation.state !== "idle" logic
with a guard that also requires navigation.formData to be present (e.g.,
isSubmitting = navigation.state !== "idle" && Boolean(navigation.formData)).
This will prevent search-param navigations triggered by setSearchParams() from
showing the fertilizer overlay ("Bemesting wordt toegevoegd...") during filter
changes; update any uses of isSubmitting (the overlay render) accordingly so it
only shows when navigation.formData is populated.

In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.rotation_.harvest._index.tsx:
- Line 379: isSubmitting currently treats any navigation (including query-param
updates from setSearchParams) as a submission because it uses navigation.state
!== "idle"; change the check to only flag real form submissions by requiring
navigation.state === "submitting" and that navigation.formData exists (e.g.,
const isSubmitting = navigation.state === "submitting" &&
Boolean(navigation.formData)), so the "Oogst wordt toegevoegd..." overlay only
appears for actual harvest form submissions (references: isSubmitting,
navigation.state, navigation.formData, setSearchParams).

---

Nitpick comments:
In `@fdm-app/app/components/blocks/soil/list.tsx`:
- Around line 97-99: The delete label is shown for every row because the loading
check uses the global fetcher.state; change the condition so the
spinner/“Verwijderen...” label is true only when the current row's id matches
the active deletion request. Modify the expression that currently uses
fetcher.state !== "idle" (and analysis.a_source === "nl-other-nmi") to also
verify the active a_id from the fetcher submission/formData (e.g.
fetcher.submission?.formData?.get("a_id") === analysis.a_id) or use a local
activeDeletingId state set when dispatching the delete; apply this check where
the label/spinner is rendered (reference: fetcher, fetcher.state,
fetcher.submission/formData and analysis.a_id).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db4f1fa and e1abff7.

📒 Files selected for processing (22)
  • .changeset/famous-hotels-travel.md
  • .changeset/lucky-tigers-report.md
  • fdm-app/app/components/blocks/atlas/atlas-panels.tsx
  • fdm-app/app/components/blocks/cultivation/card-details.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/card.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/columns.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/list.tsx
  • fdm-app/app/components/blocks/field/delete.tsx
  • fdm-app/app/components/blocks/harvest/form.tsx
  • fdm-app/app/components/blocks/mijnpercelen/form-upload.tsx
  • fdm-app/app/components/blocks/soil/form-upload.tsx
  • fdm-app/app/components/blocks/soil/list.tsx
  • fdm-app/app/lib/error.ts
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation_.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation_.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.settings.delete.tsx
  • fdm-app/app/routes/farm.$b_id_farm.settings.organic-certification.tsx
  • fdm-app/app/routes/signin.check-your-email.tsx
  • fdm-app/app/routes/signin.verify.tsx

Comment thread .changeset/famous-hotels-travel.md Outdated
Comment thread fdm-app/app/components/blocks/cultivation/card-details.tsx
Comment thread fdm-app/app/components/blocks/fertilizer-applications/list.tsx Outdated
Comment thread fdm-app/app/components/blocks/harvest/form.tsx
Comment thread fdm-app/app/lib/error.ts
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx Outdated
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation_.fertilizer._index.tsx Outdated
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation_.harvest._index.tsx Outdated
Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (2)
fdm-app/app/components/blocks/cultivation/card-details.tsx (1)

84-90: Consider extracting a shared isBusy flag to avoid condition drift.

The same busy expression is repeated in multiple places; centralizing it improves readability and reduces future inconsistencies.

♻️ Suggested refactor
+    const isBusy = form.formState.isSubmitting || fetcher.state !== "idle"
...
                             disabled={
-                                form.formState.isSubmitting ||
-                                fetcher.state !== "idle"
+                                isBusy
                             }
                         >
-                            {form.formState.isSubmitting ||
-                            fetcher.state !== "idle" ? (
+                            {isBusy ? (
...
                             disabled={
                                 !editable ||
-                                form.formState.isSubmitting ||
-                                fetcher.state !== "idle"
+                                isBusy
                             }
...
                                                     disabled={
                                                         !editable ||
-                                                        form.formState
-                                                            .isSubmitting ||
-                                                        fetcher.state !== "idle"
+                                                        isBusy
                                                     }
...
                                     disabled={
-                                        form.formState.isSubmitting ||
-                                        fetcher.state !== "idle"
+                                        isBusy
                                     }
                                 >
-                                    {form.formState.isSubmitting ||
-                                    fetcher.state !== "idle" ? (
+                                    {isBusy ? (

Also applies to: 103-107, 140-145, 214-220

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

In `@fdm-app/app/components/blocks/cultivation/card-details.tsx` around lines 84 -
90, Extract the repeated busy check (form.formState.isSubmitting ||
fetcher.state !== "idle") into a single boolean like isBusy at the top of the
CardDetails component (or the relevant render function) and replace all
occurrences (currently using form.formState.isSubmitting and fetcher.state
checks) with that isBusy variable; ensure you update the JSX props and
conditional render blocks that reference the expression (the spots around the
existing usages of form.formState.isSubmitting and fetcher.state) so they all
read from isBusy to prevent condition drift.
fdm-app/app/components/blocks/fertilizer-applications/list.tsx (1)

149-153: Consider row-scoped pending visuals instead of global spinner state.

isBusy currently renders spinner icons on every delete button during a single in-flight delete. Tracking a pending p_app_id would give clearer feedback.

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

In `@fdm-app/app/components/blocks/fertilizer-applications/list.tsx` around lines
149 - 153, Replace the global isBusy boolean with a row-scoped pending id:
introduce state (e.g., pendingDeleteId) and in the delete handler set
pendingDeleteId to the p_app_id being deleted, then clear it on success/error;
in the row UI replace the isBusy check with (pendingDeleteId === p_app.id) so
only the deleting row shows <Spinner /> while others render <Trash />, and also
disable the delete button when pendingDeleteId === p_app.id to prevent duplicate
deletes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@fdm-app/app/components/blocks/cultivation/card-details.tsx`:
- Around line 84-90: Extract the repeated busy check
(form.formState.isSubmitting || fetcher.state !== "idle") into a single boolean
like isBusy at the top of the CardDetails component (or the relevant render
function) and replace all occurrences (currently using
form.formState.isSubmitting and fetcher.state checks) with that isBusy variable;
ensure you update the JSX props and conditional render blocks that reference the
expression (the spots around the existing usages of form.formState.isSubmitting
and fetcher.state) so they all read from isBusy to prevent condition drift.

In `@fdm-app/app/components/blocks/fertilizer-applications/list.tsx`:
- Around line 149-153: Replace the global isBusy boolean with a row-scoped
pending id: introduce state (e.g., pendingDeleteId) and in the delete handler
set pendingDeleteId to the p_app_id being deleted, then clear it on
success/error; in the row UI replace the isBusy check with (pendingDeleteId ===
p_app.id) so only the deleting row shows <Spinner /> while others render <Trash
/>, and also disable the delete button when pendingDeleteId === p_app.id to
prevent duplicate deletes.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1abff7 and c0ad90a.

📒 Files selected for processing (10)
  • .changeset/famous-hotels-travel.md
  • fdm-app/app/components/blocks/cultivation/card-details.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/card.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/list.tsx
  • fdm-app/app/components/blocks/harvest/form.tsx
  • fdm-app/app/components/blocks/soil/list.tsx
  • fdm-app/app/lib/error.ts
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation_.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation_.harvest._index.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • fdm-app/app/components/blocks/soil/list.tsx
  • .changeset/famous-hotels-travel.md

@SvenVw SvenVw requested a review from BoraIneviNMI March 2, 2026 14:41
Copy link
Copy Markdown
Collaborator

@BoraIneviNMI BoraIneviNMI left a comment

Choose a reason for hiding this comment

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

All changes look correct.

@SvenVw SvenVw merged commit a138943 into development Mar 2, 2026
10 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.

2 participants