Skip to content

Add rotation feature, harvest forms, and fertilizer updates#334

Merged
SvenVw merged 59 commits into
developmentfrom
FDM283
Nov 24, 2025
Merged

Add rotation feature, harvest forms, and fertilizer updates#334
SvenVw merged 59 commits into
developmentfrom
FDM283

Conversation

@SvenVw
Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw commented Nov 7, 2025

Summary by CodeRabbit

  • New Features

    • Rotation overview: searchable, sortable table with column visibility menu, multi‑select actions and per‑rotation pages for adding fertilizer and managing harvests.
    • New UI blocks: per‑rotation fertilizer list and harvest‑dates display; improved harvest form with confirmation flow.
  • Improvements

    • Rich fertilizer badges show type icons and clearer parcel displays.
    • Sidebar and overview quick‑action texts/targets refined.
    • Date picker better handles Date values.
  • Localization

    • Harvest‑parameter warnings now show Dutch labels.

✏️ Tip: You can customize this high-level summary in your review settings.

@SvenVw SvenVw self-assigned this Nov 7, 2025
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Nov 7, 2025

🦋 Changeset detected

Latest commit: 0c2b47d

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 Nov 7, 2025

Warning

Rate limit exceeded

@BoraIneviNMI has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 12 minutes and 12 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 66d5e74 and 0c2b47d.

📒 Files selected for processing (1)
  • fdm-app/app/components/custom/date-picker-v2.tsx (1 hunks)

Walkthrough

Replaces per-field fertilizerApplications with richer fertilizers (id/type/name), adds rotation management UI and routes (table, headers, fertilizer/harvest displays, loaders/actions), refactors harvest form to support post-validation confirmation, updates sidebar/quick-actions, and adds a scroll-area dependency.

Changes

Cohort / File(s) Summary
Changesets
'.changeset/fluffy-sides-like.md', '.changeset/goofy-results-wait.md'
Added two minor-version changelog entries for @svenvw/fdm-app.
Fertilizer form API
fdm-app/app/components/blocks/fertilizer-applications/form.tsx
Exported new FertilizerOption type and changed RemixFormProvider usage to spread form with a runtime cast (form as any).
Fields columns, table & loader
fdm-app/app/components/blocks/fields/columns.tsx, fdm-app/app/components/blocks/fields/table.tsx, fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
Replaced fertilizerApplications with fertilizers ({p_id, p_type, p_name_nl}); updated sorting/searchTarget and badge rendering (icons by p_type); loader now fetches/filters fertilizers and returns fertilizers.
Rotation UI components
fdm-app/app/components/blocks/rotation/column-header.tsx, fdm-app/app/components/blocks/rotation/columns.tsx, fdm-app/app/components/blocks/rotation/fertilizer-display.tsx, fdm-app/app/components/blocks/rotation/harvest-dates-display.tsx, fdm-app/app/components/blocks/rotation/table.tsx
Added RotationExtended type, DataTable and DataTableColumnHeader, FertilizerDisplay, HarvestDatesDisplay, and column definitions enabling sorting, filtering, visibility toggles, fuzzy global search, selection, and rich cell rendering.
Rotation routes
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation._index.tsx, .../rotation.fertilizer._index.tsx, .../rotation.harvest._index.tsx
Added rotation index route (loader + UI), fertilizer-add route (loader, action, UI with multi-field selection + FertilizerApplicationForm), and harvest-add route (loader, action, overwrite-confirmation flow, UI).
Harvest form refactor
fdm-app/app/components/blocks/harvest/form.tsx
Extracted HarvestFields and HarvestFormExplainer, added action and optional handleConfirmation props, implemented a custom resolver with post-validation confirmation, and exported a new HarvestForm.
Sidebar & quick actions
fdm-app/app/components/blocks/sidebar/farm.tsx, fdm-app/app/routes/farm.$b_id_farm._index.tsx
Added Sprout icon and conditional Bouwplan link to rotation route; swapped quick-action routes/icons/titles/descriptions and migrated imports to alias paths.
Fertilizer new redirect logic
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx
Simplified return-url branching: action target chosen by returnUrl truthiness; labels updated accordingly.
Harvest parameter labels
fdm-app/app/routes/*/harvest*.tsx
Replaced English parameter lists with Dutch labels using getHarvestParameterLabel in several harvest routes and actions.
Date picker & minor UI
fdm-app/app/components/custom/date-picker-v2.tsx, fdm-app/app/components/blocks/fields/table.tsx
Date-picker effect now handles Date objects explicitly and emits ISO strings; minor classname change flex-growgrow.
Dependencies
fdm-app/package.json
Added @radix-ui/react-scroll-area (^1.2.10).
New route additions/updates
fdm-app/app/routes/...
Added multiple rotation/fertilizer/harvest routes and updated related field/harvest/fertilizer routes to consume new shapes and labels.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant UI as Rotation Page UI
    participant Loader as Rotation Loader
    participant Table as DataTable
    participant FertModal as Fertilizer Add UI
    participant HarvModal as Harvest Add UI
    participant Server as Server

    U->>UI: open rotation page
    UI->>Loader: request rotationExtended
    Loader-->>UI: rotationExtended (fields, fertilizers, harvests)
    UI->>Table: render rows (FertilizerDisplay, HarvestDatesDisplay)

    U->>Table: select rows
    U->>FertModal: click "Bemesting"
    FertModal->>Loader: load fertilizer options + selected fields
    FertModal->>Server: submit action (persist fertilizer applications)
    Server-->>FertModal: success -> redirect

    U->>HarvModal: click "Oogst toevoegen"
    HarvModal->>Loader: load harvest scaffold
    HarvModal->>HarvModal: validate (zod)
    alt handleConfirmation provided
      HarvModal->>HarvModal: call handleConfirmation -> proceed only if true
    end
    HarvModal->>Server: submit action (persist harvests)
    Server-->>HarvModal: success -> redirect
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing extra attention:
    • Cross-file propagation of the fertilizers data-shape (loaders → UI components).
    • New route loaders/actions (rotation, fertilizer add, harvest add): validation, parallel persistence, redirects and error handling.
    • Harvest form: custom async resolver, optional handleConfirmation, and DELETE vs POST branching.
    • DataTable interaction logic: shift-click selection, interactive-element click handling, responsive column visibility.
    • Fertilizer badge keys/icons (use of p_id/p_type) and ScrollArea integration.

Possibly related PRs

Suggested reviewers

  • gerardhros

Poem

🐇 I hopped through rows and left a tiny trace,
Badges sprout icons, each fertilizer in place.
Rotations align, harvests ask twice to be sure,
Forms whisper consent, so fields stay secure.
A carrot-cheer for code that helps crops endure!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.58% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the three main changes in the PR: a new rotation feature, harvest form enhancements, and fertilizer updates across multiple files.

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 Nov 7, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.70%. Comparing base (bd8df50) to head (0c2b47d).
⚠️ Report is 60 commits behind head on development.

Additional details and impacted files
@@             Coverage Diff              @@
##           development     #334   +/-   ##
============================================
  Coverage        87.70%   87.70%           
============================================
  Files               70       70           
  Lines             3675     3675           
  Branches          1045     1045           
============================================
  Hits              3223     3223           
  Misses             452      452           
Flag Coverage Δ
fdm-calculator 88.10% <ø> (ø)
fdm-core 87.03% <ø> (ø)
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.

@BoraIneviNMI BoraIneviNMI marked this pull request as ready for review November 17, 2025 11:21
@coderabbitai coderabbitai Bot added branch:development Issue only affecting development, not the main branch (yet) enhancement New feature or request fdm-app fdm-calculator fdm-core labels Nov 17, 2025
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: 7

🧹 Nitpick comments (9)
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (1)

161-161: Avoid runtime type cast; fix the underlying type mismatch.

The explicit as any cast bypasses TypeScript's type checking and may hide legitimate type mismatches between the form object and RemixFormProvider's expected props.

Investigate the type mismatch and either:

  1. Update the form initialization to match RemixFormProvider's expected types
  2. Import and use the correct type from remix-hook-form if available

If the cast is necessary due to a known library limitation, add a comment explaining why.

fdm-app/app/components/blocks/rotation/column-header.tsx (1)

19-70: Sortable column header implementation looks solid

The header correctly wires getCanSort, getIsSorted, toggleSorting, and toggleVisibility into a compact UI and falls back to a plain div when sorting is disabled. If there’s a similar header component for other tables (e.g. fields), consider consolidating into a shared utility to avoid duplication, but functionally this is good as-is.

fdm-app/app/components/blocks/rotation/fertilizer-display.tsx (1)

46-115: Fertilizer aggregation and icon rendering logic look correct

You correctly dedupe fertilizers across fields, compute “applied on all fields” via every/some, and drive the icon/colour from a central map keyed by p_type. The memoization boundaries are reasonable; if you ever want to simplify, computing the JSX in a single useMemo over cultivation.fields would also be fine.

fdm-app/app/components/blocks/rotation/harvest-dates-display.tsx (1)

10-99: Harvest date grouping logic is solid; adjust keys and verify locale import

The branching for:

  • single date,
  • one-off crops with a date range, and
  • multi-harvest crops grouped by “nth cut/harvest”

is clear and correct.

Two refinements:

  1. Avoid using the array index as React key

You currently use key={idx} for both branches. To satisfy Biome and avoid potential key issues, you can derive a stable key from the dates themselves, e.g.:

-                    {harvestsByOrder.map((harvestDates, idx) => {
+                    {harvestsByOrder.map((harvestDates, idx) => {
+                        const key = harvestDates
+                            .map((d) => d.toISOString())
+                            .join("_")
                         // harvestDates are already sorted from the previous loop
                         if (harvestDates.length === 1) {
                             return (
                                 <p
-                                    key={idx}
+                                    key={key}
                                     className="text-muted-foreground"
                                 >
@@
-                        return (
-                            <p key={idx} className="text-muted-foreground">
+                        return (
+                            <p key={key} className="text-muted-foreground">
  1. Verify the date-fns locale import

import { nl } from "date-fns/locale/nl" may need to be a default import (e.g. import nl from "date-fns/locale/nl") or imported from "date-fns/locale" depending on the date-fns version used in this repo. Please confirm it matches existing usage elsewhere in the project.

fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation._index.tsx (1)

37-46: Docstring & typing are slightly looser than implementation

Minor nits that you can address later:

  • The loader JSDoc mentions “sorted fieldOptions”, but fieldOptions are currently mapped in API order only. Either sort by b_name or update the comment to avoid confusion.
  • transformFieldsToRotationExtended and fieldsExtended/field are typed as any with TODO comments. Given you already have RotationExtended and the shape of fieldsExtended is well known, tightening these types would make the data flow safer and easier to refactor later.

Functionally everything still works; this is just about keeping the types and documentation aligned.

Also applies to: 91-101, 204-207

fdm-app/app/components/blocks/rotation/table.tsx (1)

151-197: Global filter wiring works but duplicates state; could be simplified

The combined fuzzy + productivity filter looks correct, and passing globalFilterFn with { searchTerms, showProductiveOnly } in state.globalFilter is enough for TanStack Table to apply it. However, you now maintain both searchTerms and globalFilter separately and also wire onGlobalFilterChange, even though you never call table.setGlobalFilter.

You can simplify by either:

  • Letting the table own globalFilter (use table.setGlobalFilter in the input and keep a single source of truth), or
  • Dropping onGlobalFilterChange if you intend searchTerms/globalFilter to be fully controlled from this component only.

This would remove some indirection without changing behavior.

fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx (2)

100-193: Multi‑cultivation fertilizer flow: check expectations around cultivationIds usage and form key

The loader and action correctly treat fertilizer applications as field‑level (only fieldIds are used when persisting), with cultivationIds driving field pre‑selection and page copy. Two small things to double‑check:

  • When multiple cultivations are selected from the rotation table, cultivationIds is a comma‑separated list, but cultivationName and catalogue lookup only use cultivationIds[0]. That means the title and context refer to the first crop while fields may include others. If that’s intentional, fine; otherwise consider adjusting the UI copy or restricting this route to a single cultivation.
  • b_id_or_b_lu_catalogue for FertilizerApplicationForm is set to searchParams.get("cultivationIds") || "cultivationIds", which may be a joined string like "id1,id2". If the underlying form store expects a single b_lu_catalogue, you might want to normalize to the first id (or another stable key) to avoid surprising persistence behavior when switching between single and multi‑crop flows.

Behavior is otherwise solid; these are mostly consistency checks.

Also applies to: 247-282, 628-650


685-701: Partial writes on multi‑field fertilizer add if a later call fails

In the action, you loop over fieldIds and await addFertilizerApplication per field sequentially. If one of the later calls throws, earlier fields will already have fertilizer applications persisted, and the user still sees a generic error.

If fdm-core doesn’t already wrap this in a transaction, consider batching:

  • Either use a dedicated multi‑field API in fdm-core (ideal), or
  • At least use await Promise.all(fieldIds.map(...addFertilizerApplication...)) so you fail fast and surface a single error, while letting the backend handle atomicity.

Not a blocker, but worth aligning with how multi‑harvest operations are handled elsewhere in this codebase.

fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (1)

682-731: Multi‑field harvest add may leave partial state on failure

In the action you loop over fieldIds and for each:

  • Optionally remove existing harvests (for once crops).
  • Call addHarvest for that field’s b_lu.

All of this is done sequentially inside a for loop. If a later getHarvests, removeHarvest, or addHarvest call throws, earlier fields will already have been mutated and there’s no rollback, so the operation is only partially applied across the selected fields.

Given prior guidance in this repo to prefer Promise.all for multi‑harvest operations to keep behavior as atomic as possible, it may be worth aligning here—ideally via a single multi‑field/multi‑harvest operation in fdm-core that can be wrapped in a transaction. At minimum, consider grouping per‑field work in a Promise.all and letting the backend handle atomicity.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 799b889 and 6bce0bc.

⛔ Files ignored due to path filters (2)
  • fdm-app/app/components/ui/scroll-area.tsx is excluded by !fdm-app/app/components/ui/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (19)
  • .changeset/fluffy-sides-like.md (1 hunks)
  • .changeset/goofy-results-wait.md (1 hunks)
  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx (2 hunks)
  • fdm-app/app/components/blocks/fields/columns.tsx (4 hunks)
  • fdm-app/app/components/blocks/fields/table.tsx (2 hunks)
  • fdm-app/app/components/blocks/harvest/form.tsx (2 hunks)
  • fdm-app/app/components/blocks/harvest/schema.ts (1 hunks)
  • fdm-app/app/components/blocks/rotation/column-header.tsx (1 hunks)
  • fdm-app/app/components/blocks/rotation/columns.tsx (1 hunks)
  • fdm-app/app/components/blocks/rotation/fertilizer-display.tsx (1 hunks)
  • fdm-app/app/components/blocks/rotation/harvest-dates-display.tsx (1 hunks)
  • fdm-app/app/components/blocks/rotation/table.tsx (1 hunks)
  • fdm-app/app/components/blocks/sidebar/farm.tsx (3 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (4 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx (1 hunks)
  • fdm-app/package.json (1 hunks)
🧰 Additional context used
🧠 Learnings (39)
📓 Common learnings
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 6
File: fdm-app/vite.config.ts:5-9
Timestamp: 2024-11-25T12:42:32.783Z
Learning: In the `fdm-app` project, SvenVw is preparing for migration to Remix v3 and may include type declarations or configurations for v3 features in advance, such as in `vite.config.ts`.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.
📚 Learning: 2025-09-23T12:29:34.184Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.

Applied to files:

  • .changeset/fluffy-sides-like.md
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._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/components/blocks/sidebar/farm.tsx
  • fdm-app/app/components/blocks/fields/columns.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation._index.tsx
📚 Learning: 2025-09-23T10:02:32.123Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.$p_id.tsx:151-164
Timestamp: 2025-09-23T10:02:32.123Z
Learning: The getFertilizer function from svenvw/fdm-core throws an exception if the fertilizer doesn't exist, rather than returning null or undefined.

Applied to files:

  • .changeset/fluffy-sides-like.md
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-09-23T12:27:07.391Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.

Applied to files:

  • .changeset/fluffy-sides-like.md
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._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.$calendar.rotation._index.tsx
  • .changeset/goofy-results-wait.md
📚 Learning: 2025-09-24T14:02:48.574Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.574Z
Learning: The getFertilizer function in svenvw/fdm-core does not perform authorization checks, unlike getFertilizers which includes a checkPermission call to verify farm access. This means getFertilizer(fdm, p_id) can potentially return fertilizer details for any fertilizer ID without validating user permissions.

Applied to files:

  • .changeset/fluffy-sides-like.md
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-06-02T10:31:27.097Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 151
File: fdm-app/app/routes/signin._index.tsx:101-101
Timestamp: 2025-06-02T10:31:27.097Z
Learning: In fdm-app/app/routes/signin._index.tsx, the redirect destinations are intentionally inconsistent by design: the component defaults new sign-ins to "/welcome" (line 101) while the loader redirects authenticated users to "/farm" (line 80) and the action uses "/farm" as fallback (line 434). This creates appropriate user flows where new users complete their profile via the welcome page, while existing authenticated users bypass it and go directly to the main application.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.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.$calendar.rotation._index.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A shared layout component `FarmLayoutBase` has been created in `components/custom/farm-layout-base.tsx` to maintain consistency across farm-related pages. The component handles farm selection dropdown, breadcrumb navigation, and provides a common layout structure.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
  • fdm-app/app/components/blocks/rotation/fertilizer-display.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/components/blocks/sidebar/farm.tsx
  • fdm-app/app/components/blocks/fields/columns.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation._index.tsx
  • fdm-app/app/components/blocks/rotation/harvest-dates-display.tsx
📚 Learning: 2024-12-19T13:20:44.152Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 23
File: fdm-app/app/routes/app.addfarm.new.tsx:15-17
Timestamp: 2024-12-19T13:20:44.152Z
Learning: Authentication for the “app.addfarm.new” route is already handled globally in “fdm-app/app/routes/app.tsx,” automatically redirecting unauthenticated users to the SignIn page.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.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.$calendar.rotation._index.tsx
📚 Learning: 2025-09-24T14:02:48.574Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.574Z
Learning: Both getFertilizer and getFertilizers functions in svenvw/fdm-core perform authorization checks using the user's principal_id to verify farm access before returning fertilizer data.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-02-13T08:35:59.306Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.$b_id_farm.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx:114-124
Timestamp: 2025-02-13T08:35:59.306Z
Learning: The HarvestForm component in fdm-app expects undefined (not 0) for b_lu_yield when no yield information is available, as 0 would incorrectly imply that yield data exists.

Applied to files:

  • fdm-app/app/components/blocks/harvest/form.tsx
  • fdm-app/app/components/blocks/harvest/schema.ts
📚 Learning: 2025-09-23T12:37:58.711Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.711Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._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/components/blocks/fields/columns.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation._index.tsx
📚 Learning: 2025-08-13T10:33:05.313Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
  • fdm-app/app/components/blocks/fields/columns.tsx
📚 Learning: 2025-09-26T08:34:50.413Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 279
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx:277-283
Timestamp: 2025-09-26T08:34:50.413Z
Learning: In the fdm project, fdm-core and fdm-app are updated together as part of a monorepo structure, which eliminates legacy data concerns when new fields like b_isproductive are introduced. Both packages are synchronized, so there's no need for defensive coding against undefined values for newly introduced database fields.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
  • fdm-app/package.json
  • fdm-app/app/components/blocks/fields/columns.tsx
📚 Learning: 2025-02-13T09:03:11.890Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.create.$b_id_farm.cultivations.$b_lu_catalogue.crop.harvest._index.tsx:111-135
Timestamp: 2025-02-13T09:03:11.890Z
Learning: When adding multiple harvests in fdm-app, use Promise.all instead of Promise.allSettled to ensure atomic behavior - if one harvest addition fails, all should fail and rollback to maintain data consistency.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-23T15:17:23.028Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.028Z
Learning: The `addField` function in fdm-core should use database transactions and field verification to ensure field availability before resolving its promise, eliminating the need for sleep workarounds.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-23T15:17:23.027Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.027Z
Learning: The `addField` function in fdm-core should verify field creation within the same transaction by checking the existence of the field and all its required relations (field data, acquiring info, geometry) before resolving its promise.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: When using `updateField` from fdm-core, all 8 parameters must be provided in order: fdm, b_id, b_name, b_geometry, b_area, b_id_source, b_id_farm, and b_id_farm_source.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core requires 8 parameters: fdm, b_id (required), and 6 optional parameters (b_name, b_id_source, b_geometry, b_acquiring_date, b_acquiring_method, b_discarding_date).

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-31T15:34:20.850Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:34:20.850Z
Learning: The `updateField` function in fdm-core has optional parameters that don't need to be passed as undefined. Only `fdm` and `b_id` are required.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2024-12-11T12:09:35.540Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 20
File: fdm-app/tsconfig.json:8-9
Timestamp: 2024-12-11T12:09:35.540Z
Learning: In the `fdm-app/tsconfig.json` file, the include path `.react-router/types/**/*` refers to a build-time generated directory which is intentionally not included in the repository.

Applied to files:

  • fdm-app/package.json
📚 Learning: 2025-01-14T16:06:21.832Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 45
File: fdm-app/app/routes/farm.$b_id_farm.settings._index.tsx:1-1
Timestamp: 2025-01-14T16:06:21.832Z
Learning: In the fdm project, `redirect` and other routing utilities should be imported from `react-router` instead of `react-router-dom`.

Applied to files:

  • fdm-app/package.json
📚 Learning: 2025-01-14T16:06:24.294Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 45
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:1-1
Timestamp: 2025-01-14T16:06:24.294Z
Learning: In the fdm-app codebase, the `redirect` function should be imported from `react-router`, not `react-router-dom`.

Applied to files:

  • fdm-app/package.json
📚 Learning: 2025-04-18T13:49:17.029Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 124
File: fdm-app/app/components/custom/farm/farm-title.tsx:3-3
Timestamp: 2025-04-18T13:49:17.029Z
Learning: In the fdm project, NavLink and other routing components can be imported from either "react-router" or "react-router-dom" as react-router-dom is included in react-router.

Applied to files:

  • fdm-app/package.json
📚 Learning: 2025-08-11T12:24:32.200Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx:53-53
Timestamp: 2025-08-11T12:24:32.200Z
Learning: In `fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx`, the NMI API for cultivations guarantees that each year will be unique in the cultivation history data, so using `cultivation.year` as a React list key is safe and won't cause duplicate key warnings.

Applied to files:

  • fdm-app/app/components/blocks/rotation/fertilizer-display.tsx
  • fdm-app/app/components/blocks/fields/columns.tsx
  • fdm-app/app/components/blocks/rotation/harvest-dates-display.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The `FarmLayout` component in `components/custom/farm-layout.tsx` provides a reusable layout structure for farm-related pages, with support for farm selection dropdown, customizable breadcrumb titles, and flexible content rendering through either children or Outlet components.

Applied to files:

  • fdm-app/app/components/blocks/rotation/fertilizer-display.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/components/blocks/sidebar/farm.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation._index.tsx
📚 Learning: 2025-08-11T11:55:26.053Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/integrations/nmi.ts:54-0
Timestamp: 2025-08-11T11:55:26.053Z
Learning: The NMI API Estimates endpoint (`https://api.nmi-agro.nl/estimates`) always returns the fields `b_gwl_ghg`, `b_gwl_glg`, and `cultivations` according to its specification. These fields should be kept as required (not optional) in the TypeScript return type and Zod validation schema in `fdm-app/app/integrations/nmi.ts`.

