Skip to content

Add calculate nitrogen balance target#143

Merged
SvenVw merged 21 commits into
developmentfrom
add-calculate-nitrogen-balance-target
May 27, 2025
Merged

Add calculate nitrogen balance target#143
SvenVw merged 21 commits into
developmentfrom
add-calculate-nitrogen-balance-target

Conversation

@SvenVw
Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw commented May 27, 2025

Summary by CodeRabbit

  • New Features
    • Added a bar chart visualization to the nitrogen balance page for both farm and field views, comparing supply, removal, and emission of nitrogen.
    • Nitrogen balance is now displayed alongside the target value at both farm and field levels, with visual indicators showing if the balance meets the target.
    • Enhanced descriptive context and icons in the nitrogen balance cards for clearer interpretation.
    • Introduced a new nitrogen balance target calculation feature based on cultivation type, soil, and groundwater class.
  • Bug Fixes
    • Minor formatting and UI adjustments for improved clarity.
  • Tests
    • Introduced comprehensive unit tests for nitrogen balance calculations and target determination.
  • Documentation
    • Added changeset files documenting the new features and updates.

@SvenVw SvenVw self-assigned this May 27, 2025
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 27, 2025

🦋 Changeset detected

Latest commit: 7fc4005

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

This PR includes changesets to release 2 packages
Name Type
@svenvw/fdm-app Minor
@svenvw/fdm-calculator 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 May 27, 2025

Walkthrough

The changes introduce a new nitrogen balance target calculation feature, update types and logic to support this, and enhance the UI with bar chart visualizations and comparative displays of nitrogen balance versus target at both farm and field levels. New tests and documentation are added to support the new functionality.

Changes

Files/Paths Change Summary
.changeset/rotten-carrots-fix.md
.changeset/curly-streets-cross.md
.changeset/thin-planets-see.md
Added changeset files documenting minor updates to the @svenvw/fdm-calculator and @svenvw/fdm-app packages, describing the introduction of nitrogen balance target calculation, target comparison feature, and a new bar chart visualization.
fdm-calculator/src/balance/nitrogen/index.ts
fdm-calculator/src/balance/nitrogen/target.ts
fdm-calculator/src/balance/nitrogen/types.d.ts
Added new nitrogen balance calculation module, target calculation logic, and extended types to include target values. Introduced utility functions for numeric conversions and soil analysis combination.
fdm-calculator/src/balance/nitrogen/index.test.ts
fdm-calculator/src/balance/nitrogen/target.test.ts
Added comprehensive unit tests for nitrogen balance calculations, soil analysis combination, and target determination, covering various scenarios and error cases.
fdm-app/app/components/custom/balance/nitrogen-chart.tsx Added new React component rendering a vertical bar chart of nitrogen supply, removal, and emission using recharts.
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
Updated loader functions to include farm data. Enhanced UI to display nitrogen balance versus target with conditional icons, descriptive text, and the new bar chart visualization at both field and farm levels.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI (Farm/Field Page)
    participant Loader
    participant NitrogenCalcModule

    User->>UI (Farm/Field Page): Navigates to nitrogen balance view
    UI (Farm/Field Page)->>Loader: Loads farm, field, and balance data
    Loader->>NitrogenCalcModule: Calls calculateNitrogenBalance(input)
    NitrogenCalcModule->>NitrogenCalcModule: Computes field/farm balances and target
    NitrogenCalcModule-->>Loader: Returns balance and target values
    Loader-->>UI (Farm/Field Page): Supplies balance, target, and related data
    UI (Farm/Field Page)->>UI (Farm/Field Page): Renders balance vs target, icons, and bar chart
    UI (Farm/Field Page)-->>User: Displays nitrogen balance visualization and indicators
Loading

Possibly related PRs

  • Add nitrogen balance page #141: Adds a nitrogen balance page UI and route components that consume and display nitrogen balance calculations, related to the core calculation logic introduced here.

Suggested reviewers

  • gerardhros

Poem

🥕 Hop along, the balance calls,
Nitrogen's dance in fields and halls.
Targets set, charts in sight,
Green checks gleam, alerts take flight.
From soil to farm, the numbers play,
Rabbits cheer the balanced way! 🌿🐇


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 475e357 and 7fc4005.

