Skip to content

Update farm creation form with new fields and conditional logic#450

Merged
SvenVw merged 14 commits into
developmentfrom
FDM437
Feb 10, 2026
Merged

Update farm creation form with new fields and conditional logic#450
SvenVw merged 14 commits into
developmentfrom
FDM437

Conversation

@SvenVw
Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw commented Feb 4, 2026

Summary by CodeRabbit

  • New Features

    • KvK number, grazing intention, and organic certification fields added to the farm creation form; date picker and auto-issuance behavior included.
    • Informational box at the start of the wizard and a right-side help panel.
  • Improvements

    • KvK number now always shown on farms overview with standardized formatting.
    • Derogation question/acceptance shown only for years before 2026; attempts for 2026+ are blocked with validation messaging.
  • UI

    • Responsive multi-column, card-based form layout with clearer per-field error and help displays.

Closes #437

@SvenVw SvenVw self-assigned this Feb 4, 2026
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Feb 4, 2026

🦋 Changeset detected

Latest commit: a1ecd75

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@svenvw/fdm-app Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds changeset entries; always displays KvK-nummer on the farm overview; hides/blocks derogation for 2026+; and overhauls the Add Farm form to include KvK, grazing intention, and organic certification fields with backend wiring to persist those settings.

Changes

Cohort / File(s) Summary
Changesets
/.changeset/hot-hairs-smile.md, /.changeset/social-bars-juggle.md, /.changeset/yummy-paths-wink.md
Adds changeset files documenting UI/form updates and minor/patch version bumps.
Farm Overview
fdm-app/app/routes/farm._index.tsx
Narrowed loader signature (removed params) and always renders a KvK-nummer row showing farm.b_businessid_farm or Onbekend; moved to consistent layout/styling.
Farm Creation Form
fdm-app/app/routes/farm.create._index.tsx
Large rewrite: multi-column UI, expanded form schema (b_businessid_farm, has_derogation, derogation_start_year, grazing_intention, organic_certification, organic_skal, organic_traces, organic_issued), loader signature changed, derived UI logic (hide/reset derogation for year ≥2026), Controller/DatePicker usage, and action updated to batch calls for derogation, grazing, and organic setup.
Derogation Settings Action
fdm-app/app/routes/farm.$b_id_farm.settings.derogation.tsx
Adds guard that rejects attempts to enable derogation for year ≥2026 (returns error and skips addDerogation).
Integrations / Components
fdm-app/app/components/..., fdm-app/app/utils/..., fdm-app/app/routes/...
Introduces/uses react-hook-form Controller/resolver patterns, DatePicker, and new backend calls (setGrazingIntention, addOrganicCertification) to persist grazing and organic settings.

Sequence Diagram

sequenceDiagram
    actor User
    participant Form as Farm Create Form
    participant Server as Server Action
    participant GrazingSvc as Grazing Service
    participant OrganicSvc as Organic Service
    participant DB as Database

    User->>Form: Fill fields (Year, KvK, Grazing?, Organic?, Derogation?)
    Form->>Form: If year >= 2026 hide/disable derogation
    User->>Form: Submit
    Form->>Server: POST form data
    Server->>Server: Validate, enforce rules (no derogation if year >= 2026)
    alt Grazing enabled
        Server->>GrazingSvc: setGrazingIntention(principalId, farmId, year, true)
        GrazingSvc->>DB: persist grazing intention
    end
    alt Organic enabled
        Server->>OrganicSvc: addOrganicCertification(farmId, issued, skal, traces)
        OrganicSvc->>DB: persist organic certification
    end
    Server->>DB: create/update farm record
    Server->>User: Redirect / respond with result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • BoraIneviNMI
  • gerardhros

Poem

🐰 I hop through fields of code and cheer,
KvK appears, derogation ends next year,
Grazing checked, organic flags set true,
Forms bloom new — I nibble a carrot for you,
Data hops onward, tidy and near.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main changes: updating the farm creation form with new fields (grazing, organic certification) and conditional logic (derogation restriction for 2026+).
Linked Issues check ✅ Passed The PR implementation meets all requirements from #437: derogation is hidden and disabled for 2026+, grazing_intention field is added, organic_certification with optional fields (SKAL, TRACES, issued date) is implemented, and backend calls (setGrazingIntention, addOrganicCertification) are invoked correctly.
Out of Scope Changes check ✅ Passed All changes are directly related to #437: three changesets document the updates, form.create updates implement new fields and derogation logic, farm._index shows KvK display changes related to the form improvements, and derogation.tsx enforces the 2026 restriction.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch FDM437

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.

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 4, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.07%. Comparing base (9413216) to head (a1ecd75).
⚠️ Report is 15 commits behind head on development.