Applied to files:

  • fdm-app/app/components/blocks/harvest/schema.ts
  • fdm-app/app/components/blocks/fields/columns.tsx
📚 Learning: 2025-04-18T14:20:40.975Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 124
File: fdm-core/src/db/schema-authn.ts:70-76
Timestamp: 2025-04-18T14:20:40.975Z
Learning: The schema defined in fdm-core/src/db/schema-authn.ts follows better-auth's structure and requirements. While the schema is defined in the application code, modifications to it should maintain compatibility with better-auth's expectations.

Applied to files:

  • fdm-app/app/components/blocks/harvest/schema.ts
📚 Learning: 2025-11-04T13:46:46.432Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-11-04T13:46:46.432Z
Learning: In the FDM app, file upload validation uses a two-layer approach: client-side Zod schemas (e.g., in fdm-app/app/components/blocks/soil/form-upload.tsx) provide early user-facing error feedback before upload, while server-side upload handlers enforce the same limits for security.

Applied to files:

  • fdm-app/app/components/blocks/harvest/schema.ts
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.

Applied to files:

  • 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/components/blocks/sidebar/farm.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation._index.tsx
📚 Learning: 2024-11-25T12:42:32.783Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 6
File: fdm-app/vite.config.ts:5-9
Timestamp: 2024-11-25T12:42:32.783Z
Learning: In the `fdm-app` project, SvenVw is preparing for migration to Remix v3 and may include type declarations or configurations for v3 features in advance, such as in `vite.config.ts`.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The farm layout system has been reorganized into separate components (`FarmHeader`, `ContentLayout`, `PaginationLayout`) to support different navigation patterns (sidebar, pagination) while maintaining consistent styling. Each layout component is designed to be used independently or combined as needed.

Applied to files:

  • fdm-app/app/components/blocks/sidebar/farm.tsx
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core has optional parameters after `fdm` and `b_id`. The TypeScript definitions might show 8 required parameters due to a potential version mismatch.

Applied to files:

  • fdm-app/app/components/blocks/fields/columns.tsx
📚 Learning: 2025-04-18T14:51:48.033Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 124
File: fdm-core/src/organization.ts:300-339
Timestamp: 2025-04-18T14:51:48.033Z
Learning: The `getUsersInOrganization` function in fdm-core/src/organization.ts will be updated in commit b17fac16c9e5a0de56d0346e712b2ce966d305d5 to include id and email fields, which are necessary for subsequent role updates and user removal operations.

Applied to files:

  • fdm-app/app/components/blocks/fields/columns.tsx
📚 Learning: 2025-05-09T14:58:10.465Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 138
File: fdm-app/app/components/custom/combobox.tsx:34-37
Timestamp: 2025-05-09T14:58:10.465Z
Learning: When updating React components that use both react-hook-form and React Router v7, it's important to only import types (like UseFormReturn, FieldValues) from react-hook-form to avoid naming conflicts with React Router's Form component. Use `import type { ... } from 'react-hook-form'` syntax to ensure only types are imported.

Applied to files:

  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx
📚 Learning: 2024-12-16T10:56:33.616Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 16
File: fdm-app/app/components/custom/combobox.tsx:35-37
Timestamp: 2024-12-16T10:56:33.616Z
Learning: When using `useRemixForm`, the correct type for the `form` prop is `UseRemixFormReturn<T>` from `remix-hook-form`, not `UseFormReturn` from `react-hook-form`.

Applied to files:

  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx
📚 Learning: 2025-05-09T14:41:43.484Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 138
File: fdm-app/app/components/custom/fertilizer-applications/form.tsx:6-6
Timestamp: 2025-05-09T14:41:43.484Z
Learning: The project uses React Router v7 which exports a Form component directly from the "react-router" package, not from "remix-run/react".

Applied to files:

  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx
📚 Learning: 2025-05-09T14:41:43.484Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 138
File: fdm-app/app/components/custom/fertilizer-applications/form.tsx:6-6
Timestamp: 2025-05-09T14:41:43.484Z
Learning: The project uses React Router v7 which exports a Form component directly from the "react-router" package, making importing from "remix-run/react" unnecessary.

Applied to files:

  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx
📚 Learning: 2024-12-16T11:28:58.089Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 16
File: fdm-app/app/components/custom/combobox.tsx:35-37
Timestamp: 2024-12-16T11:28:58.089Z
Learning: When using `UseRemixFormReturn` from `remix-hook-form`, it is not a generic type and should be used without type parameters.

Applied to files:

  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx
📚 Learning: 2025-05-09T14:53:44.578Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 138
File: fdm-app/app/components/custom/combobox.tsx:34-37
Timestamp: 2025-05-09T14:53:44.578Z
Learning: In the context of this React Router v7 project, it's important to follow the pattern of importing only the types (like UseFormReturn) from "react-hook-form" while importing the Form component from "react-router" to avoid naming conflicts.

Applied to files:

  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx
🧬 Code graph analysis (10)
fdm-app/app/components/blocks/harvest/form.tsx (3)
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (1)
  • action (629-745)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx (1)
  • action (172-242)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.new.tsx (1)
  • action (90-118)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (2)
fdm-core/src/db/schema.ts (1)
  • fertilizers (136-144)
fdm-core/src/fertilizer.ts (1)
  • getFertilizers (532-652)
fdm-app/app/components/blocks/rotation/fertilizer-display.tsx (2)
fdm-app/app/components/blocks/rotation/columns.tsx (1)
  • RotationExtended (27-55)
fdm-core/src/db/schema.ts (2)
  • fields (92-108)
  • fertilizers (136-144)
fdm-app/app/components/blocks/rotation/columns.tsx (4)
fdm-app/app/components/blocks/rotation/column-header.tsx (1)
  • DataTableColumnHeader (19-71)
fdm-app/app/components/custom/cultivation-colors.ts (1)
  • getCultivationColor (20-28)
fdm-app/app/components/blocks/rotation/harvest-dates-display.tsx (1)
  • HarvestDatesDisplay (10-99)
fdm-app/app/components/blocks/rotation/fertilizer-display.tsx (1)
  • FertilizerDisplay (46-115)
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (6)
fdm-app/app/components/blocks/header/base.tsx (1)
  • Header (6-29)
fdm-app/app/components/blocks/header/farm.tsx (1)
  • HeaderFarm (15-76)
fdm-app/app/components/blocks/farm/farm-title.tsx (1)
  • FarmTitle (15-36)
fdm-app/app/components/blocks/farm/farm-content.tsx (1)
  • FarmContent (13-27)
fdm-app/app/components/blocks/harvest/form.tsx (1)
  • HarvestForm (19-164)
fdm-app/app/components/blocks/harvest/schema.ts (1)
  • FormSchema (3-30)
fdm-app/app/components/blocks/fields/columns.tsx (1)
fdm-core/src/db/schema.ts (1)
  • fertilizers (136-144)
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (1)
fdm-app/app/components/blocks/fertilizer-applications/types.d.tsx (1)
  • FertilizerOption (12-19)
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation._index.tsx (4)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (2)
  • meta (34-43)
  • loader (62-182)
fdm-core/src/db/schema.ts (4)
  • farms (21-33)
  • fields (92-108)
  • fertilizers (136-144)
  • cultivations (373-385)
fdm-app/app/components/blocks/rotation/columns.tsx (2)
  • RotationExtended (27-55)
  • columns (57-239)
fdm-app/app/components/blocks/rotation/table.tsx (1)
  • DataTable (56-423)
fdm-app/app/components/blocks/rotation/table.tsx (3)
fdm-app/app/components/blocks/rotation/columns.tsx (2)
  • RotationExtended (27-55)
  • columns (57-239)