📒 Files selected for processing (4)
  • .changeset/curly-streets-cross.md (1 hunks)
  • .changeset/thin-planets-see.md (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx (5 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • .changeset/curly-streets-cross.md
  • .changeset/thin-planets-see.md
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@SvenVw SvenVw marked this pull request as draft May 27, 2025 10:32
@SvenVw SvenVw changed the base branch from development to add-nitrogen-balance-page May 27, 2025 10:32
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.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

🛑 Comments failed to post (20)
.changeset/small-jokes-rescue.md (1)

1-3: 🛠️ Refactor suggestion

Semantic version bump should be minor
Exposing new public types is a backwards-compatible API addition and warrants a minor version bump.

Proposed diff:

--- a/.changeset/small-jokes-rescue.md
+++ b/.changeset/small-jokes-rescue.md
@@
- "@svenvw/fdm-core": patch
+ "@svenvw/fdm-core": minor
📝 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.

---
"@svenvw/fdm-core": minor
---
🤖 Prompt for AI Agents
In .changeset/small-jokes-rescue.md at lines 1 to 3, the semantic version bump
is incorrectly set to patch. Since new public types are being exposed, which is
a backwards-compatible API addition, change the version bump from patch to minor
to correctly reflect the nature of the update.
.changeset/long-buttons-sink.md (1)

1-3: 🛠️ Refactor suggestion

Version bump should be minor
Adding a new catalogue property introduces a new public API; update to a minor version bump.

Proposed diff:

--- a/.changeset/long-buttons-sink.md
+++ b/.changeset/long-buttons-sink.md
@@
- "@svenvw/fdm-core": patch
+ "@svenvw/fdm-core": minor
📝 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.

---
"@svenvw/fdm-core": minor
---
🤖 Prompt for AI Agents
In the .changeset/long-buttons-sink.md file at lines 1 to 3, the version bump is
incorrectly set to patch. Since a new catalogue property is added, which is a
public API change, update the version bump from patch to minor to reflect this
new feature addition.
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.cultivations.$b_lu_catalogue.crop.harvest.$b_id_harvesting.tsx (1)

115-118: 💡 Verification agent

🧩 Analysis chain

Verify consistent data model migration across codebase.

The change from harvestables[0] to harvestable aligns with the data model refactoring described in the AI summary. The optional chaining is correctly preserved for safety.

Please ensure all references to the old harvestables array pattern have been updated:


🏁 Script executed:

#!/bin/bash
# Description: Find any remaining harvestables array references
# Expected: No results or only intentional legacy code

# Search for old harvestables array pattern
rg "harvestables\[" --type ts --type tsx
rg "\.harvestables\." --type ts --type tsx

Length of output: 143


Let’s retry the search across both .ts and .tsx files without relying on built-in types:


🏁 Script executed:

#!/bin/bash
# Description: Find any remaining harvestables array references in TS and TSX
# Expected: No results or only intentional legacy code

# Search for old harvestables array pattern
rg -g "*.ts" -g "*.tsx" "harvestables\[" -n
rg -g "*.ts" -g "*.tsx" "\.harvestables\." -n

Length of output: 1495


Update remaining harvestables[0] references

We’ve detected leftover array‐based accesses that need converting to the new singular harvestable field:

• fdm-core/src/harvest.ts:550
• fdm-app/app/routes/farm.create.$b_id_farm.$calendar.cultivations.$b_lu_catalogue.crop._index.tsx:140, 142, 144, 146

Please replace each harvestables[0] (and any chained properties) with the new harvestable access pattern—e.g.:

-  h1.harvestables[0].harvestable_analyses[0].b_lu_yield
+  h1.harvestable?.harvestable_analyses?.[0].b_lu_yield

Ensure you handle optional chaining consistently.

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

🤖 Prompt for AI Agents
In
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.cultivations.$b_lu_catalogue.crop.harvest.$b_id_harvesting.tsx
around lines 115 to 118, update all remaining references from the old
array-based access pattern harvestables[0] to the new singular harvestable
field. Replace any occurrence of harvestables[0] and its chained properties with
harvestable, preserving optional chaining for safety. Also, extend this update
to other files mentioned (fdm-core/src/harvest.ts line 550 and
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.cultivations.$b_lu_catalogue.crop._index.tsx
lines 140, 142, 144, 146) to ensure consistent data model migration across the
codebase.
fdm-calculator/src/balance/nitrogen/removal/index.test.ts (1)

6-51: 💡 Verification agent

🧩 Analysis chain

Verify expected calculation values and add test documentation.

The test logic appears sound, but the hardcoded expected values (-21.2, -20, -1.2) need verification against the actual calculation formulas.

Based on the test data:

  • b_lu_yield: 1000 with b_lu_n_harvestable: 20 should give -20 for harvest removal
  • For residue calculation, the expected -1.2 suggests a specific formula involving b_lu_n_residue: 2, b_lu_hi: 0.4, and yield

Consider adding inline comments explaining how these expected values are calculated to improve test maintainability.


🏁 Script executed:

#!/bin/bash
# Description: Verify the nitrogen removal calculation logic and expected test values
# Expected: Find the implementation formulas to confirm test assertions

# Search for nitrogen removal calculation implementations
ast-grep --pattern 'function calculateNitrogenRemovalByHarvests($_, $_, $_) {
  $$$
}'

ast-grep --pattern 'function calculateNitrogenRemovalByResidue($_, $_, $_) {
  $$$
}'

# Also search for the calculation logic in the main removal function
rg -A 10 "calculateNitrogenRemovalByHarvests"
rg -A 10 "calculateNitrogenRemovalByResidue"

Length of output: 21394


Add inline comments explaining the nitrogen removal formulas in the index.test.ts

To improve test maintainability, document how each expected value is derived:

• In fdm-calculator/src/balance/nitrogen/removal/index.test.ts, update the assertions as follows:

- expect(result.total.toNumber()).toBeCloseTo(-21.2) // -20 from harvest + -1.2 from residue
+ expect(result.total.toNumber()).toBeCloseTo(-21.2) 
+ //  Harvest removal: 1000 kg × 20 g/kg ÷ 1000 = 20 kg → -20  
+ //  Residue removal: 1000 kg × (1 - 0.4) × 2 g/kg ÷ 1000 = 1.2 kg → -1.2  

- expect(result.harvests.total.toNumber()).toBeCloseTo(-20)
+ expect(result.harvests.total.toNumber()).toBeCloseTo(-20)
+ // 1000 kg × 20 g/kg ÷ 1000 = 20 kg → -20  

- expect(result.residues.total.toNumber()).toBeCloseTo(-1.2)
+ expect(result.residues.total.toNumber()).toBeCloseTo(-1.2)
+ // 1000 kg × (1 - 0.4) × 2 g/kg ÷ 1000 = 1.2 kg → -1.2  

This makes it clear how the numbers are calculated and eases future updates.

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

    it("should calculate total nitrogen removal from harvests and residues", () => {
        const cultivations: FieldInput["cultivations"] = [
            {
                b_lu: "cultivation1",
                b_lu_catalogue: "catalogue1",
                m_cropresidue: true,
            },
        ]
        const harvests: FieldInput["harvests"] = [
            {
                b_id_harvesting: "harvest1",
                b_lu: "cultivation1",
                b_lu_harvest_date: new Date(),
                harvestable: {
                    b_id_harvestable: "harvestable1",
                    harvestable_analyses: [
                        { b_lu_yield: 1000, b_lu_n_harvestable: 20 },
                    ],
                },
            },
        ]
        const cultivationDetailsMap = new Map([
            [
                "catalogue1",
                {
                    b_lu_catalogue: "catalogue1",
                    b_lu_croprotation: "cereal",
                    b_lu_yield: 1000,
                    b_lu_n_harvestable: 20,
                    b_lu_hi: 0.4,
                    b_lu_n_residue: 2,
                    b_n_fixation: 0,
                },
            ],
        ])

        const result: NitrogenRemoval = calculateNitrogenRemoval(
            cultivations,
            harvests,
            cultivationDetailsMap,
        )

        expect(result.total.toNumber()).toBeCloseTo(-21.2)
        //  Harvest removal: 1000 kg × 20 g/kg ÷ 1000 = 20 kg → -20
        //  Residue removal: 1000 kg × (1 - 0.4) × 2 g/kg ÷ 1000 = 1.2 kg → -1.2

        expect(result.harvests.total.toNumber()).toBeCloseTo(-20)
        // 1000 kg × 20 g/kg ÷ 1000 = 20 kg → -20

        expect(result.residues.total.toNumber()).toBeCloseTo(-1.2)
        // 1000 kg × (1 - 0.4) × 2 g/kg ÷ 1000 = 1.2 kg → -1.2
    })
🤖 Prompt for AI Agents
In fdm-calculator/src/balance/nitrogen/removal/index.test.ts around lines 6 to
51, the test assertions use hardcoded expected values (-21.2, -20, -1.2) without
explanation. Add inline comments before each expect statement to document how
these values are calculated based on the input data and formulas, such as
explaining that -20 comes from yield multiplied by nitrogen content in harvest,
and -1.2 is derived from residue nitrogen calculation using residue nitrogen
factor, harvest index, and yield. This will improve test clarity and
maintainability.
fdm-data/src/cultivations/catalogues/brp.ts (1)

29-47: 🛠️ Refactor suggestion

Refactor validation logic for better maintainability.

