Skip to content

Enable Multi-Select for "New Field" using Atlas Selection Pattern#410

Merged
SvenVw merged 33 commits into
developmentfrom
FDM355
Jan 22, 2026
Merged

Enable Multi-Select for "New Field" using Atlas Selection Pattern#410
SvenVw merged 33 commits into
developmentfrom
FDM355

Conversation

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator

@BoraIneviNMI BoraIneviNMI commented Jan 9, 2026

Enhancements

  • Users can now add multiple fields at once on the atlas also through the fields table.
  • The .../field/$b_id route can now accept a fieldIds search parameter which will make it display as a wizard page, with a dropdown allowing the user to select each of the fields and only those. The header will also display "new field" instead of a field selector in order to prevent the user from going away.

Closes #355

Summary by CodeRabbit

  • New Features

    • Add and manage multiple fields at once via a new fields sidebar, atlas, multi-field form, batch add action, and soil‑analysis block.
    • New per-field detail pages with full edit, delete and soil‑analysis workflows.
  • Improvements

    • Batch selection flow with selection count, continue panel and success feedback.
    • Navigation/redirects now preserve query/search parameters for consistent back/continue behavior.
    • Map auto-fits bounds and shows clearer saved-field visuals.
  • Chore

    • Minor package version bump and changelog entry.

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

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jan 9, 2026

🦋 Changeset detected

Latest commit: f205d6f

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 Jan 9, 2026

Walkthrough

Adds multi-select field creation: map-based selection of multiple parcels, batch server action to create fields (with cultivations and optional soil analysis), new review/configuration routes and UI blocks, and query/search-preserving navigation adjustments.

Changes

Cohort / File(s) Summary
Changelog
\.changeset/fiery-wasps-win.md
Release note bump for @svenvw/fdm-app describing multi-field selection capability.
Map selection & batch creation (route)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx
Replaces single-field flow with multi-select: per-field cultivation resolution, geometry simplify, selectedFieldsData state, FieldsSourceSelected/FieldsPanelSelection UI, and an action that validates and batch-creates fields (+cultivations, optional soil analysis).
Fields review/config routes
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx, ...fields._index.tsx, ...fields.$b_id._index.tsx
New review/list route with loader/meta and nested field-detail route(s) for editing/updating/deleting created fields; index loader redirects to appropriate nested field.
Soil analysis route barrels & redirect fixes
fdm-app/app/routes/...soil.analysis.upload.tsx, ...soil.analysis._index.tsx, ...soil.analysis.$analysis_type.tsx
Added thin re-export wrapper files; adjusted redirects/navigation to preserve original query/search parameters on soil analysis flows.
New composite block & subcomponents
fdm-app/app/components/blocks/fields-new/block.tsx, .../form.tsx, .../schema.tsx, .../atlas.tsx, .../sidebar.tsx, .../soil.tsx
New NewFieldsBlock and subcomponents: form (Zod schema), atlas (map fit/bounds), sidebar (list + filters + preserve search), soil-analysis block; composed for bulk field review/edit.
farm.create usage refactor
fdm-app/app/routes/farm.create.*.fields*.tsx, fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id._index.tsx
Replaced in-file form/sidebar with shared NewFieldsBlock/NewFieldsSidebar and moved FormSchema to shared module; tightened GeoJSON typing and b_area rounding/defaults.
Nav/search preservation & small nav fixes
fdm-app/app/components/blocks/soil/form-selection.tsx, fdm-app/app/components/custom/sidebar-page.tsx, plus a few route files
Added useLocation and preserved location.search on NavLink targets and active-link logic to keep query params across navigation.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Map as Map (Client)
    participant Panel as Fields Panel (Client)
    participant Server as App Server (Remix action)
    participant DB as Database / Services

    User->>Map: Open "New Field" (with query params)
    User->>Map: Click multiple parcels
    Map->>Map: Highlight selections (selectedFieldsData)
    User->>Panel: Click "Continue" / Submit
    Panel->>Server: POST selected_fields JSON (batch create)
    Server->>Server: Validate inputs, compute names, fetch defaults
    Server->>DB: Create field records
    Server->>DB: Create cultivation records
    Server->>DB: Request soil estimates (if NMI key) and store analyses
    DB-->>Server: Return created IDs
    Server-->>User: Redirect to fields configuration/list with created IDs & success message (preserve search)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • SvenVw
  • gerardhros

Poem

🐇
I hopped the map from ridge to ridge,
I nudged each plot into a tidy bridge.
Clicked many parcels, lined them neat,
Batch-made rows with tiny feet.
Spring fields hum — small paws, big feat.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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 main change: enabling multi-select for new field creation using the atlas selection pattern, which directly matches the primary objective.
Linked Issues check ✅ Passed The PR successfully implements the two-step bulk workflow from issue #355: multi-select map interaction with FieldsSourceSelected, batch field creation in the action handler, and a configuration view (farm.$b_id_farm.$calendar.field.new.fields route) for editing field details before finalizing.
Out of Scope Changes check ✅ Passed All changes are directly related to the multi-select field workflow implementation. Search parameter preservation changes in soil analysis routes and sidebar-page active state logic are supporting changes necessary for the new workflow.

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

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

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 Jan 9, 2026

Codecov Report

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

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

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

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

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

@coderabbitai coderabbitai Bot added branch:development Issue only affecting development, not the main branch (yet) enhancement New feature or request fdm-app labels Jan 9, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In
@fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx:
- Around line 43-44: Guard the direct access to cultivations[0] before using it
in the redirect: check that the cultivations array (variable cultivations) is
non-empty and only then build the URL with new URL(request.url) and call
redirect(`./${cultivations[0].b_lu}${url.search}`); if the array is empty return
a safe fallback (e.g., redirect to a parent route or return a 404/appropriate
Response) so the code no longer throws when cultivations has length 0.

In
@fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx:
- Line 296: The template string for the redirect is concatenating the whole URL
object (`${url}`) which serializes to a full absolute URL and corrupts the path;
update the template in the redirect call that contains
`/farm/${b_id_farm}/${calendar}/field/${b_id}/cultivation/${b_lu}${url}${url.search}`
to omit `${url}` and instead append only the search or pathname+search (e.g. use
`${b_lu}${url.search}` or `${b_lu}${url.pathname}${url.search}`) so the
resulting redirect is a correct relative path.

In @fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx:
- Line 180: Remove the leftover debug statement console.log(sidebarPageItems)
from the route module (the call logging sidebarPageItems in
farm.$b_id_farm.$calendar.field.$b_id.tsx) so no debug logs remain in
production; simply delete that console.log invocation where sidebarPageItems is
logged.

In @fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx:
- Line 401: The loader currently does not include the required continueTo
property but FieldsPanelSelection expects continueTo: string and NavLink relies
on it; update the loader to return a continueTo string (e.g., compute from the
request URL or a query param or fallback to a safe path like "/") so
loaderData.continueTo is defined, and ensure the returned shape includes
continueTo so the FieldsPanelSelection prop (continueTo={loaderData.continueTo})
and the NavLink use a valid string.
- Line 574: The URL construction for navigating to the field overview
double-encodes IDs: change the template segment that currently uses
fieldIds=${encodeURIComponent(fieldIds.map(encodeURIComponent).join(","))} to
only encode each id once so it matches the delete route and URLSearchParams
decoding; use the pattern that maps encodeURIComponent over fieldIds and joins
with commas (fieldIds=${fieldIds.map(encodeURIComponent).join(",")}) in the
string that builds the overview path in the route handler where the template
literal is created.
🧹 Nitpick comments (3)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx (2)

468-482: Defensive field count retrieval with appropriate fallback.

The try-catch block handles potential errors when fetching existing fields and defaults to 1 as a safe starting index. Using console.warn for debugging is acceptable here, though consider whether this should be logged differently in production.


487-489: Validate form data before processing.

The selectedFields is parsed directly from form data without validation. Consider validating that:

  1. The parsed data is a valid FeatureCollection
  2. The features array exists and is not empty
  3. Each feature has the required properties
💡 Add validation for selected fields
 const selectedFields = JSON.parse(
     String(formData.get("selected_fields")),
 )
+
+// Validate the parsed data
+if (!selectedFields || selectedFields.type !== "FeatureCollection") {
+    throw data("Invalid field selection data", {
+        status: 400,
+        statusText: "Invalid field selection data",
+    })
+}
+
+if (!Array.isArray(selectedFields.features) || selectedFields.features.length === 0) {
+    throw data("No fields selected", {
+        status: 400,
+        statusText: "At least one field must be selected",
+    })
+}
fdm-app/app/components/blocks/field/field-dropdown.tsx (1)

42-54: Optional refactor: Remove redundant index lookup.

The advanceField function checks currentIndex > -1 but then immediately recalculates the index:

const advanceField = (amount: number) => () => {
    if (currentIndex > -1) {
        const currentIndex = fieldOptions.findIndex(
            (option) => option.b_id === b_id,
        )

The inner currentIndex shadows the outer one (line 24-26), making the outer check redundant and potentially confusing.

♻️ Proposed refactor
 const advanceField = (amount: number) => () => {
-    if (currentIndex > -1) {
-        const currentIndex = fieldOptions.findIndex(
-            (option) => option.b_id === b_id,
-        )
-        if (currentIndex > -1) {
-            const newIndex = amount + currentIndex
-            if (newIndex >= 0 && newIndex < fieldOptions.length) {
-                setFieldId(fieldOptions[newIndex].b_id)
-            }
+    if (currentIndex > -1) {
+        const newIndex = amount + currentIndex
+        if (newIndex >= 0 && newIndex < fieldOptions.length) {
+            setFieldId(fieldOptions[newIndex].b_id)
         }
     }
 }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95388f9 and 44ec6d8.

📒 Files selected for processing (17)
  • .changeset/fiery-wasps-win.md
  • fdm-app/app/components/blocks/farm/farm-content.tsx
  • fdm-app/app/components/blocks/field/field-dropdown.tsx
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.delete.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.$a_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
🧰 Additional context used
🧠 Learnings (41)
📓 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: 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.
📚 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.$b_id.cultivation.$b_lu.harvest.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.$b_id.cultivation.$b_lu.harvest.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.$a_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.delete.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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.field.$b_id.cultivation.$b_lu.harvest.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.$a_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.delete.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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.field.$b_id.cultivation.$b_lu.harvest.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.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.field.$b_id.cultivation.$b_lu.harvest.new.tsx
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/components/blocks/field/field-dropdown.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx
  • fdm-app/app/components/blocks/farm/farm-content.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.delete.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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.field.$b_id.cultivation.$b_lu.harvest.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.$a_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.delete.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.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.field.$b_id.cultivation.$b_lu.harvest.new.tsx
📚 Learning: 2025-12-15T12:19:47.858Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:47.858Z
Learning: When reviewing code, prefer storing only non-sensitive UI/state data in sessionStorage. For map viewState (e.g., longitude/latitude), ensure it represents non-personal business data and that persistence across sessions is justified, documented, and respects user privacy. If persisting, use a clearly scoped, namespaced key, guard access with try/catch, and avoid syncing with servers or exposing data to third-party scripts. Apply this guideline to all TSX files that manage client-side UI state.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.new.tsx
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.$a_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.tsx
  • fdm-app/app/components/blocks/field/field-dropdown.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx
  • fdm-app/app/components/blocks/farm/farm-content.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.delete.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
📚 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/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/components/blocks/field/field-dropdown.tsx
  • fdm-app/app/components/blocks/farm/farm-content.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.delete.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
📚 Learning: 2024-12-16T10:56:07.561Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 16
File: fdm-app/app/routes/app.addfarm.$b_id_farm.cultivations.$b_lu_catalogue.fertilizers.tsx:1-1
Timestamp: 2024-12-16T10:56:07.561Z
Learning: The project uses `react-router` v7, and the `data` function is exported and used for error handling in loaders and actions.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
📚 Learning: 2025-04-29T11:28:44.181Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 132
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.access.tsx:54-68
Timestamp: 2025-04-29T11:28:44.181Z
Learning: In React Router v7, the `json()` function has been replaced with `data()` for creating responses in loaders and actions.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.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/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.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/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
📚 Learning: 2025-07-24T08:29:44.044Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 198
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx:146-148
Timestamp: 2025-07-24T08:29:44.044Z
Learning: In React Router v7, the defer() function has been removed. Instead, loaders can return promises directly in the response object, and components use Suspense and Await to handle them without needing defer().

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._index.tsx
📚 Learning: 2025-04-29T11:28:44.181Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 132
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.access.tsx:54-68
Timestamp: 2025-04-29T11:28:44.181Z
Learning: In React Router v7, the `json()` function has been deprecated and removed. Instead, either return plain JavaScript objects directly from loaders/actions, or use the `data()` function for responses with custom status codes and headers.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil._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/components/blocks/field/field-dropdown.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.delete.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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.field.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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.fertilizer._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:

  • .changeset/fiery-wasps-win.md
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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:

  • .changeset/fiery-wasps-win.md
📚 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:

  • .changeset/fiery-wasps-win.md
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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/farm/farm-content.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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/components/blocks/farm/farm-content.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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.$b_id.delete.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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.field.new.tsx
📚 Learning: 2025-01-31T15:06:35.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas-sources.tsx:21-66
Timestamp: 2025-01-31T15:06:35.764Z
Learning: In react-map-gl components, when querying rendered features via map.queryRenderedFeatures(), the effect's dependency array must include any props that affect the map's rendered state (like source data) to ensure features are queried against the current map state.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.new.tsx
📚 Learning: 2025-01-31T14:29:37.599Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas.d.tsx:8-8
Timestamp: 2025-01-31T14:29:37.599Z
Learning: In the Atlas component's MapFieldsProps interface, mapStyle is intentionally restricted to "mapbox://styles/mapbox/satellite-streets-v12" as it's currently the only supported style option.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2025-01-31T16:06:33.810Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:164-212
Timestamp: 2025-01-31T16:06:33.810Z
Learning: Map configuration in the application should be modularized using the `useMapConfig` hook and `MapControls` component to maintain consistency across all MapGL instances.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2025-01-31T15:41:43.741Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas-panels.tsx:28-28
Timestamp: 2025-01-31T15:41:43.741Z
Learning: When handling different map event types in react-map-gl v7.1.8, use MapLayerMouseEvent for mouse events (which have the point property) and ViewStateChangeEvent for view state changes. Use a type guard like 'point' in evt to safely access event-specific properties.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.new.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.new.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.new.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.field.new.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/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
🧬 Code graph analysis (4)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx (1)
fdm-core/src/db/schema.ts (1)
  • cultivations (373-385)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx (8)
fdm-app/app/components/blocks/atlas/atlas-styles.tsx (1)
  • getFieldsStyle (8-12)
fdm-app/app/components/blocks/atlas/atlas-functions.tsx (1)
  • generateFeatureClass (4-9)
fdm-app/app/components/blocks/atlas/atlas-sources.tsx (2)
  • FieldsSourceSelected (40-100)
  • FieldsSourceNotClickable (24-38)
fdm-app/app/components/blocks/atlas/atlas-panels.tsx (3)
  • FieldsPanelSelection (170-362)
  • FieldsPanelZoom (123-168)
  • FieldsPanelHover (23-121)
fdm-app/app/components/blocks/field/popup.tsx (1)
  • FieldDetailsInfoPopup (23-55)
fdm-core/src/field.ts (2)
  • getFields (217-315)
  • addField (46-127)
fdm-app/app/integrations/nmi.ts (2)
  • getNmiApiKey (6-13)
  • getSoilParameterEstimates (15-110)
fdm-core/src/cultivation.ts (2)
  • getDefaultDatesOfCultivation (197-302)
  • addCultivation (321-506)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx (1)
fdm-app/app/components/blocks/soil/form-selection.tsx (1)
  • SoilAnalysisFormSelection (11-85)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx (5)
fdm-core/src/db/schema.ts (1)
  • fields (92-108)
fdm-app/app/components/blocks/header/base.tsx (1)
  • Header (6-29)
fdm-app/app/components/blocks/farm/farm-title.tsx (1)
  • FarmTitle (15-36)
fdm-app/app/components/blocks/farm/farm-content.tsx (1)
  • FarmContent (14-33)
fdm-app/app/components/blocks/field/field-dropdown.tsx (1)
  • FieldDropdown (18-94)
🔇 Additional comments (29)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.new.tsx (1)

186-189: LGTM: Query parameter preservation correctly implemented.

The URL construction and query string preservation are correct. The pattern supports the multi-field workflow by maintaining context (e.g., fieldIds) across navigation.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.upload.tsx (1)

195-201: LGTM: Query parameter preservation correctly implemented.

The redirect path construction correctly preserves the original query string while navigating to the newly created soil analysis detail page.

fdm-app/app/components/blocks/soil/form-selection.tsx (2)

11-15: LGTM: Component signature correctly updated for query parameter propagation.

The optional search prop with default empty string ensures backward compatibility while enabling query parameter preservation for the multi-field workflow.


27-79: LGTM: Navigation links correctly preserve query parameters.

All navigation destinations consistently append the search prop, ensuring query parameters (e.g., fieldIds) are maintained throughout the soil analysis selection flow.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.$analysis_type.tsx (1)

198-201: LGTM: Query parameter preservation correctly implemented.

The redirect correctly maintains the query string when navigating back to the soil overview after adding a soil analysis.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.$a_id.tsx (1)

214-217: LGTM: Query parameter preservation correctly implemented.

The redirect correctly maintains the query string when returning to the soil overview after updating a soil analysis, consistent with the create flow.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new._index.tsx (1)

3-8: LGTM! Query parameter preservation implemented correctly.

The addition of useLocation and passing location.search to SoilAnalysisFormSelection properly preserves query parameters across navigation, which is essential for the multi-field workflow. The implementation follows React Router v7 patterns correctly.

Also applies to: 73-73, 92-92

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

17-17: LGTM! Query parameter preservation is consistent.

The implementation correctly preserves query parameters in navigation links by appending location.search to the routes. This ensures multi-field state is maintained across soil analysis operations.

Also applies to: 154-154, 177-177, 206-208


274-275: Good defensive redirect logic.

The conditional check for url.search.length before using the query string is appropriate and prevents potential issues with empty search strings.

fdm-app/app/components/blocks/farm/farm-content.tsx (1)

10-10: LGTM! Clean extension of component API.

The addition of the optional beforeTabs prop with a sensible default value allows for flexible composition of sidebar content without breaking existing usage. The placement before SidebarPage is logical for the intended use case.

Also applies to: 14-18, 24-24

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx (9)

11-16: LGTM! Imports align with new multi-field functionality.

The new imports for GeoJSON types, field selection components, and utility functions are appropriate for the enhanced multi-field creation workflow.

Also applies to: 37-37, 40-40, 46-46, 50-50


152-155: LGTM! Loader now provides saved fields for UI rendering.

The addition of fieldsSaved to the loader response enables the UI to distinguish between already-saved fields and newly selected ones, which is essential for the multi-field workflow.

Also applies to: 190-190


233-244: LGTM! Selected fields state properly initialized.

Using generateFeatureClass() to initialize an empty FeatureCollection for selected fields is clean and type-safe.


289-293: UI text updates reflect the new workflow.

The change from "Nieuw perceel" to "Kaart" and updated description accurately reflect the new multi-select interaction pattern.


365-378: Good integration of field selection source.

The FieldsSourceSelected component is properly configured with:

  • Correct layer IDs for available fields
  • State management via selectedFieldsData and setSelectedFieldsData
  • Exclusion of already-saved fields via excludedLayerId

492-571: Verify atomic behavior with Promise.all for multi-field creation.

The use of Promise.all ensures that if any field creation fails, all will fail together, which maintains data consistency. However, this means partial success is not possible—if one field fails, none are created.

Based on learnings, Promise.all is the correct approach for atomic behavior in multi-field operations. Please verify that the UX appropriately communicates all-or-nothing creation behavior to users (e.g., error messages indicate that no fields were created on failure).


528-528: Hardcoded acquiring method might need review.

The b_acquiring_method is hardcoded as "unknown". Verify whether this is the intended default for multi-field creation from the atlas, or if users should be able to specify the acquisition method.

Should the multi-field workflow allow users to specify how fields were acquired (e.g., "drawn", "imported", "selected"), or is "unknown" appropriate for atlas-based selection?


506-515: Robust year parsing with defensive fallback.

The year parsing logic properly handles edge cases:

  • Validates the parsed integer is within reasonable bounds (1970-2100)
  • Falls back to timeframe.start.getFullYear() for invalid values

This defensive approach prevents potential issues with malformed calendar parameters.


550-566: Conditional soil analysis creation is well-structured.

The optional soil analysis creation only executes when an NMI API key is present, which is appropriate. The parameters passed to addSoilAnalysis use the estimates data correctly.

.changeset/fiery-wasps-win.md (1)

1-5: LGTM! Changelog accurately describes the feature.

The changeset correctly documents the new multi-field creation capability with an appropriate minor version bump.

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

459-464: LGTM! Query string preservation implemented correctly.

The redirect now preserves the original query parameters (including fieldIds) by appending url.search to the redirect path. This aligns with the PR's multi-field selection workflow and ensures users maintain their field selection context after adding fertilizer.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.tsx (1)

283-285: LGTM! Query string preservation implemented correctly.

The redirect after deleting a cultivation now preserves the original query parameters by appending url.search. This maintains consistency with the multi-field workflow pattern used throughout this PR.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.delete.tsx (1)

183-193: Empty fieldIds is handled gracefully downstream.

The code correctly filters the deleted field from the fieldIds parameter. If deletion results in an empty list, the ?fieldIds= query string is generated correctly. Downstream routes parse this using .filter(Boolean) which removes empty strings, and routes requiring at least one field have explicit guard clauses to handle this case.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx (6)

81-83: LGTM!

Standard URL parameter extraction pattern for reading the fieldIds query parameter.


117-135: LGTM!

The filtering logic is well-implemented:

  • Uses Set for efficient O(1) lookups during filtering
  • Gracefully handles null/undefined b_area with nullish coalescing
  • Non-existent field IDs in the query parameter are safely ignored

208-208: LGTM!

The fieldOptionsLocation flag correctly distinguishes between multi-field mode (sidebar) and single-field mode (header) based on the presence of the fieldIds query parameter.


241-249: LGTM!

Correctly hides the header field selector when in multi-field mode to avoid duplicate navigation controls.


252-261: LGTM!

The conditional title and description logic correctly handles:

  • Dutch singular/plural forms ("Nieuwe Perceel" vs "Nieuwe Percelen")
  • Switching between wizard mode (new fields) and standard mode (existing field name)

262-274: LGTM!

The FieldDropdown integration in the beforeTabs slot provides intuitive field navigation in multi-field wizard mode. The conditional rendering ensures the dropdown only appears when the fieldIds parameter is present.

Comment thread fdm-app/app/components/blocks/field/field-dropdown.tsx Outdated
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx Outdated
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx Outdated
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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: 7

🤖 Fix all issues with AI agents
In @fdm-app/app/components/blocks/field/field-dropdown.tsx:
- Around line 43-55: The advanceField function recomputes currentIndex locally,
causing redundancy and shadowing the outer currentIndex; remove the inner
declaration inside advanceField (the const currentIndex = ...), instead use the
outer currentIndex captured from the component scope when computing newIndex and
bounds, and ensure the initial outer currentIndex variable (from the earlier
findIndex call) is defined before advanceField is used so the existing checks
(currentIndex > -1) remain valid.

In
@fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx:
- Around line 64-73: In ErrorBoundary (the exported function ErrorBoundary),
explicitly return null instead of falling through undefined and add a brief
comment like "// Silent error—parent route handles empty state" above the return
to document intent; also make the error match more explicit by checking a stable
property or adding a TODO to extract the "No cultivations found" string to a
constant (e.g., "// TODO: Consider extracting to a constant or matching a status
code/property instead of statusText") so future changes are less fragile.
- Line 15: The loader currently calls handleLoaderError(error) with a return
instead of throwing, so change the error path to throw handleLoaderError(error)
in the same block where getCultivations is awaited; keep the outer cultivations
declaration (let cultivations: Awaited<ReturnType<typeof getCultivations>> = [])
and the subsequent validation logic intact so the thrown error propagates to the
ErrorBoundary as in other routes.

In @fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx:
- Around line 117-119: The current logic sets wantedFieldIds from fieldIdsStr
such that an empty query like "?fieldIds=" yields [""] and later matches an
empty ID; update the assignment for wantedFieldIds (referencing fieldIdsStr and
wantedFieldIds) to treat empty/whitespace-only strings as null and to remove
empty entries after splitting: first check fieldIdsStr?.trim() length, then
split on "," and filter out falsy/empty strings (e.g., .filter(Boolean)) so
wantedFieldIds is either null or an array of valid non-empty IDs.

In @fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx:
- Around line 488-490: Wrap the JSON.parse of formData.get("selected_fields") in
a try/catch and validate the resulting selectedFields before using it: ensure
JSON.parse succeeds, confirm Array.isArray(selectedFields) and
selectedFields.length > 0, and verify each item has the expected identifier
property (the same property used later when building fieldIds). If validation
fails, return a proper error response (bad request) or redirect with an explicit
error message instead of proceeding with an empty fieldIds array; update the
code around selectedFields and any subsequent use (e.g., where you map
selectedFields to fieldIds) to rely on the validated value.
- Around line 499-506: The code currently only checks field.properties existence
but then blindly reads field.properties.b_id_source,
field.properties.b_lu_catalogue and field.geometry; add explicit validation for
b_id_source and b_lu_catalogue (and that field.geometry is present and has
expected shape) before extracting them, and throw clear errors (or supply safe
defaults) so getDefaultDatesOfCultivation and addField receive valid inputs;
update the block around the b_name/b_id_source/b_lu_catalogue/b_geometry
assignments to validate these symbols and fail fast with descriptive messages if
any required property is missing or invalid.
- Around line 574-579: The redirect is double-encoding fieldIds by wrapping
fieldIds.map(encodeURIComponent).join(",") with another encodeURIComponent;
update the redirectWithSuccess call to remove the outer encodeURIComponent so
the query uses fieldIds.map(encodeURIComponent).join(",") (keep the rest of the
URL and success message unchanged), ensuring the receiving code using
URLSearchParams.get() will decode correctly; look for the redirectWithSuccess
invocation that builds
`/farm/${b_id_farm}/${calendar}/field/${fieldIds[0]}/overview?fieldIds=...` and
replace the double-encoded expression with the single-encoding pattern.
🧹 Nitpick comments (4)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx (2)

152-155: Remove duplicate fieldsSaved variable.

Both fieldsSaved and featureCollection contain identical data (the same features array). This duplication is unnecessary and makes the code harder to maintain. Either reuse featureCollection where fieldsSaved is needed, or if they serve different purposes in the UI workflow, add a comment explaining why both are needed.

♻️ Proposed fix to eliminate duplication
     const featureCollection: FeatureCollection = {
         type: "FeatureCollection",
         features: features,
     }

-    const fieldsSaved: FeatureCollection = {
-        type: "FeatureCollection",
-        features: features,
-    }
-
     // Get the available cultivations

Then update the return statement:

     return {
         farmOptions: farmOptions,
         b_id_farm: b_id_farm,
         b_name_farm: farm.b_name_farm,
-        fieldsSaved: fieldsSaved,
+        fieldsSaved: featureCollection,
         calendar: calendar,
         featureCollection: featureCollection,

Or better yet, return only one field name and update consuming code accordingly.

Also applies to: 190-190


290-293: Consider clarifying the instruction text for field selection.

The guidance "Zoom in en selecteer je percelen" (select your fields) might be ambiguous. Based on the PR objectives, users are selecting fields from the atlas/available fields to create new field records, not selecting their existing saved fields.

Consider text like "Zoom in en selecteer percelen van de kaart" (select fields from the map) or "Selecteer beschikbare percelen om toe te voegen" (select available fields to add) to clarify that users are choosing from the atlas, not from their already-saved fields.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx (1)

258-258: Consider extracting the description logic for clarity.

The inline template expression with conditional logic makes this line harder to read and maintain.

♻️ Proposed refactor
+                const isMultiFieldMode = loaderData.fieldOptionsLocation === "sidebar" && loaderData.fieldOptions.length !== 1
+                const fieldText = isMultiFieldMode ? "deze percelen" : "dit perceel"
                 <FarmTitle
                     title={
                         loaderData.fieldOptionsLocation === "sidebar"
                             ? loaderData.fieldOptions.length === 1
                                 ? "Nieuwe Perceel"
                                 : "Nieuwe Percelen"
                             : loaderData.field?.b_name
                     }
-                    description={`Beheer hier de gegevens van ${loaderData.fieldOptionsLocation === "sidebar" && loaderData.fieldOptions.length !== 1 ? "deze percelen" : "dit perceel"}.`}
+                    description={`Beheer hier de gegevens van ${fieldText}.`}
                 />
fdm-app/app/components/blocks/field/field-dropdown.tsx (1)

13-16: Consider making className optional.

The className prop is currently required, which forces consumers to always provide a value even when they don't need custom styling.

♻️ Proposed refactor
 export interface FieldDropdownProps {
     fieldOptions: { b_name: string; b_id: string }[]
-    className: string
+    className?: string
 }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 44ec6d8 and 869a259.

📒 Files selected for processing (5)
  • fdm-app/app/components/blocks/field/field-dropdown.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation.$b_lu.harvest.$b_id_harvesting.tsx
🧰 Additional context used
🧠 Learnings (41)
📓 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: 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.
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.
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.
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 `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.
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.
📚 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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2025-02-24T10:49:54.523Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 84
File: fdm-app/app/root.tsx:89-145
Timestamp: 2025-02-24T10:49:54.523Z
Learning: In the ErrorBoundary component of fdm-app/app/root.tsx, all client errors (400, 401, 403, 404) are intentionally displayed with a 404 status code for security purposes.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/components/blocks/field/field-dropdown.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.field.$b_id.cultivation._index.tsx
📚 Learning: 2025-02-18T11:00:54.405Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 81
File: fdm-app/app/root.tsx:79-79
Timestamp: 2025-02-18T11:00:54.405Z
Learning: In React Router v7+, to ensure error pages are displayed correctly:
1. Export the ErrorBoundary component from the root route
2. Include the Scripts component in the Layout
3. Export the Layout through the default App component

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/components/blocks/field/field-dropdown.tsx
📚 Learning: 2025-02-18T11:00:54.405Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 81
File: fdm-app/app/root.tsx:79-79
Timestamp: 2025-02-18T11:00:54.405Z
Learning: In React Router v7+, error boundaries should:
1. Use the useRouteError hook to access the error
2. Be configured using the errorElement prop in route configuration
3. Include proper error type checking using isRouteErrorResponse

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2024-12-16T10:56:07.561Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 16
File: fdm-app/app/routes/app.addfarm.$b_id_farm.cultivations.$b_lu_catalogue.fertilizers.tsx:1-1
Timestamp: 2024-12-16T10:56:07.561Z
Learning: The project uses `react-router` v7, and the `data` function is exported and used for error handling in loaders and actions.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
📚 Learning: 2025-07-24T08:29:44.044Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 198
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx:146-148
Timestamp: 2025-07-24T08:29:44.044Z
Learning: In React Router v7, the defer() function has been removed. Instead, loaders can return promises directly in the response object, and components use Suspense and Await to handle them without needing defer().

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
📚 Learning: 2025-07-24T08:29:44.044Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 198
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx:146-148
Timestamp: 2025-07-24T08:29:44.044Z
Learning: React Router v7 supports Suspense patterns by returning promises directly from loaders. The pattern is: return { data: somePromise } from loader, then use <Suspense><Await resolve={loaderData.data}>{resolvedData => ...}</Await></Suspense> in components.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
📚 Learning: 2025-12-15T12:19:47.858Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:47.858Z
Learning: When reviewing code, prefer storing only non-sensitive UI/state data in sessionStorage. For map viewState (e.g., longitude/latitude), ensure it represents non-personal business data and that persistence across sessions is justified, documented, and respects user privacy. If persisting, use a clearly scoped, namespaced key, guard access with try/catch, and avoid syncing with servers or exposing data to third-party scripts. Apply this guideline to all TSX files that manage client-side UI state.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/components/blocks/field/field-dropdown.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.field.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.$calendar.field.$b_id.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.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
  • fdm-app/app/components/blocks/field/field-dropdown.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.$b_id.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.$b_id.tsx
📚 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/app/routes/farm.$b_id_farm.$calendar.field.$b_id.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.field.$b_id.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.field.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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/routes/farm.$b_id_farm.$calendar.field.$b_id.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/routes/farm.$b_id_farm.$calendar.field.$b_id.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/routes/farm.$b_id_farm.$calendar.field.$b_id.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.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.new.tsx
📚 Learning: 2025-01-31T15:06:35.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas-sources.tsx:21-66
Timestamp: 2025-01-31T15:06:35.764Z
Learning: In react-map-gl components, when querying rendered features via map.queryRenderedFeatures(), the effect's dependency array must include any props that affect the map's rendered state (like source data) to ensure features are queried against the current map state.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.new.tsx
📚 Learning: 2025-01-31T14:29:37.599Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas.d.tsx:8-8
Timestamp: 2025-01-31T14:29:37.599Z
Learning: In the Atlas component's MapFieldsProps interface, mapStyle is intentionally restricted to "mapbox://styles/mapbox/satellite-streets-v12" as it's currently the only supported style option.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2025-01-31T16:06:33.810Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:164-212
Timestamp: 2025-01-31T16:06:33.810Z
Learning: Map configuration in the application should be modularized using the `useMapConfig` hook and `MapControls` component to maintain consistency across all MapGL instances.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2025-01-31T15:41:43.741Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas-panels.tsx:28-28
Timestamp: 2025-01-31T15:41:43.741Z
Learning: When handling different map event types in react-map-gl v7.1.8, use MapLayerMouseEvent for mouse events (which have the point property) and ViewStateChangeEvent for view state changes. Use a type guard like 'point' in evt to safely access event-specific properties.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.new.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.new.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.new.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.field.new.tsx
🧬 Code graph analysis (3)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx (3)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx (1)
  • loader (61-211)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.delete.tsx (1)
  • loader (49-88)
fdm-core/src/cultivation.ts (1)
  • getCultivations (604-677)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx (3)
fdm-app/app/components/blocks/farm/farm-title.tsx (1)
  • FarmTitle (15-36)
fdm-app/app/components/blocks/farm/farm-content.tsx (1)
  • FarmContent (14-33)
fdm-app/app/components/blocks/field/field-dropdown.tsx (1)
  • FieldDropdown (18-95)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx (5)
fdm-app/app/components/blocks/atlas/atlas-functions.tsx (1)
  • generateFeatureClass (4-9)
fdm-app/app/components/blocks/atlas/atlas-sources.tsx (2)
  • FieldsSourceSelected (40-100)
  • FieldsSourceNotClickable (24-38)
fdm-core/src/field.ts (2)
  • getFields (217-315)
  • addField (46-127)
fdm-app/app/integrations/nmi.ts (2)
  • getNmiApiKey (6-13)
  • getSoilParameterEstimates (15-110)
fdm-core/src/cultivation.ts (2)
  • getDefaultDatesOfCultivation (197-302)
  • addCultivation (321-506)
🔇 Additional comments (13)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx (4)

493-572: LGTM! Correct use of Promise.all for atomic field creation.

The use of Promise.all ensures atomic behavior—if any field creation fails, all operations fail together. This is the correct pattern for bulk operations where partial success should be avoided.

Based on learnings, this approach maintains data consistency by ensuring all fields are created successfully or none at all.


469-483: LGTM! Appropriate error handling for field naming.

The try-catch around getFields with a fallback to index 1 is good defensive coding. Since field naming is not critical to the operation's success, defaulting to "Perceel 1" when the existing field count cannot be retrieved is acceptable. The console.warn appropriately logs the issue without blocking the workflow.


366-393: LGTM! Proper layer configuration for multi-select workflow.

The map layer setup correctly implements the multi-select pattern:

  • FieldsSourceSelected enables multi-selection from available fields while excluding already-saved fields via excludedLayerId
  • FieldsSourceNotClickable displays saved fields as reference without interaction
  • Two FieldsPanelHover instances provide appropriate hover feedback: one for available fields (excluding selected and saved), and one specifically for selected fields

This configuration supports the intended user experience for bulk field creation.

Also applies to: 396-418


507-516: LGTM! Robust year parsing with proper validation.

The year parsing logic correctly handles invalid calendar parameters by falling back to timeframe.start.getFullYear(). The validation ensures the year is a valid integer within a reasonable range (1970-2100), preventing issues with cultivation date calculations.

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

2-12: LGTM! Imports support the new error handling pattern.

The added imports (data, isRouteErrorResponse, redirect, and Route type) are appropriate for implementing the ErrorBoundary and query-parameter-preserving redirect logic introduced in this PR.


59-62: Excellent! Query parameter preservation supports the multi-field workflow.

The redirect correctly preserves query parameters by appending url.search to the target path. This is essential for maintaining context in the multi-field creation wizard flow described in the PR objectives (e.g., fieldIds parameter).

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx (4)

17-17: LGTM!

The new imports for FieldDropdown and modifySearchParams are appropriate for the multi-field selection feature.

Also applies to: 27-27


172-178: LGTM!

The mutation of sidebarPageItems to append fieldIds to each URL is acceptable here since the array is locally scoped and freshly created.


206-206: LGTM!

The fieldOptionsLocation discriminator cleanly separates the two layout modes (sidebar vs header).


260-270: LGTM!

The conditional rendering of FieldDropdown in the beforeTabs slot cleanly integrates the multi-field navigation into the sidebar layout.

fdm-app/app/components/blocks/field/field-dropdown.tsx (3)

36-39: LGTM!

Preserving location.search during navigation correctly maintains the fieldIds query parameter and other URL state across field switches.


61-94: LGTM!

The UI implementation correctly handles boundary conditions with disabled states and provides clear navigation affordances with chevron buttons and a dropdown selector.


28-42: The regex pattern is correctly designed and intentional for this use case. It extracts the action type (overview, fertilizer, cultivation, soil, atlas) from field-specific routes to preserve the user's current view when switching between fields. All field-action routes in the codebase follow this naming convention consistently, so the pattern is neither fragile nor unnecessarily tightly coupled—it's the correct approach for preserving navigation context during field switching.

Likely an incorrect or invalid review comment.

Comment thread fdm-app/app/components/blocks/field/field-dropdown.tsx Outdated
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx Outdated
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx Outdated
Comment on lines +117 to +119
const wantedFieldIds = fieldIdsStr?.length
? fieldIdsStr.split(",")
: null
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 | 🟡 Minor

Handle empty fieldIds parameter edge case.

When fieldIds is an empty string (e.g., ?fieldIds=), the current check fieldIdsStr?.length evaluates to true, and split(",") produces [""], which will cause the filter to look for a field with an empty ID.

🛡️ Proposed fix
-        const wantedFieldIds = fieldIdsStr?.length
-            ? fieldIdsStr.split(",")
-            : null
+        const wantedFieldIds = fieldIdsStr?.trim()
+            ? fieldIdsStr.split(",").filter(id => id.trim())
+            : null
📝 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
const wantedFieldIds = fieldIdsStr?.length
? fieldIdsStr.split(",")
: null
const wantedFieldIds = fieldIdsStr?.trim()
? fieldIdsStr.split(",").filter(id => id.trim())
: null
🤖 Prompt for AI Agents
In @fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx around lines
117 - 119, The current logic sets wantedFieldIds from fieldIdsStr such that an
empty query like "?fieldIds=" yields [""] and later matches an empty ID; update
the assignment for wantedFieldIds (referencing fieldIdsStr and wantedFieldIds)
to treat empty/whitespace-only strings as null and to remove empty entries after
splitting: first check fieldIdsStr?.trim() length, then split on "," and filter
out falsy/empty strings (e.g., .filter(Boolean)) so wantedFieldIds is either
null or an array of valid non-empty IDs.

Comment on lines +499 to +506
if (!field.properties) {
throw new Error("missing: field.properties")
}
const b_name = `Perceel ${firstFieldIndex + index}`
const b_id_source = field.properties.b_id_source
const b_lu_catalogue = field.properties.b_lu_catalogue
const b_geometry = field.geometry

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

Validate required field properties before extraction.

The code checks for field.properties existence but doesn't validate that required properties like b_lu_catalogue, b_id_source, and valid geometry exist before using them. Missing properties will cause downstream errors in getDefaultDatesOfCultivation or addField.

🛡️ Add validation for required properties
                 if (!field.properties) {
                     throw new Error("missing: field.properties")
                 }
+                if (!field.properties.b_lu_catalogue) {
+                    throw new Error("missing: field.properties.b_lu_catalogue")
+                }
+                if (!field.properties.b_id_source) {
+                    throw new Error("missing: field.properties.b_id_source")
+                }
+                if (!field.geometry || field.geometry.type !== "Polygon") {
+                    throw new Error("invalid: field.geometry must be a Polygon")
+                }
+
                 const b_name = `Perceel ${firstFieldIndex + index}`
📝 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
if (!field.properties) {
throw new Error("missing: field.properties")
}
const b_name = `Perceel ${firstFieldIndex + index}`
const b_id_source = field.properties.b_id_source
const b_lu_catalogue = field.properties.b_lu_catalogue
const b_geometry = field.geometry
if (!field.properties) {
throw new Error("missing: field.properties")
}
if (!field.properties.b_lu_catalogue) {
throw new Error("missing: field.properties.b_lu_catalogue")
}
if (!field.properties.b_id_source) {
throw new Error("missing: field.properties.b_id_source")
}
if (!field.geometry || field.geometry.type !== "Polygon") {
throw new Error("invalid: field.geometry must be a Polygon")
}
const b_name = `Perceel ${firstFieldIndex + index}`
const b_id_source = field.properties.b_id_source
const b_lu_catalogue = field.properties.b_lu_catalogue
const b_geometry = field.geometry
🤖 Prompt for AI Agents
In @fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx around lines 499
- 506, The code currently only checks field.properties existence but then
blindly reads field.properties.b_id_source, field.properties.b_lu_catalogue and
field.geometry; add explicit validation for b_id_source and b_lu_catalogue (and
that field.geometry is present and has expected shape) before extracting them,
and throw clear errors (or supply safe defaults) so getDefaultDatesOfCultivation
and addField receive valid inputs; update the block around the
b_name/b_id_source/b_lu_catalogue/b_geometry assignments to validate these
symbols and fail fast with descriptive messages if any required property is
missing or invalid.

Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx:
- Around line 488-490: The code calls JSON.parse on
String(formData.get("selected_fields")) which can be "null" if the field is
missing; change the logic around formData.get("selected_fields") to null-check
before parsing (e.g., retrieve const raw = formData.get("selected_fields"); if
raw is null or raw === "" use a safe default like "[]" or an empty array) then
call JSON.parse only on the non-null string; update the selectedFields
assignment (the variable named selectedFields) to use this guarded value so
JSON.parse cannot throw on a null input.
🧹 Nitpick comments (2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx (2)

147-155: Remove redundant fieldsSaved FeatureCollection.

Both featureCollection (lines 147-150) and fieldsSaved (lines 152-155) contain identical data and structure. The component then assigns loaderData.featureCollection to fieldsSaved (line 209), making the loader-level duplication unnecessary.

♻️ Proposed fix to eliminate duplication
-    const featureCollection: FeatureCollection = {
-        type: "FeatureCollection",
-        features: features,
-    }
-
     const fieldsSaved: FeatureCollection = {
         type: "FeatureCollection",
         features: features,
     }

Then update the return statement:

     return {
         farmOptions: farmOptions,
         b_id_farm: b_id_farm,
         b_name_farm: farm.b_name_farm,
         fieldsSaved: fieldsSaved,
         calendar: calendar,
-        featureCollection: featureCollection,
         fieldNameDefault: fieldNameDefault,
         cultivationOptions: cultivationOptions,
         mapStyle: mapStyle,
         continueTo: `/farm/${b_id_farm}/${calendar}/field`,
     }

And update line 209 in the component:

-    const fieldsSaved = loaderData.featureCollection
+    const fieldsSaved = loaderData.fieldsSaved

69-69: Update meta title to reflect multi-select workflow.

The page meta title (line 69) still reads "Nieuw perceel" (New field - singular), but the page heading has been changed to "Kaart" (Map) with instructions for multi-select. This creates an inconsistency between the browser tab title and the actual page functionality.

♻️ Proposed title update
-    { title: `Nieuw perceel | ${clientConfig.name}` },
+    { title: `Percelen selecteren | ${clientConfig.name}` },
     {
         name: "description",
-        content: "Voeg een nieuw perceel toe",
+        content: "Selecteer percelen om toe te voegen",
     },

Also applies to: 290-290

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 869a259 and a019e8a.

📒 Files selected for processing (2)
  • fdm-app/app/components/blocks/field/field-dropdown.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • fdm-app/app/components/blocks/field/field-dropdown.tsx
🧰 Additional context used
🧠 Learnings (28)
📓 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 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.
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 `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.
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.$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.
📚 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.field.new.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.field.new.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.field.new.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.field.new.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.field.new.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.new.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.field.new.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.field.new.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.field.new.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.new.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.field.new.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.new.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.new.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.new.tsx
📚 Learning: 2025-08-13T11:05:40.105Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 237
File: fdm-data/src/cultivations/catalogues/brp.ts:65-68
Timestamp: 2025-08-13T11:05:40.105Z
Learning: In fdm-app Select components, using `undefined` instead of empty string (`""`) as fallback value prevents empty strings from being submitted as form values. This approach fixes the issue at the UI source rather than requiring backend validation.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.new.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.new.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.new.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.field.new.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.field.new.tsx
📚 Learning: 2025-01-31T15:06:35.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas-sources.tsx:21-66
Timestamp: 2025-01-31T15:06:35.764Z
Learning: In react-map-gl components, when querying rendered features via map.queryRenderedFeatures(), the effect's dependency array must include any props that affect the map's rendered state (like source data) to ensure features are queried against the current map state.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.new.tsx
📚 Learning: 2025-01-31T14:29:37.599Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas.d.tsx:8-8
Timestamp: 2025-01-31T14:29:37.599Z
Learning: In the Atlas component's MapFieldsProps interface, mapStyle is intentionally restricted to "mapbox://styles/mapbox/satellite-streets-v12" as it's currently the only supported style option.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2025-01-31T16:06:33.810Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/routes/farm.create.$b_id_farm.atlas.tsx:164-212
Timestamp: 2025-01-31T16:06:33.810Z
Learning: Map configuration in the application should be modularized using the `useMapConfig` hook and `MapControls` component to maintain consistency across all MapGL instances.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2025-01-31T15:41:43.741Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas-panels.tsx:28-28
Timestamp: 2025-01-31T15:41:43.741Z
Learning: When handling different map event types in react-map-gl v7.1.8, use MapLayerMouseEvent for mouse events (which have the point property) and ViewStateChangeEvent for view state changes. Use a type guard like 'point' in evt to safely access event-specific properties.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.new.tsx
📚 Learning: 2025-12-15T12:19:47.858Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:47.858Z
Learning: When reviewing code, prefer storing only non-sensitive UI/state data in sessionStorage. For map viewState (e.g., longitude/latitude), ensure it represents non-personal business data and that persistence across sessions is justified, documented, and respects user privacy. If persisting, use a clearly scoped, namespaced key, guard access with try/catch, and avoid syncing with servers or exposing data to third-party scripts. Apply this guideline to all TSX files that manage client-side UI state.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
🔇 Additional comments (6)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx (6)

234-237: Verify the click handler targets the correct field source.

The handleClickSavedField function and the onClick handler (line 325) check for fieldsSavedId source, which represents fields already added to the farm. However, the PR objectives describe a workflow where users select new fields from the atlas to add to their farm.

Based on the popup hint (line 430: "Dit perceel is al opgeslagen"), this appears intentional—informing users when they click an already-saved field. However, the interaction with fieldsSelectedId (lines 366-379) suggests multi-select of available fields is the primary workflow.

Please confirm:

  1. Should clicking already-saved fields show the info popup (current behavior)?
  2. Should the primary selection workflow use fieldsAvailableId instead?
  3. Is the FieldsSourceSelected component (lines 366-379) handling the actual field selection for adding new fields?

If FieldsSourceSelected handles the selection and handleClickSavedField is purely informational for already-saved fields, consider renaming to handleClickInfoForSavedField for clarity.

Also applies to: 325-332


504-504: Verify b_lu_catalogue is present in selected field properties.

The action expects field.properties.b_lu_catalogue to be available for each selected field. However, atlas field features from WFS sources typically contain only geometry and basic identifiers (like b_id_source), not cultivation data.

Please confirm:

  1. Does the FieldsSourceSelected component or selection workflow attach b_lu_catalogue to field properties before form submission?
  2. Is there a cultivation selection step in the UI that populates this property?

If b_lu_catalogue might be missing, add validation:

const b_lu_catalogue = field.properties.b_lu_catalogue
if (!b_lu_catalogue) {
    throw new Error(`Missing b_lu_catalogue for field ${b_id_source}`)
}

Alternatively, if users should select cultivation separately, consider moving cultivation selection to the configuration flow mentioned in the redirect (line 575).


507-516: LGTM! Robust year validation.

The year parsing logic includes thorough validation (integer check, range validation 1970-2100) with a sensible fallback to timeframe.start.getFullYear(). This prevents invalid date calculations in cultivation defaults.


469-483: LGTM! Defensive field index calculation.

The try-catch pattern for calculating firstFieldIndex ensures field names remain sequential even if getFields fails. The fallback to 1 is appropriate, and console.warn provides debugging context without failing the entire operation.


574-579: LGTM! Proper redirect to configuration flow with pluralized messaging.

The redirect to /field/${fieldIds[0]}/overview?fieldIds=... aligns with the PR objectives for a configuration flow after field creation. The success message correctly pluralizes ("perceel is" vs "percelen zijn"), and field IDs are properly encoded using encodeURIComponent.


493-572: Verify atomic field creation across multi-step operations.

The action uses Promise.all to create multiple fields, where each field creation involves three separate operations: addField, addCultivation, and optionally addSoilAnalysis. If any operation fails, Promise.all will reject, but there's a risk of partial data if, for example, a field is added successfully but its cultivation addition fails.

Based on learnings, Promise.all is preferred for atomicity. Please confirm:

  1. Does addField in fdm-core use database transactions internally to ensure the field and all required relations are created atomically?
  2. If addCultivation fails after addField succeeds, does the transaction rollback the field creation?

If fdm-core functions don't provide transaction boundaries, consider wrapping the entire multi-field creation in a single transaction to ensure all-or-nothing behavior.

Comment on lines +488 to 490
const selectedFields = JSON.parse(
String(formData.get("selected_fields")),
)
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

Add null check before parsing selected_fields.

formData.get("selected_fields") can return null, and String(null) produces the string "null", which will cause JSON.parse to throw an error or parse incorrectly.

🐛 Proposed fix with null safety
-    const selectedFields = JSON.parse(
-        String(formData.get("selected_fields")),
-    )
+    const selectedFieldsRaw = formData.get("selected_fields")
+    if (!selectedFieldsRaw) {
+        throw data("No fields selected", {
+            status: 400,
+            statusText: "No fields selected",
+        })
+    }
+    const selectedFields = JSON.parse(String(selectedFieldsRaw))
📝 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
const selectedFields = JSON.parse(
String(formData.get("selected_fields")),
)
const selectedFieldsRaw = formData.get("selected_fields")
if (!selectedFieldsRaw) {
throw data("No fields selected", {
status: 400,
statusText: "No fields selected",
})
}
const selectedFields = JSON.parse(String(selectedFieldsRaw))
🤖 Prompt for AI Agents
In @fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx around lines 488
- 490, The code calls JSON.parse on String(formData.get("selected_fields"))
which can be "null" if the field is missing; change the logic around
formData.get("selected_fields") to null-check before parsing (e.g., retrieve
const raw = formData.get("selected_fields"); if raw is null or raw === "" use a
safe default like "[]" or an empty array) then call JSON.parse only on the
non-null string; update the selectedFields assignment (the variable named
selectedFields) to use this guarded value so JSON.parse cannot throw on a null
input.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx (1)

354-354: Remove as any and properly type the layer configuration.

The as any on lines 354 and 369 bypasses TypeScript's type checking when adding the layout property to the LayerProps objects. Instead of casting to any, consider:

  1. Creating a proper type for the layer props by explicitly typing the combined object
  2. Verifying whether LayerProps from react-map-gl v8.1.0 should include the layout property, and if not, whether a different approach to managing visibility would be more appropriate

For example:

const layerConfig: LayerProps = {
  ...fieldsAvailableStyle,
  layout: { visibility: showFields ? "visible" : "none" }
}
<Layer {...layerConfig} />

Or investigate if the visibility toggle could be managed through a different mechanism that aligns with the react-map-gl API's type expectations.

🤖 Fix all issues with AI agents
In @fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx:
- Around line 499-502: The validation currently checks the wrong variable:
replace the condition that tests b_id_source with a check for b_lu_catalogue
(the variable declared on the previous line) so the block reads something like
if (!b_lu_catalogue) throw new Error("missing:
field.properties.b_lu_catalogue"); locate this in the file around the if
statement that references b_id_source and b_lu_catalogue and update the
condition to validate the correct variable.
- Around line 485-576: The field creation sequence calls addField,
addCultivation, and addSoilAnalysis separately which can leave partial state if
a later call fails; wrap the per-field operations inside a single
fdm.transaction(...) so the addField, addCultivation and conditional
addSoilAnalysis calls execute atomically and rollback on error. Locate the async
mapper (the function passed to selectedFields.features.map) and replace the
sequential calls with a single transactional callback using
fdm.transaction(async tx => { await addField(tx or tx-bound fdm, ...); await
addCultivation(...); if (nmiApiKey) await addSoilAnalysis(...); return b_id; })
ensuring you pass the transactional executor/context to
addField/addCultivation/addSoilAnalysis so they run in the same transaction.
Ensure error propagation so Promise.all rejects and the transaction rolls back
on failure.
🧹 Nitpick comments (6)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx (6)

133-173: Consider extracting the field feature generation logic.

The feature generation logic including async cultivation fetching is complex and could benefit from extraction into a separate helper function for better testability and reusability.

♻️ Suggested refactor

Consider extracting to a helper function:

async function generateFieldFeature(
  field: Field,
  fdm: Fdm,
  principal_id: string,
  timeframe: Timeframe
): Promise<Feature> {
  // Get field cultivation if available or get the first cultivation created by the farmer
  let cultivation = field.b_lu_name
  if (!cultivation) {
    try {
      const cultivations = await getCultivations(
        fdm,
        principal_id,
        field.b_id,
        timeframe,
      )
      cultivation = cultivations.length > 0 
        ? cultivations[0].b_lu_name 
        : "geen gewassen"
    } catch (e) {
      console.error("Failed to fetch cultivations for field:", field.b_id, e)
      cultivation = "gewassen onbekend"
    }
  }

  return {
    type: "Feature" as const,
    properties: {
      b_id: field.b_id,
      b_name: field.b_name,
      b_area: Math.round(field.b_area * 10) / 10,
      b_lu_name: cultivation,
      b_id_source: field.b_id_source,
    },
    geometry: simplify(field.b_geometry as Geometry, {
      tolerance: 0.00001,
      highQuality: true,
    }),
  }
}

Then use it in the loader:

-const features = await Promise.all(
-  fields.map(async (field) => {
-    // ... entire logic
-  })
-)
+const features = await Promise.all(
+  fields.map(field => 
+    generateFieldFeature(field, fdm, session.principal_id, timeframe)
+  )
+)

152-152: Use console.error for exception logging.

The catch block uses console.warn for exceptions, but errors during cultivation fetching should be logged as errors for better observability.

-console.warn(e)
+console.error("Failed to fetch cultivations for field:", field.b_id, e)

263-276: Remove commented-out code.

The old FarmHeader component code is commented out and should be removed to maintain code cleanliness.

-{/* <FarmHeader
-    farmOptions={loaderData.farmOptions}
-    b_id_farm={loaderData.b_id_farm}
-    fieldOptions={undefined}
-    b_id={undefined}
-    layerOptions={[]}
-    layerSelected={undefined}
-    fertilizerOptions={undefined}
-    p_id={undefined}
-    action={{
-        to: `/farm/${loaderData.b_id_farm}/${calendar}/field/`,
-        label: "Terug naar percelen",
-    }}
-/> */}

473-473: Use console.error for exception logging.

The catch block uses console.warn but failures to fetch existing fields during index calculation should be logged as errors.

-console.warn(e)
+console.error("Failed to fetch existing fields for index calculation:", e)

442-587: Consider refactoring the action function for better maintainability.

The action function is 145 lines long and handles multiple concerns: form parsing, field index calculation, batch field creation, cultivation creation, and soil analysis. This could be split into smaller, testable functions.

♻️ Suggested refactor structure

Consider extracting helper functions:

async function calculateNextFieldIndex(
  fdm: Fdm,
  principal_id: string,
  b_id_farm: string,
  timeframe: Timeframe
): Promise<number> {
  try {
    const fields = await getFields(fdm, principal_id, b_id_farm, timeframe)
    return fields.length + 1
  } catch (e) {
    console.error("Failed to fetch existing fields for index calculation:", e)
    return 1
  }
}

async function createFieldWithCultivation(
  fdm: Fdm,
  session: { principal_id: string },
  b_id_farm: string,
  field: Feature<Polygon, GeoJsonProperties>,
  fieldIndex: number,
  currentYear: number,
  nmiApiKey: string | undefined
): Promise<string> {
  // Field creation logic here
  // Returns b_id
}

// Then action becomes:
export async function action({ request, params }: ActionFunctionArgs) {
  try {
    const formData = await request.formData()
    const b_id_farm = params.b_id_farm
    if (!b_id_farm) {
      throw data("Farm ID is required", { status: 400, statusText: "Farm ID is required" })
    }

    const session = await getSession(request)
    const calendar = getCalendar(params)
    const timeframe = getTimeframe(params)
    const firstFieldIndex = await calculateNextFieldIndex(fdm, session.principal_id, b_id_farm, timeframe)
    const nmiApiKey = getNmiApiKey()
    const selectedFields = JSON.parse(String(formData.get("selected_fields")))
    
    const parsedYear = Number.parseInt(String(calendar ?? ""), 10)
    const currentYear = /* year calculation */

    const fieldIds: string[] = await Promise.all(
      selectedFields.features.map((field, index) =>
        createFieldWithCultivation(
          fdm,
          session,
          b_id_farm,
          field,
          firstFieldIndex + index,
          currentYear,
          nmiApiKey
        )
      )
    )

    return redirectWithSuccess(/* ... */)
  } catch (error) {
    throw handleActionError(error)
  }
}

580-580: Unnecessary URI encoding for field IDs.

Field IDs are UUIDs which contain only alphanumeric characters and hyphens, so encodeURIComponent is unnecessary (though harmless).

-`/farm/${b_id_farm}/${calendar}/field/${fieldIds[0]}/overview?fieldIds=${fieldIds.map(encodeURIComponent).join(",")}`
+`/farm/${b_id_farm}/${calendar}/field/${fieldIds[0]}/overview?fieldIds=${fieldIds.join(",")}`
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a019e8a and 50c8074.

📒 Files selected for processing (3)
  • fdm-app/app/components/blocks/field/field-dropdown.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • fdm-app/app/components/blocks/field/field-dropdown.tsx
🧰 Additional context used
🧠 Learnings (43)
📓 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: 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.
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.
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.
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-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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2025-02-24T10:49:54.523Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 84
File: fdm-app/app/root.tsx:89-145
Timestamp: 2025-02-24T10:49:54.523Z
Learning: In the ErrorBoundary component of fdm-app/app/root.tsx, all client errors (400, 401, 403, 404) are intentionally displayed with a 404 status code for security purposes.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.$b_id.cultivation._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.field.$b_id.cultivation._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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2025-01-30T08:51:44.741Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 64
File: fdm-core/src/farm.ts:25-48
Timestamp: 2025-01-30T08:51:44.741Z
Learning: The `handleError` function always throws an error, but for better code clarity and to satisfy TypeScript's control flow analysis, it's recommended to add a return statement after it, even though it will never be reached.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2025-02-18T11:00:54.405Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 81
File: fdm-app/app/root.tsx:79-79
Timestamp: 2025-02-18T11:00:54.405Z
Learning: In React Router v7+, error boundaries should:
1. Use the useRouteError hook to access the error
2. Be configured using the errorElement prop in route configuration
3. Include proper error type checking using isRouteErrorResponse

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
📚 Learning: 2025-02-18T11:00:54.405Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 81
File: fdm-app/app/root.tsx:79-79
Timestamp: 2025-02-18T11:00:54.405Z
Learning: In React Router v7+, to ensure error pages are displayed correctly:
1. Export the ErrorBoundary component from the root route
2. Include the Scripts component in the Layout
3. Export the Layout through the default App component

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
📚 Learning: 2024-12-16T10:56:07.561Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 16
File: fdm-app/app/routes/app.addfarm.$b_id_farm.cultivations.$b_lu_catalogue.fertilizers.tsx:1-1
Timestamp: 2024-12-16T10:56:07.561Z
Learning: The project uses `react-router` v7, and the `data` function is exported and used for error handling in loaders and actions.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2025-07-24T08:29:44.044Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 198
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx:146-148
Timestamp: 2025-07-24T08:29:44.044Z
Learning: In React Router v7, the defer() function has been removed. Instead, loaders can return promises directly in the response object, and components use Suspense and Await to handle them without needing defer().

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
📚 Learning: 2025-07-24T08:29:44.044Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 198
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx:146-148
Timestamp: 2025-07-24T08:29:44.044Z
Learning: React Router v7 supports Suspense patterns by returning promises directly from loaders. The pattern is: return { data: somePromise } from loader, then use <Suspense><Await resolve={loaderData.data}>{resolvedData => ...}</Await></Suspense> in components.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
📚 Learning: 2025-12-15T12:19:47.858Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:47.858Z
Learning: When reviewing code, prefer storing only non-sensitive UI/state data in sessionStorage. For map viewState (e.g., longitude/latitude), ensure it represents non-personal business data and that persistence across sessions is justified, documented, and respects user privacy. If persisting, use a clearly scoped, namespaced key, guard access with try/catch, and avoid syncing with servers or exposing data to third-party scripts. Apply this guideline to all TSX files that manage client-side UI state.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.new.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.field.new.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.new.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.new.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.new.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.new.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.new.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.new.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.new.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.field.new.tsx
📚 Learning: 2025-08-13T11:05:40.105Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 237
File: fdm-data/src/cultivations/catalogues/brp.ts:65-68
Timestamp: 2025-08-13T11:05:40.105Z
Learning: In fdm-app Select components, using `undefined` instead of empty string (`""`) as fallback value prevents empty strings from being submitted as form values. This approach fixes the issue at the UI source rather than requiring backend validation.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.new.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/routes/farm.$b_id_farm.$calendar.field.new.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/routes/farm.$b_id_farm.$calendar.field.new.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/routes/farm.$b_id_farm.$calendar.field.new.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/routes/farm.$b_id_farm.$calendar.field.new.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.field.new.tsx
📚 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/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.new.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.field.new.tsx
📚 Learning: 2025-01-31T15:06:35.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas-sources.tsx:21-66
Timestamp: 2025-01-31T15:06:35.764Z
Learning: In react-map-gl components, when querying rendered features via map.queryRenderedFeatures(), the effect's dependency array must include any props that affect the map's rendered state (like source data) to ensure features are queried against the current map state.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.new.tsx
📚 Learning: 2025-01-31T15:41:43.741Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas-panels.tsx:28-28
Timestamp: 2025-01-31T15:41:43.741Z
Learning: When handling different map event types in react-map-gl v7.1.8, use MapLayerMouseEvent for mouse events (which have the point property) and ViewStateChangeEvent for view state changes. Use a type guard like 'point' in evt to safely access event-specific properties.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
🧬 Code graph analysis (2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx (2)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.cultivations.tsx (1)
  • loader (46-97)
fdm-core/src/cultivation.ts (1)
  • getCultivations (604-677)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx (9)
fdm-core/src/index.ts (5)
  • getFields (107-107)
  • getCultivations (49-49)
  • getDefaultDatesOfCultivation (51-51)
  • addField (105-105)
  • addCultivation (45-45)
fdm-core/src/field.ts (2)
  • getFields (217-315)
  • addField (46-127)
fdm-core/src/cultivation.ts (3)
  • getCultivations (604-677)
  • getDefaultDatesOfCultivation (197-302)
  • addCultivation (321-506)
fdm-app/app/components/blocks/atlas/atlas-viewstate.tsx (1)
  • getViewState (19-42)
fdm-app/app/components/blocks/atlas/atlas-functions.tsx (1)
  • generateFeatureClass (4-9)
fdm-app/app/components/blocks/atlas/atlas-sources.tsx (2)
  • FieldsSourceSelected (40-100)
  • FieldsSourceNotClickable (24-38)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.overview.tsx (1)
  • action (262-296)
fdm-app/app/routes/welcome.tsx (1)
  • action (264-294)
fdm-app/app/integrations/nmi.ts (2)
  • getNmiApiKey (6-13)
  • getSoilParameterEstimates (15-110)
🔇 Additional comments (5)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.cultivation._index.tsx (5)

2-7: LGTM! Import additions align with React Router v7 patterns.

The additions of data, isRouteErrorResponse, and redirect from react-router are appropriate for the new loader error handling and redirect logic. The Route type import enables proper typing for the ErrorBoundary props.

Also applies to: 12-12


14-14: Good use of exported constant for status text consistency.

Exporting noCultivationsFoundStatusText allows both the loader and ErrorBoundary to reference the same value, and enables parent/sibling routes to import and compare against it if needed.


16-16: Intentional scoping of cultivations outside try-catch is appropriate.

Declaring cultivations outside the try-catch allows the empty-check logic (lines 53-58) to execute after error handling, cleanly separating unexpected fetch errors from the expected "no cultivations" business case.

Also applies to: 43-51


53-62: Query parameter preservation on redirect is correctly implemented.

The redirect logic properly appends url.search to preserve query parameters, which aligns with the broader PR pattern of maintaining fieldIds and other search params across navigation. The empty check before accessing cultivations[0] ensures safety.


65-76: ErrorBoundary pattern is correctly implemented for handling no-cultivations case.

The parent route (farm.$b_id_farm.$calendar.field.$b_id.cultivation.tsx) successfully handles the empty state. When the index route throws the "no cultivations" error, its ErrorBoundary returns null, allowing the parent route to continue rendering the CultivationListCard with an empty cultivations array (retrieved from the parent's loader). This is a valid React Router v7 pattern where the parent layout gracefully displays the empty state rather than propagating the error.

Comment on lines +485 to 576
const fieldIds: string[] = await Promise.all(
selectedFields.features.map(
async (
field: Feature<Polygon, GeoJsonProperties>,
index: number,
) => {
if (!field.properties) {
throw new Error("missing: field.properties")
}
const b_name = `Perceel ${firstFieldIndex + index}`
const b_id_source = field.properties.b_id_source
if (!b_id_source)
throw new Error("missing: field.properties.b_id_source")
const b_lu_catalogue = field.properties.b_lu_catalogue
if (!b_id_source)
throw new Error(
"missing: field.properties.b_lu_catalogue",
)
const b_geometry = field.geometry
if (!b_geometry) {
throw new Error("missing: b_geometry")
}

const parsedYear = Number.parseInt(
String(calendar ?? ""),
10,
)
const currentYear =
Number.isInteger(parsedYear) &&
parsedYear >= 1970 &&
parsedYear < 2100
? parsedYear
: timeframe.start?.getFullYear()
if (!currentYear && currentYear !== 0) {
throw new Error("missing: year")
}
const cultivationDefaultDates =
await getDefaultDatesOfCultivation(
fdm,
session.principal_id,
b_id_farm,
b_lu_catalogue,
currentYear,
)
const b_start = new Date(`${currentYear}-01-01`)
const b_lu_start = cultivationDefaultDates.b_lu_start
const b_lu_end = cultivationDefaultDates.b_lu_end
const b_end = undefined
const b_acquiring_method = "unknown"

const b_id = await addField(
fdm,
session.principal_id,
b_id_farm,
b_name,
b_id_source,
b_geometry,
b_start,
b_acquiring_method,
b_end,
)
await addCultivation(
fdm,
session.principal_id,
b_lu_catalogue,
b_id,
b_lu_start,
b_lu_end,
)

if (nmiApiKey) {
const estimates = await getSoilParameterEstimates(
field,
nmiApiKey,
)

await addSoilAnalysis(
fdm,
session.principal_id,
undefined,
estimates.a_source,
b_id,
estimates.a_depth_lower,
undefined,
estimates,
)
}

return b_id
},
),
)
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

🏁 Script executed:

find . -type f -name "*.ts" -path "*/fdm-core/*" | grep -E "(field|cultivation)" | head -20

Repository: SvenVw/fdm

Length of output: 229


🏁 Script executed:

cat -n ./fdm-core/src/field.ts | head -100

Repository: SvenVw/fdm

Length of output: 4452


🏁 Script executed:

cat -n ./fdm-core/src/field.ts | sed -n '100,150p'

Repository: SvenVw/fdm

Length of output: 2116


🏁 Script executed:

cat -n ./fdm-core/src/cultivation.ts | head -120

Repository: SvenVw/fdm

Length of output: 5142


🏁 Script executed:

rg -n "export.*function addCultivation" ./fdm-core/src/cultivation.ts

Repository: SvenVw/fdm

Length of output: 148


🏁 Script executed:

cat -n ./fdm-core/src/cultivation.ts | sed -n '321,400p'

Repository: SvenVw/fdm

Length of output: 3556


🏁 Script executed:

cat -n ./fdm-core/src/cultivation.ts | sed -n '400,450p'

Repository: SvenVw/fdm

Length of output: 2448


🏁 Script executed:

cat -n ./fdm-core/src/cultivation.ts | sed -n '450,500p'

Repository: SvenVw/fdm

Length of output: 2015


🏁 Script executed:

rg -n "export.*function addSoilAnalysis" ./fdm-core/src/

Repository: SvenVw/fdm

Length of output: 118


🏁 Script executed:

cat -n ./fdm-core/src/soil.ts | sed -n '35,120p'

Repository: SvenVw/fdm

Length of output: 3959


Wrap multi-step field creation in a single database transaction.

Each field creation involves sequential calls to addField, addCultivation, and optionally addSoilAnalysis. Although these fdm-core functions use internal transactions, they execute as separate transactions at the caller level. If addCultivation or addSoilAnalysis fails after addField succeeds, the field will remain in an inconsistent state without its cultivation or soil data.

Wrap all three operations within a single fdm.transaction() block to ensure atomicity: if any step fails, all changes rollback together.

🤖 Prompt for AI Agents
In @fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx around lines 485
- 576, The field creation sequence calls addField, addCultivation, and
addSoilAnalysis separately which can leave partial state if a later call fails;
wrap the per-field operations inside a single fdm.transaction(...) so the
addField, addCultivation and conditional addSoilAnalysis calls execute
atomically and rollback on error. Locate the async mapper (the function passed
to selectedFields.features.map) and replace the sequential calls with a single
transactional callback using fdm.transaction(async tx => { await addField(tx or
tx-bound fdm, ...); await addCultivation(...); if (nmiApiKey) await
addSoilAnalysis(...); return b_id; }) ensuring you pass the transactional
executor/context to addField/addCultivation/addSoilAnalysis so they run in the
same transaction. Ensure error propagation so Promise.all rejects and the
transaction rolls back on failure.

Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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: 2

🤖 Fix all issues with AI agents
In @fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx:
- Around line 506-518: The year validation can be simplified: assign currentYear
exactly as you do now from parsedYear (only when Number.isInteger(parsedYear) &&
parsedYear >= 1970 && parsedYear < 2100) or from timeframe.start?.getFullYear(),
then replace the confusing check if (!currentYear && currentYear !== 0) with an
explicit null/undefined check (e.g., if (currentYear == null) throw new
Error("missing: year")); update references to parsedYear and currentYear
accordingly and remove the redundant zero check since parsedYear cannot be 0
given the range validation.
- Around line 533-569: Wrap the three dependent operations (addField,
addCultivation, addSoilAnalysis) in a single DB transaction using the fdm
transaction API so they commit or roll back together: start a transaction on
fdm, call addField(...) to obtain b_id, then call addCultivation(...) and (if
nmiApiKey) getSoilParameterEstimates(...) followed by addSoilAnalysis(...), and
on error roll back the transaction; ensure you pass the transaction
context/connection into addField, addCultivation, and addSoilAnalysis (rather
than calling them with independent awaits) so they use the same transaction
scope tied to session.principal_id and the created b_id.
🧹 Nitpick comments (3)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx (3)

151-153: Consider using structured logging instead of console.warn.

While the error handling logic is sound (falling back to "gewassen onbekend"), using console.warn for error reporting is less observable than structured logging. Consider using a proper logging mechanism that can be monitored in production.

♻️ Proposed refactor using structured logging
                     } catch (e) {
-                        console.warn(e)
+                        console.error("Failed to fetch cultivations for field", {
+                            b_id: field.b_id,
+                            error: e
+                        })
                         cultivation = "gewassen onbekend"
                     }

576-581: Add defensive check for empty field selection.

While the UI flow likely prevents this, the code doesn't guard against an empty fieldIds array before accessing fieldIds[0] in the redirect URL. If somehow no fields are selected, this would cause a runtime error or malformed URL.

🛡️ Proposed defensive check
+        if (fieldIds.length === 0) {
+            throw new Error("No fields were created")
+        }
+
         return redirectWithSuccess(
             `/farm/${b_id_farm}/${calendar}/field/${fieldIds[0]}/overview?fieldIds=${fieldIds.map(encodeURIComponent).join(",")}`,
             {
                 message: `${fieldIds.length} ${fieldIds.length === 1 ? "perceel is" : "percelen zijn"} toegevoegd! 🎉`,
             },
         )

461-475: Consider consistent error logging pattern.

Similar to line 152, using console.warn for error reporting is less observable than structured logging. While the fallback to firstFieldIndex = 1 is appropriate for resilience, consistent error logging would improve production observability.

♻️ Proposed logging consistency
         } catch (e) {
-            console.warn(e)
+            console.error("Failed to calculate firstFieldIndex, defaulting to 1", {
+                b_id_farm,
+                error: e
+            })
             firstFieldIndex = 1
         }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50c8074 and f1e59a9.

📒 Files selected for processing (1)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
🧰 Additional context used
🧠 Learnings (39)
📓 Common learnings
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.
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.
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: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.field.new.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.field.new.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.field.new.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.field.new.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.field.new.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.field.new.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.new.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.field.new.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.new.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.new.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.field.new.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.field.new.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.new.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.new.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.new.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.new.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.new.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.field.new.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.field.new.tsx
📚 Learning: 2025-08-13T11:05:40.105Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 237
File: fdm-data/src/cultivations/catalogues/brp.ts:65-68
Timestamp: 2025-08-13T11:05:40.105Z
Learning: In fdm-app Select components, using `undefined` instead of empty string (`""`) as fallback value prevents empty strings from being submitted as form values. This approach fixes the issue at the UI source rather than requiring backend validation.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.new.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.field.new.tsx
📚 Learning: 2024-11-28T15:39:25.432Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-core/src/field.ts:185-187
Timestamp: 2024-11-28T15:39:25.432Z
Learning: In Drizzle ORM, when using `fdm.transaction(async (tx) => { ... })`, any error thrown inside the transaction function automatically triggers a rollback. Therefore, it's unnecessary to call `tx.rollback()` explicitly in the `catch` block.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2024-11-27T11:27:27.797Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-data/src/cultivations/index.test.ts:57-59
Timestamp: 2024-11-27T11:27:27.797Z
Learning: When cleaning up test data in `afterAll` hooks in `fdm-data/src/cultivations/index.test.ts`, use `fdm.delete(schema.tableName).execute();` to delete rows from a table without dropping the table itself.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.new.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/routes/farm.$b_id_farm.$calendar.field.new.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/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2024-12-16T10:56:07.561Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 16
File: fdm-app/app/routes/app.addfarm.$b_id_farm.cultivations.$b_lu_catalogue.fertilizers.tsx:1-1
Timestamp: 2024-12-16T10:56:07.561Z
Learning: The project uses `react-router` v7, and the `data` function is exported and used for error handling in loaders and actions.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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/routes/farm.$b_id_farm.$calendar.field.new.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/routes/farm.$b_id_farm.$calendar.field.new.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.field.new.tsx
📚 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/app/routes/farm.$b_id_farm.$calendar.field.new.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.field.new.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.field.new.tsx
📚 Learning: 2025-01-31T15:06:35.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas-sources.tsx:21-66
Timestamp: 2025-01-31T15:06:35.764Z
Learning: In react-map-gl components, when querying rendered features via map.queryRenderedFeatures(), the effect's dependency array must include any props that affect the map's rendered state (like source data) to ensure features are queried against the current map state.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.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.new.tsx
📚 Learning: 2025-01-31T15:41:43.741Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas-panels.tsx:28-28
Timestamp: 2025-01-31T15:41:43.741Z
Learning: When handling different map event types in react-map-gl v7.1.8, use MapLayerMouseEvent for mouse events (which have the point property) and ViewStateChangeEvent for view state changes. Use a type guard like 'point' in evt to safely access event-specific properties.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
📚 Learning: 2025-12-15T12:19:47.858Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:47.858Z
Learning: When reviewing code, prefer storing only non-sensitive UI/state data in sessionStorage. For map viewState (e.g., longitude/latitude), ensure it represents non-personal business data and that persistence across sessions is justified, documented, and respects user privacy. If persisting, use a clearly scoped, namespaced key, guard access with try/catch, and avoid syncing with servers or exposing data to third-party scripts. Apply this guideline to all TSX files that manage client-side UI state.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
🔇 Additional comments (5)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx (5)

166-169: LGTM! Appropriate geometry simplification.

The simplification parameters (tolerance: 0.00001, highQuality: true) are well-chosen for reducing map rendering payload while maintaining visual accuracy.


237-242: LGTM! Clean state management for multi-select workflow.

The state initialization and management pattern is well-structured:

  • selectedFieldsData properly initialized as an empty FeatureCollection
  • showFields toggle provides good UX for layer visibility control
  • Derived layerLayout follows standard pattern

358-371: LGTM! Proper component integration for multi-select workflow.

The atlas component wiring correctly implements the multi-select pattern:

  • FieldsSourceSelected properly excludes already-saved fields to prevent conflicts
  • FieldsPanelSelection provides clear visual feedback with field count and navigation
  • Separate hover panels for available vs. selected fields enhance UX

Also applies to: 388-411


417-424: LGTM! Clear user guidance for already-saved fields.

The FieldDetailsInfoPopup provides helpful context when users click existing fields, directing them to the appropriate management page. The hint message effectively prevents confusion in the multi-select workflow.


5-5: No issue to address. getCultivations is the correct function for this context. It retrieves field-specific cultivations (not catalogue options), accepts the required parameters including timeframe, and matches the established pattern throughout the codebase for this use case.

Comment on lines +506 to +518
const parsedYear = Number.parseInt(
String(calendar ?? ""),
10,
)
const currentYear =
Number.isInteger(parsedYear) &&
parsedYear >= 1970 &&
parsedYear < 2100
? parsedYear
: timeframe.start?.getFullYear()
if (!currentYear && currentYear !== 0) {
throw new Error("missing: year")
}
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 | 🟡 Minor

Simplify year validation logic.

The condition on line 516 (!currentYear && currentYear !== 0) is potentially confusing. Since currentYear is assigned from either parsedYear (which must be >= 1970 to pass) or timeframe.start?.getFullYear() (which returns a number or undefined), the currentYear !== 0 check is unnecessary because 0 would never pass the range validation in the first place.

♻️ Proposed simplification
                     const currentYear =
                         Number.isInteger(parsedYear) &&
                         parsedYear >= 1970 &&
                         parsedYear < 2100
                             ? parsedYear
                             : timeframe.start?.getFullYear()
-                    if (!currentYear && currentYear !== 0) {
+                    if (currentYear === undefined) {
                         throw new Error("missing: year")
                     }
📝 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
const parsedYear = Number.parseInt(
String(calendar ?? ""),
10,
)
const currentYear =
Number.isInteger(parsedYear) &&
parsedYear >= 1970 &&
parsedYear < 2100
? parsedYear
: timeframe.start?.getFullYear()
if (!currentYear && currentYear !== 0) {
throw new Error("missing: year")
}
const parsedYear = Number.parseInt(
String(calendar ?? ""),
10,
)
const currentYear =
Number.isInteger(parsedYear) &&
parsedYear >= 1970 &&
parsedYear < 2100
? parsedYear
: timeframe.start?.getFullYear()
if (currentYear === undefined) {
throw new Error("missing: year")
}
🤖 Prompt for AI Agents
In @fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx around lines 506
- 518, The year validation can be simplified: assign currentYear exactly as you
do now from parsedYear (only when Number.isInteger(parsedYear) && parsedYear >=
1970 && parsedYear < 2100) or from timeframe.start?.getFullYear(), then replace
the confusing check if (!currentYear && currentYear !== 0) with an explicit
null/undefined check (e.g., if (currentYear == null) throw new Error("missing:
year")); update references to parsedYear and currentYear accordingly and remove
the redundant zero check since parsedYear cannot be 0 given the range
validation.

Comment on lines +533 to +569
const b_id = await addField(
fdm,
session.principal_id,
b_id_farm,
b_name,
b_id_source,
b_geometry,
b_start,
b_acquiring_method,
b_end,
)
await addCultivation(
fdm,
session.principal_id,
b_lu_catalogue,
b_id,
b_lu_start,
b_lu_end,
)

if (nmiApiKey) {
const estimates = await getSoilParameterEstimates(
field,
nmiApiKey,
)

await addSoilAnalysis(
fdm,
session.principal_id,
undefined,
estimates.a_source,
b_id,
estimates.a_depth_lower,
undefined,
estimates,
)
}
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the addField, addCultivation, and addSoilAnalysis function implementations

echo "=== Locating addField function ==="
rg "export.*function\s+addField|export\s+const\s+addField" --type ts --type tsx -A 5

echo -e "\n=== Locating addCultivation function ==="
rg "export.*function\s+addCultivation|export\s+const\s+addCultivation" --type ts --type tsx -A 5

echo -e "\n=== Locating addSoilAnalysis function ==="
rg "export.*function\s+addSoilAnalysis|export\s+const\s+addSoilAnalysis" --type ts --type tsx -A 5

Repository: SvenVw/fdm

Length of output: 274


🏁 Script executed:

#!/bin/bash
# Find where these functions are imported from in the reviewed file
echo "=== Checking imports in farm.$b_id_farm.$calendar.field.new.tsx ==="
rg "import.*addField|import.*addCultivation|import.*addSoilAnalysis" fdm-app/app/routes/ --type ts --type tsx -A 2

Repository: SvenVw/fdm

Length of output: 147


🏁 Script executed:

#!/bin/bash
# Search for transaction usage patterns in the codebase
echo "=== Searching for transaction patterns ==="
rg "\.transaction\(|transaction\s*\(" --type ts --type tsx | head -20

echo -e "\n=== Searching for database.transaction usage ==="
rg "database\.transaction|db\.transaction|fdm\.transaction" --type ts --type tsx | head -20

Repository: SvenVw/fdm

Length of output: 219


🏁 Script executed:

#!/bin/bash
# Find TypeScript files and search for function definitions
echo "=== Finding addField function ==="
fd -e ts -e tsx | xargs rg "export.*function\s+addField|export\s+const\s+addField" -A 10

echo -e "\n=== Finding addCultivation function ==="
fd -e ts -e tsx | xargs rg "export.*function\s+addCultivation|export\s+const\s+addCultivation" -A 10

echo -e "\n=== Finding addSoilAnalysis function ==="
fd -e ts -e tsx | xargs rg "export.*function\s+addSoilAnalysis|export\s+const\s+addSoilAnalysis" -A 10

Repository: SvenVw/fdm

Length of output: 3658


🏁 Script executed:

#!/bin/bash
# Find the reviewed file and check its imports
echo "=== Finding the reviewed file ==="
fd "farm.*field.new" -e ts -e tsx

echo -e "\n=== Checking imports from that file ==="
fd "farm.*field.new" -e ts -e tsx | xargs cat | head -50

Repository: SvenVw/fdm

Length of output: 1614


🏁 Script executed:

#!/bin/bash
# Search for where addField is defined by looking for its implementation
echo "=== Searching for addField implementations across codebase ==="
rg "addField\s*=|function addField|const addField" | head -30

echo -e "\n=== Searching for transaction patterns in database operations ==="
rg "transaction\(" | head -30

Repository: SvenVw/fdm

Length of output: 2780


🏁 Script executed:

#!/bin/bash
# Read the addField function implementation
echo "=== addField implementation ==="
sed -n '/^export async function addField/,/^}/p' fdm-core/src/field.ts | head -100

Repository: SvenVw/fdm

Length of output: 2848


🏁 Script executed:

#!/bin/bash
# Read the addCultivation function implementation
echo "=== addCultivation implementation ==="
sed -n '/^export async function addCultivation/,/^}/p' fdm-core/src/cultivation.ts | head -100

Repository: SvenVw/fdm

Length of output: 4578


🏁 Script executed:

#!/bin/bash
# Read the addSoilAnalysis function implementation
echo "=== addSoilAnalysis implementation ==="
sed -n '/^export async function addSoilAnalysis/,/^}/p' fdm-core/src/soil.ts | head -100

Repository: SvenVw/fdm

Length of output: 2277


Wrap field creation, cultivation, and soil analysis operations in a single database transaction.

Each of addField, addCultivation, and addSoilAnalysis correctly uses database transactions internally. However, at the application layer (lines 533–569), these three operations are called sequentially with separate awaits. If addCultivation or addSoilAnalysis fails after addField succeeds, the field is created without its cultivation or soil analysis—resulting in an inconsistent state. These operations should be wrapped in a single transaction to ensure atomicity.

🤖 Prompt for AI Agents
In @fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx around lines 533
- 569, Wrap the three dependent operations (addField, addCultivation,
addSoilAnalysis) in a single DB transaction using the fdm transaction API so
they commit or roll back together: start a transaction on fdm, call
addField(...) to obtain b_id, then call addCultivation(...) and (if nmiApiKey)
getSoilParameterEstimates(...) followed by addSoilAnalysis(...), and on error
roll back the transaction; ensure you pass the transaction context/connection
into addField, addCultivation, and addSoilAnalysis (rather than calling them
with independent awaits) so they use the same transaction scope tied to
session.principal_id and the created b_id.

@BoraIneviNMI BoraIneviNMI requested a review from SvenVw January 9, 2026 15:01
Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw left a comment

Choose a reason for hiding this comment

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

I would prefer that after selecting the fields a page similar as fdm-app\app\routes\farm.create.$b_id_farm.$calendar.fields.tsx is shown. This would it more intuitive which information you need to check and update. Also the user does not now the name of the field (as it is automatically generated), so showing the map with the fields makes it more understandable which field they are editing

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: 3

🤖 Fix all issues with AI agents
In
`@fdm-app/app/routes/farm`.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx:
- Around line 496-533: The POST handler currently updates the field via
updateField(...) then calls getCultivations(...); if cultivations is empty it
falls through and triggers the generic "invalid method" error—add an explicit
success return after updateField when cultivations is falsy or length === 0 so
the handler returns dataWithSuccess(...) (same shape/message used inside the
cultivations branch) and exits; update the code path around getCultivations(...)
/ the if (cultivations && cultivations.length > 0) block to return early on no
cultivations.
- Around line 534-549: Remove the dead call to url.searchParams.get("fieldIds")
and use the already-obtained fieldIds array when updating the search params;
after filtering out b_id, set the param via url.searchParams.set("fieldIds",
...) as shown, then build the redirect using the pathname plus url.search (not
`?${url.search}`) or by appending `?${url.searchParams.toString()}` so you avoid
the double-? bug; update the DELETE handler around getField/removeField and the
redirectWithSuccess call to use the corrected URL construction.

In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.field.new.fields.tsx:
- Around line 124-137: The useMemo for sidebarPageItems builds link URLs using
location.search but does not include location.search in its dependency array;
update the dependency list for the sidebarPageItems useMemo (the useMemo that
defines sidebarPageItems) to include location.search alongside [fields,
showProductiveOnly, b_id_farm, calendar] so the memo recalculates when query
params change and links remain up-to-date.
♻️ Duplicate comments (2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx (2)

480-482: Add null check before parsing selected_fields.

If selected_fields is missing from the form data, formData.get("selected_fields") returns null, and String(null) produces "null". JSON.parse("null") returns null, causing selectedFields.features on line 486 to throw.

🐛 Proposed fix with null safety
-        const selectedFields = JSON.parse(
-            String(formData.get("selected_fields")),
-        )
+        const selectedFieldsRaw = formData.get("selected_fields")
+        if (!selectedFieldsRaw) {
+            throw data("No fields selected", {
+                status: 400,
+                statusText: "No fields selected",
+            })
+        }
+        const selectedFields = JSON.parse(String(selectedFieldsRaw))

533-569: Wrap field creation operations in a single database transaction to ensure atomicity.

Currently, addField, addCultivation, and addSoilAnalysis each execute in separate transactions. If addCultivation or addSoilAnalysis fails after addField succeeds, the field persists without its cultivation or soil analysis data, leaving it in an inconsistent state.

Modify these fdm-core functions to accept an optional transaction parameter. For example, addField(fdm, principal_id, ..., tx?) can use (tx || fdm).transaction() internally, allowing callers to wrap multiple operations in a single transaction:

await fdm.transaction(async (tx) => {
    const b_id = await addField(..., tx)
    await addCultivation(..., b_id, ..., tx)
    if (nmiApiKey) {
        await addSoilAnalysis(..., b_id, ..., tx)
    }
})

This approach maintains backward compatibility while enabling atomic operations across multiple functions.

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

255-261: Consider using a stable reference for the reset dependency.

While form.reset from react-hook-form is typically stable, explicitly depending on it can be fragile. A common pattern is to depend only on loaderData and access form.reset directly:

♻️ Optional improvement
     useEffect(() => {
         form.reset({
             b_name: loaderData.b_name ?? "",
             b_area: Math.round(loaderData.b_area * 10) / 10,
             b_lu_catalogue: loaderData.b_lu_catalogue ?? "",
         })
-    }, [loaderData, form.reset])
+    }, [loaderData, form])
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx (1)

506-518: Simplify year validation logic.

The condition !currentYear && currentYear !== 0 is confusing. Since currentYear is assigned from either a validated parsedYear (>= 1970) or timeframe.start?.getFullYear(), the value 0 would never pass the range validation. Use an explicit undefined check instead.

♻️ Proposed simplification
                     const currentYear =
                         Number.isInteger(parsedYear) &&
                         parsedYear >= 1970 &&
                         parsedYear < 2100
                             ? parsedYear
                             : timeframe.start?.getFullYear()
-                    if (!currentYear && currentYear !== 0) {
+                    if (currentYear === undefined) {
                         throw new Error("missing: year")
                     }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1e59a9 and e1b9f5a.

📒 Files selected for processing (10)
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.$analysis_type.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.upload.tsx
🧰 Additional context used
🧠 Learnings (44)
📓 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: 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.
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.
📚 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.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.$analysis_type.tsx
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.upload.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.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._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.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
📚 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/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.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/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.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/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx
📚 Learning: 2024-12-16T10:56:07.561Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 16
File: fdm-app/app/routes/app.addfarm.$b_id_farm.cultivations.$b_lu_catalogue.fertilizers.tsx:1-1
Timestamp: 2024-12-16T10:56:07.561Z
Learning: The project uses `react-router` v7, and the `data` function is exported and used for error handling in loaders and actions.

Applied to files:

  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.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/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.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/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx
📚 Learning: 2025-04-29T11:28:44.181Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 132
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.access.tsx:54-68
Timestamp: 2025-04-29T11:28:44.181Z
Learning: In React Router v7, the `json()` function has been replaced with `data()` for creating responses in loaders and actions.

Applied to files:

  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
📚 Learning: 2025-12-15T12:19:47.858Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:47.858Z
Learning: When reviewing code, prefer storing only non-sensitive UI/state data in sessionStorage. For map viewState (e.g., longitude/latitude), ensure it represents non-personal business data and that persistence across sessions is justified, documented, and respects user privacy. If persisting, use a clearly scoped, namespaced key, guard access with try/catch, and avoid syncing with servers or exposing data to third-party scripts. Apply this guideline to all TSX files that manage client-side UI state.

Applied to files:

  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.$analysis_type.tsx
  • fdm-app/app/components/blocks/soil/form-selection.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.$analysis_type.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.upload.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx
📚 Learning: 2025-02-18T11:00:54.405Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 81
File: fdm-app/app/root.tsx:79-79
Timestamp: 2025-02-18T11:00:54.405Z
Learning: In React Router v7+, to ensure error pages are displayed correctly:
1. Export the ErrorBoundary component from the root route
2. Include the Scripts component in the Layout
3. Export the Layout through the default App component

Applied to files:

  • fdm-app/app/components/blocks/soil/form-selection.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.field.new._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.new._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.new._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.field.new._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.new._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.new._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.new._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.new._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.new._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.field.new._index.tsx
📚 Learning: 2025-08-13T11:05:40.105Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 237
File: fdm-data/src/cultivations/catalogues/brp.ts:65-68
Timestamp: 2025-08-13T11:05:40.105Z
Learning: In fdm-app Select components, using `undefined` instead of empty string (`""`) as fallback value prevents empty strings from being submitted as form values. This approach fixes the issue at the UI source rather than requiring backend validation.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.field.new._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.field.new._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.field.new._index.tsx
📚 Learning: 2024-11-28T15:39:25.432Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-core/src/field.ts:185-187
Timestamp: 2024-11-28T15:39:25.432Z
Learning: In Drizzle ORM, when using `fdm.transaction(async (tx) => { ... })`, any error thrown inside the transaction function automatically triggers a rollback. Therefore, it's unnecessary to call `tx.rollback()` explicitly in the `catch` block.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx
📚 Learning: 2024-11-27T11:27:27.797Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-data/src/cultivations/index.test.ts:57-59
Timestamp: 2024-11-27T11:27:27.797Z
Learning: When cleaning up test data in `afterAll` hooks in `fdm-data/src/cultivations/index.test.ts`, use `fdm.delete(schema.tableName).execute();` to delete rows from a table without dropping the table itself.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.field.new._index.tsx
📚 Learning: 2025-01-31T15:06:35.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas-sources.tsx:21-66
Timestamp: 2025-01-31T15:06:35.764Z
Learning: In react-map-gl components, when querying rendered features via map.queryRenderedFeatures(), the effect's dependency array must include any props that affect the map's rendered state (like source data) to ensure features are queried against the current map state.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._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.new._index.tsx
📚 Learning: 2025-01-31T15:41:43.741Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 67
File: fdm-app/app/components/custom/atlas/atlas-panels.tsx:28-28
Timestamp: 2025-01-31T15:41:43.741Z
Learning: When handling different map event types in react-map-gl v7.1.8, use MapLayerMouseEvent for mouse events (which have the point property) and ViewStateChangeEvent for view state changes. Use a type guard like 'point' in evt to safely access event-specific properties.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx
🧬 Code graph analysis (2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx (6)
fdm-core/src/db/schema.ts (2)
  • farms (21-33)
  • fields (92-108)
fdm-app/app/store/field-filter.ts (1)
  • useFieldFilterStore (10-24)
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/custom/field-filter-toggle.tsx (1)
  • FieldFilterToggle (11-44)
fdm-app/app/components/custom/sidebar-page.tsx (1)
  • SidebarPage (12-49)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx (7)
fdm-core/src/db/schema.ts (2)
  • fields (92-108)
  • cultivations (373-385)
fdm-app/app/integrations/map.ts (1)
  • getMapStyle (4-23)
fdm-app/app/components/blocks/atlas/atlas-viewstate.tsx (1)
  • getViewState (19-42)
fdm-app/app/components/blocks/atlas/atlas-styles.tsx (1)
  • getFieldsStyle (8-12)
fdm-app/app/components/blocks/field/popup.tsx (1)
  • FieldDetailsInfoPopup (23-55)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.upload.tsx (1)
  • action (99-225)
fdm-app/app/integrations/nmi.ts (2)
  • getNmiApiKey (6-13)
  • getSoilParameterEstimates (15-110)
🪛 Biome (2.1.2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx

[error] 124-124: This hook does not specify its dependency on location.search.

This dependency is being used here, but is not specified in the hook dependency list.

Unsafe fix: Add the missing dependency to the list.

(lint/correctness/useExhaustiveDependencies)

⏰ Context from checks skipped due to timeout of 300000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: core (24)
🔇 Additional comments (12)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx (1)

73-86: LGTM! Query parameter preservation is correctly implemented.

The use of useLocation() to preserve search parameters during back navigation ensures the fieldIds context is maintained throughout the multi-field creation workflow.

fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.upload.tsx (1)

195-198: LGTM! Server-side query parameter preservation is correctly implemented.

Using new URL(request.url).search in action functions is the appropriate pattern since React hooks are unavailable server-side. The redirect correctly maintains the fieldIds context after successful upload.

fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.$analysis_type.tsx (1)

191-194: LGTM! Query parameter preservation is consistent with other route actions.

The redirect correctly preserves search parameters after adding a soil analysis, maintaining the multi-field creation workflow context.

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

11-83: LGTM! Consistent query parameter preservation across all navigation options.

All five soil analysis type selection cards correctly append location.search to their NavLink destinations, ensuring the fieldIds context is preserved regardless of which analysis type the user selects.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.upload.tsx (1)

1-7: Verify that the re-exported action's redirect resolves correctly for this route path.

The re-export pattern is clean and avoids code duplication. However, since the action in farm.create...upload.tsx uses a relative redirect (../${url.search}), ensure the resulting navigation path is correct when accessed via this route (/farm/$b_id_farm/$calendar/field/new/fields/$b_id/soil/analysis/upload).

The relative ../ should navigate to .../soil/analysis/ which appears correct, but this warrants manual testing to confirm the redirect lands at the expected destination in the multi-field creation flow.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis.$analysis_type.tsx (1)

1-7: LGTM!

Clean barrel re-export pattern that delegates to the existing soil analysis implementation. This aligns with the route structure for the new field management workflow.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id.soil.analysis._index.tsx (1)

1-4: LGTM!

Consistent barrel re-export for the soil analysis index route.

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

61-114: LGTM!

The loader properly validates parameters, fetches field data in parallel, and constructs the response with appropriate error handling via handleLoaderError.


139-206: LGTM!

The UI layout follows the established patterns with a sidebar for field navigation, filter toggle, and an Outlet for nested field detail routes.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx (1)

108-229: LGTM!

The loader properly validates parameters, handles redirects when no field is selected, constructs GeoJSON features, and fetches all necessary data for the field detail view.

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

133-173: LGTM!

The per-field cultivation resolution with fallback handling ensures all fields display meaningful cultivation information. The use of simplify from turf helps reduce geometry complexity for rendering. Based on learnings, Promise.all with individual API calls performs acceptably even with farms containing 90+ fields.


198-427: LGTM!

The component properly implements the multi-select pattern with FieldsSourceSelected for tracking selections and FieldsPanelSelection for displaying count and navigation. The popup hint for saved fields provides good UX guidance.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.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: 2

🤖 Fix all issues with AI agents
In
`@fdm-app/app/routes/farm`.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx:
- Line 491: The code assigns timeframe = getTimeframe(params) but getTimeframe
is async (used awaited in the loader) so timeframe becomes a Promise; change
this to await getTimeframe(params) and ensure the enclosing function is declared
async (or already is); then pass the resolved timeframe to getCultivations so
getCultivations receives the actual value rather than a Promise (refer to
getTimeframe, timeframe, params, and getCultivations).

In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.field.new.fields.tsx:
- Around line 78-80: When building fieldIds from the URL, filter out
empty/whitespace-only entries so we don't call getField with invalid IDs: after
reading fieldIdsParam and splitting by ",", trim each id and remove any empty
strings (e.g., via map(trim) then filter(length>0)) before merging with the
initial b_id-derived array (fieldIds) so fieldIds contains only valid non-empty
IDs.
♻️ Duplicate comments (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx (1)

520-533: Missing return statement when no cultivations exist.

If cultivations.length === 0, the POST handler skips the if block and falls through to the else branch at line 549, which throws "invalid method". This incorrectly treats a successful field name update as an error when the field has no cultivations.

🐛 Proposed fix — move return outside the if block
             if (cultivations && cultivations.length > 0) {
                 await updateCultivation(
                     fdm,
                     session.principal_id,
                     cultivations[0].b_lu,
                     formValues.b_lu_catalogue,
                     undefined,
                     undefined,
                 )
-
-                return dataWithSuccess("fields have been updated", {
-                    message: `${formValues.b_name} is bijgewerkt! 🎉`,
-                })
             }
+
+            return dataWithSuccess("fields have been updated", {
+                message: `${formValues.b_name} is bijgewerkt! 🎉`,
+            })
         } else if (request.method === "DELETE") {
🧹 Nitpick comments (3)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx (3)

32-33: Consider using consistent import aliases.

These imports use relative paths (../components/...) while other component imports use the ~/components/... alias pattern. For consistency with the rest of the file, consider updating these to use the alias.

♻️ Suggested change
-import { HeaderFarm } from "../components/blocks/header/farm"
-import { HeaderField } from "../components/blocks/header/field"
+import { HeaderFarm } from "~/components/blocks/header/farm"
+import { HeaderField } from "~/components/blocks/header/field"

100-102: Consider handling individual field fetch failures gracefully.

If any field ID is invalid or a field was deleted, the entire Promise.all will reject, causing the page to fail completely. For a bulk operation, you might want to handle individual failures gracefully (e.g., using Promise.allSettled and filtering successful results).

Based on learnings, the Promise.all pattern performs acceptably, but error resilience for bulk operations may warrant consideration.


186-186: Remove commented-out code.

The commented <Separator /> should be removed if it's no longer needed, to keep the codebase clean.

♻️ Suggested change
                                     <CardFooter className="flex flex-col items-center space-y-2 relative">
-                                        {/* <Separator /> */}
                                         <Button variant={"link"} asChild>
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1b9f5a and 0f0baed.

📒 Files selected for processing (2)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
🧰 Additional context used
🧠 Learnings (22)
📓 Common learnings
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.
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: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.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.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.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.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.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.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.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.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.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.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.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.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.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.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.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.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.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.new.fields.($b_id)._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.field.new.fields.($b_id)._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.new.fields.($b_id)._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.new.fields.($b_id)._index.tsx
📚 Learning: 2025-12-15T12:19:47.858Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:47.858Z
Learning: When reviewing code, prefer storing only non-sensitive UI/state data in sessionStorage. For map viewState (e.g., longitude/latitude), ensure it represents non-personal business data and that persistence across sessions is justified, documented, and respects user privacy. If persisting, use a clearly scoped, namespaced key, guard access with try/catch, and avoid syncing with servers or exposing data to third-party scripts. Apply this guideline to all TSX files that manage client-side UI state.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.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.$calendar.field.new.fields.tsx
📚 Learning: 2024-12-16T10:56:07.561Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 16
File: fdm-app/app/routes/app.addfarm.$b_id_farm.cultivations.$b_lu_catalogue.fertilizers.tsx:1-1
Timestamp: 2024-12-16T10:56:07.561Z
Learning: The project uses `react-router` v7, and the `data` function is exported and used for error handling in loaders and actions.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
🧬 Code graph analysis (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx (6)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx (1)
  • loader (87-195)
fdm-app/app/store/field-filter.ts (1)
  • useFieldFilterStore (10-24)
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/header/field.tsx (1)
  • HeaderField (16-92)
fdm-app/app/components/custom/sidebar-page.tsx (1)
  • SidebarPage (12-49)
⏰ Context from checks skipped due to timeout of 300000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: core (24)
🔇 Additional comments (8)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx (3)

35-47: LGTM!

The meta function correctly sets the page title and description, following the established pattern in other routes.


124-137: LGTM!

The useMemo dependency array now correctly includes location.search, ensuring sidebar links update when query parameters change. The filtering and sorting logic is well-implemented.


152-156: LGTM!

Passing empty fieldOptions and undefined b_id is appropriate for this "new fields" context, where the header should show the field breadcrumb without a specific field selection dropdown.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx (5)

1-65: LGTM!

Imports are well-organized and appropriate for the route's functionality—form handling, map rendering, FDM core operations, and UI components.


67-92: LGTM!

Meta function and form schema are correctly defined. The schema validation requirements match the form field usage in the component.


108-229: LGTM!

Loader handles parameter validation, redirect logic for missing field IDs, and data fetching comprehensively. Error handling is properly delegated to handleLoaderError.


236-462: LGTM!

The component correctly handles form state synchronization with loader data, map rendering with bounds fitting, and navigation with preserved search parameters. The form submission states are properly managed.


534-548: LGTM!

The DELETE handler correctly updates the fieldIds search parameter after removing the deleted field and constructs the redirect URL properly. The previous issues with dead code and URL construction have been resolved.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +78 to +80
let fieldIds: string[] = b_id ? [b_id] : []
const fieldIdsParam = url.searchParams.get("fieldIds")
if (fieldIdsParam) fieldIds = fieldIdsParam.split(",")
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 | 🟡 Minor

Filter out empty strings from fieldIds to prevent invalid API calls.

If fieldIdsParam is an empty string or contains trailing/leading commas (e.g., "id1,,id2" or ",id1"), splitting by comma will produce empty strings that could cause invalid getField calls.

🐛 Proposed fix
         let fieldIds: string[] = b_id ? [b_id] : []
         const fieldIdsParam = url.searchParams.get("fieldIds")
-        if (fieldIdsParam) fieldIds = fieldIdsParam.split(",")
+        if (fieldIdsParam) fieldIds = fieldIdsParam.split(",").filter(Boolean)
📝 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
let fieldIds: string[] = b_id ? [b_id] : []
const fieldIdsParam = url.searchParams.get("fieldIds")
if (fieldIdsParam) fieldIds = fieldIdsParam.split(",")
let fieldIds: string[] = b_id ? [b_id] : []
const fieldIdsParam = url.searchParams.get("fieldIds")
if (fieldIdsParam) fieldIds = fieldIdsParam.split(",").filter(Boolean)
🤖 Prompt for AI Agents
In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.field.new.fields.tsx around
lines 78 - 80, When building fieldIds from the URL, filter out
empty/whitespace-only entries so we don't call getField with invalid IDs: after
reading fieldIdsParam and splitting by ",", trim each id and remove any empty
strings (e.g., via map(trim) then filter(length>0)) before merging with the
initial b_id-derived array (fieldIds) so fieldIds contains only valid non-empty
IDs.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@fdm-app/app/routes/farm`.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx:
- Around line 152-174: The code constructs a GeoJSON Feature and
FeatureCollection using field.b_geometry before validating it; move the null
check for field.b_geometry to before creating the feature/featureCollection so
you throw data("Field geometry is required", ...) early. Specifically, validate
field.b_geometry (the property used in the feature geometry) at the top of this
block and only then build the feature object (variable feature) and the
FeatureCollection to ensure you never create invalid GeoJSON.
🧹 Nitpick comments (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx (1)

256-262: Consider stabilizing the useEffect dependency.

Including form.reset directly in the dependency array may not behave as expected since function references can change. The typical pattern is to either include the entire form object (if stable) or wrap the reset logic.

♻️ Suggested improvement
     useEffect(() => {
         form.reset({
             b_name: loaderData.b_name ?? "",
             b_area: Math.round(loaderData.b_area * 10) / 10,
             b_lu_catalogue: loaderData.b_lu_catalogue ?? "",
         })
-    }, [loaderData, form.reset])
+    }, [loaderData, form])

Alternatively, if form reference changes frequently, consider using a ref or extracting the reset function once at the top level.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f0baed and 59018d4.

📒 Files selected for processing (1)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx
🧰 Additional context used
🧠 Learnings (20)
📓 Common learnings
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.
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: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.
📚 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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.new.fields.($b_id)._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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.field.new.fields.($b_id)._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.new.fields.($b_id)._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.field.new.fields.($b_id)._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.new.fields.($b_id)._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.new.fields.($b_id)._index.tsx
📚 Learning: 2025-12-15T12:19:47.858Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:47.858Z
Learning: When reviewing code, prefer storing only non-sensitive UI/state data in sessionStorage. For map viewState (e.g., longitude/latitude), ensure it represents non-personal business data and that persistence across sessions is justified, documented, and respects user privacy. If persisting, use a clearly scoped, namespaced key, guard access with try/catch, and avoid syncing with servers or exposing data to third-party scripts. Apply this guideline to all TSX files that manage client-side UI state.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx
🧬 Code graph analysis (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx (8)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis._index.tsx (1)
  • loader (26-65)
fdm-app/app/integrations/map.ts (1)
  • getMapStyle (4-23)
fdm-app/app/components/blocks/atlas/atlas-viewstate.tsx (1)
  • getViewState (19-42)
fdm-app/app/components/blocks/atlas/atlas-styles.tsx (1)
  • getFieldsStyle (8-12)
fdm-app/app/components/custom/combobox.tsx (1)
  • Combobox (41-157)
fdm-app/app/components/blocks/atlas/atlas-attribution.tsx (1)
  • MapTilerAttribution (3-24)
fdm-app/app/components/blocks/atlas/atlas-sources.tsx (1)
  • FieldsSourceNotClickable (24-38)
fdm-app/app/components/blocks/field/delete.tsx (1)
  • FieldDeleteDialog (20-66)
⏰ Context from checks skipped due to timeout of 300000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: core (24)
🔇 Additional comments (3)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.($b_id)._index.tsx (3)

521-534: Good fix - return statement now correctly placed outside the conditional.

The previous review flagged that the success return was inside the if (cultivations.length > 0) block. This has been correctly moved outside, ensuring a proper response is returned regardless of whether cultivations exist.


536-550: DELETE handler correctly fixed.

The previous issues have been addressed:

  • Dead code (discarded url.searchParams.get("fieldIds") call) has been removed
  • URL construction now correctly uses ${url.search} instead of ?${url.search}

492-492: getTimeframe is a synchronous function that returns a Timeframe object directly. It does not require await. The function performs only synchronous operations (Date object creation and basic conditionals) and does not return a Promise, so the earlier concern about a missing await was based on a misunderstanding of the function's nature.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id._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: 3

🤖 Fix all issues with AI agents
In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.field.new.fields._index.tsx:
- Line 1: The import statement currently brings data, type LoaderFunctionArgs,
and redirect from "react-router-dom"; change the module source to "react-router"
so the line imports data, LoaderFunctionArgs, and redirect from "react-router"
instead (verify the named exports data, LoaderFunctionArgs, and redirect are
available from react-router and adjust any other nearby imports to the same
module if needed).
- Around line 26-37: The try block contains a conditional that handles when b_id
is falsy but never returns when b_id is truthy, causing an implicit undefined
return; either remove the entire b_id branch (the if (!b_id) { ... } block) if
this route never provides params.b_id, or make the intent explicit by adding a
return for the b_id present case (e.g., return null or a redirect) so the
loader/handler always returns a value; update the code around the b_id check and
the redirect calls (referencing variables b_id, fieldIds, b_id_farm, calendar,
and the redirect calls) accordingly.

In
`@fdm-app/app/routes/farm`.$b_id_farm.$calendar.field.new.fields.$b_id._index.tsx:
- Around line 523-537: getField can return null/undefined causing a crash when
accessing field.b_name; guard the value before using it. After calling
getField(fdm, session.principal_id, b_id) in the DELETE branch, check if field
is falsy and handle gracefully (e.g., set a fallback name or return a
redirectWithSuccess/error message indicating the field was already removed)
before calling removeField and before using field.b_name in the
redirectWithSuccess call; ensure you still call removeField or skip it
appropriately if the field is missing.
♻️ Duplicate comments (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id._index.tsx (1)

139-161: Geometry validation occurs after it's already used.

The feature is constructed using field.b_geometry at line 148 before the null check at line 156. If the geometry is null, an invalid FeatureCollection is created before the error is thrown.

🐛 Proposed fix: Move validation before feature construction
         if (!field) {
             throw data("Field not found", {
                 status: 404,
                 statusText: "Field not found",
             })
         }
+
+        if (!field.b_geometry) {
+            throw data("Field geometry is required", {
+                status: 400,
+                statusText: "Field geometry is required",
+            })
+        }
+
         const feature: GeoJSON.Feature = {
             type: "Feature",
             properties: {
                 b_id: field.b_id,
                 b_name: field.b_name,
                 b_area: Math.round(field.b_area * 10) / 10,
                 b_lu_name: field.b_lu_name,
                 b_id_source: field.b_id_source,
             },
             geometry: field.b_geometry,
         }
         const featureCollection: FeatureCollection = {
             type: "FeatureCollection",
             features: [feature],
         }
-
-        // Get the geojson
-        if (!field.b_geometry) {
-            throw data("Field geometry is required", {
-                status: 400,
-                statusText: "Field geometry is required",
-            })
-        }
🧹 Nitpick comments (3)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id._index.tsx (3)

386-390: interactiveLayerIds is redundant when map is non-interactive.

The map is configured with interactive={false}, making interactiveLayerIds ineffective since no layer interactions are possible anyway.

♻️ Suggested simplification
                             <MapGL
                                 {...viewState}
                                 style={{
                                     height: "100%",
                                     width: "100%",
                                     borderRadius: "0.75rem",
                                 }}
                                 interactive={false}
                                 mapStyle={loaderData.mapStyle}
                                 mapLib={maplibregl}
-                                interactiveLayerIds={[id]}
                                 ref={mapRef}
                             >

258-266: Simplify map loading state management.

The mapContainerRef check in the effect will always evaluate to true after the initial render since refs are synchronously populated. This pattern doesn't actually guard against anything meaningful—the skeleton will flash briefly then always show the map.

Consider removing this conditional and rendering the map directly, or using a proper map onLoad callback if you need to track when the map tiles are fully loaded.


527-531: Consider handling empty fieldIds gracefully.

When all fields are deleted, this will result in a URL like ?fieldIds= (empty value). While functional, you may want to remove the parameter entirely when empty for cleaner URLs.

♻️ Suggested improvement
             const fieldIds = url.searchParams.get("fieldIds")?.split(",") ?? []
-            url.searchParams.set(
-                "fieldIds",
-                fieldIds.filter((id) => id !== b_id).join(","),
-            )
+            const remainingIds = fieldIds.filter((id) => id !== b_id)
+            if (remainingIds.length > 0) {
+                url.searchParams.set("fieldIds", remainingIds.join(","))
+            } else {
+                url.searchParams.delete("fieldIds")
+            }

@@ -0,0 +1,38 @@
import { data, type LoaderFunctionArgs, redirect } from "react-router-dom"
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 | 🟡 Minor

Import from react-router instead of react-router-dom.

Based on learnings, the fdm-app codebase uses React Router v7 and imports should be from react-router, not react-router-dom.

Proposed fix
-import { data, type LoaderFunctionArgs, redirect } from "react-router-dom"
+import { data, type LoaderFunctionArgs, redirect } from "react-router"
📝 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
import { data, type LoaderFunctionArgs, redirect } from "react-router-dom"
import { data, type LoaderFunctionArgs, redirect } from "react-router"
🤖 Prompt for AI Agents
In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.field.new.fields._index.tsx at
line 1, The import statement currently brings data, type LoaderFunctionArgs, and
redirect from "react-router-dom"; change the module source to "react-router" so
the line imports data, LoaderFunctionArgs, and redirect from "react-router"
instead (verify the named exports data, LoaderFunctionArgs, and redirect are
available from react-router and adjust any other nearby imports to the same
module if needed).

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: 3

🤖 Fix all issues with AI agents
In `@fdm-app/app/components/blocks/fields-new/block.tsx`:
- Around line 52-64: The TypeScript build fails because NewFieldsBlockProps
references maplibregl.StyleSpecification but maplibregl types are not imported;
add an import for the maplibre-gl types (e.g., import type maplibregl from
'maplibre-gl') at the top of the file so the mapStyle prop's type (mapStyle:
string | maplibregl.StyleSpecification) resolves correctly and restores
compilation for the component defined in block.tsx.

In `@fdm-app/app/components/blocks/fields-new/form.tsx`:
- Line 22: The import path for Spinner contains a stray double slash; update the
import for the Spinner component (the line importing Spinner from
"~/components//ui/spinner") to use the correct path "~/components/ui/spinner" so
the module path is consistent and typo-free.

In `@fdm-app/app/components/blocks/fields-new/sidebar.tsx`:
- Around line 25-39: The active-state matching breaks because SidebarPage uses
useLocation().pathname for comparisons but items like sidebarPageItems (in
NewFieldsSidebar) set to include location.search in their to value; update
SidebarPage to build the full current location string (e.g., combine
location.pathname + location.search or use location.href/path + search) and use
that fullLocation when comparing against item.to in the checks that use pathname
=== item.to and pathname.startsWith(item.to) so items whose to includes query
params correctly get aria-current and active classes.
♻️ Duplicate comments (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx (1)

64-66: Filter out empty strings from fieldIds to prevent invalid API calls.

This issue was flagged in a previous review. If fieldIdsParam is an empty string or contains trailing/leading commas (e.g., "id1,,id2"), splitting by comma will produce empty strings that could cause invalid getField calls in line 87.

🐛 Proposed fix
         let fieldIds: string[] = b_id ? [b_id] : []
         const fieldIdsParam = url.searchParams.get("fieldIds")
-        if (fieldIdsParam) fieldIds = fieldIdsParam.split(",")
+        if (fieldIdsParam) fieldIds = fieldIdsParam.split(",").filter(Boolean)
🧹 Nitpick comments (2)
fdm-app/app/components/blocks/fields-new/form.tsx (1)

47-54: Consider including form in the dependency array instead of form.reset.

While form.reset may work, ESLint's exhaustive-deps rule typically expects the entire form object when using methods from hooks. This is a minor concern—if you encounter stale closure issues, consider adjusting the dependency.

♻️ Alternative pattern
     useEffect(() => {
         form.reset({
             b_name: b_name ?? "",
             b_area: Math.round(b_area * 10) / 10,
             b_lu_catalogue: b_lu_catalogue ?? "",
             b_bufferstrip: b_bufferstrip ?? false,
         })
-    }, [form.reset, b_name, b_area, b_lu_catalogue, b_bufferstrip])
+    }, [form, b_name, b_area, b_lu_catalogue, b_bufferstrip])
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx (1)

85-88: Consider handling potential null/undefined fields from getField.

If any b_id in fieldIds doesn't correspond to an existing field, getField may return null/undefined. This could cause issues in the component when iterating over fields. Consider filtering out invalid results or adding validation.

♻️ Proposed defensive handling
         // Get the fields
         const fields = await Promise.all(
             fieldIds.map((b_id) => getField(fdm, session.principal_id, b_id)),
         )
+        // Filter out any null/undefined results from invalid field IDs
+        const validFields = fields.filter((field) => field != null)

         return {
-            fields: fields,
+            fields: validFields,
             b_id_farm: b_id_farm,

Comment thread fdm-app/app/components/blocks/fields-new/block.tsx
Comment thread fdm-app/app/components/blocks/fields-new/form.tsx Outdated
Comment on lines +25 to +39
const sidebarPageItems = useMemo(
() =>
fields
.filter((field) =>
showProductiveOnly ? field.b_bufferstrip === false : true,
)
.slice()
.sort((a, b) => b.b_area - a.b_area) // Sort by area in descending order
.map((field) => ({
title: field.b_name,
to:
(isFarmCreateWizard ?? false)
? `/farm/create/${b_id_farm}/${calendar}/fields/${field.b_id}`
: `/farm/${b_id_farm}/${calendar}/field/new/fields/${field.b_id}${location.search}`,
})),
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find SidebarPage component
fd -t f SidebarPage

Repository: SvenVw/fdm

Length of output: 36


🏁 Script executed:

# Search for the SidebarPage implementation and active state logic
rg -t tsx -t ts "SidebarPage\|aria-current" --max-count 20

Repository: SvenVw/fdm

Length of output: 81


🏁 Script executed:

# Look at the full sidebar.tsx file to understand the context
cat fdm-app/app/components/blocks/fields-new/sidebar.tsx

Repository: SvenVw/fdm

Length of output: 3070


🏁 Script executed:

# Find where sidebarPageItems is used and how active state is determined
rg "sidebarPageItems\|pathname.startsWith" --max-count 15

Repository: SvenVw/fdm

Length of output: 36


🏁 Script executed:

# Find the sidebar-page file based on import path
fd "sidebar-page" fdm-app/app/components/

Repository: SvenVw/fdm

Length of output: 100


🏁 Script executed:

# List files in custom directory
ls -la fdm-app/app/components/custom/ 2>/dev/null | head -20

Repository: SvenVw/fdm

Length of output: 1049


🏁 Script executed:

# Search for SidebarPage component definition
rg "export.*SidebarPage|function SidebarPage|const SidebarPage" -A 10 --max-count 5

Repository: SvenVw/fdm

Length of output: 1241


🏁 Script executed:

# Search for where pathname.startsWith or aria-current is used
rg "pathname.startsWith|aria-current" -B 2 -A 2 --max-count 10

Repository: SvenVw/fdm

Length of output: 3748


Active state breaks when to includes search params.

In NewFieldsSidebar, the to field appends location.search (line 37), but SidebarPage only extracts pathname from useLocation() for active matching. Since pathname never contains query params, the checks pathname === item.to and pathname.startsWith(item.to) will fail when item.to contains ?…, breaking both the aria-current attribute and the active styling (bg-muted class).

Update SidebarPage to include search params in the matching:

Suggested fix
- const { pathname } = useLocation()
+ const { pathname, search } = useLocation()

- aria-current={pathname === item.to ? "page" : undefined}
+ aria-current={pathname + search === item.to ? "page" : undefined}

- pathname.startsWith(item.to)
+ (pathname + search).startsWith(item.to)
🤖 Prompt for AI Agents
In `@fdm-app/app/components/blocks/fields-new/sidebar.tsx` around lines 25 - 39,
The active-state matching breaks because SidebarPage uses useLocation().pathname
for comparisons but items like sidebarPageItems (in NewFieldsSidebar) set to
include location.search in their to value; update SidebarPage to build the full
current location string (e.g., combine location.pathname + location.search or
use location.href/path + search) and use that fullLocation when comparing
against item.to in the checks that use pathname === item.to and
pathname.startsWith(item.to) so items whose to includes query params correctly
get aria-current and active classes.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
fdm-app/app/components/custom/sidebar-page.tsx (1)

18-38: Fix aria-current detection when URLs contain query parameters.

The sidebar items are hardcoded without query parameters (e.g., /farm/{id}/field/{id}/overview), but the current URL may include them (e.g., ?cultivation=wheat). This causes the exact match on line 33 to fail, breaking the aria-current="page" attribute for screen readers even though the visual styling works correctly via startsWith.

Use startsWith for both the aria-current and className checks to handle URLs with query parameters:

-                    aria-current={domainUrl === item.to ? "page" : undefined}
+                    aria-current={domainUrl.startsWith(item.to) ? "page" : undefined}
🧹 Nitpick comments (3)
fdm-app/app/components/blocks/fields-new/block.tsx (1)

42-48: Remove unnecessary col-span-4 class.

The col-span-4 class on line 42 has no effect because this div is outside the grid container (which ends at line 41). The parent uses space-y-6 (flexbox), not grid layout.

♻️ Suggested fix
-            <div className="col-span-4">
+            <div>
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx (2)

22-33: Meta title may be misleading.

The title "Percelen beheren - Bedrijf toevoegen" (Manage fields - Add farm) appears to be copied from the farm creation wizard. For the "new field" flow, consider updating to reflect the actual context, e.g., "Percelen beheren - Nieuw perceel" (Manage fields - New field).

♻️ Suggested fix
         {
-            title: `Percelen beheren - Bedrijf toevoegen | ${clientConfig.name}`,
+            title: `Percelen beheren - Nieuw perceel | ${clientConfig.name}`,
         },

17-19: Inconsistent import path style.

These imports use relative paths (../components/...) while lines 9-11 use the ~/ alias. Consider using consistent import paths for maintainability.

♻️ Suggested fix
-import { HeaderFarm } from "../components/blocks/header/farm"
-import { HeaderField } from "../components/blocks/header/field"
-import { NewFieldsSidebar } from "../components/blocks/fields-new/sidebar"
+import { HeaderFarm } from "~/components/blocks/header/farm"
+import { HeaderField } from "~/components/blocks/header/field"
+import { NewFieldsSidebar } from "~/components/blocks/fields-new/sidebar"

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (3)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id._index.tsx (3)

87-108: Validate geometry before constructing the GeoJSON.

Line 95 uses field.b_geometry before the null check at Line 103.

🐛 Proposed fix
-        const feature: GeoJSON.Feature = {
+        if (!field.b_geometry) {
+            throw data("Field geometry is required", {
+                status: 400,
+                statusText: "Field geometry is required",
+            })
+        }
+        const feature: GeoJSON.Feature = {
             type: "Feature",
             properties: {
                 b_id: field.b_id,
                 b_name: field.b_name,
                 b_area: Math.round((field.b_area ?? 0) * 10) / 10,
                 b_id_source: field.b_id_source,
             },
             geometry: field.b_geometry as Geometry,
         }
@@
-        // Get the geojson
-        if (!field.b_geometry) {
-            throw data("Field geometry is required", {
-                status: 400,
-                statusText: "Field geometry is required",
-            })
-        }

218-219: Await getTimeframe in the action.

Line 218 passes a Promise into getCultivations.

🐛 Proposed fix
-        const timeframe = getTimeframe(params)
+        const timeframe = await getTimeframe(params)

261-268: Add null-check before accessing field.b_name.

getField can return null, but the DELETE handler accesses field.b_name without verification. The GET/POST handler earlier in the same file already includes the guard—add it here for consistency.

Proposed fix
         } else if (request.method === "DELETE") {
             // Delete field
             const field = await getField(fdm, session.principal_id, b_id)
+            if (!field) {
+                throw data("Field not found", {
+                    status: 404,
+                    statusText: "Field not found",
+                })
+            }
             await removeField(fdm, session.principal_id, b_id)
🤖 Fix all issues with AI agents
In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.field.new._index.tsx:
- Around line 458-461: The action currently calls getTimeframe(params) but does
not await it, passing a Promise into downstream logic (e.g., getFields); update
the action to await getTimeframe(params) so timeframe is the resolved value
before using it with getFields and any other functions; locate the block where
getCalendar(params) and getTimeframe(params) are assigned (symbols: getCalendar,
getTimeframe, getFields) and change timeframe = await getTimeframe(params) and
adjust surrounding code to be async/await-safe so no Promise is propagated.

In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.field.new.fields.tsx:
- Around line 71-96: The code currently assumes getFarm and getField always
return records; add null guards: after calling getFarm(...) check if farm is
falsy and handle it (e.g., throw a descriptive Error or return an appropriate
response) before accessing farm.b_name_farm; for fields, replace the raw
Promise.all result with a filtered array (filter out undefined/null results from
the array returned by Promise.all on getField calls) and use that cleaned fields
array for the returned payload; update any callers/return shape accordingly and
keep the same symbols (getFarm, getField, fieldIds, farmOptions, fields,
b_name_farm) so the fix is easy to locate.
♻️ Duplicate comments (6)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.$b_id._index.tsx (3)

205-206: Await getTimeframe in the action.

Line 205 has the same issue as the loader; a Promise will flow into getCultivations.

🐛 Proposed fix
-        const timeframe = getTimeframe(params)
+        const timeframe = await getTimeframe(params)

252-263: Guard against a missing field before using field.b_name.

getField can return null; field.b_name would throw.

🐛 Proposed fix
         // Delete field
         const field = await getField(fdm, session.principal_id, b_id)
+        if (!field) {
+            throw data("Field not found", {
+                status: 404,
+                statusText: "Field not found",
+            })
+        }
         await removeField(fdm, session.principal_id, b_id)

61-65: Await getTimeframe in the loader.

Line 64 calls getTimeframe(params) without await. If it’s async (as used elsewhere), downstream calls receive a Promise.

🐛 Proposed fix
-        const timeframe = getTimeframe(params)
+        const timeframe = await getTimeframe(params)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx (3)

479-482: Guard selected_fields before JSON.parse.

If the form field is missing, String(null) becomes "null" and JSON.parse throws.

🐛 Proposed fix
-        const selectedFields = JSON.parse(
-            String(formData.get("selected_fields")),
-        )
+        const selectedFieldsRaw = formData.get("selected_fields")
+        if (!selectedFieldsRaw) {
+            throw data("No fields selected", {
+                status: 400,
+                statusText: "No fields selected",
+            })
+        }
+        const selectedFields = JSON.parse(String(selectedFieldsRaw))

533-569: Make field creation atomic across field + cultivation + soil analysis.

If addCultivation or addSoilAnalysis fails, you’ll persist a field without its related records. Wrap the per-field workflow in a single transaction.

🧩 Suggested transactional wrapper
-                    const b_id = await addField(
-                        fdm,
-                        session.principal_id,
-                        b_id_farm,
-                        b_name,
-                        b_id_source,
-                        b_geometry,
-                        b_start,
-                        b_acquiring_method,
-                        b_end,
-                    )
-                    await addCultivation(
-                        fdm,
-                        session.principal_id,
-                        b_lu_catalogue,
-                        b_id,
-                        b_lu_start,
-                        b_lu_end,
-                    )
-
-                    if (nmiApiKey) {
-                        const estimates = await getSoilParameterEstimates(
-                            field,
-                            nmiApiKey,
-                        )
-
-                        await addSoilAnalysis(
-                            fdm,
-                            session.principal_id,
-                            undefined,
-                            estimates.a_source,
-                            b_id,
-                            estimates.a_depth_lower,
-                            undefined,
-                            estimates,
-                        )
-                    }
+                    const b_id = await fdm.transaction(async (tx) => {
+                        const newId = await addField(
+                            tx,
+                            session.principal_id,
+                            b_id_farm,
+                            b_name,
+                            b_id_source,
+                            b_geometry,
+                            b_start,
+                            b_acquiring_method,
+                            b_end,
+                        )
+                        await addCultivation(
+                            tx,
+                            session.principal_id,
+                            b_lu_catalogue,
+                            newId,
+                            b_lu_start,
+                            b_lu_end,
+                        )
+                        if (nmiApiKey) {
+                            const estimates = await getSoilParameterEstimates(
+                                field,
+                                nmiApiKey,
+                            )
+                            await addSoilAnalysis(
+                                tx,
+                                session.principal_id,
+                                undefined,
+                                estimates.a_source,
+                                newId,
+                                estimates.a_depth_lower,
+                                undefined,
+                                estimates,
+                            )
+                        }
+                        return newId
+                    })

122-125: Await getTimeframe in the loader.

Line 124 uses getTimeframe(params) without await, risking a Promise flowing into getFields.

🐛 Proposed fix
-        const timeframe = getTimeframe(params)
+        const timeframe = await getTimeframe(params)

Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new._index.tsx
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.fields.tsx
@SvenVw SvenVw merged commit d53536d into development Jan 22, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enable Multi-Select for "New Field" using Atlas Selection Pattern

2 participants