fdm-app/app/store/field-filter.ts (1)
  • useFieldFilterStore (10-24)
fdm-app/app/components/custom/field-filter-toggle.tsx (1)
  • FieldFilterToggle (11-44)
fdm-app/app/components/blocks/rotation/harvest-dates-display.tsx (1)
fdm-app/app/components/blocks/rotation/columns.tsx (1)
  • RotationExtended (27-55)
🪛 Biome (2.1.2)
fdm-app/app/components/blocks/harvest/schema.ts

[error] 6-6: Using == may be unsafe if you are relying on type coercion.

== is only allowed when comparing against null.
Unsafe fix: Use === instead.

(lint/suspicious/noDoubleEquals)


[error] 17-17: Using == may be unsafe if you are relying on type coercion.

== is only allowed when comparing against null.
Unsafe fix: Use === instead.

(lint/suspicious/noDoubleEquals)


[error] 24-24: Using == may be unsafe if you are relying on type coercion.

== is only allowed when comparing against null.
Unsafe fix: Use === instead.

(lint/suspicious/noDoubleEquals)

fdm-app/app/components/blocks/rotation/harvest-dates-display.tsx

[error] 70-70: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation.

(lint/suspicious/noArrayIndexKey)


[error] 87-87: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation.

(lint/suspicious/noArrayIndexKey)

🔇 Additional comments (12)
.changeset/goofy-results-wait.md (1)

1-5: LGTM!

The changeset entry is well-formed and accurately describes the rotation page feature being added.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (2)

116-142: LGTM! Efficient fertilizer filtering approach.

The implementation correctly fetches fertilizers once per farm and filters them per field based on applications. This is more efficient than fetching fertilizers individually for each field.


158-168: LGTM! Data structure change aligns with UI updates.

The change from fertilizerApplications to fertilizers with richer data (p_id, p_type, p_name_nl) correctly supports the enhanced fields table UI that displays fertilizer icons.

fdm-app/app/components/blocks/sidebar/farm.tsx (2)

71-78: LGTM! Rotation link logic follows established patterns.

The rotationLink variable follows the same conditional logic pattern as fieldsLink above it, ensuring consistent behavior across sidebar items.


206-225: LGTM! Bouwplan menu item implemented consistently.

The new Bouwplan (rotation) sidebar item follows the established pattern for conditional rendering, matching the implementation of the Percelen item above it.

fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx (1)

112-123: LGTM! Simplified action logic.

The change from pattern-matching specific routes to a simple truthiness check on returnUrl is cleaner and more maintainable, while still maintaining security through the isOfOrigin validation in the loader.

fdm-app/app/components/blocks/fields/columns.tsx (1)

142-167: LGTM! Icon rendering logic is well-implemented.

The fertilizer icons are clearly differentiated by type with appropriate colors and shapes, enhancing the visual distinction between manure, mineral, and compost fertilizers.

.changeset/fluffy-sides-like.md (1)

1-5: LGTM!

The changeset entry accurately describes the fertilizer icon feature added to the fields table.

fdm-app/package.json (1)

26-26: No action required — package version is current and secure.

Version 1.2.10 is the latest stable release, and no direct security vulnerabilities are reported. The caret constraint (^1.2.10) allows safe minor and patch updates. The package is appropriate for use as specified.

Note: The component injects inline styles, which requires CSP configuration allowing style-src 'unsafe-inline' or a nonce — ensure your application's CSP policy accommodates this if strict CSP is in place.

fdm-app/app/components/blocks/fertilizer-applications/form.tsx (1)

41-45: The type change in form.tsx is intentional and properly handled.

The code at line 220 uses optional chaining (selectedFertilizer?.applicationMethodOptions?.map(...)) to safely access the property, confirming that making applicationMethodOptions optional is deliberate. The form component correctly treats it as potentially undefined and won't error if the property is missing.

However, there is a broader type system inconsistency in the codebase: form.tsx exports its own FertilizerOption type with an optional field, while types.d.tsx defines the same type with a required field, and card.tsx imports from types.d.tsx. This creates a mismatch where routes (using form.tsx type) may provide different structures than what card.tsx expects (using types.d.tsx type). Though routes currently always populate the field, this type inconsistency could lead to issues if consuming code doesn't align. Consider consolidating the type definition to a single source of truth.

fdm-app/app/components/blocks/fields/table.tsx (1)

117-122: Field search & layout changes look consistent with new fertilizers model

Using item.fertilizers.map((f) => f.p_name_nl) in searchTarget matches the updated FieldExtended shape and keeps fuzzy search semantics intact. The container change to grow is a harmless Tailwind cleanup and should not affect behavior.

Also applies to: 262-263

fdm-app/app/components/blocks/harvest/form.tsx (1)

19-48: HarvestForm props and form wiring are consistent with the new schema

Adding an explicit action prop and widening b_lu_start / b_lu_end to Date | null | undefined matches the Zod schema and enables reuse of this form across the new harvest routes. The defaultValues wiring remains type-safe and consistent.

Also applies to: 61-69