The validation logic for b_lu_croprotation works correctly but is verbose and difficult to maintain. Consider using a more concise approach with predefined valid values.

-        // Validate b_lu_croprotation
-        cultivation.b_lu_croprotation !== "other" &&
-        cultivation.b_lu_croprotation !== "clover" &&
-        cultivation.b_lu_croprotation !== "nature" &&
-        cultivation.b_lu_croprotation !== "potato" &&
-        cultivation.b_lu_croprotation !== "grass" &&
-        cultivation.b_lu_croprotation !== "rapeseed" &&
-        cultivation.b_lu_croprotation !== "starch" &&
-        cultivation.b_lu_croprotation !== "maize" &&
-        cultivation.b_lu_croprotation !== "cereal" &&
-        cultivation.b_lu_croprotation !== "sugarbeet" &&
-        cultivation.b_lu_croprotation !== "alfalfa" &&
-        cultivation.b_lu_croprotation !== "catchcrop"
-            ? (() => {
-                  throw new Error(
-                      `Invalid value for b_lu_croprotation: ${cultivation.b_lu_croprotation}.`,
-                  )
-              })()
-            : cultivation.b_lu_croprotation
+        // Validate b_lu_croprotation
+        const validCropRotations = [
+            "other", "clover", "nature", "potato", "grass", "rapeseed", 
+            "starch", "maize", "cereal", "sugarbeet", "alfalfa", "catchcrop"
+        ] as const
+        
+        if (!validCropRotations.includes(cultivation.b_lu_croprotation as any)) {
+            throw new Error(
+                `Invalid value for b_lu_croprotation: ${cultivation.b_lu_croprotation}. Expected one of: ${validCropRotations.join(", ")}`
+            )
+        }
+        
+        const cropRotation = cultivation.b_lu_croprotation
📝 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.

        // Validate b_lu_croprotation
        const validCropRotations = [
            "other", "clover", "nature", "potato", "grass", "rapeseed",
            "starch", "maize", "cereal", "sugarbeet", "alfalfa", "catchcrop"
        ] as const

        if (!validCropRotations.includes(cultivation.b_lu_croprotation as any)) {
            throw new Error(
                `Invalid value for b_lu_croprotation: ${cultivation.b_lu_croprotation}. Expected one of: ${validCropRotations.join(", ")}`
            )
        }

        const cropRotation = cultivation.b_lu_croprotation
🤖 Prompt for AI Agents
In fdm-data/src/cultivations/catalogues/brp.ts around lines 29 to 47, the
validation for b_lu_croprotation is done by multiple inequality checks which is
verbose and hard to maintain. Refactor this by defining an array or set of valid
values for b_lu_croprotation and then check if cultivation.b_lu_croprotation is
included in that collection. If not included, throw the error as before;
otherwise, return the value. This approach improves readability and
maintainability.
fdm-calculator/src/balance/nitrogen/supply/deposition.test.ts (1)

28-28: 💡 Verification agent

🧩 Analysis chain

Verify the hardcoded expected value for nitrogen deposition.

The test expects a very specific value (19.572) for nitrogen deposition calculation. Consider whether this hardcoded value is reliable and won't break with future changes to the underlying deposition data or calculation logic.

Let me verify if this expected value is documented or derived from a known formula:


🏁 Script executed:

#!/bin/bash
# Description: Search for documentation or calculation details for nitrogen deposition values
# Expected: Find references to deposition calculation formulas or data sources

rg -A 10 -B 5 "19\.572|19\.626|9\.7592" --type ts
rg -A 5 "deposition.*calculation|nitrogen.*deposition.*formula" --type ts

Length of output: 5170


I need to inspect how the test obtains its input data and whether it mocks or pins the deposition source. Let me dump the entire test file and look for fixtures or HTTP stubbing:


🏁 Script executed:

#!/bin/bash
# Show the full deposition test to see data setup and mocking
cat fdm-calculator/src/balance/nitrogen/supply/deposition.test.ts

# Search for any fixtures or HTTP mocks in the repo
rg -n "fixture|__fixtures__|nock|fetchMock|msw" --type ts

Length of output: 2893


I need to confirm how calculateNitrogenSupplyByDeposition fetches and uses the public data URL and whether it’s pinned to a static data source. Let me dump those implementation files:


🏁 Script executed:

#!/bin/bash
# Show getFdmPublicDataUrl implementation
cat fdm-calculator/src/balance/nitrogen/index.ts

# Show deposition calculation logic
cat fdm-calculator/src/balance/nitrogen/supply/deposition.ts

Length of output: 19644


Mock external GeoTIFF data in deposition tests to prevent brittle hardcoded values

The tests in fdm-calculator/src/balance/nitrogen/supply/deposition.test.ts call calculateNitrogenSupplyByDeposition, which fetches a live GeoTIFF from storage.googleapis.com and computes values like 19.572, 19.626, and 9.7592. Relying on an external dataset will make these assertions break as soon as the source data or TIFF reader behavior changes.

Suggested fixes:

  • In deposition.test.ts, mock geotiff.fromUrl (and its returned image) so you control:
    • getBoundingBox(), getWidth()/getHeight(), and
    • readRasters({ window }) to return a known Float32Array([X])
      Then assert deposition = X * ((days + 1) / 365).
  • Use Vitest’s vi.mock() or dependency injection to override fromUrl before importing the module.
  • Update expected values in the test to reflect your mocked raster value, isolating logic from external data.
  • Ensure no real network call occurs in CI by verifying the mock is active in every test case.
🤖 Prompt for AI Agents
In fdm-calculator/src/balance/nitrogen/supply/deposition.test.ts at line 28, the
test relies on a hardcoded expected value (19.572) derived from a live GeoTIFF
fetched from an external URL, which makes the test brittle and prone to failure
if the external data changes. To fix this, mock the geotiff.fromUrl function and
its returned image methods (getBoundingBox, getWidth, getHeight, readRasters) to
return controlled, static values. Then update the test to assert the deposition
calculation based on these mocked values, ensuring no real network calls occur
and the test is stable and isolated from external data changes.
fdm-calculator/src/balance/nitrogen/target.test.ts (1)

66-66: 💡 Verification agent

🧩 Analysis chain

Verify target calculation values against specification.

The test assertions use specific expected values (80, 125, 50, 70, 125, etc.) but it would be good to verify these match the specification or calculation logic.

Also applies to: 79-79, 94-94, 109-109, 124-124, 139-139, 154-154, 173-173


🏁 Script executed:

#!/bin/bash
# Description: Find the target calculation implementation to verify the expected values
# Expected: Implementation showing how target values are determined

ast-grep --pattern 'export function calculateTargetForNitrogenBalance($$$) {
  $$$
}'

Length of output: 87


🏁 Script executed:

#!/bin/bash
# List all TypeScript files in the nitrogen directory
rg --files -g "fdm-calculator/src/balance/nitrogen/*.ts"

# Search for any “calculate” functions in that directory
rg -n "calculate" fdm-calculator/src/balance/nitrogen

# If a target implementation file exists, show its first 200 lines
if [[ -f fdm-calculator/src/balance/nitrogen/target.ts ]]; then
  sed -n '1,200p' fdm-calculator/src/balance/nitrogen/target.ts
fi

Length of output: 28936


Confirm target values in calculateTargetForNitrogenBalance

The implementation in src/balance/nitrogen/target.ts hard-codes these base values from Ros et al. 2023:

  • Grassland + sand + dry → 80
  • Grassland + other soils → 125
  • Arable + sand + dry → 50
  • Arable + sand + average → 70
  • Arable + sand + wet → 125
  • Arable + clay + dry → 115 then immediately overwritten to 125
  • Arable + clay + other → 125

Please verify against the Ros et al. 2023 specification that these are the intended values. In particular, confirm whether the dry-clay arable case should be 115 or 125 and remove the redundant assignment if necessary.

Files/locations to review:

  • fdm-calculator/src/balance/nitrogen/target.ts (clay/dry logic around lines 60–75)
  • fdm-calculator/src/balance/nitrogen/target.test.ts (expected values for clay/dry and other clay cases)
🤖 Prompt for AI Agents
In fdm-calculator/src/balance/nitrogen/target.test.ts at line 66 and related
test lines (79, 94, 109, 124, 139, 154, 173), verify that the expected target
values in the test assertions match the specification from Ros et al. 2023 as
implemented in fdm-calculator/src/balance/nitrogen/target.ts around lines 60 to
75. Specifically, confirm whether the dry-clay arable target should be 115 or
125 and remove any redundant assignments in the implementation. Update the test
expected values accordingly to reflect the correct specification.
fdm-core/src/harvest.test.ts (1)

203-203: 💡 Verification agent

🧩 Analysis chain

Verify the data model change from array to singular object.

The change from harvestables[0] to harvestable indicates a significant structural change in the data model. Ensure this breaking change has been consistently applied throughout the codebase.

Also applies to: 219-219, 263-264


🏁 Script executed:

#!/bin/bash
# Description: Verify that all references to harvestables array have been updated to singular harvestable

# Search for any remaining array access patterns for harvestables
rg -A 3 -B 3 "harvestables\[" --type ts

# Search for any remaining references to harvestables as an array
rg -A 3 -B 3 "\.harvestables\s*\." --type ts

Length of output: 5563


Inconsistent singular “harvestable” usage

Although the core test (harvest.test.ts) and the harvest logic now use harvest.harvestable instead of harvestables[0], the UI layer still references the old array form. Please update all remaining harvestables[0] occurrences to the new singular model. For example:

• fdm-app/app/routes/farm.create.$b_id_farm.$calendar.cultivations.$b_lu_catalogue.crop._index.tsx
– Replace
ts h1.harvestables[0].harvestable_analyses[0].b_lu_yield h2.harvestables[0].harvestable_analyses[0].b_lu_yield … h1.harvestables[0].harvestable_analyses[0].b_lu_n_harvestable h2.harvestables[0].harvestable_analyses[0].b_lu_n_harvestable
with
ts h1.harvestable.harvestable_analyses[0].b_lu_yield h2.harvestable.harvestable_analyses[0].b_lu_yield … h1.harvestable.harvestable_analyses[0].b_lu_n_harvestable h2.harvestable.harvestable_analyses[0].b_lu_n_harvestable

• Run a global search for harvestables[0] in the fdm-app/ directory and replace all instances with harvestable.

🤖 Prompt for AI Agents
In fdm-core/src/harvest.test.ts around line 203 and also in fdm-app/ directory
files such as
app/routes/farm.create.$b_id_farm.$calendar.cultivations.$b_lu_catalogue.crop._index.tsx,
update all references from the old array form harvestables[0] to the new
singular object form harvestable. This includes changing expressions like
harvestables[0].harvestable_analyses[0].b_lu_yield to
harvestable.harvestable_analyses[0].b_lu_yield. Perform a global search for
harvestables[0] in the fdm-app/ directory and replace all occurrences with
harvestable to ensure consistency with the updated data model.
fdm-calculator/src/balance/nitrogen/removal/harvest.ts (1)

66-74: 🛠️ Refactor suggestion

Consider edge case: empty harvestable analyses.

The current logic handles 1 or multiple analyses but doesn't explicitly handle the case where removalsHarvest.length === 0. While this might be prevented by data validation elsewhere, consider adding explicit handling for robustness.

        let removalHarvest = new Decimal(0)