Additional details and impacted files
@@             Coverage Diff              @@
##           development     #450   +/-   ##
============================================
  Coverage        88.07%   88.07%           
============================================
  Files               91       91           
  Lines             4621     4621           
  Branches          1492     1492           
============================================
  Hits              4070     4070           
  Misses             551      551           
Flag Coverage Δ
fdm-calculator 88.76% <ø> (ø)
fdm-core 86.77% <ø> (ø)
fdm-data 92.12% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot changed the title @coderabbitai Update farm creation form with new fields and conditional logic Feb 4, 2026
@coderabbitai coderabbitai Bot added branch:development Issue only affecting development, not the main branch (yet) enhancement New feature or request fdm-app labels Feb 4, 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: 2

Caution

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

⚠️ Outside diff range comments (1)
fdm-app/app/routes/farm.create._index.tsx (1)

126-133: ⚠️ Potential issue | 🟡 Minor

Empty destructuring pattern in loader signature.

The static analysis correctly flags the empty object pattern {}. Since LoaderFunctionArgs isn't being used, consider either using an underscore prefix or removing the parameter entirely.

🔧 Proposed fix
-export async function loader({}: LoaderFunctionArgs) {
+export async function loader(_args: LoaderFunctionArgs) {
     const yearSelection = getCalendarSelection()

Or if the type annotation isn't needed:

-export async function loader({}: LoaderFunctionArgs) {
+export async function loader() {
     const yearSelection = getCalendarSelection()
🤖 Fix all issues with AI agents
In `@fdm-app/app/routes/farm._index.tsx`:
- Around line 317-330: The conditional currently checking farm.b_postalcode_farm
to decide whether to render the KvK number is wrong; update the JSX conditional
in the farm listing block to check farm.b_businessid_farm instead so the KvK
(farm.b_businessid_farm) is shown when present and "Onbekend" otherwise—locate
the JSX that renders "KvK-nummer:" and replace the conditional reference to
farm.b_postalcode_farm with farm.b_businessid_farm.

In `@fdm-app/app/routes/farm.create._index.tsx`:
- Around line 686-720: Fix the typo and duplicate list header in the help-list:
change the misspelled word "zdoat" in the paragraph under "Bedrijfsgegevens
(Nu):" to "zodat", and rename the second duplicate list header "Percelen:" (the
item that talks about checking hoofdgewas, bufferstroken and bodemeigenschappen)
to a distinct label such as "Perceelsdetails:" (or merge its content into the
previous "Percelen:" item) so headers are not duplicated; update the
corresponding JSX strings where "Bedrijfsgegevens (Nu):", "Percelen:" and the
second "Percelen:" appear to reflect these text changes.
🧹 Nitpick comments (1)
fdm-app/app/routes/farm.create._index.tsx (1)

756-761: Remove commented-out code.

This commented-out tip block should be removed or restored. Leaving dead code in the codebase reduces readability.

🧹 Proposed fix
                                    <p className="text-muted-foreground">
                                        Ja, je kunt ook je gegevens na het
                                        doorlopen van het stappenplan invullen
                                        of aanpassen. Met het stappenplan staat
                                        het alleen makkelijk op een rijtje om
                                        een nieuw bedrijf aan te maken.
                                    </p>
-                                   {/* <p className="text-xs text-muted-foreground italic">
-                                       Tip: KVK- en SKAL-nummers zijn optioneel
-                                       maar handig voor het automatisch
-                                       invullen van rapportages.
-                                   </p> */}
                                </div>

Comment thread fdm-app/app/routes/farm._index.tsx
Comment thread fdm-app/app/routes/farm.create._index.tsx
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: 0

Caution

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

⚠️ Outside diff range comments (1)
fdm-app/app/routes/farm.create._index.tsx (1)

786-817: ⚠️ Potential issue | 🟡 Minor

Normalize derogation flags for year ≥ 2026 before processing

If a user enables derogation for 2025 and then switches to 2026, the hidden flag can still submit as true. This conflicts with the requirement to default to false on submission; consider normalizing the values before the block.

✅ Suggested normalization
-        if (year < 2026 && has_derogation && derogation_start_year) {
+        const isDerogationAllowed = year < 2026
+        const effectiveHasDerogation = isDerogationAllowed && has_derogation
+        const effectiveDerogationStartYear = isDerogationAllowed
+            ? derogation_start_year
+            : undefined
+
+        if (effectiveHasDerogation && effectiveDerogationStartYear) {
             const years = Array.from(
-                { length: 2025 - derogation_start_year + 1 },
-                (_, i) => derogation_start_year + i,
+                { length: 2025 - effectiveDerogationStartYear + 1 },
+                (_, i) => effectiveDerogationStartYear + i,
             )
             await Promise.all(
                 years.map((year) =>
                     addDerogation(fdm, session.principal_id, b_id_farm, year),
                 ),
             )
         }
🧹 Nitpick comments (1)
fdm-app/app/routes/farm.create._index.tsx (1)

93-101: Normalize optional derogation_start_year parsing

Consider converting empty-string inputs to undefined before coercion so optional numeric fields don’t fail with NaN if a cleared value posts as "".

♻️ Proposed refactor
-    derogation_start_year: z.coerce
-        .number()
-        .min(2006, {
-            error: "Startjaar moet minimaal 2006 zijn",
-        })
-        .max(2025, {
-            error: "Startjaar mag maximaal 2025 zijn",
-        })
-        .optional(),
+    derogation_start_year: z.preprocess(
+        (val) => (val === "" ? undefined : val),
+        z.coerce
+            .number()
+            .min(2006, {
+                error: "Startjaar moet minimaal 2006 zijn",
+            })
+            .max(2025, {
+                error: "Startjaar mag maximaal 2025 zijn",
+            })
+            .optional(),
+    ),
Based on learnings: In fdm-app Zod validation schemas, required numeric fields use direct z.coerce.number() without preprocessing, while optional numeric fields use z.preprocess((val) => (val === "" ? undefined : val), z.coerce.number().optional()) to convert empty strings to undefined.

@SvenVw SvenVw requested a review from gerardhros February 6, 2026 08:36
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: 2

🤖 Fix all issues with AI agents
In `@fdm-app/app/routes/farm.create._index.tsx`:
- Around line 724-737: The sentence in the JSX list item after the <strong
className="text-foreground">Bouwplan:</strong> contains a double verb ("Hier vul
je ... invullen"); update that text node to use a single correct verb form—e.g.,
change "Hier vul je op gewasniveau de bemestingen, oogsten en andere gegevens
van je gewassen invullen." to either "Hier vul je op gewasniveau de bemestingen,
oogsten en andere gegevens van je gewassen in." or "Hier kun je op gewasniveau
de bemestingen, oogsten en andere gegevens van je gewassen invullen."—modify the
string in the JSX accordingly.
- Around line 714-716: Replace the typo "buffestroken" with "bufferstroken" in
the Dutch UI string that reads "Check het hoofdgewas van de percelen, markeer
buffestroken en bekijk de geschatte bodemeigenschappen." (locate the
JSX/translation entry in the farm create route file where that exact sentence is
rendered) and, if this string is part of an i18n resource or translation key,
update the translation source entry instead of hardcoded JSX so the change
propagates correctly.
🧹 Nitpick comments (4)
fdm-app/app/routes/farm.$b_id_farm.settings.derogation.tsx (1)

86-91: Good server-side guard for the 2026 derogation cutoff.

The UI on line 110 already filters to year <= 2025, so this action-side check provides proper defense-in-depth. One minor nit from Biome: line 89 uses a template literal without interpolation.

Suggested fix
             if (year >= 2026) {
                 return dataWithError(
                     {},
-                    `Derogatie is niet meer beschikbaar vanaf 2026.`,
+                    "Derogatie is niet meer beschikbaar vanaf 2026.",
                 )
             }
fdm-app/app/routes/farm.create._index.tsx (3)

164-177: Consider resetting has_derogation when year changes to ≥ 2026.

When the user checks derogation for a pre-2026 year and then switches the year to 2026+, the derogation section hides but has_derogation stays true in form state. The action handles this correctly (line 811-812), but the stale form state could be confusing during development or if validation rules change later. A small useEffect to clear it would keep form state consistent with the UI.

Suggested addition near line 177
     }, [organicCertified, selectedYear])
+
+    // Reset derogation when year >= 2026
+    // biome-ignore lint/correctness/useExhaustiveDependencies: form.setValue is stable
+    useEffect(() => {
+        if (Number(selectedYear) >= 2026) {
+            form.setValue("has_derogation", false)
+        }
+    }, [selectedYear])

70-126: Schema validates organic fields even when organic_certification is false.

The organic_skal and organic_traces refinements run regardless of the organic_certification value. Since the fields are hidden in the UI and defaults are "" (which passes !val), this works in practice. However, if a future change initializes these with non-empty defaults or if form state leaks, validation could unexpectedly fail. A .superRefine() or conditional schema (e.g., discriminated union on organic_certification) would be more robust, but the current approach is acceptable given the UI guards.


789-800: Sequential awaits for independent operations — consider Promise.all.

The setGrazingIntention (lines 830-837) and addOrganicCertification (lines 840-849) calls are independent of each other and of the derogation block. Grouping them with Promise.all (as already done for derogation years and fertilizers) would reduce latency.

Also applies to: 829-849

Comment thread fdm-app/app/routes/farm.create._index.tsx
Comment thread fdm-app/app/routes/farm.create._index.tsx
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: 1

🤖 Fix all issues with AI agents
In `@fdm-app/app/routes/farm.create._index.tsx`:
- Around line 70-134: The superRefine block uses the deprecated enum
z.ZodIssueCode.custom when calling ctx.addIssue; replace both occurrences with
the string literal "custom" (in the ctx.addIssue calls that validate
organic_skal and organic_traces) so the two addIssue calls use code: "custom"
instead of z.ZodIssueCode.custom.
🧹 Nitpick comments (2)
fdm-app/app/routes/farm.create._index.tsx (2)

550-552: Use existing organicCertified variable instead of a second form.watch call.

organicCertified is already derived from form.watch("organic_certification") on line 173. Reuse it here instead of calling form.watch again. Same applies to has_derogation on line 393 — consider extracting it to a component-level variable for consistency.

♻️ Suggested diff
-                                                {form.watch(
-                                                    "organic_certification",
-                                                ) && (
+                                                {organicCertified && (

833-894: Partial farm state on setup failure.

If Promise.all(setupPromises) at line 894 throws (e.g., addOrganicCertification or enableFertilizerCatalogue fails), the farm created at line 818 persists without its catalogues, derogation, grazing, or organic data. With more operations batched here now, the blast radius of a partial failure has grown. Consider whether the user has a recovery path (e.g., re-running setup from farm settings) or whether a compensating cleanup is warranted.

Comment thread fdm-app/app/routes/farm.create._index.tsx
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: 1

🤖 Fix all issues with AI agents
In `@fdm-app/app/routes/farm.create._index.tsx`:
- Around line 176-185: When organic_certification is toggled off the form should
clear organic_issued so re-checking can re-initialize it to the current
selectedYear; update the useEffect that watches organicCertified and
selectedYear (the hook using form.getValues and form.setValue) to set
form.setValue("organic_issued", undefined) when organicCertified becomes false,
and keep the existing logic that sets organic_issued to new
Date(Number(selectedYear), 0, 1) when organicCertified is true and
form.getValues("organic_issued") is empty so the DatePicker default and the
stored form value stay in sync.
🧹 Nitpick comments (2)
fdm-app/app/routes/farm.create._index.tsx (2)

393-395: Extract has_derogation watch to a named variable for consistency.

Lines 172–173 store watched values in variables (selectedYear, organicCertified), but has_derogation is watched inline here. Extracting it keeps the pattern consistent and avoids a repeated string literal.


815-891: Farm creation is not rolled back if a subsequent setup step fails.

addFarm at line 815 creates the farm before any of the setup promises run. If Promise.all(setupPromises) or the fertilizer setup (lines 893–909) throws, the farm persists in a partially configured state. A user retrying the form would create a duplicate farm.

This is a pre-existing pattern, but the risk surface has grown now that derogation, grazing, organic certification, and catalogue enablement all run in the same batch.

Consider wrapping the entire operation in a database transaction, or at minimum making the action idempotent (e.g., check for an existing farm with the same name/business ID before creating a new one).

Comment thread fdm-app/app/routes/farm.create._index.tsx
@SvenVw SvenVw merged commit 2aa2a9d into development Feb 10, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

branch:development Issue only affecting development, not the main branch (yet) enhancement New feature or request fdm-app

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve farm create form by not showing derogation when 2026 or later, and add more farm settings questions

1 participant