Comment on lines 129 to 135
sortingFn: (rowA, rowB, _columnId) => {
const fertilizerA =
rowA.original.fertilizerApplications[0]?.p_name_nl || ""
rowA.original.fertilizers[0].p_name_nl || ""
const fertilizerB =
rowB.original.fertilizerApplications[0]?.p_name_nl || ""
rowB.original.fertilizers[0].p_name_nl || ""
return fertilizerA.localeCompare(fertilizerB)
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix unsafe array access in sorting function.

The sorting function accesses fertilizers[0].p_name_nl without checking if the array is empty, which will cause a runtime error when a field has no fertilizers.

Apply this diff to safely handle empty arrays:

         sortingFn: (rowA, rowB, _columnId) => {
             const fertilizerA =
-                rowA.original.fertilizers[0].p_name_nl || ""
+                rowA.original.fertilizers[0]?.p_name_nl || ""
             const fertilizerB =
-                rowB.original.fertilizers[0].p_name_nl || ""
+                rowB.original.fertilizers[0]?.p_name_nl || ""
             return fertilizerA.localeCompare(fertilizerB)
         },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sortingFn: (rowA, rowB, _columnId) => {
const fertilizerA =
rowA.original.fertilizerApplications[0]?.p_name_nl || ""
rowA.original.fertilizers[0].p_name_nl || ""
const fertilizerB =
rowB.original.fertilizerApplications[0]?.p_name_nl || ""
rowB.original.fertilizers[0].p_name_nl || ""
return fertilizerA.localeCompare(fertilizerB)
},
sortingFn: (rowA, rowB, _columnId) => {
const fertilizerA =
rowA.original.fertilizers[0]?.p_name_nl || ""
const fertilizerB =
rowB.original.fertilizers[0]?.p_name_nl || ""
return fertilizerA.localeCompare(fertilizerB)
},
🤖 Prompt for AI Agents
In fdm-app/app/components/blocks/fields/columns.tsx around lines 129 to 135, the
sorting function directly accesses fertilizers[0].p_name_nl which will throw if
fertilizers is empty or undefined; change the extraction to safely handle
missing arrays/properties (e.g. use optional chaining and a default:
rowA.original.fertilizers?.[0]?.p_name_nl ?? "" and same for rowB) so the
function never accesses index 0 of an empty array and returns a consistent
string for localeCompare.

Comment on lines +4 to +29
b_lu_harvest_date: z.preprocess(
(arg) => {
if (typeof arg == "string" || arg instanceof Date)
return new Date(arg)
},
z.date({
required_error: "Een oogstdatum is verplicht.",
}),
),
b_lu_yield: z.coerce.number().optional(),
b_lu_n_harvestable: z.coerce.number().optional(),
b_lu_start: z.preprocess(
(arg) => {
if (typeof arg == "string" || arg instanceof Date)
return new Date(arg)
},
z.date().nullable().optional(),
),
b_lu_end: z.preprocess(
(arg) => {
if (typeof arg == "string" || arg instanceof Date)
return new Date(arg)
},
z.date().nullable().optional(),
),
b_lu_harvestable: z.enum(["once", "multiple", "none"]).optional(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Tighten preprocessing (===) and avoid coercing empty yield to 0

Two things to address here:

  1. Use strict equality in preprocessors

typeof arg == "string" is flagged by Biome and is unnecessary here. Prefer strict equality:

-        (arg) => {
-            if (typeof arg == "string" || arg instanceof Date)
-                return new Date(arg)
-        },
+        (arg) => {
+            if (typeof arg === "string" || arg instanceof Date) {
+                return new Date(arg)
+            }
+        },

Apply the same change for b_lu_start and b_lu_end.

  1. Don’t coerce “no input” to numeric 0 for yield fields

With z.coerce.number().optional(), an empty <input type="number"> typically posts "", which z.coerce.number() converts to 0. That conflicts with the established semantic that “no yield info” must be undefined, not 0, because 0 implies an actual measured yield. Based on learnings, this is a behavioral regression.

Consider a preprocess-based schema that maps “empty” form values to undefined and only parses real numbers:

-    b_lu_yield: z.coerce.number().optional(),
-    b_lu_n_harvestable: z.coerce.number().optional(),
+    b_lu_yield: z.preprocess(
+        (arg) => (arg === "" || arg == null ? undefined : arg),
+        z.number().optional(),
+    ),
+    b_lu_n_harvestable: z.preprocess(
+        (arg) => (arg === "" || arg == null ? undefined : arg),
+        z.number().optional(),
+    ),

This keeps the undefined semantics for “no value entered” while still enforcing valid numbers when something is provided.


🏁 Script executed:

#!/bin/bash
# Verify the actual content of the schema file and look for `==` usage
echo "=== Checking harvest schema file ==="
cat fdm-app/app/components/blocks/harvest/schema.ts || echo "File not found"

echo -e "\n=== Searching for HarvestForm component ==="
fd -t f "HarvestForm" fdm-app/app --exec head -50 {} + 2>/dev/null || \
  rg -n "HarvestForm" fdm-app/app -A 5 -B 2 | head -50

echo -e "\n=== Searching for preprocess patterns with optional numeric fields ==="
rg -n "z\.preprocess.*undefined.*z\.number\(\)\.optional\(\)" fdm-app/app --type ts

Length of output: 352


🏁 Script executed:

#!/bin/bash
# Verify the actual content of the schema file and look for `==` usage
echo "=== Checking harvest schema file ==="
cat fdm-app/app/components/blocks/harvest/schema.ts 2>/dev/null || echo "File not found at expected path"

echo -e "\n=== Searching for harvest schema files ==="
fd -t f "schema" fdm-app/app/components/blocks/harvest/ 2>/dev/null | head -10

echo -e "\n=== Searching for preprocess patterns with optional numeric fields ==="
rg -n "z\.preprocess.*undefined.*z\.number" fdm-app/app --type ts -A 2 | head -30

echo -e "\n=== Checking for HarvestForm references ==="
rg -n "HarvestForm" fdm-app/app --type ts | head -20

Length of output: 3201


🏁 Script executed:

#!/bin/bash
# Find the HarvestForm component and check how it handles b_lu_yield
echo "=== Checking HarvestForm form.tsx ==="
cat fdm-app/app/components/blocks/harvest/form.tsx | head -150

echo -e "\n\n=== Searching for preprocess patterns with empty string mapping ==="
rg -n "val === \"\" \? undefined" fdm-app/app --type ts -B 2 -A 4 | head -50

echo -e "\n\n=== Looking for soil formschema example from learning ==="
cat fdm-app/app/components/custom/soil/formschema.tsx 2>/dev/null | head -80

Length of output: 10814


Tighten preprocessing (===) and avoid coercing empty yield to 0

Two issues require attention:

  1. Use strict equality in preprocessors

typeof arg == "string" is flagged by Biome. Apply strict equality to all three date preprocessors (b_lu_harvest_date, b_lu_start, b_lu_end):

-        (arg) => {
-            if (typeof arg == "string" || arg instanceof Date)
-                return new Date(arg)
-        },
+        (arg) => {
+            if (typeof arg === "string" || arg instanceof Date) {
+                return new Date(arg)
+            }
+        },
  1. Don't coerce empty input to numeric 0 for yield fields

With z.coerce.number().optional(), an empty form <input type="number"> posts "", which z.coerce.number() converts to 0. This conflicts with the established semantic: b_lu_yield must be undefined when no value is provided (not 0), because 0 implies an actual measured yield. The HarvestForm component relies on b_lu_yield !== undefined to detect updates, and coercing empty strings to 0 breaks this logic.

The codebase already uses this pattern in fdm-app/app/components/blocks/soil/formschema.tsx:

-    b_lu_yield: z.coerce.number().optional(),
-    b_lu_n_harvestable: z.coerce.number().optional(),
+    b_lu_yield: z.preprocess(
+        (val) => (val === "" ? undefined : val),
+        z.coerce.number().optional(),
+    ),
+    b_lu_n_harvestable: z.preprocess(
+        (val) => (val === "" ? undefined : val),
+        z.coerce.number().optional(),
+    ),
🧰 Tools
🪛 Biome (2.1.2)

[error] 6-6: Using == may be unsafe if you are relying on type coercion.

== is only allowed when comparing against null.
Unsafe fix: Use === instead.

(lint/suspicious/noDoubleEquals)


[error] 17-17: Using == may be unsafe if you are relying on type coercion.

== is only allowed when comparing against null.
Unsafe fix: Use === instead.

(lint/suspicious/noDoubleEquals)


[error] 24-24: Using == may be unsafe if you are relying on type coercion.

== is only allowed when comparing against null.
Unsafe fix: Use === instead.

(lint/suspicious/noDoubleEquals)

🤖 Prompt for AI Agents
In fdm-app/app/components/blocks/harvest/schema.ts around lines 4 to 29, tighten
the three date preprocessors to use strict equality (replace all instances of
typeof arg == "string" with typeof arg === "string") and prevent empty number
inputs from being coerced to 0 by changing b_lu_yield and b_lu_n_harvestable to
first preprocess empty-string into undefined (e.g. a preprocess that returns
undefined when arg === "") and then apply the numeric schema (so empty ""
becomes undefined and real numbers are coerced); keep the fields optional after
this change.

Comment thread fdm-app/app/components/blocks/rotation/columns.tsx
Comment thread fdm-app/app/components/blocks/rotation/table.tsx Outdated
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx Outdated
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation._index.tsx
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.

Actionable comments posted: 1

♻️ Duplicate comments (2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (1)

812-952: Prevent partial harvest updates across multiple fields.

Inside both loops we mutate per field in sequence (remove existing harvests, then add new ones). If an iteration throws halfway through, earlier fields stay mutated while later ones are untouched — exactly the inconsistent state we warned about earlier. Please collect the per-field work into promises, run them with Promise.all (after preparing any removals), and roll back previously removed harvests if an add fails so the whole request succeeds or fails as a unit.

-        for (const fieldId of fieldIds) {
-            // fetch cultivation, optionally remove harvests, then await addHarvest
-        }
+        const timeframe = getTimeframe(params)
+        const operations = fieldIds.map(async (fieldId) => {
+            const cultivationsForField = await getCultivations(
+                fdm,
+                session.principal_id,
+                fieldId,
+                timeframe,
+            )
+            // resolve target cultivation, collect & remove existing harvests if needed
+            // then await addHarvest(...)
+        })
+        await Promise.all(operations)

A similar batching pattern should wrap the DELETE branch so we don’t leave half the farm wiped if one removal fails. Based on learnings

fdm-app/app/components/blocks/rotation/columns.tsx (1)

101-127: Return a scalar sort key for Zaaidatum.

sortingFn: "datetime" expects a single date-like value, but row.getValue("b_lu_start") hands it a Date[]. For cultivations with more than one sowing date this coerces to an “Invalid Date”, so sorting silently becomes a no-op. Expose a single representative date (e.g. the earliest sowing date) via accessorFn before handing it to the datetime sorter.

-    {
-        accessorKey: "b_lu_start",
-        enableSorting: true,
-        sortingFn: "datetime",
+    {
+        accessorFn: (cultivation) => {
+            const sorted = [...cultivation.b_lu_start].sort(
+                (a, b) => a.getTime() - b.getTime(),
+            )
+            return sorted[0] ?? null
+        },
+        id: "b_lu_start",
+        enableSorting: true,
+        sortingFn: "datetime",

Keep the cell renderer as-is (it still uses row.original) and the column names list should reference the new id.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50e5e22 and 877aad7.

📒 Files selected for processing (5)
  • fdm-app/app/components/blocks/rotation/columns.tsx (1 hunks)
  • fdm-app/app/components/blocks/rotation/table.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (4 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx (4 hunks)
🧰 Additional context used
🧠 Learnings (31)
📓 Common learnings
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The farm layout system has been reorganized into separate components (`FarmHeader`, `ContentLayout`, `PaginationLayout`) to support different navigation patterns (sidebar, pagination) while maintaining consistent styling. Each layout component is designed to be used independently or combined as needed.
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A shared layout component `FarmLayoutBase` has been created in `components/custom/farm-layout-base.tsx` to maintain consistency across farm-related pages. The component handles farm selection dropdown, breadcrumb navigation, and provides a common layout structure.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The `FarmLayout` component in `components/custom/farm-layout.tsx` provides a reusable layout structure for farm-related pages, with support for farm selection dropdown, customizable breadcrumb titles, and flexible content rendering through either children or Outlet components.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2025-06-02T10:31:27.097Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 151
File: fdm-app/app/routes/signin._index.tsx:101-101
Timestamp: 2025-06-02T10:31:27.097Z
Learning: In fdm-app/app/routes/signin._index.tsx, the redirect destinations are intentionally inconsistent by design: the component defaults new sign-ins to "/welcome" (line 101) while the loader redirects authenticated users to "/farm" (line 80) and the action uses "/farm" as fallback (line 434). This creates appropriate user flows where new users complete their profile via the welcome page, while existing authenticated users bypass it and go directly to the main application.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2024-12-19T13:20:44.152Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 23
File: fdm-app/app/routes/app.addfarm.new.tsx:15-17
Timestamp: 2024-12-19T13:20:44.152Z
Learning: Authentication for the “app.addfarm.new” route is already handled globally in “fdm-app/app/routes/app.tsx,” automatically redirecting unauthenticated users to the SignIn page.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2025-09-23T12:29:34.184Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2025-02-13T09:03:11.890Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.create.$b_id_farm.cultivations.$b_lu_catalogue.crop.harvest._index.tsx:111-135
Timestamp: 2025-02-13T09:03:11.890Z
Learning: When adding multiple harvests in fdm-app, use Promise.all instead of Promise.allSettled to ensure atomic behavior - if one harvest addition fails, all should fail and rollback to maintain data consistency.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-09-23T12:27:07.391Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2025-02-13T08:35:59.306Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.$b_id_farm.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx:114-124
Timestamp: 2025-02-13T08:35:59.306Z
Learning: The HarvestForm component in fdm-app expects undefined (not 0) for b_lu_yield when no yield information is available, as 0 would incorrectly imply that yield data exists.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-08-11T12:24:32.200Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx:53-53
Timestamp: 2025-08-11T12:24:32.200Z
Learning: In `fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx`, the NMI API for cultivations guarantees that each year will be unique in the cultivation history data, so using `cultivation.year` as a React list key is safe and won't cause duplicate key warnings.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-07-31T11:38:50.661Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 194
File: fdm-core/src/harvest.ts:488-644
Timestamp: 2025-07-31T11:38:50.661Z
Learning: The validation logic in updateHarvest is intentionally different from checkHarvestDateCompability because updateHarvest is for updating existing harvests while checkHarvestDateCompability is for inserting new harvests. The insertion function includes checks that don't apply to updates, such as verifying no harvest already exists for "once" harvestable cultivations.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2024-11-27T12:15:36.425Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-data/src/cultivations/index.test.ts:57-59
Timestamp: 2024-11-27T12:15:36.425Z
Learning: In `fdm-data/src/cultivations/index.test.ts`, the `fdm` object created by `drizzle` does not have an `.end()` method. Cleanup code should not attempt to call `fdm.end();`.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-09-23T10:02:32.123Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.$p_id.tsx:151-164
Timestamp: 2025-09-23T10:02:32.123Z
Learning: The getFertilizer function from svenvw/fdm-core throws an exception if the fertilizer doesn't exist, rather than returning null or undefined.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-09-23T12:37:58.711Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.711Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-04-04T14:27:39.518Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 116
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx:111-154
Timestamp: 2025-04-04T14:27:39.518Z
Learning: In the FDM application, cultivation retrieval logic should be centralized in utility functions rather than duplicated across loader and action functions to improve maintainability and ensure consistent behavior.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-24T11:39:57.805Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-core/src/cultivation.ts:625-671
Timestamp: 2025-01-24T11:39:57.805Z
Learning: Every cultivation has an associated `cultivationTerminating` record created by `addCultivation`, with `b_terminate_date` initially set to null. Therefore, `updateCultivation` can safely assume the record exists and only needs to handle updates.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-23T15:17:23.027Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.027Z
Learning: The `addField` function in fdm-core should verify field creation within the same transaction by checking the existence of the field and all its required relations (field data, acquiring info, geometry) before resolving its promise.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-23T15:17:23.028Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.028Z
Learning: The `addField` function in fdm-core should use database transactions and field verification to ensure field availability before resolving its promise, eliminating the need for sleep workarounds.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-08-11T11:55:26.053Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/integrations/nmi.ts:54-0
Timestamp: 2025-08-11T11:55:26.053Z
Learning: The NMI API Estimates endpoint (`https://api.nmi-agro.nl/estimates`) always returns the fields `b_gwl_ghg`, `b_gwl_glg`, and `cultivations` according to its specification. These fields should be kept as required (not optional) in the TypeScript return type and Zod validation schema in `fdm-app/app/integrations/nmi.ts`.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-24T11:38:05.693Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-24T11:38:05.693Z
Learning: The field creation and cultivation addition should be performed within a single database transaction to ensure atomicity and prevent transaction isolation issues that could lead to "Field does not exist" errors.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-24T11:28:01.882Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-24T11:28:01.882Z
Learning: The `addField` function in fdm-core should use database transactions to ensure atomicity, and since transactions provide ACID guarantees, awaiting the inserts is sufficient to ensure field availability - no additional verification queries are needed.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-09-24T14:02:48.574Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.574Z
Learning: Both getFertilizer and getFertilizers functions in svenvw/fdm-core perform authorization checks using the user's principal_id to verify farm access before returning fertilizer data.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-09-26T08:34:50.413Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 279
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx:277-283
Timestamp: 2025-09-26T08:34:50.413Z
Learning: In the fdm project, fdm-core and fdm-app are updated together as part of a monorepo structure, which eliminates legacy data concerns when new fields like b_isproductive are introduced. Both packages are synchronized, so there's no need for defensive coding against undefined values for newly introduced database fields.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2025-09-24T14:02:48.574Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.574Z
Learning: The getFertilizer function in svenvw/fdm-core does not perform authorization checks, unlike getFertilizers which includes a checkPermission call to verify farm access. This means getFertilizer(fdm, p_id) can potentially return fertilizer details for any fertilizer ID without validating user permissions.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-08-13T10:33:05.313Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core has optional parameters after `fdm` and `b_id`. The TypeScript definitions might show 8 required parameters due to a potential version mismatch.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: When using `updateField` from fdm-core, all 8 parameters must be provided in order: fdm, b_id, b_name, b_geometry, b_area, b_id_source, b_id_farm, and b_id_farm_source.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core requires 8 parameters: fdm, b_id (required), and 6 optional parameters (b_name, b_id_source, b_geometry, b_acquiring_date, b_acquiring_method, b_discarding_date).

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-31T15:34:20.850Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:34:20.850Z
Learning: The `updateField` function in fdm-core has optional parameters that don't need to be passed as undefined. Only `fdm` and `b_id` are required.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The farm layout system has been reorganized into separate components (`FarmHeader`, `ContentLayout`, `PaginationLayout`) to support different navigation patterns (sidebar, pagination) while maintaining consistent styling. Each layout component is designed to be used independently or combined as needed.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
🧬 Code graph analysis (3)
fdm-app/app/components/blocks/rotation/table.tsx (3)
fdm-app/app/components/blocks/rotation/columns.tsx (2)
  • RotationExtended (22-50)
  • columns (52-246)
fdm-app/app/store/field-filter.ts (1)
  • useFieldFilterStore (10-24)
fdm-app/app/components/custom/field-filter-toggle.tsx (1)
  • FieldFilterToggle (11-44)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (2)
fdm-core/src/db/schema.ts (1)
  • fertilizers (136-144)
fdm-core/src/fertilizer.ts (1)
  • getFertilizers (532-652)
fdm-app/app/components/blocks/rotation/columns.tsx (4)
fdm-app/app/components/blocks/rotation/column-header.tsx (1)
  • DataTableColumnHeader (19-71)
fdm-app/app/components/custom/cultivation-colors.ts (1)
  • getCultivationColor (20-28)
fdm-app/app/components/blocks/rotation/harvest-dates-display.tsx (1)
  • HarvestDatesDisplay (10-99)
fdm-app/app/components/blocks/rotation/fertilizer-display.tsx (1)
  • FertilizerDisplay (46-115)

Comment on lines +812 to +848
if (request.method === "DELETE") {
for (const fieldId of fieldIds) {
const cultivationsForField = await getCultivations(
fdm,
session.principal_id,
fieldId,
{ start: new Date(0), end: new Date() }, // Get all cultivations for the field
)

const targetCultivationInstance = cultivationsForField.find(
(c) => c.b_lu_catalogue === cultivationIds[0],
)

if (!targetCultivationInstance) {
return dataWithError(
null,
`Gewas niet gevonden voor perceel ${fieldId}.`,
)
}

const b_lu = targetCultivationInstance.b_lu

// Check for existing harvests for this specific cultivation instance
const existingHarvests = await getHarvests(
fdm,
session.principal_id,
b_lu,
)
// If there are existing harvests, remove them before adding new ones
for (const harvest of existingHarvests) {
await removeHarvest(
fdm,
session.principal_id,
harvest.b_id_harvesting,
)
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use the calendar timeframe when refetching cultivations.

Both the DELETE and POST branches call getCultivations with { start: new Date(0), end: new Date() }. As soon as a farmer switches to a future calendar year (e.g. 2026), every cultivation in that year sits beyond new Date() and the lookup returns nothing, so we hit the “Gewas niet gevonden…” error and can’t add or delete harvests. Pull the same timeframe = getTimeframe(params) you already use in the loader and reuse it in these action calls (and the subsequent getHarvests lookups) so future-year workflows keep working.

-        const cultivationsForField = await getCultivations(
-            fdm,
-            session.principal_id,
-            fieldId,
-            { start: new Date(0), end: new Date() },
-        )
+        const cultivationsForField = await getCultivations(
+            fdm,
+            session.principal_id,
+            fieldId,
+            timeframe,
+        )

Apply the same change in the POST loop as well as any related getHarvests calls that should stay within the selected calendar.

Also applies to: 863-952

🤖 Prompt for AI Agents
In fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
around lines 812-848 (and similarly update the POST loop and related blocks in
~863-952), replace the hardcoded full-range calls to getCultivations/getHarvests
that use { start: new Date(0), end: new Date() } with the calendar timeframe you
already compute via const timeframe = getTimeframe(params); pass that timeframe
into getCultivations(...) and getHarvests(...) so both DELETE and POST paths
query within the selected calendar period (ensure you import/use params/context
where getTimeframe is available and propagate timeframe into all related lookups
and removals).

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

♻️ Duplicate comments (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (1)

849-906: Use the calendar timeframe in getCultivations instead of { new Date(0), new Date() }

Both DELETE and POST branches call:

const cultivationsForField = await getCultivations(
    fdm,
    session.principal_id,
    fieldId,
    { start: new Date(0), end: new Date() },
)

This breaks as soon as the user switches the calendar to a future year (e.g. 2026): cultivations in that year fall outside end: new Date(), so lookups return nothing and you hit “Gewas niet gevonden…” errors even though valid cultivations exist.

You already compute the correct timeframe in the loader via getTimeframe(params). Reuse it in the action:

 export async function action({ request, params }: ActionFunctionArgs) {
     try {
         const { b_id_farm, calendar = "all" } = params
@@
-        const session = await getSession(request)
+        const session = await getSession(request)
+        const timeframe = getTimeframe(params)
@@
-                const cultivationsForField = await getCultivations(
+                const cultivationsForField = await getCultivations(
                     fdm,
                     session.principal_id,
                     fieldId,
-                    { start: new Date(0), end: new Date() },
+                    timeframe,
                 )
@@
-            const cultivationsForField = await getCultivations(
+            const cultivationsForField = await getCultivations(
                 fdm,
                 session.principal_id,
                 fieldId,
-                { start: new Date(0), end: new Date() },
+                timeframe,
             )

This keeps add/delete harvest operations aligned with the currently selected calendar period. Based on learnings

🧹 Nitpick comments (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (1)

115-162: Fertilizer aggregation per field looks correct; consider pre-indexing for large farms

The loader now correctly:

  • Fetches all fertilizers once per farm (getFertilizers) and
  • Filters them per field based on getFertilizerApplications, exposing a fertilizers array on each extended field.

That aligns with the new UI and data model and leverages the core‑side permission checks on getFertilizers. For very fertilizer‑heavy farms, you could optionally pre-index fertilizers by p_id (e.g., a Map<string, Fertilizer>) and build each field’s set from its applications to avoid repeatedly scanning the full fertilizers array, but that’s an optimization rather than a correctness issue. Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50e5e22 and 5504f48.

📒 Files selected for processing (5)
  • fdm-app/app/components/blocks/rotation/columns.tsx (1 hunks)
  • fdm-app/app/components/blocks/rotation/table.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (4 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • fdm-app/app/components/blocks/rotation/table.tsx
🧰 Additional context used
🧠 Learnings (32)
📓 Common learnings
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The farm layout system has been reorganized into separate components (`FarmHeader`, `ContentLayout`, `PaginationLayout`) to support different navigation patterns (sidebar, pagination) while maintaining consistent styling. Each layout component is designed to be used independently or combined as needed.
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A shared layout component `FarmLayoutBase` has been created in `components/custom/farm-layout-base.tsx` to maintain consistency across farm-related pages. The component handles farm selection dropdown, breadcrumb navigation, and provides a common layout structure.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The `FarmLayout` component in `components/custom/farm-layout.tsx` provides a reusable layout structure for farm-related pages, with support for farm selection dropdown, customizable breadcrumb titles, and flexible content rendering through either children or Outlet components.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2024-12-19T13:20:44.152Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 23
File: fdm-app/app/routes/app.addfarm.new.tsx:15-17
Timestamp: 2024-12-19T13:20:44.152Z
Learning: Authentication for the “app.addfarm.new” route is already handled globally in “fdm-app/app/routes/app.tsx,” automatically redirecting unauthenticated users to the SignIn page.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-09-23T12:29:34.184Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2025-02-13T09:03:11.890Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.create.$b_id_farm.cultivations.$b_lu_catalogue.crop.harvest._index.tsx:111-135
Timestamp: 2025-02-13T09:03:11.890Z
Learning: When adding multiple harvests in fdm-app, use Promise.all instead of Promise.allSettled to ensure atomic behavior - if one harvest addition fails, all should fail and rollback to maintain data consistency.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-06-02T10:31:27.097Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 151
File: fdm-app/app/routes/signin._index.tsx:101-101
Timestamp: 2025-06-02T10:31:27.097Z
Learning: In fdm-app/app/routes/signin._index.tsx, the redirect destinations are intentionally inconsistent by design: the component defaults new sign-ins to "/welcome" (line 101) while the loader redirects authenticated users to "/farm" (line 80) and the action uses "/farm" as fallback (line 434). This creates appropriate user flows where new users complete their profile via the welcome page, while existing authenticated users bypass it and go directly to the main application.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2025-09-23T12:27:07.391Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
📚 Learning: 2025-07-31T11:38:50.661Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 194
File: fdm-core/src/harvest.ts:488-644
Timestamp: 2025-07-31T11:38:50.661Z
Learning: The validation logic in updateHarvest is intentionally different from checkHarvestDateCompability because updateHarvest is for updating existing harvests while checkHarvestDateCompability is for inserting new harvests. The insertion function includes checks that don't apply to updates, such as verifying no harvest already exists for "once" harvestable cultivations.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-08-11T12:24:32.200Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx:53-53
Timestamp: 2025-08-11T12:24:32.200Z
Learning: In `fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx`, the NMI API for cultivations guarantees that each year will be unique in the cultivation history data, so using `cultivation.year` as a React list key is safe and won't cause duplicate key warnings.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2024-11-27T12:15:36.425Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-data/src/cultivations/index.test.ts:57-59
Timestamp: 2024-11-27T12:15:36.425Z
Learning: In `fdm-data/src/cultivations/index.test.ts`, the `fdm` object created by `drizzle` does not have an `.end()` method. Cleanup code should not attempt to call `fdm.end();`.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-02-13T08:35:59.306Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.$b_id_farm.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx:114-124
Timestamp: 2025-02-13T08:35:59.306Z
Learning: The HarvestForm component in fdm-app expects undefined (not 0) for b_lu_yield when no yield information is available, as 0 would incorrectly imply that yield data exists.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-09-23T12:37:58.711Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.711Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-09-23T10:02:32.123Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.$p_id.tsx:151-164
Timestamp: 2025-09-23T10:02:32.123Z
Learning: The getFertilizer function from svenvw/fdm-core throws an exception if the fertilizer doesn't exist, rather than returning null or undefined.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-04-04T14:27:39.518Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 116
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx:111-154
Timestamp: 2025-04-04T14:27:39.518Z
Learning: In the FDM application, cultivation retrieval logic should be centralized in utility functions rather than duplicated across loader and action functions to improve maintainability and ensure consistent behavior.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-24T11:39:57.805Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-core/src/cultivation.ts:625-671
Timestamp: 2025-01-24T11:39:57.805Z
Learning: Every cultivation has an associated `cultivationTerminating` record created by `addCultivation`, with `b_terminate_date` initially set to null. Therefore, `updateCultivation` can safely assume the record exists and only needs to handle updates.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-23T15:17:23.028Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.028Z
Learning: The `addField` function in fdm-core should use database transactions and field verification to ensure field availability before resolving its promise, eliminating the need for sleep workarounds.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-23T15:17:23.027Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.027Z
Learning: The `addField` function in fdm-core should verify field creation within the same transaction by checking the existence of the field and all its required relations (field data, acquiring info, geometry) before resolving its promise.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-08-11T11:55:26.053Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/integrations/nmi.ts:54-0
Timestamp: 2025-08-11T11:55:26.053Z
Learning: The NMI API Estimates endpoint (`https://api.nmi-agro.nl/estimates`) always returns the fields `b_gwl_ghg`, `b_gwl_glg`, and `cultivations` according to its specification. These fields should be kept as required (not optional) in the TypeScript return type and Zod validation schema in `fdm-app/app/integrations/nmi.ts`.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-24T11:38:05.693Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-24T11:38:05.693Z
Learning: The field creation and cultivation addition should be performed within a single database transaction to ensure atomicity and prevent transaction isolation issues that could lead to "Field does not exist" errors.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-24T11:28:01.882Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-24T11:28:01.882Z
Learning: The `addField` function in fdm-core should use database transactions to ensure atomicity, and since transactions provide ACID guarantees, awaiting the inserts is sufficient to ensure field availability - no additional verification queries are needed.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-02-13T08:28:00.183Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-core/src/cultivation.ts:572-597
Timestamp: 2025-02-13T08:28:00.183Z
Learning: In the FDM database schema, `b_id_harvesting` is a primary key in the `cultivationHarvesting` table, ensuring no duplicate harvest entries can exist. Therefore, duplicate checking is not needed when processing harvest records from database queries.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-08-13T10:33:05.313Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-09-26T08:34:50.413Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 279
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx:277-283
Timestamp: 2025-09-26T08:34:50.413Z
Learning: In the fdm project, fdm-core and fdm-app are updated together as part of a monorepo structure, which eliminates legacy data concerns when new fields like b_isproductive are introduced. Both packages are synchronized, so there's no need for defensive coding against undefined values for newly introduced database fields.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-09-24T14:02:48.574Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.574Z
Learning: Both getFertilizer and getFertilizers functions in svenvw/fdm-core perform authorization checks using the user's principal_id to verify farm access before returning fertilizer data.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core has optional parameters after `fdm` and `b_id`. The TypeScript definitions might show 8 required parameters due to a potential version mismatch.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-09-24T14:02:48.574Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.574Z
Learning: The getFertilizer function in svenvw/fdm-core does not perform authorization checks, unlike getFertilizers which includes a checkPermission call to verify farm access. This means getFertilizer(fdm, p_id) can potentially return fertilizer details for any fertilizer ID without validating user permissions.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: When using `updateField` from fdm-core, all 8 parameters must be provided in order: fdm, b_id, b_name, b_geometry, b_area, b_id_source, b_id_farm, and b_id_farm_source.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core requires 8 parameters: fdm, b_id (required), and 6 optional parameters (b_name, b_id_source, b_geometry, b_acquiring_date, b_acquiring_method, b_discarding_date).

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-31T15:34:20.850Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:34:20.850Z
Learning: The `updateField` function in fdm-core has optional parameters that don't need to be passed as undefined. Only `fdm` and `b_id` are required.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The farm layout system has been reorganized into separate components (`FarmHeader`, `ContentLayout`, `PaginationLayout`) to support different navigation patterns (sidebar, pagination) while maintaining consistent styling. Each layout component is designed to be used independently or combined as needed.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm._index.tsx
🧬 Code graph analysis (2)
fdm-app/app/components/blocks/rotation/columns.tsx (4)
fdm-app/app/components/blocks/rotation/column-header.tsx (1)
  • DataTableColumnHeader (19-71)
fdm-app/app/components/custom/cultivation-colors.ts (1)
  • getCultivationColor (20-28)
fdm-app/app/components/blocks/rotation/harvest-dates-display.tsx (1)
  • HarvestDatesDisplay (10-99)
fdm-app/app/components/blocks/rotation/fertilizer-display.tsx (1)
  • FertilizerDisplay (46-115)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (3)
fdm-core/src/db/schema.ts (1)
  • fertilizers (136-144)
fdm-core/src/index.ts (1)
  • getFertilizers (89-89)
fdm-core/src/fertilizer.ts (1)
  • getFertilizers (532-652)
🪛 Biome (2.1.2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx

[error] 262-263: This variable implicitly has the any type.

Variable declarations without type annotation and initialization implicitly have the any type. Declare a type or initialize the variable with some value.

(lint/suspicious/noImplicitAnyLet)

🔇 Additional comments (2)
fdm-app/app/routes/farm.$b_id_farm._index.tsx (1)

145-205: Dashboard overview & navigation updates are consistent with field/rotation routes

The updated “Overzichten” section cleanly reroutes:

  • “Percelen” to ${calendar}/field (the dedicated field overview), and
  • “Bouwplan” to ${calendar}/rotation (the new rotation overview),

all driven off the calendar store and wrapped in the shared farm layout components. This keeps navigation consistent with the established farm layout and the existing field overview design. Based on learnings

fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (1)

340-399: Harvest selection & overwrite confirmation flow is robust

The rotation harvest UI now:

  • Keeps selectedFieldIds in sync with URL search params and loader data.
  • Aggregates overwrite detection across all currently selected fields before deciding whether to show the confirmation dialog.
  • Uses a dedicated promise/resolve mechanism so the dialog result cleanly gates form submission, without double‑submits.

This addresses the earlier single‑field confirmation issue and provides clear UX around overwriting existing harvests for “once” crops.

Comment on lines +101 to +127
accessorKey: "b_lu_start",
enableSorting: true,
sortingFn: "datetime",
header: ({ column }) => {
return <DataTableColumnHeader column={column} title="Zaaidatum" />
},
enableHiding: true, // Enable hiding for mobile
cell: ({ row }) => {
const cultivation = row.original

const formattedDateRange = React.useMemo(() => {
const b_lu_start = cultivation.b_lu_start

if (b_lu_start.length === 1) {
return format(b_lu_start[0], "PP", { locale: nl })
}
const b_lu_start_sorted = [...b_lu_start].sort(
(a, b) => a.getTime() - b.getTime(),
)
const firstDate = b_lu_start_sorted[0]
const lastDate = b_lu_start_sorted[b_lu_start_sorted.length - 1]
return `${format(firstDate, "PP", { locale: nl })} - ${format(lastDate, "PP", { locale: nl })}`
}, [cultivation.b_lu_start])

return <p className="text-muted-foreground">{formattedDateRange}</p>
},
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Zaaidatum column sorting uses Date[] with datetime sorter; define a scalar sort key

b_lu_start is typed as Date[], but the column uses:

accessorKey: "b_lu_start",
enableSorting: true,
sortingFn: "datetime",

while the cell derives a formatted range from the array. TanStack’s built‑in "datetime" sorter expects a single Date/string; with row.getValue("b_lu_start") returning an array, sorting is likely a no‑op or inconsistent.

Define a scalar sort key that matches what you display, e.g. the earliest sowing date:

-    {
-        accessorKey: "b_lu_start",
-        enableSorting: true,
-        sortingFn: "datetime",
+    {
+        id: "b_lu_start",
+        enableSorting: true,
+        sortingFn: (rowA, rowB) => {
+            const a = [...rowA.original.b_lu_start].sort(
+                (x, y) => x.getTime() - y.getTime(),
+            )[0]
+            const b = [...rowB.original.b_lu_start].sort(
+                (x, y) => x.getTime() - y.getTime(),
+            )[0]
+            return (a?.getTime() ?? 0) - (b?.getTime() ?? 0)
+        },

and keep the existing cell rendering. This ensures the sort actually follows the sowing range shown to users.


I'll verify the review comment by inspecting the codebase to confirm the type definitions and sorting behavior.


---

🏁 Script executed:

```shell
#!/bin/bash
# Find the RotationExtended type definition
rg -n "type\s+RotationExtended|interface\s+RotationExtended" --type=ts --type=tsx

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Inspect the columns.tsx file to see the full context around lines 101-127
cat fdm-app/app/components/blocks/rotation/columns.tsx | head -150 | tail -70

Length of output: 2522


🏁 Script executed:

#!/bin/bash
# Search for where RotationExtended is imported/defined in the rotation context
fd -e ts -e tsx | xargs rg -l "RotationExtended" | head -10

Length of output: 291


🏁 Script executed:

#!/bin/bash
# Check if there are other Date[] columns using "datetime" sorting
rg -n "sortingFn.*datetime" --type=ts --type=tsx -A2 -B2

Length of output: 27



---

🏁 Script executed:

```shell
#!/bin/bash
# Find RotationExtended type definition without file type filter
rg -n "type RotationExtended|interface RotationExtended" -A 20

Length of output: 4123


🏁 Script executed:

#!/bin/bash
# Search in the table.tsx file which likely imports or uses RotationExtended
cat fdm-app/app/components/blocks/rotation/table.tsx | grep -A 30 "RotationExtended"

Length of output: 1436


🏁 Script executed:

#!/bin/bash
# Check for type definitions in the rotation index route file
cat fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation._index.tsx | grep -A 30 "RotationExtended"

Length of output: 77


🏁 Script executed:

#!/bin/bash
# Look for b_lu_start type definition
rg "b_lu_start" -B 5 -A 2 --context=5 | head -60

Length of output: 5904



---

🌐 Web query:

```web
TanStack table datetime sorting with array values behavior

💡 Result:

Short answer: TanStack's built‑in "datetime" sorter expects Date objects (or values you return as the column value). If your cell value is an array (e.g. [Date, number]) the default sort will compare the array value (reference) and not the inner Date, so sorting will behave unexpectedly. Use an accessor that extracts the Date (or supply a custom sortingFn) and then use 'datetime' to get correct date ordering. Also be aware of the table's undefined-handling options for empty/malformed values. [1][2][3]

Recommended fixes (concepts — adapt to your code):

  • Use an accessorKey/index or accessorFn to return the Date itself:
    • accessorKey: 0 (if rows are arrays) or accessorFn: row => row[0]
  • Then set sortingFn: 'datetime' on that column.
  • Or supply a custom sortingFn that compares rowA.original[...] and rowB.original[...] by parsing/extracting the date (return -1/0/1).
  • If some rows can be undefined/null, configure sortUndefined (or handle in your sortingFn).

Relevant docs/discussion: Sorting guide (built‑in datetime + undefined behavior), columns accessor/array indices, and community discussion about date arrays. [1][2][3]


Zaaidatum column sorting uses Date[] with datetime sorter; define a scalar sort key

b_lu_start is typed as Date[], but the column uses:

accessorKey: "b_lu_start",
enableSorting: true,
sortingFn: "datetime",

while the cell derives a formatted range from the array. TanStack's "datetime" sorter expects a scalar Date value; with row.getValue("b_lu_start") returning an array, it compares array references instead of dates, resulting in ineffective or inconsistent sorting.

Define a custom sortingFn that extracts and compares the earliest sowing date:

-    {
-        accessorKey: "b_lu_start",
-        enableSorting: true,
-        sortingFn: "datetime",
+    {
+        id: "b_lu_start",
+        enableSorting: true,
+        sortingFn: (rowA, rowB) => {
+            const a = [...rowA.original.b_lu_start].sort(
+                (x, y) => x.getTime() - y.getTime(),
+            )[0]
+            const b = [...rowB.original.b_lu_start].sort(
+                (x, y) => x.getTime() - y.getTime(),
+            )[0]
+            return (a?.getTime() ?? 0) - (b?.getTime() ?? 0)
+        },

Keep the existing cell rendering. This ensures sorting follows the sowing range displayed to users.

🤖 Prompt for AI Agents
fdm-app/app/components/blocks/rotation/columns.tsx around lines 101-127: the
column currently sets sortingFn:"datetime" but accessorKey "b_lu_start" is a
Date[]; the datetime sorter expects a scalar Date which leads to incorrect
comparisons — replace the sorter with a custom sortingFn that extracts a scalar
key (the earliest date) from each row's b_lu_start array and compares their
timestamps; ensure the function handles empty/null arrays (treat as -Infinity or
null-safe) and returns the appropriate comparison (-1, 0, 1), keep the existing
cell rendering unchanged.

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.

Actionable comments posted: 4

♻️ Duplicate comments (2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (2)

804-909: Use calendar timeframe in action getCultivations calls to support future years.

In the action, both the DELETE and POST paths call getCultivations with a hard‑coded { start: new Date(0), end: new Date() } timeframe (lines 854–859 and 903–909). As soon as the user is on a future calendar (e.g. 2026), cultivations in that year fall outside end: new Date(), so getCultivations returns nothing and you hit “Gewas niet gevonden…” even though the rotation contains the crop for that year.

You already compute timeframe = getTimeframe(params) in the loader; the action should mirror that to keep behavior consistent across calendars.

Suggested fix:

 export async function action({ request, params }: ActionFunctionArgs) {
     try {
         const { b_id_farm, calendar = "all" } = params
         if (!b_id_farm) {
             throw new Error("Farm ID is missing")
         }
+
+        const timeframe = getTimeframe(params)
@@
-            for (const fieldId of fieldIds) {
-                const cultivationsForField = await getCultivations(
-                    fdm,
-                    session.principal_id,
-                    fieldId,
-                    { start: new Date(0), end: new Date() }, // Get all cultivations for the field
-                )
+            for (const fieldId of fieldIds) {
+                const cultivationsForField = await getCultivations(
+                    fdm,
+                    session.principal_id,
+                    fieldId,
+                    timeframe,
+                )
@@
-        for (const fieldId of fieldIds) {
-            const cultivationsForField = await getCultivations(
-                fdm,
-                session.principal_id,
-                fieldId,
-                { start: new Date(0), end: new Date() }, // Get all cultivations for the field
-            )
+        for (const fieldId of fieldIds) {
+            const cultivationsForField = await getCultivations(
+                fdm,
+                session.principal_id,
+                fieldId,
+                timeframe,
+            )

This keeps DELETE/POST aligned with the selected calendar and avoids false “crop not found” errors for future years.


265-296: Fix implicit any for firstFieldWithData to satisfy strict TS/biome.

firstFieldWithData is declared as an uninitialized let, which Biome correctly flags as having an implicit any type. This will break in a strict TypeScript setup (noImplicitAny). You can keep the current behavior while giving TS a concrete type by initializing it via a ternary:

-        let firstFieldWithData
-        if (targetCultivation.b_lu_harvestable === "once") {
-            firstFieldWithData = selectedFields.find((f) => f.hasHarvest)
-        }
+        const firstFieldWithData =
+            targetCultivation.b_lu_harvestable === "once"
+                ? selectedFields.find((f) => f.hasHarvest)
+                : undefined

This removes the implicit any, keeps the downstream optional chaining logic unchanged, and aligns with the Biome hint.

🧹 Nitpick comments (4)
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx (4)

198-208: Extract rounding logic to avoid duplication.

The rounding logic Math.round(field.b_area * 10) / 10 appears at lines 205, 266, and 279. Extract it to a helper function.

Apply this diff at the top of the loader function:

+        const roundArea = (area: number) => Math.round(area * 10) / 10
+
         // Get all fields for the farm and their cultivations

Then use it consistently:

-                b_area: Math.round(field.b_area * 10) / 10,
+                b_area: roundArea(field.b_area),

297-305: Simplify state initialization.

The selectedFieldIds state is initialized from loaderData.selectedFields and then immediately synchronized via useEffect. This creates an unnecessary render cycle. Initialize directly from the correct source.

Assuming you accept the previous refactor suggestion, apply this diff:

-    const [selectedFieldIds, setSelectedFieldIds] = useState<string[]>(
-        loaderData.selectedFields.map((field) => field.b_id!),
-    )
-
-    useEffect(() => {
-        setSelectedFieldIds(
-            loaderData.selectedFields.map((field) => field.b_id!),
-        )
-    }, [loaderData.selectedFields])
+    const [selectedFieldIds, setSelectedFieldIds] = useState<string[]>(
+        loaderData.selectedFieldIds,
+    )

If the loader data can change between navigations and you need to sync, consider using key prop on the component or useMemo instead.


320-328: Optimize selection checks with a Set.

The isSelected and toggleSelection functions use array.includes() which is O(n). For better performance, especially with many fields, use a Set.

Apply this diff:

-    const [selectedFieldIds, setSelectedFieldIds] = useState<string[]>(
-        loaderData.selectedFieldIds,
-    )
+    const [selectedFieldIds, setSelectedFieldIds] = useState<Set<string>>(
+        new Set(loaderData.selectedFieldIds),
+    )

-    const isSelected = (fieldId: string) => selectedFieldIds.includes(fieldId)
+    const isSelected = (fieldId: string) => selectedFieldIds.has(fieldId)

     const toggleSelection = (fieldId: string) => {
-        setSelectedFieldIds((prev) =>
-            isSelected(fieldId)
-                ? prev.filter((id) => id !== fieldId)
-                : [...prev, fieldId],
+        setSelectedFieldIds((prev) => {
+            const next = new Set(prev)
+            if (next.has(fieldId)) {
+                next.delete(fieldId)
+            } else {
+                next.add(fieldId)
+            }
+            return next
+        })
-        )
     }

Update references to convert Set to Array when needed:

-        newSearchParams.set("fieldIds", selectedFieldIds.join(","))
+        newSearchParams.set("fieldIds", Array.from(selectedFieldIds).join(","))

709-714: Remove redundant ZodError handling.

The extractFormValuesFromRequest function already validates and throws errors. This catch block for ZodError is unreachable because the error would have been thrown earlier at line 688-691.

Apply this diff:

     } catch (error) {
-        if (error instanceof z.ZodError) {
-            return dataWithError(
-                null,
-                "Invoer is ongeldig. Controleer het formulier.",
-            )
-        }
         throw handleActionError(error)
     }

If validation errors need to be caught, handle them before calling extractFormValuesFromRequest or let the utility function return the error format directly.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5504f48 and 37f9ec1.

📒 Files selected for processing (2)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (26)
📓 Common learnings
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The farm layout system has been reorganized into separate components (`FarmHeader`, `ContentLayout`, `PaginationLayout`) to support different navigation patterns (sidebar, pagination) while maintaining consistent styling. Each layout component is designed to be used independently or combined as needed.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A shared layout component `FarmLayoutBase` has been created in `components/custom/farm-layout-base.tsx` to maintain consistency across farm-related pages. The component handles farm selection dropdown, breadcrumb navigation, and provides a common layout structure.
📚 Learning: 2025-09-23T12:29:34.184Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.

Applied to files:

  • 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
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A shared layout component `FarmLayoutBase` has been created in `components/custom/farm-layout-base.tsx` to maintain consistency across farm-related pages. The component handles farm selection dropdown, breadcrumb navigation, and provides a common layout structure.

Applied to files:

  • 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
📚 Learning: 2025-09-23T12:27:07.391Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.

Applied to files:

  • 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
📚 Learning: 2024-12-19T13:20:44.152Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 23
File: fdm-app/app/routes/app.addfarm.new.tsx:15-17
Timestamp: 2024-12-19T13:20:44.152Z
Learning: Authentication for the “app.addfarm.new” route is already handled globally in “fdm-app/app/routes/app.tsx,” automatically redirecting unauthenticated users to the SignIn page.

Applied to files:

  • 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
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The `FarmLayout` component in `components/custom/farm-layout.tsx` provides a reusable layout structure for farm-related pages, with support for farm selection dropdown, customizable breadcrumb titles, and flexible content rendering through either children or Outlet components.

Applied to files:

  • 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
📚 Learning: 2025-06-02T10:31:27.097Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 151
File: fdm-app/app/routes/signin._index.tsx:101-101
Timestamp: 2025-06-02T10:31:27.097Z
Learning: In fdm-app/app/routes/signin._index.tsx, the redirect destinations are intentionally inconsistent by design: the component defaults new sign-ins to "/welcome" (line 101) while the loader redirects authenticated users to "/farm" (line 80) and the action uses "/farm" as fallback (line 434). This creates appropriate user flows where new users complete their profile via the welcome page, while existing authenticated users bypass it and go directly to the main application.

Applied to files:

  • 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
📚 Learning: 2024-11-25T12:42:32.783Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 6
File: fdm-app/vite.config.ts:5-9
Timestamp: 2024-11-25T12:42:32.783Z
Learning: In the `fdm-app` project, SvenVw is preparing for migration to Remix v3 and may include type declarations or configurations for v3 features in advance, such as in `vite.config.ts`.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.

Applied to files:

  • 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
📚 Learning: 2025-09-23T12:37:58.711Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.711Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.

Applied to files:

  • 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
📚 Learning: 2025-02-13T09:03:11.890Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.create.$b_id_farm.cultivations.$b_lu_catalogue.crop.harvest._index.tsx:111-135
Timestamp: 2025-02-13T09:03:11.890Z
Learning: When adding multiple harvests in fdm-app, use Promise.all instead of Promise.allSettled to ensure atomic behavior - if one harvest addition fails, all should fail and rollback to maintain data consistency.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-07-31T11:38:50.661Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 194
File: fdm-core/src/harvest.ts:488-644
Timestamp: 2025-07-31T11:38:50.661Z
Learning: The validation logic in updateHarvest is intentionally different from checkHarvestDateCompability because updateHarvest is for updating existing harvests while checkHarvestDateCompability is for inserting new harvests. The insertion function includes checks that don't apply to updates, such as verifying no harvest already exists for "once" harvestable cultivations.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-08-11T12:24:32.200Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx:53-53
Timestamp: 2025-08-11T12:24:32.200Z
Learning: In `fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx`, the NMI API for cultivations guarantees that each year will be unique in the cultivation history data, so using `cultivation.year` as a React list key is safe and won't cause duplicate key warnings.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2024-11-27T12:15:36.425Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-data/src/cultivations/index.test.ts:57-59
Timestamp: 2024-11-27T12:15:36.425Z
Learning: In `fdm-data/src/cultivations/index.test.ts`, the `fdm` object created by `drizzle` does not have an `.end()` method. Cleanup code should not attempt to call `fdm.end();`.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-02-13T08:35:59.306Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.$b_id_farm.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx:114-124
Timestamp: 2025-02-13T08:35:59.306Z
Learning: The HarvestForm component in fdm-app expects undefined (not 0) for b_lu_yield when no yield information is available, as 0 would incorrectly imply that yield data exists.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-09-23T10:02:32.123Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.$p_id.tsx:151-164
Timestamp: 2025-09-23T10:02:32.123Z
Learning: The getFertilizer function from svenvw/fdm-core throws an exception if the fertilizer doesn't exist, rather than returning null or undefined.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-04-04T14:27:39.518Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 116
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx:111-154
Timestamp: 2025-04-04T14:27:39.518Z
Learning: In the FDM application, cultivation retrieval logic should be centralized in utility functions rather than duplicated across loader and action functions to improve maintainability and ensure consistent behavior.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-24T11:39:57.805Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-core/src/cultivation.ts:625-671
Timestamp: 2025-01-24T11:39:57.805Z
Learning: Every cultivation has an associated `cultivationTerminating` record created by `addCultivation`, with `b_terminate_date` initially set to null. Therefore, `updateCultivation` can safely assume the record exists and only needs to handle updates.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-23T15:17:23.028Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.028Z
Learning: The `addField` function in fdm-core should use database transactions and field verification to ensure field availability before resolving its promise, eliminating the need for sleep workarounds.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-23T15:17:23.027Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.027Z
Learning: The `addField` function in fdm-core should verify field creation within the same transaction by checking the existence of the field and all its required relations (field data, acquiring info, geometry) before resolving its promise.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-08-11T11:55:26.053Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/integrations/nmi.ts:54-0
Timestamp: 2025-08-11T11:55:26.053Z
Learning: The NMI API Estimates endpoint (`https://api.nmi-agro.nl/estimates`) always returns the fields `b_gwl_ghg`, `b_gwl_glg`, and `cultivations` according to its specification. These fields should be kept as required (not optional) in the TypeScript return type and Zod validation schema in `fdm-app/app/integrations/nmi.ts`.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-24T11:38:05.693Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-24T11:38:05.693Z
Learning: The field creation and cultivation addition should be performed within a single database transaction to ensure atomicity and prevent transaction isolation issues that could lead to "Field does not exist" errors.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-24T11:28:01.882Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-24T11:28:01.882Z
Learning: The `addField` function in fdm-core should use database transactions to ensure atomicity, and since transactions provide ACID guarantees, awaiting the inserts is sufficient to ensure field availability - no additional verification queries are needed.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-02-13T08:28:00.183Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-core/src/cultivation.ts:572-597
Timestamp: 2025-02-13T08:28:00.183Z
Learning: In the FDM database schema, `b_id_harvesting` is a primary key in the `cultivationHarvesting` table, ensuring no duplicate harvest entries can exist. Therefore, duplicate checking is not needed when processing harvest records from database queries.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-08-13T10:33:05.313Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-09-26T08:34:50.413Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 279
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx:277-283
Timestamp: 2025-09-26T08:34:50.413Z
Learning: In the fdm project, fdm-core and fdm-app are updated together as part of a monorepo structure, which eliminates legacy data concerns when new fields like b_isproductive are introduced. Both packages are synchronized, so there's no need for defensive coding against undefined values for newly introduced database fields.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
🧬 Code graph analysis (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx (6)
fdm-core/src/db/schema.ts (2)
  • fertilizers (136-144)
  • applicationMethodOptions (169-180)
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (2)
  • FertilizerOption (41-45)
  • FertilizerApplicationForm (47-311)
fdm-app/app/components/blocks/header/base.tsx (1)
  • Header (6-29)
fdm-app/app/components/blocks/header/farm.tsx (1)
  • HeaderFarm (15-76)
fdm-app/app/components/blocks/farm/farm-title.tsx (1)
  • FarmTitle (15-36)
fdm-app/app/components/blocks/farm/farm-content.tsx (1)
  • FarmContent (13-27)
🪛 Biome (2.1.2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx

[error] 265-266: This variable implicitly has the any type.

Variable declarations without type annotation and initialization implicitly have the any type. Declare a type or initialize the variable with some value.

(lint/suspicious/noImplicitAnyLet)

🔇 Additional comments (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (1)

387-409: Overwrite confirmation flow now correctly guards destructive “once” overwrites.

The confirmation logic looks solid now:

  • handleConfirmation short‑circuits for "multiple" harvestables and only triggers the dialog when at least one currently selected field has hasHarvest === true.
  • initiateConfirmation/resolveConfirmation properly manage a single in‑flight promise and reset showOverwriteWarning once resolved.
  • Closing the dialog via backdrop/escape also resolves false, so the pending submission is unblocked cleanly.

From the route’s perspective this gives you a consistent, centralized guard before the action’s delete‑and‑reinsert behavior runs.

Also applies to: 762-797


// Redirect to farms overview if user has no farm
if (farms.length === 0) {
return redirect("./farm")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix the redirect path.

The relative redirect "./farm" will resolve incorrectly. Use an absolute path instead.

Apply this diff:

-            return redirect("./farm")
+            return redirect("/farm")
🤖 Prompt for AI Agents
In fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
around line 120, the code returns a relative redirect " ./farm" which will
resolve incorrectly; change it to an absolute path (for example "/farm") by
replacing the relative string with the appropriate absolute route to ensure the
redirect always resolves correctly.

Comment on lines +257 to +282
selectedFields: selectedFields.map(
(field: {
b_id: string
b_name: string
b_area: number
cultivations: string[]
}) => ({
b_id: field.b_id,
b_name: field.b_name,
b_area: Math.round(field.b_area * 10) / 10,
cultivations: field.cultivations,
}),
),
fieldOptions: fieldOptions.map(
(field: {
b_id: string
b_name: string
b_area: number
cultivations: string[]
}) => ({
b_id: field.b_id,
b_name: field.b_name,
b_area: field.b_area,
cultivations: field.cultivations,
}),
), // All fields for selection
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove duplicate field mapping.

The field mapping logic is duplicated for selectedFields (lines 257-268) and fieldOptions (lines 270-281). Since fieldOptions already contains all the necessary data with the same structure, you can derive selectedFields from it.

Apply this diff:

         return {
             b_id_farm: b_id_farm,
             farmOptions: farmOptions,
             fieldAmount: selectedFields.length,
             fertilizerOptions: fertilizerOptions,
             calendar: calendar,
-            selectedFields: selectedFields.map(
-                (field: {
-                    b_id: string
-                    b_name: string
-                    b_area: number
-                    cultivations: string[]
-                }) => ({
-                    b_id: field.b_id,
-                    b_name: field.b_name,
-                    b_area: Math.round(field.b_area * 10) / 10,
-                    cultivations: field.cultivations,
-                }),
-            ),
-            fieldOptions: fieldOptions.map(
-                (field: {
-                    b_id: string
-                    b_name: string
-                    b_area: number
-                    cultivations: string[]
-                }) => ({
-                    b_id: field.b_id,
-                    b_name: field.b_name,
-                    b_area: field.b_area,
-                    cultivations: field.cultivations,
-                }),
-            ), // All fields for selection
+            selectedFieldIds: selectedFields.map(f => f.b_id),
+            fieldOptions: fieldOptions,
             cultivationName: cultivationName,
             cultivationIds: cultivationIds,
         }

Then in the component, derive selectedFields from fieldOptions:

+    const selectedFieldsFromLoader = loaderData.fieldOptions.filter(field => 
+        loaderData.selectedFieldIds.includes(field.b_id)
+    )
     const [selectedFieldIds, setSelectedFieldIds] = useState<string[]>(
-        loaderData.selectedFields.map((field) => field.b_id!),
+        loaderData.selectedFieldIds,
     )

     useEffect(() => {
         setSelectedFieldIds(
-            loaderData.selectedFields.map((field) => field.b_id!),
+            loaderData.selectedFieldIds,
         )
-    }, [loaderData.selectedFields])
+    }, [loaderData.selectedFieldIds])

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
around lines 257 to 282, the mapping of field objects is duplicated for
selectedFields and fieldOptions; remove the duplicate mapping by keeping a
single mapped fieldOptions array (with b_id, b_name, b_area rounded as needed)
and derive selectedFields from fieldOptions in the component (e.g., filter or
pick entries from fieldOptions instead of remapping), ensuring types remain
consistent and removing the redundant map call for selectedFields.

Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._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.

Actionable comments posted: 1

♻️ Duplicate comments (6)
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (3)

902-992: Multi-field harvest writes remain non-atomic (sequential loop).

The POST branch iterates fieldIds sequentially, removing existing harvests (for "once") and calling addHarvest per field. If an error occurs midway, some fields will be updated and others not, leaving inconsistent state across the rotation.

Earlier feedback suggested batching with Promise.all (or delegating atomicity to fdm-core when possible). If changing fdm-core is out of scope, at minimum consider:

  • Collecting per-field operations into an array and executing them with Promise.all so all writes happen together, and/or
  • Tracking which fields were updated so you can surface a clear error/toast when a later field fails.

Tagging as tech debt if you intend to address atomic behavior in a later core refactor.


129-136: Use an absolute redirect path for “no farms” case.

redirect("./farm") is relative to this deeply nested route and will resolve to a wrong URL (e.g. appending /farm to the rotation/harvest path). Use an absolute path instead so users land on the farm overview correctly.

-        if (farms.length === 0) {
-            return redirect("./farm")
-        }
+        if (farms.length === 0) {
+            return redirect("/farm")
+        }
In React Router v6+ (data APIs), how does `redirect("./farm")` resolve when used in a nested route loader, and what is the recommended way to redirect to a top-level `/farm` route?

851-887: Use calendar timeframe instead of { start: new Date(0), end: new Date() } in the action.

Both DELETE and POST paths call getCultivations with an unbounded timeframe. As soon as the user is working in a future calendar year, cultivations in that year can fall outside new Date() and won’t be found, causing “Gewas niet gevonden…” errors and blocking harvest add/delete for future years.

Align the action with the loader by reusing getTimeframe(params):

 export async function action({ request, params }: ActionFunctionArgs) {
     try {
-        const { b_id_farm, calendar = "all" } = params
+        const { b_id_farm, calendar = "all" } = params
         if (!b_id_farm) {
             throw new Error("Farm ID is missing")
         }
 
         const session = await getSession(request)
+        const timeframe = getTimeframe(params)
@@
-                const cultivationsForField = await getCultivations(
-                    fdm,
-                    session.principal_id,
-                    fieldId,
-                    { start: new Date(0), end: new Date() },
-                )
+                const cultivationsForField = await getCultivations(
+                    fdm,
+                    session.principal_id,
+                    fieldId,
+                    timeframe,
+                )
@@
-            const cultivationsForField = await getCultivations(
-                fdm,
-                session.principal_id,
-                fieldId,
-                { start: new Date(0), end: new Date() },
-            )
+            const cultivationsForField = await getCultivations(
+                fdm,
+                session.principal_id,
+                fieldId,
+                timeframe,
+            )

Also applies to: 902-908

fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx (3)

250-285: Avoid duplicating field mapping for selectedFields and fieldOptions.

The loader maps fields twice into almost identical shapes: once for selectedFields and once for fieldOptions. This adds maintenance overhead and risk of divergence.

You can instead return:

  • fieldOptions as the single mapped array, and
  • a selectedFieldIds array derived from selectedFields,

then derive the selected fields in the component from fieldOptions.

Example refactor (adapted from earlier suggestion):

-        return {
-            b_id_farm: b_id_farm,
-            farmOptions: farmOptions,
-            fieldAmount: selectedFields.length,
-            fertilizerOptions: fertilizerOptions,
-            calendar: calendar,
-            selectedFields: selectedFields.map(
-                (field: {
-                    b_id: string
-                    b_name: string
-                    b_area: number
-                    cultivations: string[]
-                }) => ({
-                    b_id: field.b_id,
-                    b_name: field.b_name,
-                    b_area: Math.round(field.b_area * 10) / 10,
-                    cultivations: field.cultivations,
-                }),
-            ),
-            fieldOptions: fieldOptions.map(
-                (field: {
-                    b_id: string
-                    b_name: string
-                    b_area: number
-                    cultivations: string[]
-                }) => ({
-                    b_id: field.b_id,
-                    b_name: field.b_name,
-                    b_area: field.b_area,
-                    cultivations: field.cultivations,
-                }),
-            ), // All fields for selection
-            cultivationName: cultivationName,
-            cultivationIds: cultivationIds,
-        }
+        return {
+            b_id_farm: b_id_farm,
+            farmOptions: farmOptions,
+            fieldAmount: selectedFields.length,
+            fertilizerOptions: fertilizerOptions,
+            calendar: calendar,
+            selectedFieldIds: selectedFields.map((field) => field.b_id),
+            fieldOptions: fieldOptions,
+            cultivationName: cultivationName,
+            cultivationIds: cultivationIds,
+        }

Then in the component:

-    const [selectedFieldIds, setSelectedFieldIds] = useState<string[]>(
-        loaderData.selectedFields.map((field) => field.b_id!),
-    )
+    const [selectedFieldIds, setSelectedFieldIds] = useState<string[]>(
+        loaderData.selectedFieldIds,
+    )
 
-    useEffect(() => {
-        setSelectedFieldIds(
-            loaderData.selectedFields.map((field) => field.b_id!),
-        )
-    }, [loaderData.selectedFields])
+    useEffect(() => {
+        setSelectedFieldIds(loaderData.selectedFieldIds)
+    }, [loaderData.selectedFieldIds])

This keeps a single source of truth for field shapes.


115-121: Redirect to /farm instead of using a relative "./farm" path.

Same as the harvest route: redirect("./farm") from this nested route will resolve to a path under the rotation/fertilizer URL, not the intended farm overview. Use an absolute path.

-        // Redirect to farms overview if user has no farm
-        if (farms.length === 0) {
-            return redirect("./farm")
-        }
+        // Redirect to farms overview if user has no farm
+        if (farms.length === 0) {
+            return redirect("/farm")
+        }
In React Router v6+ data loaders, what is the difference between `redirect("./farm")` and `redirect("/farm")` when called from a nested route like `/farm/:id/:calendar/rotation/fertilizer`, and which form is recommended for redirecting to the farm overview?

647-651: Fix fallback key for b_id_or_b_lu_catalogue.

b_id_or_b_lu_catalogue falls back to the literal string "cultivationIds" when the query param is missing:

b_id_or_b_lu_catalogue={
    searchParams.get("cultivationIds") || "cultivationIds"
}

This is unlikely to be a meaningful key for the draft store and will diverge from what the loader enforces (that cultivationIds must be present). Prefer using the loader’s cultivationIds directly, or treat absence as an error.

For example:

-                                            b_id_or_b_lu_catalogue={
-                                                searchParams.get(
-                                                    "cultivationIds",
-                                                ) || "cultivationIds"
-                                            }
+                                            b_id_or_b_lu_catalogue={
+                                                loaderData.cultivationIds.join(
+                                                    ",",
+                                                )
+                                            }

This keeps the form storage key consistent with the actual cultivation selection.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37f9ec1 and bfff011.

📒 Files selected for processing (2)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (31)
📓 Common learnings
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The farm layout system has been reorganized into separate components (`FarmHeader`, `ContentLayout`, `PaginationLayout`) to support different navigation patterns (sidebar, pagination) while maintaining consistent styling. Each layout component is designed to be used independently or combined as needed.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A shared layout component `FarmLayoutBase` has been created in `components/custom/farm-layout-base.tsx` to maintain consistency across farm-related pages. The component handles farm selection dropdown, breadcrumb navigation, and provides a common layout structure.
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A shared layout component `FarmLayoutBase` has been created in `components/custom/farm-layout-base.tsx` to maintain consistency across farm-related pages. The component handles farm selection dropdown, breadcrumb navigation, and provides a common layout structure.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The `FarmLayout` component in `components/custom/farm-layout.tsx` provides a reusable layout structure for farm-related pages, with support for farm selection dropdown, customizable breadcrumb titles, and flexible content rendering through either children or Outlet components.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-09-23T12:29:34.184Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2024-12-19T13:20:44.152Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 23
File: fdm-app/app/routes/app.addfarm.new.tsx:15-17
Timestamp: 2024-12-19T13:20:44.152Z
Learning: Authentication for the “app.addfarm.new” route is already handled globally in “fdm-app/app/routes/app.tsx,” automatically redirecting unauthenticated users to the SignIn page.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-06-02T10:31:27.097Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 151
File: fdm-app/app/routes/signin._index.tsx:101-101
Timestamp: 2025-06-02T10:31:27.097Z
Learning: In fdm-app/app/routes/signin._index.tsx, the redirect destinations are intentionally inconsistent by design: the component defaults new sign-ins to "/welcome" (line 101) while the loader redirects authenticated users to "/farm" (line 80) and the action uses "/farm" as fallback (line 434). This creates appropriate user flows where new users complete their profile via the welcome page, while existing authenticated users bypass it and go directly to the main application.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-02-13T09:03:11.890Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.create.$b_id_farm.cultivations.$b_lu_catalogue.crop.harvest._index.tsx:111-135
Timestamp: 2025-02-13T09:03:11.890Z
Learning: When adding multiple harvests in fdm-app, use Promise.all instead of Promise.allSettled to ensure atomic behavior - if one harvest addition fails, all should fail and rollback to maintain data consistency.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-09-23T12:27:07.391Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-07-31T11:38:50.661Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 194
File: fdm-core/src/harvest.ts:488-644
Timestamp: 2025-07-31T11:38:50.661Z
Learning: The validation logic in updateHarvest is intentionally different from checkHarvestDateCompability because updateHarvest is for updating existing harvests while checkHarvestDateCompability is for inserting new harvests. The insertion function includes checks that don't apply to updates, such as verifying no harvest already exists for "once" harvestable cultivations.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-08-11T12:24:32.200Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx:53-53
Timestamp: 2025-08-11T12:24:32.200Z
Learning: In `fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx`, the NMI API for cultivations guarantees that each year will be unique in the cultivation history data, so using `cultivation.year` as a React list key is safe and won't cause duplicate key warnings.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2024-11-27T12:15:36.425Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-data/src/cultivations/index.test.ts:57-59
Timestamp: 2024-11-27T12:15:36.425Z
Learning: In `fdm-data/src/cultivations/index.test.ts`, the `fdm` object created by `drizzle` does not have an `.end()` method. Cleanup code should not attempt to call `fdm.end();`.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-02-13T08:35:59.306Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-app/app/routes/farm.$b_id_farm.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx:114-124
Timestamp: 2025-02-13T08:35:59.306Z
Learning: The HarvestForm component in fdm-app expects undefined (not 0) for b_lu_yield when no yield information is available, as 0 would incorrectly imply that yield data exists.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-09-23T12:37:58.711Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.711Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-09-23T10:02:32.123Z
Learnt from: BoraIneviNMI
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.$p_id.tsx:151-164
Timestamp: 2025-09-23T10:02:32.123Z
Learning: The getFertilizer function from svenvw/fdm-core throws an exception if the fertilizer doesn't exist, rather than returning null or undefined.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-04-04T14:27:39.518Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 116
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx:111-154
Timestamp: 2025-04-04T14:27:39.518Z
Learning: In the FDM application, cultivation retrieval logic should be centralized in utility functions rather than duplicated across loader and action functions to improve maintainability and ensure consistent behavior.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-01-24T11:39:57.805Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-core/src/cultivation.ts:625-671
Timestamp: 2025-01-24T11:39:57.805Z
Learning: Every cultivation has an associated `cultivationTerminating` record created by `addCultivation`, with `b_terminate_date` initially set to null. Therefore, `updateCultivation` can safely assume the record exists and only needs to handle updates.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-23T15:17:23.028Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.028Z
Learning: The `addField` function in fdm-core should use database transactions and field verification to ensure field availability before resolving its promise, eliminating the need for sleep workarounds.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-01-23T15:17:23.027Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-23T15:17:23.027Z
Learning: The `addField` function in fdm-core should verify field creation within the same transaction by checking the existence of the field and all its required relations (field data, acquiring info, geometry) before resolving its promise.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-08-11T11:55:26.053Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/integrations/nmi.ts:54-0
Timestamp: 2025-08-11T11:55:26.053Z
Learning: The NMI API Estimates endpoint (`https://api.nmi-agro.nl/estimates`) always returns the fields `b_gwl_ghg`, `b_gwl_glg`, and `cultivations` according to its specification. These fields should be kept as required (not optional) in the TypeScript return type and Zod validation schema in `fdm-app/app/integrations/nmi.ts`.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-01-24T11:38:05.693Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-24T11:38:05.693Z
Learning: The field creation and cultivation addition should be performed within a single database transaction to ensure atomicity and prevent transaction isolation issues that could lead to "Field does not exist" errors.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-24T11:28:01.882Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 49
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:208-208
Timestamp: 2025-01-24T11:28:01.882Z
Learning: The `addField` function in fdm-core should use database transactions to ensure atomicity, and since transactions provide ACID guarantees, awaiting the inserts is sufficient to ensure field availability - no additional verification queries are needed.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-02-13T08:28:00.183Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 71
File: fdm-core/src/cultivation.ts:572-597
Timestamp: 2025-02-13T08:28:00.183Z
Learning: In the FDM database schema, `b_id_harvesting` is a primary key in the `cultivationHarvesting` table, ensuring no duplicate harvest entries can exist. Therefore, duplicate checking is not needed when processing harvest records from database queries.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-08-13T10:33:05.313Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-09-26T08:34:50.413Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 279
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx:277-283
Timestamp: 2025-09-26T08:34:50.413Z
Learning: In the fdm project, fdm-core and fdm-app are updated together as part of a monorepo structure, which eliminates legacy data concerns when new fields like b_isproductive are introduced. Both packages are synchronized, so there's no need for defensive coding against undefined values for newly introduced database fields.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx
📚 Learning: 2025-01-14T16:06:24.294Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 45
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:1-1
Timestamp: 2025-01-14T16:06:24.294Z
Learning: In the fdm-app codebase, the `redirect` function should be imported from `react-router`, not `react-router-dom`.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-01-14T16:06:21.832Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 45
File: fdm-app/app/routes/farm.$b_id_farm.settings._index.tsx:1-1
Timestamp: 2025-01-14T16:06:21.832Z
Learning: In the fdm project, `redirect` and other routing utilities should be imported from `react-router` instead of `react-router-dom`.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core has optional parameters after `fdm` and `b_id`. The TypeScript definitions might show 8 required parameters due to a potential version mismatch.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-01-31T15:34:20.850Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:34:20.850Z
Learning: The `updateField` function in fdm-core has optional parameters that don't need to be passed as undefined. Only `fdm` and `b_id` are required.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2024-11-25T14:42:26.660Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 6
File: fdm-app/app/components/blocks/field-map.tsx:0-0
Timestamp: 2024-11-25T14:42:26.660Z
Learning: In `fdm-app/app/components/blocks/field-map.tsx`, explicit cleanup of Mapbox GL resources is not necessary, as `react-map-gl` handles it automatically upon component unmount, and `MapRef` does not have a `remove` method.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
📚 Learning: 2025-08-11T12:14:28.354Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-core/src/db/schema.ts:370-0
Timestamp: 2025-08-11T12:14:28.354Z
Learning: In the FDM database schema, the field `b_lu_rest_oravib` in the `cultivations_catalogue` table is intentionally nullable (can be undefined). When undefined, it should be treated as `false` in the UI layer. This allows distinguishing between "explicitly set to false" and "not set" at the database level while providing simplified boolean logic in the UI.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx
🔇 Additional comments (2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.harvest._index.tsx (1)

214-295: Loader harvest defaults and typing look solid.

The HarvestApplication alias, per-field harvest lookup, and fallback/default handling (including getDefaultsForHarvestParameters when no analysis is present) are well-structured and match the other harvest routes’ patterns.

fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.fertilizer._index.tsx (1)

688-705: Good use of Promise.all for fertilizer applications.

Parallelizing addFertilizerApplication across fieldIds is a nice improvement over a sequential loop and matches the guidance used elsewhere in the project for bulk operations.

coderabbitai[bot]

This comment was marked as resolved.

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator

setSelectedDate(field.value)
+        setInputValue(formatDate(field.value))
+        setMonth(field.value)

@coderabbitai what issues can having the onChange in the dependencies array can cause?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 21, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the date-picker-v2.tsx file to see the full useEffect implementation
cat fdm-app/app/components/custom/date-picker-v2.tsx

Length of output: 5635


@BoraIneviNMI Having field.onChange in the dependencies array can cause several issues:

1. Circular Dependency Risk

The effect calls field.onChange() inside its body (line converting Date to ISO string), but also depends on field.onChange. This creates a circular dependency:

  • Effect runs → calls field.onChange() → updates form state → potentially triggers effect again

While React Hook Form typically keeps onChange stable, this pattern is inherently risky.

2. Potential Infinite Loop

If field.onChange gets a new reference for any reason (component re-render, form reset, etc.), it will trigger the effect. If the effect then calls onChange, which causes another reference change, you get an infinite loop.

3. Unnecessary Re-executions

Even without an infinite loop, if field.onChange changes reference between renders, the effect will run unnecessarily, causing performance issues and potential UI flickering.

Recommendation

Remove field.onChange from the dependency array:

useEffect(() => {
    if (field.value && field.value instanceof Date) {
        field.onChange(field.value.toISOString())
    } else if (field.value) {
        const date = parseDateText(field.value)
        setSelectedDate(date || undefined)
        setInputValue(date ? formatDate(date) : "")
        setMonth(date || undefined)
    } else {
        setInputValue("")
        setSelectedDate(undefined)
        setMonth(undefined)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
}, [field.value])

Since field.onChange is a stable function from React Hook Form, it's safe to omit it. The effect should only run when field.value changes, not when onChange potentially gets a new reference.


🧠 Learnings used
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx:53-53
Timestamp: 2025-08-11T12:24:32.200Z
Learning: In `fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx`, the NMI API for cultivations guarantees that each year will be unique in the cultivation history data, so using `cultivation.year` as a React list key is safe and won't cause duplicate key warnings.

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator

Tip

For best results, initiate chat on the files or code changes.
🧩 Analysis chain

@BoraIneviNMI Having field.onChange in the dependencies array can cause several issues:

1. Circular Dependency Risk

The effect calls field.onChange() inside its body (line converting Date to ISO string), but also depends on field.onChange. This creates a circular dependency:

* Effect runs → calls `field.onChange()` → updates form state → potentially triggers effect again

While React Hook Form typically keeps onChange stable, this pattern is inherently risky.

2. Potential Infinite Loop

If field.onChange gets a new reference for any reason (component re-render, form reset, etc.), it will trigger the effect. If the effect then calls onChange, which causes another reference change, you get an infinite loop.

3. Unnecessary Re-executions

Even without an infinite loop, if field.onChange changes reference between renders, the effect will run unnecessarily, causing performance issues and potential UI flickering.

Recommendation

Remove field.onChange from the dependency array:

useEffect(() => {
    if (field.value && field.value instanceof Date) {
        field.onChange(field.value.toISOString())
    } else if (field.value) {
        const date = parseDateText(field.value)
        setSelectedDate(date || undefined)
        setInputValue(date ? formatDate(date) : "")
        setMonth(date || undefined)
    } else {
        setInputValue("")
        setSelectedDate(undefined)
        setMonth(undefined)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
}, [field.value])

Since field.onChange is a stable function from React Hook Form, it's safe to omit it. The effect should only run when field.value changes, not when onChange potentially gets a new reference.
🧠 Learnings used

@coderabbitai we keep having to deal with linting error regarding onChange event handlers as effect dependencies. How could we improve our linter config so it understands capturing onChange as a dependency is not needed?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 21, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if there's an ESLint configuration file
fd -t f "eslint" -e js -e json -e cjs -e mjs -e yaml -e yml | head -10

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Also check package.json for eslint config
cat fdm-app/package.json | jq -r 'if .eslintConfig then "ESLint config found in package.json" else "No eslintConfig in package.json" end'

Length of output: 31


🏁 Script executed:

#!/bin/bash
# Look for any ESLint config files at the root and in fdm-app
ls -la .eslintrc* eslint.config.* 2>/dev/null || echo "No root ESLint config files"
ls -la fdm-app/.eslintrc* fdm-app/eslint.config.* 2>/dev/null || echo "No fdm-app ESLint config files"

Length of output: 58


🏁 Script executed:

#!/bin/bash
# Check if there's a root ESLint config by searching for common patterns
cat package.json 2>/dev/null | jq '.eslintConfig' 2>/dev/null || echo "No root package.json eslintConfig"

Length of output: 4


🏁 Script executed:

#!/bin/bash
# Search for any files containing "eslint" in their name
find . -name "*eslint*" -type f 2>/dev/null | grep -v node_modules | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check if Remix has default ESLint config or if it's in tsconfig
cat fdm-app/tsconfig.json 2>/dev/null | head -50

Length of output: 1043


🏁 Script executed:

#!/bin/bash
# Look for vite or remix config files that might contain ESLint settings
ls -la fdm-app/vite.config.* fdm-app/remix.config.* 2>/dev/null

Length of output: 71


🏁 Script executed:

#!/bin/bash
# Check root package.json for linting scripts and dependencies
cat package.json 2>/dev/null | jq '{scripts: .scripts, devDependencies: .devDependencies} | with_entries(select(.value | tostring | contains("eslint") or contains("lint")))'

Length of output: 553


🏁 Script executed:

#!/bin/bash
# Check fdm-app package.json for ESLint related dependencies
cat fdm-app/package.json | jq '{devDependencies: .devDependencies} | to_entries | map(select(.key | contains("eslint"))) | from_entries'

Length of output: 2


🏁 Script executed:

#!/bin/bash
# The project uses Biome, not ESLint. Look for Biome config
find . -name "biome.json" -o -name "biome.jsonc" 2>/dev/null | head -5

Length of output: 12


🏁 Script executed:

#!/bin/bash
# Read the Biome configuration
cat biome.json 2>/dev/null || cat biome.jsonc 2>/dev/null || echo "No biome config found"

Length of output: 1779


🏁 Script executed:

#!/bin/bash
# Check for react-hooks rules in biome
cat biome.json 2>/dev/null | jq '.linter.rules.nursery' 2>/dev/null || echo "Checking for nursery rules failed"

Length of output: 4


🌐 Web query:

Biome useExhaustiveDependencies rule configuration options stable functions

💡 Result:

Summary — how to configure stable return values for useExhaustiveDependencies:

  • The rule accepts an options.hooks array where each entry configures a custom hook to validate (name, closureIndex, dependenciesIndex). [1][2].
  • To mark a hook’s return value as stable you use the stableResult field on a hook entry. Supported forms:
    • stableResult: true — whole return value is stable (e.g., useRef()). [1][2]
    • stableResult: 1 or stableResult: [1] — treat the given index(s) of an array return as stable (shorthand: number = single-index). Useful for useState() where index 1 is the setter. [1][2]
    • stableResult: ["propName"] — treat listed property name(s) of an object return as stable. [1][2]
  • Example config (biome.json):
    {
    "linter": {
    "rules": {
    "correctness": {
    "useExhaustiveDependencies": {
    "options": {
    "hooks": [
    { "name": "useDispatch", "stableResult": true },
    { "name": "useStateLike", "stableResult": [1] },
    { "name": "useObj", "stableResult": ["set"] }
    ]
    }
    }
    }
    }
    }
    } [1][2]

Notes:

  • The rule is a port of react-hooks/exhaustive-deps and accepts custom hook shapes (closureIndex/dependenciesIndex). [1][2]
  • There is ongoing discussion/feature requests (e.g., handling functions that change identity) — see related GitHub issue. [3]

Sources:
[1] Biome docs — useExhaustiveDependencies rule. (biomejs.dev)
[2] Biome docs (alternate mirror). (biomejs.cn)
[3] Biome GitHub issue re: reactCompiler / function identity. (github.com)

Citations:


@BoraIneviNMI Great question! I see your project uses Biome (not ESLint), which has a useExhaustiveDependencies rule that's equivalent to ESLint's react-hooks/exhaustive-deps.

Biome supports marking specific return values as stable using the stableResult configuration option. For React Hook Form's field.onChange (and other stable field properties), you can configure your biome.json like this:

{
    "$schema": "https://biomejs.dev/schemas/2.0.6/schema.json",
    "vcs": {
        "enabled": true,
        "clientKind": "git",
        "useIgnoreFile": true
    },
    "files": {
        "ignoreUnknown": false,
        "includes": ["**"]
    },
    "formatter": {
        "enabled": true,
        "indentStyle": "space",
        "indentWidth": 4
    },
    "assist": { "actions": { "source": { "organizeImports": "on" } } },
    "linter": {
        "enabled": true,
        "rules": {
            "recommended": true,
            "correctness": {
                "useExhaustiveDependencies": {
                    "level": "error",
                    "options": {
                        "hooks": [
                            {
                                "name": "useController",
                                "stableResult": ["onChange", "onBlur", "ref"]
                            }
                        ]
                    }
                }
            },
            "style": {
                "noParameterAssign": "error",
                "useAsConstAssertion": "error",
                "useDefaultParameterLast": "error",
                "useEnumInitializers": "error",
                "useSelfClosingElements": "error",
                "useSingleVarDeclarator": "error",
                "noUnusedTemplateLiteral": "error",
                "useNumberNamespace": "error",
                "noInferrableTypes": "error",
                "noUselessElse": "error"
            }
        }
    },
    "javascript": {
        "formatter": {
            "enabled": true,
            "quoteStyle": "double",
            "trailingCommas": "all",
            "semicolons": "asNeeded",
            "arrowParentheses": "always",
            "indentStyle": "space",
            "indentWidth": 4
        },
        "linter": {
            "enabled": true
        }
    },
    "json": {
        "parser": {
            "allowComments": false,
            "allowTrailingCommas": false
        },
        "formatter": {
            "enabled": true,
            "indentStyle": "space",
            "indentWidth": 4,
            "trailingCommas": "none"
        },
        "linter": {
            "enabled": true
        }
    }
}

How it works:

  • stableResult: ["onChange", "onBlur", "ref"] tells Biome that these specific properties of the object returned by useController are stable references and don't need to be included in dependency arrays.
  • The configuration marks these properties globally for all uses of useController in your codebase.

Additional note:

If you're using the <Controller> component and accessing field from the render prop, the same configuration applies since internally it uses useController. You can also add other hooks that return stable functions if needed (like useForm which returns methods like handleSubmit, reset, etc.):

{
    "name": "useForm",
    "stableResult": ["handleSubmit", "reset", "setValue", "getValues", "watch", "control", "formState"]
}

This approach is cleaner than using eslint-disable-next-line comments everywhere and properly teaches Biome about React Hook Form's API guarantees.


🧠 Learnings used
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx:53-53
Timestamp: 2025-08-11T12:24:32.200Z
Learning: In `fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx`, the NMI API for cultivations guarantees that each year will be unique in the cultivation history data, so using `cultivation.year` as a React list key is safe and won't cause duplicate key warnings.

Learnt from: SvenVw
Repo: SvenVw/fdm PR: 138
File: fdm-app/app/components/custom/combobox.tsx:34-37
Timestamp: 2025-05-09T14:58:10.465Z
Learning: When updating React components that use both react-hook-form and React Router v7, it's important to only import types (like UseFormReturn, FieldValues) from react-hook-form to avoid naming conflicts with React Router's Form component. Use `import type { ... } from 'react-hook-form'` syntax to ensure only types are imported.

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

🧹 Nitpick comments (1)
fdm-app/app/components/custom/date-picker-v2.tsx (1)

50-50: Consider global Biome configuration instead of inline ignore comments.

Based on the PR comments discussion, the recommended approach is to configure Biome's useExhaustiveDependencies rule to mark onChange, onBlur, and ref as stable globally. This avoids needing ignore comments in every file that uses react-hook-form Controllers.

Add this to your biome.json:

{
  "linter": {
    "rules": {
      "correctness": {
        "useExhaustiveDependencies": {
          "level": "error",
          "options": {
            "hooks": [
              {
                "name": "useController",
                "stableResult": ["onChange", "onBlur", "ref"]
              },
              {
                "name": "useForm",
                "stableResult": ["register", "handleSubmit", "reset", "setValue", "getValues"]
              }
            ]
          }
        }
      }
    }
  }
}

This configuration makes the linter understand that these properties are stable and don't need to be included in dependency arrays.

As per coding guidelines

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 813e01b and 66d5e74.

📒 Files selected for processing (1)
  • fdm-app/app/components/custom/date-picker-v2.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The farm layout system has been reorganized into separate components (`FarmHeader`, `ContentLayout`, `PaginationLayout`) to support different navigation patterns (sidebar, pagination) while maintaining consistent styling. Each layout component is designed to be used independently or combined as needed.

Comment thread fdm-app/app/components/custom/date-picker-v2.tsx
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 fdm-calculator fdm-core fdm-data

Projects

None yet

2 participants