-        if (removalsHarvest.length === 1) {
+        if (removalsHarvest.length === 0) {
+            // No harvestable analyses found, keep default value of 0
+            removalHarvest = new Decimal(0)
+        } else if (removalsHarvest.length === 1) {
            removalHarvest = removalsHarvest[0]
        } else if (removalsHarvest.length > 1) {
📝 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.

        let removalHarvest = new Decimal(0)
        if (removalsHarvest.length === 0) {
            // No harvestable analyses found, keep default value of 0
            removalHarvest = new Decimal(0)
        } else if (removalsHarvest.length === 1) {
            removalHarvest = removalsHarvest[0]
        } else if (removalsHarvest.length > 1) {
            // If multiple harvestable analyses exist take the average
            removalHarvest = removalsHarvest
                .reduce((a, b) => a.add(b), new Decimal(0))
                .dividedBy(new Decimal(removalsHarvest.length))
        }
🤖 Prompt for AI Agents
In fdm-calculator/src/balance/nitrogen/removal/harvest.ts around lines 66 to 74,
the code does not explicitly handle the case when removalsHarvest is empty. To
fix this, add a condition to check if removalsHarvest.length is 0 and explicitly
set removalHarvest to Decimal(0) in that case, ensuring robustness against empty
input arrays.
fdm-core/src/harvest.d.ts (1)

7-7: 💡 Verification agent

🧩 Analysis chain

Verify the breaking change: harvestables → harvestable.

The change from plural harvestables: HarvestableType[] to singular harvestable: Harvestable is a significant breaking change that enforces a 1-to-1 relationship. Ensure all consuming code has been updated accordingly.


🏁 Script executed:

#!/bin/bash
# Search for any remaining references to the old plural form
rg -i "harvestables" --type ts --type tsx
# Search for usage of the old interface names
rg "HarvestType|HarvestableType|HarvestableAnalysisType" --type ts --type tsx

Length of output: 177


🏁 Script executed:

#!/bin/bash
# Search for any references to the old plural property or type names across all files
rg -n "harvestables"
rg -n "HarvestableType"

Length of output: 5918


Fix required: propagate harvestable rename across all consuming modules

The change in fdm-core/src/harvest.d.ts from

 harvestables: HarvestableType[]
+ harvestable: Harvestable

is a breaking change. A quick rg -n harvestables still shows dozens of references to the old plural form. You must update every consumer to use the new singular property (or handle an array if that’s intended).

Key locations needing updates:

  • fdm-core/src/cultivation.ts (lines 597, 663, 666, 672)
  • fdm-core/src/harvest.ts (inserts/deletes at lines 85, 337–339, 528–550, 565–569)
  • fdm-core/src/db/schema.ts (table definitions & type aliases at lines 353–355, 364–365, 373, 432)
  • fdm-docs/docs/core-concepts/02-schema.md (schema docs at lines 169, 205, 220)
  • fdm-app/app/routes/ (all occurrences of harvestables[...] in the farm.create and farm.$b_id_farm... pages)
  • fdm-app/app/components/custom/harvest/ (types.tsx, list.tsx)

Ensure each reference to

object.harvestables

is replaced with

object.harvestable

or refactored to work with an array if multiple harvestables remain valid.

🤖 Prompt for AI Agents
In fdm-core/src/harvest.d.ts at line 7, the property name changed from the
plural harvestables: HarvestableType[] to the singular harvestable: Harvestable,
which is a breaking change. You need to update all consuming modules to reflect
this rename by replacing every reference to object.harvestables with
object.harvestable or refactor the code to handle an array if multiple
harvestables are still needed. Key files to update include
fdm-core/src/cultivation.ts (lines 597, 663, 666, 672), fdm-core/src/harvest.ts
(lines 85, 337–339, 528–550, 565–569), fdm-core/src/db/schema.ts (lines 353–355,
364–365, 373, 432), fdm-docs/docs/core-concepts/02-schema.md (lines 169, 205,
220), and all relevant files in fdm-app/app/routes and
fdm-app/app/components/custom/harvest/. Ensure consistent usage of the singular
property or adjust logic to handle arrays as appropriate.
fdm-calculator/src/balance/nitrogen/input.ts (1)

125-131: 💡 Verification agent

🧩 Analysis chain

Minor inconsistency: fdmPublicDataUrl not in NitrogenBalanceInput type.

The function returns fdmPublicDataUrl but this property doesn't appear in the NitrogenBalanceInput type definition shown in the relevant code snippets. Verify if this is intentional or if the type needs updating.


🏁 Script executed:

#!/bin/bash
# Check the complete NitrogenBalanceInput type definition
ast-grep --pattern 'export type NitrogenBalanceInput = {
  $$$
}'

Length of output: 737


🏁 Script executed:

#!/bin/bash
# Locate usages and definition of fdmPublicDataUrl within the nitrogen balance input generator
rg "fdmPublicDataUrl" -n fdm-calculator/src/balance/nitrogen

Length of output: 2806


Add missing fdmPublicDataUrl to NitrogenBalanceInput

The input.ts generator returns an fdmPublicDataUrl property, but the NitrogenBalanceInput type in fdm-calculator/src/balance/nitrogen/types.d.ts is missing this field. Update the type so they stay in sync:

 export type NitrogenBalanceInput = {
     fields: FieldInput[]
     fertilizerDetails: FertilizerDetail[]
     cultivationDetails: CultivationDetail[]
     timeFrame: {
         start: Date
         end: Date
     }
+    fdmPublicDataUrl: string
 }

This ensures consumers of NitrogenBalanceInput won’t see a type error when accessing the URL.

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

// fdm-calculator/src/balance/nitrogen/types.d.ts
export type NitrogenBalanceInput = {
    fields: FieldInput[]
    fertilizerDetails: FertilizerDetail[]
    cultivationDetails: CultivationDetail[]
    timeFrame: {
        start: Date
        end: Date
    }
    fdmPublicDataUrl: string
}
🤖 Prompt for AI Agents
In fdm-calculator/src/balance/nitrogen/input.ts around lines 125 to 131, the
returned object includes the property fdmPublicDataUrl, but this property is
missing from the NitrogenBalanceInput type definition in
fdm-calculator/src/balance/nitrogen/types.d.ts. To fix this, add the
fdmPublicDataUrl field with the appropriate type to the NitrogenBalanceInput
type so that the return value matches the type definition and prevents type
errors when accessing this property.
fdm-calculator/src/balance/nitrogen/target.ts (2)

112-113: ⚠️ Potential issue

Fix duplicate assignment bug.

There's a duplicate assignment where targetValue is set to 115 and immediately overwritten to 125 on the next line.

         targetValue = new Decimal(115)
-        targetValue = new Decimal(125)
     } else if (cultivationType === "arable" && soilType === "clay") {
📝 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.

        targetValue = new Decimal(115)
     } else if (cultivationType === "arable" && soilType === "clay") {
🤖 Prompt for AI Agents
In fdm-calculator/src/balance/nitrogen/target.ts at lines 112 to 113, there is a
duplicate assignment to targetValue where it is first set to 115 and then
immediately overwritten to 125. Remove the redundant assignment so that
targetValue is assigned only once with the intended value.

29-39: 🛠️ Refactor suggestion

Add validation for empty cultivations array.

The function doesn't validate that the cultivations array contains elements, which could lead to unexpected behavior.

     // Determine whether field is grassland or arable
+    if (cultivations.length === 0) {
+        throw new Error("No cultivations provided for target calculation")
+    }
+    
     let cultivationType = "arable"
📝 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.

    // Determine whether field is grassland or arable
    if (cultivations.length === 0) {
        throw new Error("No cultivations provided for target calculation")
    }

    let cultivationType = "arable"
    cultivations.map((cultivation) => {
        const cultivationDetail = cultivationDetailsMap.get(
            cultivation.b_lu_catalogue,
        )

        if (cultivationDetail?.b_lu_croprotation === "grass") {
            cultivationType = "grassland"
        }
    })
🤖 Prompt for AI Agents
In fdm-calculator/src/balance/nitrogen/target.ts around lines 29 to 39, add a
validation check to ensure the cultivations array is not empty before processing
it. If the array is empty, handle this case appropriately, such as returning a
default value or throwing an error, to prevent unexpected behavior later in the
function.
fdm-calculator/src/conversions/soil.ts (1)

96-97: ⚠️ Potential issue

Fix incorrect unit in documentation.

The bulk density unit should be g/cm³, not kg/m³. The clamping values (0.5 to 3) confirm this.

- * for agricultural soils (0.5 to 3 kg / m³) to maintain accuracy.
- * @param a_som_loi - The soil organic matter content as a percentage (%).
+ * for agricultural soils (0.5 to 3 g/cm³) to maintain accuracy.
+ * @param a_som_loi - The soil organic matter content as a percentage (%).
📝 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.

 * for agricultural soils (0.5 to 3 g/cm³) to maintain accuracy.
 * @param a_som_loi - The soil organic matter content as a percentage (%).
🤖 Prompt for AI Agents
In fdm-calculator/src/conversions/soil.ts around lines 96 to 97, the
documentation incorrectly states the bulk density unit as kg/m³. Update the unit
to g/cm³ to match the clamping values (0.5 to 3) and ensure the documentation
accurately reflects the expected unit for bulk density.
fdm-core/src/soil.ts (1)

482-482: ⚠️ Potential issue

Fix unit notation for organic carbon.

The unit should be "g C/kg" to match the conversion functions and standard notation.

-            unit: "g C/g",
+            unit: "g C/kg",
📝 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.

            unit: "g C/kg",
🤖 Prompt for AI Agents
In fdm-core/src/soil.ts at line 482, the unit notation for organic carbon is
incorrectly set as "g C/g". Change this to "g C/kg" to align with the conversion
functions and standard notation for organic carbon units.
fdm-calculator/src/balance/nitrogen/index.ts (2)

314-319: ⚠️ Potential issue

Add null safety for date sorting.

The sort function doesn't handle null or undefined sampling dates, which could cause runtime errors.

     // Sort the soil analyses by date (most recent first)
     soilAnalyses.sort((a, b) => {
+        // Handle null/undefined dates by treating them as very old dates
+        const dateA = a.b_sampling_date ? new Date(a.b_sampling_date).getTime() : 0;
+        const dateB = b.b_sampling_date ? new Date(b.b_sampling_date).getTime() : 0;
         return (
-            new Date(b.b_sampling_date).getTime() -
-            new Date(a.b_sampling_date).getTime()
+            dateB - dateA
         )
     })
📝 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.

    // Sort the soil analyses by date (most recent first)
    soilAnalyses.sort((a, b) => {
        // Handle null/undefined dates by treating them as very old dates
        const dateA = a.b_sampling_date ? new Date(a.b_sampling_date).getTime() : 0;
        const dateB = b.b_sampling_date ? new Date(b.b_sampling_date).getTime() : 0;
        return (
            dateB - dateA
        );
    });
🤖 Prompt for AI Agents
In fdm-calculator/src/balance/nitrogen/index.ts around lines 314 to 319, the
sorting logic for soilAnalyses does not handle cases where b_sampling_date might
be null or undefined, leading to potential runtime errors. Update the sort
comparator to first check if either date is null or undefined and handle those
cases safely, for example by treating missing dates as the earliest or latest,
before converting to Date and comparing their time values.

129-136: ⚠️ Potential issue

Avoid mutating input parameters.

Directly modifying the timeFrame object can cause unexpected side effects for callers. Create a local copy instead.

+    // Create a local copy of timeFrame to avoid mutation
+    const localTimeFrame = { ...timeFrame };
+    
     // If timeframe is broader than field existence, shorten it
-    if (field.b_start?.getTime() > timeFrame.start.getTime()) {
-        timeFrame.start = field.b_start
+    if (field.b_start?.getTime() > localTimeFrame.start.getTime()) {
+        localTimeFrame.start = field.b_start
     }
-    if (field.b_end?.getTime() < timeFrame.end.getTime()) {
-        timeFrame.end = field.b_end
+    if (field.b_end?.getTime() < localTimeFrame.end.getTime()) {
+        localTimeFrame.end = field.b_end
     }

Then use localTimeFrame in subsequent function calls instead of timeFrame.

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

    // Create a local copy of timeFrame to avoid mutation
    const localTimeFrame = { ...timeFrame };

    // If timeframe is broader than field existence, shorten it
    if (field.b_start?.getTime() > localTimeFrame.start.getTime()) {
        localTimeFrame.start = field.b_start
    }
    if (field.b_end?.getTime() < localTimeFrame.end.getTime()) {
        localTimeFrame.end = field.b_end
    }
🤖 Prompt for AI Agents
In fdm-calculator/src/balance/nitrogen/index.ts around lines 129 to 136, avoid
mutating the input parameter timeFrame directly. Instead, create a local copy of
timeFrame (e.g., localTimeFrame) before these conditional checks, then update
localTimeFrame.start and localTimeFrame.end as needed. Use this localTimeFrame
in subsequent function calls to prevent side effects on the original timeFrame
object.
fdm-core/src/cultivation.ts (1)

758-772: 💡 Verification agent

🧩 Analysis chain

Verify impact of harvestables structure change.

The change from harvestables array to singular harvestable object is a significant structural modification. Ensure this change is consistently applied across all consuming code.


🏁 Script executed:

#!/bin/bash
# Description: Search for usage of harvestables vs harvestable to verify consistency

echo "=== Searching for 'harvestables' usage (should be updated) ==="
rg -n "harvestables" --type ts

echo "=== Searching for 'harvestable' usage (new pattern) ==="
rg -n "harvestable[^s]" --type ts

echo "=== Check test files for harvestables usage ==="
fd "test" --type f | xargs rg -l "harvestables"

Length of output: 47042


🏁 Script executed:

#!/usr/bin/env bash
# Locate the crop index file in the fdm-app routes and inspect the loader output usage
file=$(fd -t f -g "*cultivations*crop._index.tsx" | head -n1)
echo "Inspecting file: $file"
sed -n '100,180p' "$file"

Length of output: 3845


Incomplete Migration of Harvestable Structure — Fix Required

The change from an array of harvestables to a singular harvestable object isn’t consistently applied. There are still many consumers expecting harvestables[]—they must be updated to use the new shape (harvestable.harvestable_analyses) throughout:

fdm-app loader (…crop._index.tsx)
interface HarvestInfo { harvestables: { … }[] }
– all h1.harvestables[0]… / harvestables[0].harvestable_analyses[0] usages

UI components in fdm-app/app/components/custom/harvest/
list.tsx, form.tsx referencing harvestable?.harvestable_analyses?.[0]
– pages under routes/farm…cultivations…harvest… using harvestables

fdm-calculator modules
balance/nitrogen/volatization/residues.ts & tests
balance/nitrogen/removal/* & tests

Type definitions in fdm-core
src/cultivation.d.ts, src/harvest.d.ts still define arrays
– update harvestablesTypeSelect, harvestableSamplingTypeSelect, etc.

Service layer in fdm-core/src/harvest.ts & src/cultivation.ts
– functions that build or consume harvestables arrays

Please refactor all usages to the new singular harvestable object and its harvestable_analyses array.

🤖 Prompt for AI Agents
In fdm-core/src/cultivation.ts around lines 758 to 772, the data structure for
harvestables has been changed from an array named 'harvestables' to a singular
object 'harvestable' containing an array 'harvestable_analyses'. However, this
change is not consistently applied across the codebase. To fix this, update all
references and type definitions in the entire project that still expect
'harvestables' as an array to use the new singular 'harvestable' object and
access its 'harvestable_analyses' array accordingly. This includes updating
interfaces, UI components, service layers, and tests to reflect the new
structure consistently.
fdm-core/src/harvest.ts (2)

550-550: 🛠️ Refactor suggestion

Verify data structure consistency in getHarvestSimplified.

The function correctly assigns the singular harvestable object, but there's still inconsistency with harvestable_analyses:

  1. Line 588 assigns the entire harvestableAnalyses array to harvest.harvestable.harvestable_analyses
  2. This conflicts with the pattern of converting to singular objects
  3. The access pattern in removeHarvest expects an array with [0] indexing

To maintain consistency with the singular object pattern, consider updating the type definition and assignment:

-    harvest.harvestable.harvestable_analyses = harvestableAnalyses
+    harvest.harvestable.harvestable_analysis = harvestableAnalyses[0]

This would align with the TODO comment's mention of "a single analysis" and make the data structure more predictable.

Also applies to: 583-583, 588-588

🤖 Prompt for AI Agents
In fdm-core/src/harvest.ts around lines 550, 583, and 588, the assignment of
harvestable_analyses is inconsistent with the singular object pattern used for
harvestable. Instead of assigning the entire harvestableAnalyses array to
harvest.harvestable.harvestable_analyses, update the type definition to expect a
single analysis object and assign only the first element of harvestableAnalyses
to harvest.harvestable.harvestable_analyses. This change will align with the
TODO comment about "a single analysis" and ensure the data structure is
consistent and predictable, matching the expected access pattern in
removeHarvest.

299-302: 🛠️ Refactor suggestion

Potential inconsistency in data structure handling.

There's an inconsistency in how harvestable_analyses is being handled:

  • Line 301 accesses harvestable_analyses[0], treating it as an array
  • But in getHarvestSimplified (line 588), the entire harvestableAnalyses array is assigned to harvestable_analyses

This suggests harvestable_analyses is still an array, which is inconsistent with the pattern of converting from arrays to singular objects.

Consider making the data structure consistent. If the intention is to have a single analysis per harvestable (as suggested by the TODO comment), update the assignment in getHarvestSimplified:

-    harvest.harvestable.harvestable_analyses = harvestableAnalyses
+    harvest.harvestable.harvestable_analysis = harvestableAnalyses[0]

And update the access pattern here:

             const b_id_harvestable_analysis =
-                harvest.harvestable.harvestable_analyses[0]
+                harvest.harvestable.harvestable_analysis
                     .b_id_harvestable_analysis
📝 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.

            const b_id_harvestable = harvest.harvestable.b_id_harvestable
            const b_id_harvestable_analysis =
                harvest.harvestable.harvestable_analysis
                    .b_id_harvestable_analysis
🤖 Prompt for AI Agents
In fdm-core/src/harvest.ts around lines 299 to 302, the code accesses
harvest.harvestable.harvestable_analyses[0] as if it is an array, but elsewhere
harvestable_analyses is assigned the entire array, causing inconsistency. To fix
this, modify getHarvestSimplified (around line 588) to assign only the first
element of harvestableAnalyses to harvestable_analyses if only one analysis is
intended. Then update the access here to treat harvestable_analyses as a
singular object rather than an array, ensuring consistent data structure usage
throughout the code.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 27, 2025

Codecov Report

Attention: Patch coverage is 93.23308% with 9 lines in your changes missing coverage. Please review.

Project coverage is 93.83%. Comparing base (0b7978d) to head (7fc4005).
Report is 22 commits behind head on development.

Files with missing lines Patch % Lines
fdm-calculator/src/balance/nitrogen/target.ts 92.00% 8 Missing ⚠️
fdm-calculator/src/balance/nitrogen/index.ts 96.96% 1 Missing ⚠️
Additional details and impacted files
@@               Coverage Diff               @@
##           development     #143      +/-   ##
===============================================
- Coverage        93.85%   93.83%   -0.02%     
===============================================
  Files               65       66       +1     
  Lines             7779     7900     +121     
  Branches           916      943      +27     
===============================================
+ Hits              7301     7413     +112     
- Misses             476      485       +9     
  Partials             2        2              
Flag Coverage Δ
fdm-calculator 94.66% <93.23%> (-0.22%) ⬇️
fdm-core 93.68% <ø> (ø)
fdm-data 92.92% <ø> (ø)

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 base branch from add-nitrogen-balance-page to development May 27, 2025 15:19
@SvenVw SvenVw marked this pull request as ready for review May 27, 2025 19:51
@coderabbitai coderabbitai Bot added the fdm-app label May 27, 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: 2

🧹 Nitpick comments (7)
.changeset/curly-streets-cross.md (1)

5-5: Improve grammar for better clarity.

The description could be clearer and more grammatically correct.

-Show at nitrogen balance a comparison of the balance with the target level at farm and field level
+Show a comparison of the nitrogen balance with the target level on both farm and field pages
🧰 Tools
🪛 LanguageTool

[uncategorized] ~5-~5: Possible missing comma found.
Context: ...w/fdm-app": minor --- Show at nitrogen balance a comparison of the balance with the ta...

(AI_HYDRA_LEO_MISSING_COMMA)

.changeset/thin-planets-see.md (1)

5-5: Fix grammar for subject-verb agreement.

The sentence has grammatical issues with noun number and verb agreement.

-At the nitrogen balance page for farm and field show a bar chart to compare supply, removal and emission
+Show a bar chart on the nitrogen balance pages for farms and fields to compare supply, removal, and emission
🧰 Tools
🪛 LanguageTool

[uncategorized] ~5-~5: The grammatical number of this noun doesn’t look right. Consider replacing it.
Context: ... the nitrogen balance page for farm and field show a bar chart to compare supply, rem...

(AI_EN_LECTOR_REPLACEMENT_NOUN_NUMBER)


[uncategorized] ~5-~5: This verb does not appear to agree with the subject. Consider using a different form.
Context: ...itrogen balance page for farm and field show a bar chart to compare supply, removal ...

(AI_EN_LECTOR_REPLACEMENT_VERB_AGREEMENT)

fdm-app/app/components/custom/balance/nitrogen-chart.tsx (2)

22-28: Simplify object property assignments.

The object property assignments are redundant and can be simplified using shorthand syntax.

     const chartData = [
         {
-            supply: supply,
-            removal: removal,
-            emission: emission,
+            supply,
+            removal,
+            emission,
         },
     ]

17-17: Consider using the balance prop or removing it.

The balance prop is defined in the component interface but never used in the chart rendering. Either utilize this prop in the visualization or remove it from the interface.

Should the balance be displayed as a reference line or additional visual element in the chart?

fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx (2)

163-174: Improve template literal formatting and icon styling consistency.

The target comparison logic is good, but the formatting and styling could be improved.

                                         <div className="flex items-center gap-4">
                                             <p>
-                                                {`${nitrogenBalanceResult.balance} /
-                                                ${nitrogenBalanceResult.target}`}
+                                                {`${nitrogenBalanceResult.balance} / ${nitrogenBalanceResult.target}`}
                                             </p>
                                             {nitrogenBalanceResult.balance <=
                                             nitrogenBalanceResult.target ? (
-                                                <CircleCheck className="text-green-500 bg-green-100 p-0 rounded-full " />
+                                                <CircleCheck className="text-green-500 bg-green-100 rounded-full" />
                                             ) : (
-                                                <CircleAlert className="text-red-500 bg-red-100 p-0 rounded-full " />
+                                                <CircleAlert className="text-red-500 bg-red-100 rounded-full" />
                                             )}
                                         </div>

243-250: Fix grammar in descriptive text.

There's a missing period and the sentence structure could be improved.

                                 <CardDescription>
-                                    De stikstofbalans voor {field.b_name} van{" "}
-                                    {farm.b_name_farm} . De balans is het
+                                    De stikstofbalans voor {field.b_name} van{" "}
+                                    {farm.b_name_farm}. De balans is het
                                     verschil tussen de totale aanvoer, afvoer en
                                     emissie van stikstof. Een positieve balans
                                     betekent een overschot aan stikstof, een
                                     negatieve balans een tekort.
                                 </CardDescription>
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx (1)

282-296: Optimize field data lookup for better performance.

The current implementation performs fields.find() operations inside the map function, which results in O(n²) complexity. Consider creating a lookup map for better performance.

- {nitrogenBalanceResult.fields.map(
-     (field) => (
+ {(() => {
+     const fieldsMap = new Map(fields.map(f => [f.b_id, f]));
+     return nitrogenBalanceResult.fields.map((field) => {
+         const fieldData = fieldsMap.get(field.b_id);
+         return (
              <div
                  className="flex items-center"
                  key={field.b_id}
              >
                  {field.balance <=
                  field.target ? (
                      <CircleCheck className="text-green-500 bg-green-100 p-0 rounded-full w-6 h-6" />
                  ) : (
                      <CircleAlert className="text-red-500 bg-red-100 p-0 rounded-full w-6 h-6" />
                  )}

                  <div className="ml-4 space-y-1">
                      <NavLink
                          to={`./${field.b_id}`}
                      >
                          <p className="text-sm font-medium leading-none hover:underline">
-                             {
-                                 fields.find(
-                                     (f) =>
-                                         f.b_id ===
-                                         field.b_id,
-                                 )?.b_name
-                             }
+                             {fieldData?.b_name}
                          </p>
                      </NavLink>
                      <p className="text-sm text-muted-foreground">
-                         {
-                             fields.find(
-                                 (f) =>
-                                     f.b_id ===
-                                     field.b_id,
-                             )?.b_area
-                         }{" "}
+                         {fieldData?.b_area}{" "}
                          ha
                      </p>
                  </div>
                  <div className="ml-auto font-medium">
                      {field.balance} /{" "}
                      {field.target}
                  </div>
              </div>
-         ),
-     )}
+         );
+     });
+ })()}

Also applies to: 302-303

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bbad620 and 6b21513.

📒 Files selected for processing (6)
  • .changeset/curly-streets-cross.md (1 hunks)
  • .changeset/thin-planets-see.md (1 hunks)
  • fdm-app/app/components/custom/balance/nitrogen-chart.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx (5 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx (7 hunks)
  • fdm-calculator/src/balance/nitrogen/types.d.ts (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • fdm-calculator/src/balance/nitrogen/types.d.ts
🧰 Additional context used
🪛 LanguageTool
.changeset/thin-planets-see.md

[uncategorized] ~5-~5: The grammatical number of this noun doesn’t look right. Consider replacing it.
Context: ... the nitrogen balance page for farm and field show a bar chart to compare supply, rem...

(AI_EN_LECTOR_REPLACEMENT_NOUN_NUMBER)


[uncategorized] ~5-~5: This verb does not appear to agree with the subject. Consider using a different form.
Context: ...itrogen balance page for farm and field show a bar chart to compare supply, removal ...

(AI_EN_LECTOR_REPLACEMENT_VERB_AGREEMENT)

.changeset/curly-streets-cross.md

[uncategorized] ~5-~5: Possible missing comma found.
Context: ...w/fdm-app": minor --- Show at nitrogen balance a comparison of the balance with the ta...

(AI_HYDRA_LEO_MISSING_COMMA)

🔇 Additional comments (10)
fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx (3)

124-124: LGTM! Good addition of farm context.

Adding farm data to the loader return provides useful context for the field-level view.


154-156: LGTM! Better card title and icon choice.

The updated title "Overschot / Doel (Perceel)" and ArrowRightLeft icon better represent the comparison functionality.


258-271: LGTM! Excellent integration of the nitrogen balance chart.

The chart component is properly integrated with the correct props mapping from the nitrogen balance result.

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

30-34: LGTM! Import additions are well-organized.

The new imports for icons (ArrowRightLeft, Check, CircleAlert, CircleCheck) and the NitrogenBalanceChart component are properly added and used consistently throughout the component for the balance vs target functionality.

Also applies to: 39-39


103-103: LGTM! Farm data integration is correct.

The farm object is properly added to the loader return data and correctly destructured in the component, enabling the display of farm-specific information in the UI.

Also applies to: 116-116


126-126: LGTM! Card title and icon updates are semantically appropriate.

The change from "Bedrijfsoverschot" to "Overschot / Doel (Bedrijf)" and the icon change from House to ArrowRightLeft better represents the comparative nature of balance vs target display.

Also applies to: 128-128


135-146: LGTM! Balance vs target display with conditional indicators is well-implemented.

The conditional rendering logic correctly shows a green check icon when balance is within target (balance <= target) and a red alert icon when it exceeds the target. The display format clearly shows both values.


215-222: LGTM! Descriptive text enhances user understanding.

The addition of contextual description using the farm name and explaining the nitrogen balance concept improves user experience and provides valuable context.


229-236: LGTM! Chart component integration is properly implemented.

The NitrogenBalanceChart component is correctly integrated with appropriate props passed from the nitrogen balance result data.


269-274: LGTM! Field-level balance indicators are consistent.

The conditional icons for each field follow the same logic as the farm-level display, providing consistent visual feedback across the interface.

Comment thread fdm-app/app/components/custom/balance/nitrogen-chart.tsx
Comment thread fdm-app/app/components/custom/balance/nitrogen-chart.tsx Outdated
@SvenVw SvenVw requested a review from gerardhros May 27, 2025 20:13
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.

clear. LGTM.

@gerardhros
Copy link
Copy Markdown
Collaborator

see some checks failing...

@SvenVw
Copy link
Copy Markdown
Collaborator Author

SvenVw commented May 27, 2025

see some checks failing...

Coverage is 0.02% lower...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants