Skip to content

Add persistent non-productive fields filter; sort cultivations by area#279

Merged
SvenVw merged 23 commits into
developmentfrom
FDM249
Sep 26, 2025
Merged

Add persistent non-productive fields filter; sort cultivations by area#279
SvenVw merged 23 commits into
developmentfrom
FDM249

Conversation

@SvenVw
Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw commented Sep 25, 2025

Summary by CodeRabbit

  • New Features

    • Persistent toggle to show all fields or only productive fields across lists, norms, nitrogen balance, and farm creation.
  • UI/UX

    • Cultivation plan groups fields, sorts cultivations by total area (desc), and shows parcel counts with area badges (tiny totals as “< 0.1 ha”).
    • Filter controls added to relevant headers.
    • Farm creation sidebar redesigned as a card with header/footer and a “Back to map” link.
    • Field perimeter and productivity now influence filtering and display.
  • Chores

    • Version bumps recorded in changesets.

Closes #249

@SvenVw SvenVw self-assigned this Sep 25, 2025
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Sep 25, 2025

🦋 Changeset detected

Latest commit: f0b1e79

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

This PR includes changesets to release 3 packages
Name Type
@svenvw/fdm-app Minor
@svenvw/fdm-core Minor
@svenvw/fdm-calculator Patch

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 Sep 25, 2025

Walkthrough

Adds a persistent UI toggle to hide non-productive fields, computes and exposes field geometry properties (b_perimeter, b_isproductive) in core queries, and wires client-side filtering and UI placements across multiple routes and components while adjusting loaders to return fields for client composition.

Changes

Cohort / File(s) Summary
Changesets
.changeset/clear-bats-find.md, .changeset/five-hairs-chew.md, .changeset/lemon-spies-cheat.md, .changeset/pretty-comics-drop.md
Add changelog entries / minor bumps documenting the UI toggle, cultivation sorting, and new b_perimeter / b_isproductive outputs.
Field filter store & SSR storage
fdm-app/app/store/field-filter.ts, fdm-app/app/store/storage.ts, fdm-app/app/store/changelog.ts
New persistent zustand store useFieldFilterStore (showProductiveOnly, toggle). Add ssrSafeJSONStorage adapter and switch changelog store to SSR-safe storage.
Field filter UI component
fdm-app/app/components/custom/field-filter-toggle.tsx
New FieldFilterToggle component with tooltip and icon; reads and toggles useFieldFilterStore.
UI integrations & lists
fdm-app/app/components/blocks/fields/table.tsx, fdm-app/app/components/blocks/norms/field-norms.tsx, fdm-app/app/components/blocks/cultivation/list-plan.tsx
Insert FieldFilterToggle into headers; use store flag to filter rendered fields; refactor cultivation list to aggregate fields per cultivation, sum b_area, sort by total area and render badges; minor header/layout tweaks.
Routes / loaders / page composition
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx, fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx, fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx, fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.tsx
Loader shapes changed to return fields (instead of precomputed options/items); client constructs fieldOptions/sidebar items and applies filtering using useFieldFilterStore; farm.create sidebar wrapped in Card with header/footer and toggle; DataTable sources switched to filtered fields where applicable.
Core types
fdm-core/src/field.d.ts, fdm-core/src/cultivation.d.ts
Add b_perimeter and b_isproductive to Field; add b_isproductive to CultivationPlan.fields; update some nested ID type references.
Core logic
fdm-core/src/field.ts, fdm-core/src/cultivation.ts
Compute b_perimeter (ST_Perimeter) and derive b_isproductive (perimeter/area heuristic) in getField/getFields/getCultivationPlan; add exported determineIfFieldIsProductiveByShape.
Core tests & minor test edits
fdm-core/src/field.test.ts, fdm-core/src/soil.test.ts
Update test geometries; add assertions for b_perimeter and b_isproductive (including hole-perimeter test); small stylistic test changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Toggle as FieldFilterToggle
  participant Store as useFieldFilterStore
  participant Page as App Page (route/component)
  participant Loader as Route Loader (returns fields with geometry props)

  Note over Loader: Loader returns fields with b_area, b_perimeter, b_isproductive

  User->>Toggle: click toggle
  Toggle->>Store: toggleShowProductiveOnly()
  Store-->>Page: showProductiveOnly (subscribed)

  Page->>Loader: read loaderData.fields
  alt showProductiveOnly = true
    Page->>Page: filtered = fields.filter(f => f.b_isproductive)
  else
    Page->>Page: filtered = fields
  end
  Page-->>User: render lists/tables/cards using filtered
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • gerardhros
  • BoraIneviNMI

Poem

I hopped along the perimeter line,
counted edges, areas fine.
A little toggle, snap and blink,
productive fields stay—others shrink.
Badges, lists, and maps in tow—hop! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning While the pull request delivers the UI switch, client-side filtering logic, and persistent store for hiding non-productive fields as well as sorting cultivations by area, it does not include the required database schema migration for b_isproductive, lacks updates to field creation and update functions to accept and persist that flag, and omits integration with FGB files and shapefile upload estimation specified in issue #249. Please add the missing database migration for b_isproductive, extend the field create and update endpoints to accept and store the flag, incorporate b_isproductive in FGB import logic, and implement the shapefile upload estimation so all objectives of issue #249 are met.
Out of Scope Changes Check ⚠️ Warning The changeset includes unrelated modifications such as type reference updates in cultivation.d.ts, updates to the changelog store’s persistence configuration, and various test refactorings that do not pertain to adding the non-productive field filter or sorting cultivations by area. Consider removing or isolating the type reference adjustments, changelog store changes, and test stylistic updates into separate pull requests so that this branch focuses solely on implementing the filter and sorting features.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly summarizes the two primary changes by highlighting both the addition of a persistent filter for non-productive fields and the sorting of cultivations by area, making it concise, specific, and directly related to the core functionality introduced in the pull request.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch FDM249

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 Sep 25, 2025

Codecov Report

❌ Patch coverage is 96.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 92.93%. Comparing base (d80e12e) to head (f0b1e79).
⚠️ Report is 24 commits behind head on development.

Files with missing lines Patch % Lines
fdm-core/src/field.ts 94.73% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           development     #279   +/-   ##
============================================
  Coverage        92.92%   92.93%           
============================================
  Files               79       79           
  Lines            12926    12951   +25     
  Branches          1266     1268    +2     
============================================
+ Hits             12012    12036   +24     
- Misses             912      913    +1     
  Partials             2        2           
Flag Coverage Δ
fdm-calculator 94.36% <ø> (ø)
fdm-core 91.92% <96.00%> (+0.01%) ⬆️
fdm-data 94.40% <ø> (ø)

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.

@SvenVw SvenVw changed the title FDM249 @coderabbitai Sep 25, 2025
@SvenVw SvenVw marked this pull request as ready for review September 25, 2025 14:57
@coderabbitai coderabbitai Bot changed the title @coderabbitai Add persistent non-productive fields filter; sort cultivations by area Sep 25, 2025
@coderabbitai coderabbitai Bot added branch:development Issue only affecting development, not the main branch (yet) enhancement New feature or request fdm-app fdm-core labels Sep 25, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (1)
fdm-core/src/soil.test.ts (1)

147-164: Fix the async rejection assertion

.rejects expects a promise, but here you're passing a function. Vitest now throws before reaching the assertion, breaking the test. Call addSoilAnalysis and pass its promise directly.

-        await expect(async () => {
-            await addSoilAnalysis(
-                fdm,
-                principal_id,
-                a_date,
-                a_source,
-                b_id,
-                a_depth_lower,
-                b_sampling_date,
-                {
-                    a_p_al: a_p_al,
-                    a_p_cc: a_p_cc,
-                    b_soiltype_agr: b_soiltype_agr,
-                    b_gwl_class: b_gwl_class,
-                },
-                a_depth_upper,
-            )
-        }).rejects.toThrowError("Exception for addSoilAnalysis")
+        await expect(
+            addSoilAnalysis(
+                fdm,
+                principal_id,
+                a_date,
+                a_source,
+                b_id,
+                a_depth_lower,
+                b_sampling_date,
+                {
+                    a_p_al: a_p_al,
+                    a_p_cc: a_p_cc,
+                    b_soiltype_agr: b_soiltype_agr,
+                    b_gwl_class: b_gwl_class,
+                },
+                a_depth_upper,
+            ),
+        ).rejects.toThrowError("Exception for addSoilAnalysis")
🧹 Nitpick comments (15)
fdm-core/src/field.test.ts (2)

92-95: Remove noisy console.log from tests

Keep test output clean.

Apply this diff:

-            console.log(field)
             expect(field.b_perimeter).toBeGreaterThan(0)
             expect(field.b_isproductive).toBe(true)

189-192: DRY up repeated perimeter/productivity assertions

Extract a small helper (e.g., expectFieldMetrics(field)) to reduce duplication across these loops.

Also applies to: 331-333, 349-352, 370-373, 388-391, 401-404

fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.tsx (5)

2-2: Remove unused icon import

Car is not used.

Apply this diff:

-import { ArrowLeft, Car } from "lucide-react"
+import { ArrowLeft } from "lucide-react"

10-10: Add useMemo import for computed sidebar items

Preps for memoization change below.

Apply this diff:

 } from "react-router"
+import { useMemo } from "react"

23-31: Prefer alias imports for consistency

Use ~ alias like the rest of the file.

Apply this diff:

-import { FieldFilterToggle } from "../components/custom/field-filter-toggle"
+import { FieldFilterToggle } from "~/components/custom/field-filter-toggle"
 import {
-    Card,
-    CardContent,
-    CardFooter,
-    CardHeader,
-    CardTitle,
-} from "../components/ui/card"
+    Card,
+    CardContent,
+    CardFooter,
+    CardHeader,
+    CardTitle,
+} from "~/components/ui/card"

47-63: Update loader JSDoc to match returned shape

Docs still mention sidebarPageItems/cultivationOptions/mapboxToken but loader now returns fields.

Apply this diff:

- * The fields are sorted alphabetically by name and converted into sidebar navigation items for use in the UI.
+ * The fields are returned for client-side filtering/sorting into sidebar navigation items.
@@
- * - sidebarPageItems: Navigation items for each field.
- * - cultivationOptions: A list of available cultivation options.
- * - mapboxToken: The Mapbox access token.
+ * - fields: Array of fields for the farm (respects timeframe).
  * - b_id_farm: The farm ID.
  * - b_name_farm: The name of the farm.
  * - action: The URL for field update submissions.

Also applies to: 93-97


109-117: Memoize and sort sidebar items for stable UX

Prevents re-compute on every render and ensures deterministic alpha order.

Apply this diff:

-    // Create the sidenav
-    const sidebarPageItems = fields
-        .filter((field) => (showProductiveOnly ? field.b_isproductive : true))
-        .map((field) => {
-            return {
-                title: field.b_name,
-                to: `/farm/create/${b_id_farm}/${calendar}/fields/${field.b_id}`,
-            }
-        })
+    // Create the sidenav
+    const sidebarPageItems = useMemo(
+        () =>
+            fields
+                .filter((field) =>
+                    showProductiveOnly ? field.b_isproductive : true,
+                )
+                .slice()
+                .sort((a, b) => a.b_name.localeCompare(b.b_name))
+                .map((field) => ({
+                    title: field.b_name,
+                    to: `/farm/create/${b_id_farm}/${calendar}/fields/${field.b_id}`,
+                })),
+        [fields, showProductiveOnly, b_id_farm, calendar],
+    )
.changeset/pretty-comics-drop.md (1)

5-6: Changeset wording likely inaccurate: b_area already existed

If b_area was already part of the Field output, this entry should only mention b_perimeter to avoid confusion.

Proposed tweak:

-Adds `b_perimeter` and `b_area` to output of `getField` and `getFields`
+Expose `b_perimeter` in the output of `getField` and `getFields`
.changeset/five-hairs-chew.md (1)

5-6: Polish the changeset sentence for clarity

Small grammar/style improvement.

-At farm create wizard show the cultivations in bouwplan on descending total area instead of random order
+In the farm create wizard, show the cultivations in the bouwplan sorted by descending total area (instead of a random order)
fdm-app/app/components/custom/field-filter-toggle.tsx (1)

20-45: Remove unnecessary wrapper div

The outer div with an empty className is not needed.

-    return (
-        <div className="">
-            <TooltipProvider>
+    return (
+        <TooltipProvider>
             <Tooltip>
                 <TooltipTrigger asChild>
                     <Button
                         variant="outline"
                         size="icon"
                         onClick={toggleShowProductiveOnly}
                         className="border hover:bg-accent hover:text-accent-foreground"
                         aria-label={tooltipContent}
                         aria-pressed={showProductiveOnly}
                     >
                         {showProductiveOnly ? (
                             <Square className="h-4 w-4 text-primary" />
                         ) : (
                             <PanelsRightBottom className="h-4 w-4" />
                         )}
                     </Button>
                 </TooltipTrigger>
                 <TooltipContent>
                     <p>{tooltipContent}</p>
                 </TooltipContent>
             </Tooltip>
-            </TooltipProvider>
-        </div>
+        </TooltipProvider>
     )
fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (2)

30-31: Remove unused import.

FieldFilterToggle is imported but not rendered here. The DataTable header already includes it; drop this import to avoid duplicate UI and reduce bundle size.

-import { FieldFilterToggle } from "~/components/custom/field-filter-toggle"

153-154: Default b_isproductive to true for backward compatibility
Replace in fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (line 153):

- b_isproductive: field.b_isproductive,
+ b_isproductive: field.b_isproductive ?? true,
fdm-core/src/cultivation.ts (2)

620-622: Avoid rounding in SQL for area/perimeter (round at presentation).

Rounding here propagates precision loss into classification (b_isproductive) and summed areas. Prefer raw floats and round only in the UI.

-                b_area: sql<number>`ROUND((ST_Area(b_geometry::geography)/10000)::NUMERIC, 2)::FLOAT`,
-                b_perimeter: sql<number>`ROUND((ST_Perimeter(b_geometry::geography))::NUMERIC, 2)::FLOAT`,
+                b_area: sql<number>`(ST_Area(b_geometry::geography)/10000)::FLOAT`,
+                b_perimeter: sql<number>`ST_Perimeter(b_geometry::geography)::FLOAT`,

775-780: Verify shape ratio choice for b_isproductive.

The current ratio uses perimeter (m) / area (m²), which is not dimensionless and scales with size. Consider a dimensionless metric (e.g., 4πA/P² or P/√A) to improve robustness, then recalibrate the constant. At minimum, document units explicitly.

I can propose a drop-in calculation using the isoperimetric quotient if you confirm the desired threshold.

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

196-204: Consider exposing the toggle in this view’s header.

You use the store but don’t render a toggle here. If HeaderNorms doesn’t include it, add FieldFilterToggle near the title for discoverability.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48fe689 and 57b4fe8.

📒 Files selected for processing (19)
  • .changeset/clear-bats-find.md (1 hunks)
  • .changeset/five-hairs-chew.md (1 hunks)
  • .changeset/lemon-spies-cheat.md (1 hunks)
  • .changeset/pretty-comics-drop.md (1 hunks)
  • fdm-app/app/components/blocks/cultivation/list-plan.tsx (3 hunks)
  • fdm-app/app/components/blocks/fields/table.tsx (2 hunks)
  • fdm-app/app/components/blocks/norms/field-norms.tsx (2 hunks)
  • fdm-app/app/components/custom/field-filter-toggle.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx (6 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (5 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (6 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.tsx (6 hunks)
  • fdm-app/app/store/field-filter.ts (1 hunks)
  • fdm-core/src/cultivation.d.ts (3 hunks)
  • fdm-core/src/cultivation.ts (3 hunks)
  • fdm-core/src/field.d.ts (1 hunks)
  • fdm-core/src/field.test.ts (12 hunks)
  • fdm-core/src/field.ts (5 hunks)
  • fdm-core/src/soil.test.ts (8 hunks)
🧰 Additional context used
🧠 Learnings (24)
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
PR: SvenVw/fdm#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-core/src/field.d.ts
  • .changeset/pretty-comics-drop.md
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
PR: SvenVw/fdm#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-core/src/field.d.ts
📚 Learning: 2025-01-31T15:34:20.850Z
Learnt from: SvenVw
PR: SvenVw/fdm#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-core/src/field.d.ts
  • .changeset/pretty-comics-drop.md
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
PR: SvenVw/fdm#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-core/src/field.d.ts
📚 Learning: 2025-08-11T11:55:26.053Z
Learnt from: SvenVw
PR: SvenVw/fdm#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-core/src/cultivation.d.ts
📚 Learning: 2025-01-23T15:18:57.212Z
Learnt from: SvenVw
PR: SvenVw/fdm#49
File: fdm-core/src/db/schema.ts:407-426
Timestamp: 2025-01-23T15:18:57.212Z
Learning: In the farm data model, each cultivation (b_lu) can have only one termination date but can have multiple harvest dates. This is enforced through the database schema where cultivationTerminating uses b_lu as primary key while cultivationHarvesting uses a composite primary key of b_id_harvestable and b_lu.

Applied to files:

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

Applied to files:

  • fdm-core/src/cultivation.d.ts
📚 Learning: 2025-02-13T08:35:59.306Z
Learnt from: SvenVw
PR: SvenVw/fdm#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-core/src/cultivation.d.ts
  • fdm-core/src/field.test.ts
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#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._index.tsx
  • fdm-app/app/components/blocks/cultivation/list-plan.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.tsx
📚 Learning: 2025-09-23T12:27:07.359Z
Learnt from: SvenVw
PR: SvenVw/fdm#274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.359Z
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._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.tsx
📚 Learning: 2025-09-23T12:29:34.158Z
Learnt from: SvenVw
PR: SvenVw/fdm#274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.158Z
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._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.tsx
📚 Learning: 2025-09-23T12:37:58.700Z
Learnt from: SvenVw
PR: SvenVw/fdm#274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.700Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#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._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.tsx
📚 Learning: 2025-08-11T12:24:32.200Z
Learnt from: SvenVw
PR: SvenVw/fdm#233
File: fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx:53-53
Timestamp: 2025-08-11T12:24:32.200Z
Learning: In `fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx`, the NMI API for cultivations guarantees that each year will be unique in the cultivation history data, so using `cultivation.year` as a React list key is safe and won't cause duplicate key warnings.

Applied to files:

  • fdm-app/app/components/blocks/cultivation/list-plan.tsx
📚 Learning: 2025-04-18T13:49:17.029Z
Learnt from: SvenVw
PR: SvenVw/fdm#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.balance.nitrogen._index.tsx
📚 Learning: 2024-12-16T10:56:07.561Z
Learnt from: SvenVw
PR: SvenVw/fdm#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.balance.nitrogen._index.tsx
📚 Learning: 2025-05-09T14:58:10.465Z
Learnt from: SvenVw
PR: SvenVw/fdm#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.balance.nitrogen._index.tsx
📚 Learning: 2025-05-09T14:53:44.578Z
Learnt from: SvenVw
PR: SvenVw/fdm#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.balance.nitrogen._index.tsx
📚 Learning: 2025-05-09T14:41:43.484Z
Learnt from: SvenVw
PR: SvenVw/fdm#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.balance.nitrogen._index.tsx
📚 Learning: 2025-05-09T14:41:43.484Z
Learnt from: SvenVw
PR: SvenVw/fdm#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.balance.nitrogen._index.tsx
📚 Learning: 2025-01-23T15:17:23.027Z
Learnt from: SvenVw
PR: SvenVw/fdm#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-core/src/field.test.ts
📚 Learning: 2025-08-11T12:14:28.354Z
Learnt from: SvenVw
PR: SvenVw/fdm#233
File: fdm-core/src/db/schema.ts:370-0
Timestamp: 2025-08-11T12:14:28.354Z
Learning: In the FDM database schema, the field `b_lu_rest_oravib` in the `cultivations_catalogue` table is intentionally nullable (can be undefined). When undefined, it should be treated as `false` in the UI layer. This allows distinguishing between "explicitly set to false" and "not set" at the database level while providing simplified boolean logic in the UI.

Applied to files:

  • fdm-core/src/field.test.ts
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#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.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#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.tsx
🧬 Code graph analysis (14)
fdm-core/src/field.d.ts (1)
fdm-core/src/db/schema.ts (2)
  • fieldAcquiringTypeSelect (83-83)
  • fieldDiscardingTypeSelect (127-127)
fdm-app/app/components/blocks/norms/field-norms.tsx (1)
fdm-app/app/components/custom/field-filter-toggle.tsx (1)
  • FieldFilterToggle (11-46)
fdm-core/src/cultivation.d.ts (2)
fdm-core/src/db/schema.ts (3)
  • cultivationHarvestingTypeSelect (474-475)
  • harvestablesTypeSelect (397-397)
  • cultivationsCatalogueTypeSelect (381-382)
fdm-core/src/index.ts (1)
  • CultivationCatalogue (49-49)
fdm-app/app/components/custom/field-filter-toggle.tsx (1)
fdm-app/app/store/field-filter.ts (1)
  • useFieldFilterStore (9-23)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (2)
fdm-app/app/store/field-filter.ts (1)
  • useFieldFilterStore (9-23)
fdm-app/app/components/blocks/fields/table.tsx (1)
  • DataTable (51-339)
fdm-app/app/components/blocks/cultivation/list-plan.tsx (1)
fdm-core/src/db/schema.ts (1)
  • fields (87-103)
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx (2)
fdm-app/app/store/field-filter.ts (1)
  • useFieldFilterStore (9-23)
fdm-app/app/components/custom/field-filter-toggle.tsx (1)
  • FieldFilterToggle (11-46)
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (2)
fdm-core/src/db/schema.ts (1)
  • fields (87-103)
fdm-app/app/store/field-filter.ts (1)
  • useFieldFilterStore (9-23)
fdm-core/src/soil.test.ts (1)
fdm-core/src/db/schema.ts (1)
  • fieldsTypeInsert (106-106)
fdm-app/app/components/blocks/fields/table.tsx (1)
fdm-app/app/components/custom/field-filter-toggle.tsx (1)
  • FieldFilterToggle (11-46)
fdm-core/src/field.ts (1)
fdm-core/src/db/schema.ts (1)
  • fields (87-103)
fdm-core/src/field.test.ts (2)
fdm-core/src/db/schema.ts (1)
  • fields (87-103)
fdm-core/src/field.ts (2)
  • addField (48-129)
  • getField (145-202)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.tsx (3)
fdm-app/app/store/field-filter.ts (1)
  • useFieldFilterStore (9-23)
fdm-app/app/components/custom/field-filter-toggle.tsx (1)
  • FieldFilterToggle (11-46)
fdm-app/app/components/custom/sidebar-page.tsx (1)
  • SidebarPage (12-49)
fdm-core/src/cultivation.ts (1)
fdm-core/src/field.ts (1)
  • BUFFERSTROKEN_CONSTANT (23-23)
🔇 Additional comments (23)
fdm-core/src/field.test.ts (4)

59-63: Unit-square geometry change LGTM

Simple, valid polygon. Good for deterministic area/perimeter in tests.


427-431: Unit-square geometry in update test LGTM


506-510: Unit-square geometry in partial-update test LGTM


731-733: Catalogue defaults updated LGTM

Explicit boolean and empty array are sensible defaults for test data.

fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.tsx (3)

106-108: Loader data destructuring LGTM


139-147: Proceed button disabling condition LGTM

Disables when no visible fields (respects current filter).


156-179: Sidebar Card composition LGTM

Good integration of the filter toggle into the header and a clear back-to-map footer.

.changeset/clear-bats-find.md (1)

5-6: Changeset LGTM

Clear and aligned with the PR intent.

.changeset/lemon-spies-cheat.md (1)

5-6: Changeset LGTM

Accurately documents the new b_isproductive in getCultivationPlan output.

fdm-app/app/components/blocks/norms/field-norms.tsx (1)

8-9: Good UX addition; confirm data is filtered when toggled

Header integration looks good. Please confirm the list of fieldNorms respects showProductiveOnly (either filtered upstream or handled here); otherwise the toggle won’t affect this view.

If needed, mirror the optional approach from the table: read showProductiveOnly from the store and filter out entries where b_isproductive === false before mapping.

Also applies to: 43-46

fdm-core/src/field.d.ts (1)

11-11: Types extended appropriately; document null semantics

b_perimeter: number | null and b_isproductive: boolean | null are fine. Please document how null is treated downstream (e.g., null => treat as productive) to keep filtering consistent across UI and loaders.

Ensure the following are updated accordingly:

  • getField/getFields return shapes include these props.
  • Any CSV/GeoJSON export or shapefile processing that serializes fields accounts for the new keys (either emitting null or omitting).

Also applies to: 15-15

fdm-app/app/components/custom/field-filter-toggle.tsx (1)

1-1: PanelsRightBottom support confirmed lucide-react is pinned to ^0.544.0, which includes PanelsRightBottom (introduced in v0.302.0).

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

44-44: Toggle added but DataTable doesn’t filter by productive flag
Confirm that parent routes filter the data array by b_isproductive before passing it into this component; otherwise toggling “productive only” won’t change the table. If you’d prefer local handling, wrap data in a useMemo that applies showProductiveOnly from useFieldFilterStore (e.g. filter out items where b_isproductive === false) before passing to useReactTable.

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

184-191: Client-side filtering looks good.

Filter guard reads well and matches the toggle semantics.


242-247: Title wrapper LGTM.

Structure change is fine and non-breaking.


252-253: Passing filtered data to the table is correct.

Right source connected to the DataTable.

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

144-145: Productive-only filtering is wired correctly; clarify totals behavior.

  • Using the store, filtering the field list, and rendering the toggle in the card header all look correct.
  • Confirm whether the toggle should also affect company-level totals (balance/supply/removal/emission). Currently, only the list respects the filter, which may be surprising to users.

If totals should respect the filter, we can derive filtered aggregates from resolvedNitrogenBalanceResult.fields similarly to the list.

Also applies to: 216-223, 325-328, 333-365

fdm-core/src/cultivation.d.ts (2)

32-32: Type addition LGTM.

b_isproductive: boolean on fields aligns with runtime changes.


42-46: Type fixes LGTM.

Corrected b_id_harvesting and b_id_harvestable references reflect schema types.

fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (3)

175-176: Returning fields (not fieldOptions) is fine.

Downstream transformation to options is handled locally; this keeps the loader generic.


267-283: Filtering logic is correct and consistent.

Using a fieldsMap to consult b_isproductive is efficient and clear.


300-302: Props wiring LGTM.

Filtered norms and rebuilt fieldOptions are correctly passed.

fdm-app/app/components/blocks/cultivation/list-plan.tsx (1)

100-103: Area formatting LGTM.

Threshold display for small areas is clear and user-friendly.

Comment thread fdm-app/app/components/blocks/cultivation/list-plan.tsx Outdated
Comment thread fdm-app/app/store/field-filter.ts
Comment thread fdm-core/src/field.test.ts
Comment thread fdm-core/src/field.ts Outdated
@SvenVw SvenVw requested a review from gerardhros September 26, 2025 08:21
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

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.norms.tsx (1)

169-177: Avoid sending full field objects (likely includes geometry); return minimal shape.

Returning fields directly can bloat the streamed payload (SSR) and leak unnecessary data (e.g., geometry). Return only { b_id, b_name, b_isproductive }.

Apply this diff to the return:

-            fields: fields,
+            fields: fieldsBasic,

Add this mapping after fetching fields:

// Keep client payload minimal (avoid b_geometry and other heavy props)
const fieldsBasic = fields.map((f) => ({
  b_id: f.b_id,
  b_name: f.b_name,
  b_isproductive: f.b_isproductive,
}))
🧹 Nitpick comments (8)
fdm-core/src/soil.test.ts (1)

147-165: Correct use of await expect(...).rejects.
Assertion matches error wrapping behavior from handleError.

Consider asserting on the specific cause too to reduce brittleness:

await expect(addSoilAnalysis(/*...*/)).rejects.toThrowError(/addSoilAnalysis/)
fdm-core/src/field.ts (3)

190-193: Avoid using rounded area for classification.
b_area is rounded to 2 decimals (ha). Near the 2.5 ha boundary this can misclassify fields. Compute the heuristic with unrounded area.

Proposed approach (outside this range):

  • Add an unrounded area alias in both queries (e.g., b_area_ha_raw) and use it for determineIfFieldIsProductiveByShape, keeping rounded b_area for display.

Example change in both select projections:

b_area: sql<number>`ROUND((ST_Area(b_geometry::geography)/10000)::NUMERIC, 2)::FLOAT`,
b_area_ha_raw: sql<number>`(ST_Area(b_geometry::geography)/10000)::FLOAT`,

Then update usage:

field[0].b_isproductive = determineIfFieldIsProductiveByShape(
  field[0].b_area_ha_raw,
  field[0].b_perimeter,
)

303-306: Same: use unrounded area for b_isproductive in the loop.
Mirror the change here to keep consistency between single and batch fetch.


654-665: Minor: declare an explicit return type.
Make the function signature explicit and slightly more defensive.

Apply:

-export function determineIfFieldIsProductiveByShape(
-    b_area: number,
-    b_perimeter: number,
-) {
+export function determineIfFieldIsProductiveByShape(
+    b_area: number,
+    b_perimeter: number,
+): boolean {
     const BUFFERSTROKEN_CONSTANT = 20

     return (
         b_perimeter / Math.sqrt(b_area * 10000) < BUFFERSTROKEN_CONSTANT ||
         b_area >= 2.5
     )
 }
fdm-app/app/store/storage.ts (1)

3-14: Harden storage detection and add a resilient fallback.
window existing doesn’t guarantee localStorage is usable (e.g., Safari private mode). Wrap in try/catch and use in-memory fallback.

Apply:

-const createSSRStorage = (): StateStorage => {
-    if (typeof window !== "undefined") {
-        return localStorage
-    }
-
-    // Return a no-op storage for SSR
-    return {
-        getItem: (_name: string) => null,
-        setItem: (_name: string, _value: string) => {},
-        removeItem: (_name: string) => {},
-    }
-}
+const createSSRStorage = (): StateStorage => {
+    if (typeof window !== "undefined") {
+        try {
+            const testKey = "__z_test__"
+            window.localStorage.setItem(testKey, "1")
+            window.localStorage.removeItem(testKey)
+            return window.localStorage
+        } catch {
+            // fall through to memory storage
+        }
+    }
+
+    // Memory storage for SSR or blocked storage environments
+    const memory = new Map<string, string>()
+    return {
+        getItem: (name: string) => memory.get(name) ?? null,
+        setItem: (name: string, value: string) => {
+            memory.set(name, value)
+        },
+        removeItem: (name: string) => {
+            memory.delete(name)
+        },
+    }
+}
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.tsx (2)

105-122: Use a selector to reduce re-renders.
Select just showProductiveOnly from the store.

Apply:

-    const { showProductiveOnly } = useFieldFilterStore()
+    const showProductiveOnly = useFieldFilterStore((s) => s.showProductiveOnly)

108-122: Optional: micro-optimizations.
Use Intl.NumberFormat if you later display areas here; stable formatting across locales.

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

224-224: Use a selector with zustand to avoid unnecessary re-renders.

Select only showProductiveOnly instead of subscribing to the entire store.

Apply this diff:

-    const { showProductiveOnly } = useFieldFilterStore()
+    const showProductiveOnly = useFieldFilterStore((s) => s.showProductiveOnly)

Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57b4fe8 and d110f27.

📒 Files selected for processing (14)
  • .changeset/five-hairs-chew.md (1 hunks)
  • .changeset/pretty-comics-drop.md (1 hunks)
  • fdm-app/app/components/blocks/cultivation/list-plan.tsx (4 hunks)
  • fdm-app/app/components/custom/field-filter-toggle.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx (5 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (5 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.tsx (6 hunks)
  • fdm-app/app/store/changelog.ts (2 hunks)
  • fdm-app/app/store/field-filter.ts (1 hunks)
  • fdm-app/app/store/storage.ts (1 hunks)
  • fdm-core/src/cultivation.ts (3 hunks)
  • fdm-core/src/field.test.ts (12 hunks)
  • fdm-core/src/field.ts (5 hunks)
  • fdm-core/src/soil.test.ts (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • fdm-app/app/components/custom/field-filter-toggle.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx
  • .changeset/five-hairs-chew.md
  • fdm-core/src/field.test.ts
  • fdm-core/src/cultivation.ts
  • fdm-app/app/store/field-filter.ts
🧰 Additional context used
🧠 Learnings (13)
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
PR: SvenVw/fdm#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:

  • .changeset/pretty-comics-drop.md
📚 Learning: 2025-01-31T15:34:20.850Z
Learnt from: SvenVw
PR: SvenVw/fdm#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:

  • .changeset/pretty-comics-drop.md
📚 Learning: 2025-09-25T15:10:59.699Z
Learnt from: SvenVw
PR: SvenVw/fdm#272
File: fdm-app/app/store/field-fertilizer-form.tsx:45-47
Timestamp: 2025-09-25T15:10:59.699Z
Learning: In the FDM application, Zustand stores with persist middleware using sessionStorage/localStorage don't require SSR hardening guards. The existing store patterns in fdm-app work without typeof window checks or memory storage fallbacks, as evidenced by the changelog store using createJSONStorage(() => localStorage) directly.

Applied to files:

  • fdm-app/app/store/storage.ts
  • fdm-app/app/store/changelog.ts
📚 Learning: 2025-09-25T15:10:59.699Z
Learnt from: SvenVw
PR: SvenVw/fdm#272
File: fdm-app/app/store/field-fertilizer-form.tsx:45-47
Timestamp: 2025-09-25T15:10:59.699Z
Learning: In the FDM application, Zustand stores with persist middleware using sessionStorage/localStorage don't require SSR hardening guards. The existing store patterns in fdm-app work without typeof window checks or memory storage fallbacks.

Applied to files:

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

Applied to files:

  • fdm-app/app/components/blocks/cultivation/list-plan.tsx
📚 Learning: 2025-04-18T13:49:17.029Z
Learnt from: SvenVw
PR: SvenVw/fdm#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/components/blocks/cultivation/list-plan.tsx
📚 Learning: 2025-01-14T16:06:24.294Z
Learnt from: SvenVw
PR: SvenVw/fdm#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/components/blocks/cultivation/list-plan.tsx
📚 Learning: 2025-01-14T16:06:21.832Z
Learnt from: SvenVw
PR: SvenVw/fdm#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/components/blocks/cultivation/list-plan.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#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.tsx
📚 Learning: 2025-09-23T12:27:07.359Z
Learnt from: SvenVw
PR: SvenVw/fdm#274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.359Z
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.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#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.tsx
📚 Learning: 2025-09-23T12:29:34.158Z
Learnt from: SvenVw
PR: SvenVw/fdm#274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.158Z
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.norms.tsx
📚 Learning: 2025-09-23T12:37:58.700Z
Learnt from: SvenVw
PR: SvenVw/fdm#274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.700Z
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.norms.tsx
🧬 Code graph analysis (6)
fdm-app/app/components/blocks/cultivation/list-plan.tsx (1)
fdm-core/src/db/schema.ts (1)
  • fields (87-103)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.tsx (4)
fdm-app/app/store/field-filter.ts (1)
  • useFieldFilterStore (10-24)
fdm-app/app/components/blocks/header/create-farm.tsx (1)
  • HeaderFarmCreate (8-80)
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.norms.tsx (2)
fdm-core/src/db/schema.ts (1)
  • fields (87-103)
fdm-app/app/store/field-filter.ts (1)
  • useFieldFilterStore (10-24)
fdm-core/src/soil.test.ts (2)
fdm-core/src/db/schema.ts (1)
  • fieldsTypeInsert (106-106)
fdm-core/src/soil.ts (1)
  • addSoilAnalysis (35-98)
fdm-core/src/field.ts (1)
fdm-core/src/db/schema.ts (1)
  • fields (87-103)
fdm-app/app/store/changelog.ts (1)
fdm-app/app/store/storage.ts (1)
  • ssrSafeJSONStorage (16-16)
🔇 Additional comments (13)
fdm-core/src/soil.test.ts (3)

19-19: Nice type alias to keep tests aligned with schema.
Keeps geometry typing DRY and resilient to future changes.


52-63: LGTM: Geometry typed via alias.
Clear and correct GeoJSON polygon for test setup.


410-419: Good switch from .map to .forEach for side-effect assertions.
Avoids creating unused arrays while keeping per-item checks.

Also applies to: 431-439, 452-460, 473-478, 491-496

fdm-core/src/field.ts (1)

281-282: Same perimeter issue in fields list query.
Duplicate of the above; switch to ST_Perimeter(...) here as well.

.changeset/pretty-comics-drop.md (1)

5-5: Changeset reads well.
Scope and API surface changes are clear.

fdm-app/app/store/changelog.ts (2)

4-4: Good move to SSR-safe storage.
Prevents server-side crashes while keeping client persistence.


49-50: Storage provider integration looks correct.
createJSONStorage(() => ssrSafeJSONStorage) matches the new adapter.

fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.tsx (3)

91-96: Loader shape change is clear and aligns with client-side filtering.
Returning raw fields enables local sort/filter without extra roundtrips.


142-156: Continue button disablement respects filtered list.
Nice UX detail; prevents navigating with no visible fields.


160-184: Good: Toggle exposed in the sidebar header and applied to items.
Matches the persistent filter objective.

fdm-app/app/components/blocks/cultivation/list-plan.tsx (1)

33-54: Apply productive-only filter/toggle and ensure unique React keys

  • Read showProductiveOnly from useFieldFilterStore(), filter cultivation.fields before computing b_area, add <FieldFilterToggle /> to the header, and format totalArea with Number(totalArea.toFixed(1)).
  • Confirm b_lu_catalogue is unique before using it as key={cultivation.b_lu_catalogue} in each <NavLink> (e.g. line 73); if duplicates are possible, append an index (\${b_lu_catalogue}-${index}``) or use a stable unique ID.
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (2)

29-35: Imports LGTM.

Correct integration of the filter store and UI components.


300-302: Prop wiring LGTM.

Passing filteredFieldNorms and computed fieldOptions is correct.

Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx Outdated
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx
Comment thread fdm-core/src/field.ts
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

🧹 Nitpick comments (2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (2)

271-276: Memoize filtering to avoid recomputation on re-renders.

Logic === true is fine given the monorepo guarantee; wrap the map and filter in useMemo for stability/perf.

As per learnings

Apply:

-        const fieldsMap = new Map(loaderData.fields.map((f) => [f.b_id, f]))
-        const filteredFieldNorms = fieldNorms.filter((fieldNorm) => {
-            if (!showProductiveOnly) return true
-            const fieldData = fieldsMap.get(fieldNorm.b_id)
-            return fieldData ? fieldData.b_isproductive === true : false
-        })
+        const fieldsMap = useMemo(
+            () => new Map(loaderData.fields.map((f) => [f.b_id, f])),
+            [loaderData.fields],
+        )
+        const filteredFieldNorms = useMemo(
+            () =>
+                !showProductiveOnly
+                    ? fieldNorms
+                    : fieldNorms.filter((fieldNorm) => {
+                          const fieldData = fieldsMap.get(fieldNorm.b_id)
+                          return fieldData
+                              ? fieldData.b_isproductive === true
+                              : false
+                      }),
+            [showProductiveOnly, fieldNorms, fieldsMap],
+        )

And add import:

-import { Suspense, use } from "react"
+import { Suspense, use, useMemo } from "react"

294-296: Sorting by area: ensure desired ordering before render.

If FieldNorms doesn’t sort, consider pre-sorting by b_area to satisfy the PR goal.

Option:

-                <FieldNorms
-                    fieldNorms={filteredFieldNorms}
-                    fieldOptions={fieldOptions}
-                />
+                <FieldNorms
+                    fieldNorms={[...filteredFieldNorms].sort((a, b) => b.b_area - a.b_area)}
+                    fieldOptions={fieldOptions}
+                />

If FieldNorms already handles sorting, ignore this.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d110f27 and f0b1e79.

📒 Files selected for processing (1)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (5 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#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.norms.tsx
📚 Learning: 2025-09-23T12:29:34.158Z
Learnt from: SvenVw
PR: SvenVw/fdm#274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.158Z
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.norms.tsx
📚 Learning: 2025-09-26T08:34:50.347Z
Learnt from: SvenVw
PR: SvenVw/fdm#279
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx:277-283
Timestamp: 2025-09-26T08:34:50.347Z
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.norms.tsx
📚 Learning: 2025-08-13T10:33:05.313Z
Learnt from: SvenVw
PR: SvenVw/fdm#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.norms.tsx
📚 Learning: 2025-09-23T12:37:58.700Z
Learnt from: SvenVw
PR: SvenVw/fdm#274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.700Z
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.norms.tsx
🧬 Code graph analysis (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (2)
fdm-core/src/db/schema.ts (1)
  • fields (87-103)
fdm-app/app/store/field-filter.ts (1)
  • useFieldFilterStore (10-24)
🔇 Additional comments (4)
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (4)

29-34: Imports integration looks good.

All newly added imports are used. No dead code introduced.


224-224: Good: persistent filter state wired in.

Using useFieldFilterStore() here aligns with the persisted toggle from zustand.


267-270: Nice fix: filter invalid fields instead of throwing.

Pre-filtering loaderData.fields for b_id and b_name avoids render-time exceptions and matches prior feedback.


169-177: No loaderData.fieldOptions usages in norms route
This file uses loaderData.fields and derives fieldOptions locally.

Comment thread fdm-core/src/field.ts
Copy link
Copy Markdown
Collaborator

@gerardhros gerardhros left a comment

Choose a reason for hiding this comment

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

nice update.

@SvenVw SvenVw merged commit 4511ee8 into development Sep 26, 2025
10 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Sep 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add switch to hide non-productive fields

2 participants