Skip to content

Integrate Fertilizer Management into Farm Creation Wizard#272

Merged
SvenVw merged 34 commits into
developmentfrom
FDM266
Sep 29, 2025
Merged

Integrate Fertilizer Management into Farm Creation Wizard#272
SvenVw merged 34 commits into
developmentfrom
FDM266

Conversation

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator

@BoraIneviNMI BoraIneviNMI commented Sep 19, 2025

  • New Features
    • Users on the add field fertilizer form can now directly navigate to the fertilizer management page...
    • then come back when they are done.

Closes #266

Summary by CodeRabbit

  • New Features

    • Full fertilizer creation & management flow: pages for adding custom or based-on fertilizers, new creation buttons, and dedicated create/edit pages.
    • Persistent draft storage for fertilizer forms (save/load/delete) so drafts survive navigation.
  • UX Improvements

    • From field fertilizer form, jump to manage pages and return with inputs preserved; dialog auto-opens when a draft exists.
    • Management subpages render without full page chrome for focused workflows; improved breadcrumbs and contextual navigation.
  • Documentation

    • Updated page description for editing an existing fertilizer.

@BoraIneviNMI BoraIneviNMI self-assigned this Sep 19, 2025
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Sep 19, 2025

🦋 Changeset detected

Latest commit: cd8fce5

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

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

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

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 19, 2025

Walkthrough

Adds nested in-context fertilizer management routes for farm and field workflows, a session-persisted Zustand store to save/restore fertilizer-application form state, wires persistence into the application card/form (save-before-navigate, auto-open restore, cleanup), refactors fertilizer creation/edit pages into reusable blocks and buttons, and tweaks layout/breadcrumbs to render manage subroutes without full page chrome.

Changes

Cohort / File(s) Summary
Changeset
​.changeset/spicy-knives-pump.md
Adds a minor-release changeset documenting the UX navigation between fertilizer-application form and fertilizer management.
Form persistence store
fdm-app/app/store/field-fertilizer-form.tsx
New persisted Zustand store useFieldFertilizerFormStore (sessionStorage) with save, load, and delete keyed by farm + catalogue id.
Application card & form wiring
fdm-app/app/components/blocks/fertilizer-applications/card.tsx, fdm-app/app/components/blocks/fertilizer-applications/form.tsx
Wire route params (b_id_farm, b_id_or_b_lu_catalogue) into the form; hydrate from store on mount; auto-open dialog when saved state exists; save before navigating to manage pages; delete persisted state on close or after submit; FertilizerApplicationForm now accepts b_id_farm and b_id_or_b_lu_catalogue.
Reusable fertilizer blocks & buttons
fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx, fdm-app/app/components/blocks/fertilizer/new-custom-fertilizer-page.tsx, fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx
New block components that mount Remix forms (useRemixForm + zodResolver) and render FertilizerForm; CustomFertilizerButton and BasedOffFertilizerButton added for manage UI.
Field-context manage routes
fdm-app/app/routes/...field.$b_id.fertilizer.manage.new*.tsx, fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new*.*.tsx, fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new._index.tsx, ...manage.new.custom.tsx
New nested routes for listing/selecting/creating/editing fertilizers within the field calendar flow; each adds loader/action/meta and renders the new block components.
Farm-create (wizard) manage routes
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new*.tsx, ...manage.new._index.tsx, ...manage.new.custom.tsx
Mirror manage routes under the farm creation wizard: loaders/actions/meta and pages rendering the new blocks so users can manage fertilizers in-wizard.
Refactor existing fertilizer routes
fdm-app/app/routes/farm.$b_id_farm.fertilizers.*.tsx, fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.*.tsx, fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx
Replace inline form setup with reusable blocks, rename default exports to page-style names, update imports/links, and centralize form UI while preserving loader/action semantics.
Layout / Outlet early-return for manage subroutes
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx, fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.tsx
Use useLocation() to detect /manage paths and render only the nested Outlet (skip surrounding header/layout) for in-context manage routes.
Header breadcrumbs tweak
fdm-app/app/components/blocks/header/create-farm.tsx
Conditional breadcrumb: when on manage paths render linked “Bemesting” + “Meststoffen”; otherwise keep original breadcrumb.
Route/prop signature updates
fdm-app/app/routes/**/*.tsx (multiple files)
Added/renamed exports (meta, loader, action, default components) across new/manage/refactored fertilizer routes; some components now accept Route.ComponentProps (params) and new props like b_id_farm / b_id_or_b_lu_catalogue.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant App as FertilizerApplicationForm
  participant Store as FieldFertilizerFormStore
  participant Router as Router
  participant Manage as Fertilizer Manage Routes
  participant Server as Loaders/Actions

  User->>App: Open application dialog
  App->>Store: load(b_id_farm, b_id_or_b_lu_catalogue)
  Note over App,Store: Hydrate form if saved state exists

  User->>App: Click "Manage fertilizers"
  App->>Store: save(b_id_farm, b_id_or_b_lu_catalogue, current values)
  App->>Router: navigate(.../fertilizer/manage)

  Router->>Manage: render manage route (loader fetches options)
  User->>Manage: create/edit fertilizer
  Manage->>Server: action(...) -> addFertilizerToCatalogue + addFertilizer
  Server-->>Manage: redirect with success

  User->>Router: Navigate back to application step
  Router->>App: re-render (loader refresh)
  App->>Store: load(...) -> restore saved values
  User->>App: Submit application
  App->>Server: action(submit)
  Server-->>App: success
  App->>Store: delete(...) to clear saved state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Possibly related PRs

Suggested reviewers

  • gerardhros

Poem

I'm a rabbit with a tiny map,
I stash your form in session's gap.
Hop to manage, plant and sow,
Bounce back quick — your values show.
Fields saved, seeds sown — hop, let's go! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning The pull request implements the “Manage Fertilizers” navigation button beside the selection dropdown and creates nested routes for creating and editing fertilizers within the wizard path, aligning with the linked issue’s requirement for in-context management (#266), but it does not clearly include the main management list view or a prominent “Back to Applications” or “Done” control to return to the application step, and it is not evident that the existing DataTable component is reused for listing fertilizers. Add or verify the root “Manage” route that lists existing fertilizers using the existing DataTable component, and include a clear “Back to Applications” or “Done” button in the nested management pages to satisfy the return workflow specified in the linked issue.
Out of Scope Changes Check ⚠️ Warning The changeset includes numerous new route modules and signature refactors under fdm-app/app/routes/farm.$b_id_farm… for existing farm management scenarios that are not required by the wizard‐focused integration described in issue #266. Separate or remove the additions under farm.$b_id_farm routes and unrelated public API changes into a dedicated pull request, keeping this PR focused solely on the farm creation wizard’s fertilizer management integration.
✅ 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 succinctly captures the core purpose of the pull request by describing the integration of fertilizer management into the farm creation wizard, matches the issue summary, and is concise without including extraneous details.
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 FDM266

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

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.93%. Comparing base (988cb8e) to head (cd8fce5).
⚠️ Report is 35 commits behind head on development.

Additional details and impacted files
@@             Coverage Diff              @@
##           development     #272   +/-   ##
============================================
  Coverage        92.93%   92.93%           
============================================
  Files               79       79           
  Lines            12952    12952           
  Branches          1268     1268           
============================================
  Hits             12037    12037           
  Misses             913      913           
  Partials             2        2           
Flag Coverage Δ
fdm-calculator 94.36% <ø> (ø)
fdm-core 91.92% <ø> (ø)
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.

@coderabbitai coderabbitai Bot added enhancement New feature or request fdm-app labels Sep 19, 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-app/app/components/blocks/fertilizer-applications/form.tsx (1)

62-66: Don’t wipe p_app_method when hydrating saved values.

Resetting the method whenever p_id changes also runs after you load saved values, clearing the user’s previously chosen method. Guard using the previous p_id and only clear on actual user change.

Apply:

-import { useEffect } from "react"
+import { useEffect, useRef } from "react"
...
-useEffect(() => {
-    if (p_id) {
-        form.setValue("p_app_method", "")
-    }
-}, [p_id, form.setValue])
+const prevPidRef = useRef<string | undefined>(undefined)
+useEffect(() => {
+    if (prevPidRef.current && prevPidRef.current !== p_id) {
+        form.setValue("p_app_method", undefined)
+    }
+    prevPidRef.current = p_id
+}, [p_id, form])
♻️ Duplicate comments (1)
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx (1)

3-5: Duplicate: path alias verification

Same note as the manage.new._index route regarding @/app alias.

🧹 Nitpick comments (29)
fdm-app/app/components/blocks/fertilizer/index-page.tsx (1)

21-25: Update the JSDoc comment to match the component.

The JSDoc comment describes "managing farm settings" but this component is specifically for rendering the fertilizers index page.

 /**
- * Renders the layout for managing farm settings.
+ * Renders the farm fertilizers index page.
  *
- * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields.
- * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster.
+ * This component displays a sidebar layout with farm and fertilizer headers,
+ * and renders the main content area with a fertilizers data table.
  */
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage._index.tsx (1)

26-82: **Significant code duplication with existing route.**The loader function is nearly identical to the one in fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx (lines 25-81). The same data fetching logic, validation, and return structure are duplicated.

Consider extracting the shared loader logic into a reusable function to follow the DRY principle, which states "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system":

// Create a shared loader utility in lib/loaders/fertilizer.server.ts
export async function loadFarmFertilizersData(params: { b_id_farm?: string }, request: Request) {
  // Shared validation and data fetching logic
  // Return the common data structure
}

Then use it in both routes:

 export async function loader({ request, params }: LoaderFunctionArgs) {
   try {
-    // Get the farm id
-    const b_id_farm = params.b_id_farm
-    if (!b_id_farm) {
-      throw data("invalid: b_id_farm", {
-        status: 400,
-        statusText: "invalid: b_id_farm",
-      })
-    }
-    // ... rest of duplicated logic
+    return await loadFarmFertilizersData(params, request)
   } catch (error) {
     throw handleLoaderError(error)
   }
 }
fdm-app/app/components/blocks/fertilizer/fertilizer-page.tsx (2)

32-37: Update JSDoc comment to match component functionality.

The JSDoc comment is generic and doesn't accurately describe this specific fertilizer management component.

 /**
- * Renders the layout for managing farm settings.
+ * Renders the fertilizer management page layout.
  *
- * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields.
- * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster.
+ * This component displays a sidebar layout with farm and fertilizer headers,
+ * and renders a main section with the fertilizer title and form for viewing/editing.
  */

47-97: Consider extracting default values initialization.

The large defaultValues object with 40+ properties makes the form initialization verbose and potentially error-prone.

Consider extracting this into a utility function:

// In a utils file
function createFertilizerFormDefaults(fertilizer: Fertilizer): z.infer<typeof FormSchema> {
  return {
    p_name_nl: fertilizer.p_name_nl,
    p_type: fertilizer.p_type,
    // ... rest of the properties
    p_app_method_options: fertilizer.p_app_method_options || [],
  }
}

Then use it in the component:

 const form = useRemixForm<z.infer<typeof FormSchema>>({
   mode: "onTouched",
   resolver: zodResolver(FormSchema),
-  defaultValues: {
-    p_name_nl: fertilizer.p_name_nl,
-    // ... 40+ lines
-    p_app_method_options: fertilizer.p_app_method_options || [],
-  },
+  defaultValues: createFertilizerFormDefaults(fertilizer),
 })
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.$p_id.tsx (2)

35-120: Significant code duplication with existing fertilizer route.

This loader is nearly identical to the one in fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.tsx. The same validation, data fetching, and return structure are duplicated.

Following the DRY principle, consider extracting the shared loader logic into a reusable function:

// In lib/loaders/fertilizer.server.ts
export async function loadFertilizerData(params: { b_id_farm?: string, p_id?: string }, request: Request) {
  // Shared validation and data fetching logic
  // Return the common data structure
}

This would eliminate the ~80 lines of duplicated code between the two routes.


133-172: Action function shows similar duplication pattern.

The action function is also nearly identical to the one in the existing fertilizer route, with the same validation, form processing, and update logic.

Consider extracting the shared action logic into a reusable function to maintain consistency and reduce duplication:

// In lib/actions/fertilizer.server.ts
export async function updateFertilizerAction(params: { b_id_farm?: string, p_id?: string }, request: Request) {
  // Shared validation, form parsing, and update logic
}
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (2)

84-94: Use the same key source (props) for save/delete to avoid drift.

handleManageFertilizers uses URL params while handleSubmit uses props. Stick to props to ensure symmetric save/delete, regardless of where the component is mounted.

Apply:

-    function handleManageFertilizers(e: MouseEvent<HTMLButtonElement>) {
+    function handleManageFertilizers(e: MouseEvent<HTMLButtonElement>) {
         e.preventDefault()
-        if (params.b_id_farm && params.b_id) {
-            fieldFertilizerFormStore.save(
-                params.b_id_farm,
-                params.b_id,
-                form.getValues(),
-            )
-        }
+        if (b_id_farm && b_id) {
+            fieldFertilizerFormStore.save(b_id_farm, b_id, form.getValues())
+        }
         navigate("./manage")
     }

96-99: Guard delete() to avoid writing under empty keys.

If b_id_farm or b_id are missing, this would delete under the ""/"" key.

Apply:

-    function handleSubmit(e: FormEvent<HTMLFormElement>) {
-        fieldFertilizerFormStore.delete(b_id_farm, b_id)
-        form.handleSubmit(e)
-    }
+    function handleSubmit(e: FormEvent<HTMLFormElement>) {
+        if (b_id_farm && b_id) {
+            fieldFertilizerFormStore.delete(b_id_farm, b_id)
+        }
+        form.handleSubmit(e)
+    }
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx (1)

101-119: Drop unused fertilizerOptions from loader to reduce payload.

The page no longer consumes fertilizerOptions; fetching and returning them is unnecessary.

Apply:

-        const fertilizers = await getFertilizers(
-            fdm,
-            session.principal_id,
-            b_id_farm,
-        )
-        const fertilizerOptions = fertilizers.map((fertilizer) => {
-            return {
-                p_id: fertilizer.p_id,
-                p_name_nl: fertilizer.p_name_nl,
-            }
-        })
...
         return {
-            fertilizerOptions: fertilizerOptions,
             fertilizer: fertilizer,
             fertilizerParameters: fertilizerParameters,
         }

If other nested routes still rely on fertilizerOptions via Outlet context, keep it; otherwise, remove and update any types/tests expecting it.

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

218-220: Avoid passing empty IDs into the form props.

If params are ever absent, empty strings propagate to the store key. Prefer gating render until both params exist.

Apply:

-                        <FertilizerApplicationForm
+                        {params.b_id_farm && params.b_id && (
+                            <FertilizerApplicationForm
                             options={fertilizerOptions}
                             action={location.pathname}
                             navigation={navigation}
-                            b_id_farm={params.b_id_farm || ""}
-                            b_id={params.b_id || ""}
-                        />
+                            b_id_farm={params.b_id_farm}
+                            b_id={params.b_id}
+                        />
+                        )}

Confirm these params are guaranteed on this route; if so, this is defensive only.

fdm-app/app/components/blocks/fertilizer/new-custom-fertilizer-page.tsx (2)

16-21: Outdated docstring.

The comment describes a layout/sidebar, not this block. Update or remove to avoid confusion.


81-129: Effect deps: prefer stable deps and avoid function identity.

Depending on form.reset can be brittle. Depend on fertilizer only; form.reset is stable but not necessary in deps.

Apply:

-}, [fertilizer, form.reset])
+}, [fertilizer])

Ensure eslint-plugin-react-hooks is satisfied; if it warns, suppress with a brief comment explaining reset’s stability.

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

77-82: Docstring is misleading

This block describes “managing farm settings,” but the route is “add fertilizer.” Update to reflect the actual purpose.

fdm-app/app/components/blocks/fertilizer/new-layout.tsx (1)

14-19: Clarify docstring

Revise the comment to “Layout for creating a new fertilizer” to avoid confusion with generic farm settings.

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

8-23: Unify loader error handling with the rest of the app

Use data(...) for parameter errors and wrap with handleLoaderError for consistency.

Apply:

-import type { LoaderFunctionArgs } from "react-router"
+import type { LoaderFunctionArgs } from "react-router"
+import { data } from "react-router"
+import { handleLoaderError } from "~/lib/error"

 export async function loader({ request, params }: LoaderFunctionArgs) {
-    const { b_id_farm } = params
-    if (!b_id_farm) {
-        throw new Error("Farm ID is required")
-    }
-
-    const session = await getSession(request)
-
-    const fertilizers = await getFertilizers(
-        fdm,
-        session.principal_id,
-        b_id_farm,
-    )
-
-    return { b_id_farm: b_id_farm, fertilizers: fertilizers }
+    try {
+        const { b_id_farm } = params
+        if (!b_id_farm) {
+            throw data("invalid: b_id_farm", { status: 400 })
+        }
+        const session = await getSession(request)
+        const fertilizers = await getFertilizers(
+            fdm,
+            session.principal_id,
+            b_id_farm,
+        )
+        return { b_id_farm, fertilizers }
+    } catch (error) {
+        throw handleLoaderError(error)
+    }
 }
fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx (1)

17-22: Docstring mismatch

Comment talks about “managing farm settings.” Update to “Block for creating a new fertilizer (form-only).”

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

31-31: Typo in meta description

“Bekij” → “Bekijk”.

Apply:

-            content: "Bekij de details van deze meststof",
+            content: "Bekijk de details van deze meststof",
fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.tsx (2)

31-31: Typo in meta description

“Bekij” → “Bekijk”.

Apply:

-            content: "Bekij de details van deze meststof",
+            content: "Bekijk de details van deze meststof",

151-154: Return proper 403 on forbidden updates

Throw a typed 403 instead of a generic error for clarity and consistent handling.

Apply:

-        if (fertilizer.p_source !== b_id_farm) {
-            throw new Error("Forbidden")
-        }
+        if (fertilizer.p_source !== b_id_farm) {
+            throw data("forbidden", { status: 403 })
+        }
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx (5)

108-113: Guard against undefined fertilizer names in options

Some fertilizers may lack p_name_nl. Use a safe fallback to avoid rendering “undefined” in selects.

Apply this diff:

-        const fertilizerOptions = fertilizers.map((fertilizer) => {
-            return {
-                p_id: fertilizer.p_id,
-                p_name_nl: fertilizer.p_name_nl,
-            }
-        })
+        const fertilizerOptions = fertilizers.map((fertilizer) => ({
+            p_id: fertilizer.p_id,
+            p_name_nl: fertilizer.p_name_nl ?? "",
+        }))

126-131: Fix stale JSDoc block

Comment describes a generic “manage farm settings” layout; this file renders the “new custom fertilizer” page inside the field wizard.

Apply this diff:

-/**
- * Renders the layout for managing farm settings.
- *
- * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields.
- * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster.
- */
+/**
+ * Route: create a new custom fertilizer within the field fertilizer management flow.
+ * Renders FarmNewCustomFertilizerBlock with loader-provided data and returns to manage on success.
+ */

22-23: Use the app alias for internal imports

Prefer "~" to avoid brittle relative paths in routes.

Apply this diff:

-import { getCalendar } from "../lib/calendar"
+import { getCalendar } from "~/lib/calendar"

160-213: Reduce duplicated payload mapping

The long addFertilizerToCatalogue payload is duplicated across routes. Extract a shared builder to keep fields in sync.

Apply this diff to inline the helper usage:

-        const p_id_catalogue = await addFertilizerToCatalogue(
-            fdm,
-            session.principal_id,
-            b_id_farm,
-            {
-                p_name_nl: formValues.p_name_nl,
-                p_name_en: formValues.p_name_en,
-                p_description: formValues.p_description,
-                p_type: formValues.p_type,
-                p_dm: formValues.p_dm,
-                p_density: formValues.p_density,
-                p_om: formValues.p_om,
-                p_a: formValues.p_a,
-                p_hc: formValues.p_hc,
-                p_eom: formValues.p_eom,
-                p_eoc: formValues.p_eoc,
-                p_c_rt: formValues.p_c_rt,
-                p_c_of: formValues.p_c_of,
-                p_c_if: formValues.p_c_if,
-                p_c_fr: formValues.p_c_fr,
-                p_cn_of: formValues.p_cn_of,
-                p_n_rt: formValues.p_n_rt,
-                p_n_if: formValues.p_n_if,
-                p_n_of: formValues.p_n_of,
-                p_n_wc: formValues.p_n_wc,
-                p_no3_rt: formValues.p_no3_rt,
-                p_nh4_rt: formValues.p_nh4_rt,
-                p_p_rt: formValues.p_p_rt,
-                p_k_rt: formValues.p_k_rt,
-                p_mg_rt: formValues.p_mg_rt,
-                p_ca_rt: formValues.p_ca_rt,
-                p_ne: formValues.p_ne,
-                p_s_rt: formValues.p_s_rt,
-                p_s_wc: formValues.p_s_wc,
-                p_cu_rt: formValues.p_cu_rt,
-                p_zn_rt: formValues.p_zn_rt,
-                p_na_rt: formValues.p_na_rt,
-                p_si_rt: formValues.p_si_rt,
-                p_b_rt: formValues.p_b_rt,
-                p_mn_rt: formValues.p_mn_rt,
-                p_ni_rt: formValues.p_ni_rt,
-                p_fe_rt: formValues.p_fe_rt,
-                p_mo_rt: formValues.p_mo_rt,
-                p_co_rt: formValues.p_co_rt,
-                p_as_rt: formValues.p_as_rt,
-                p_cd_rt: formValues.p_cd_rt,
-                p_cr_rt: formValues.p_cr_rt,
-                p_cr_vi: formValues.p_cr_vi,
-                p_pb_rt: formValues.p_pb_rt,
-                p_hg_rt: formValues.p_hg_rt,
-                p_cl_rt: formValues.p_cl_rt,
-                p_ef_nh3: undefined,
-                p_app_method_options: formValues.p_app_method_options,
-            },
-        )
+        const p_id_catalogue = await addFertilizerToCatalogue(
+            fdm,
+            session.principal_id,
+            b_id_farm,
+            buildCataloguePayload(formValues),
+        )

Add this helper once (e.g., in ~/lib/fertilizer.ts) and reuse across routes:

// ~/lib/fertilizer.ts
import type { z } from "zod"
import type { FormSchema } from "~/components/blocks/fertilizer/formschema"

export function buildCataloguePayload(v: z.infer<typeof FormSchema>) {
  return {
    p_name_nl: v.p_name_nl,
    p_name_en: v.p_name_en,
    p_description: v.p_description,
    p_type: v.p_type,
    p_dm: v.p_dm,
    p_density: v.p_density,
    p_om: v.p_om,
    p_a: v.p_a,
    p_hc: v.p_hc,
    p_eom: v.p_eom,
    p_eoc: v.p_eoc,
    p_c_rt: v.p_c_rt,
    p_c_of: v.p_c_of,
    p_c_if: v.p_c_if,
    p_c_fr: v.p_c_fr,
    p_cn_of: v.p_cn_of,
    p_n_rt: v.p_n_rt,
    p_n_if: v.p_n_if,
    p_n_of: v.p_n_of,
    p_n_wc: v.p_n_wc,
    p_no3_rt: v.p_no3_rt,
    p_nh4_rt: v.p_nh4_rt,
    p_p_rt: v.p_p_rt,
    p_k_rt: v.p_k_rt,
    p_mg_rt: v.p_mg_rt,
    p_ca_rt: v.p_ca_rt,
    p_ne: v.p_ne,
    p_s_rt: v.p_s_rt,
    p_s_wc: v.p_s_wc,
    p_cu_rt: v.p_cu_rt,
    p_zn_rt: v.p_zn_rt,
    p_na_rt: v.p_na_rt,
    p_si_rt: v.p_si_rt,
    p_b_rt: v.p_b_rt,
    p_mn_rt: v.p_mn_rt,
    p_ni_rt: v.p_ni_rt,
    p_fe_rt: v.p_fe_rt,
    p_mo_rt: v.p_mo_rt,
    p_co_rt: v.p_co_rt,
    p_as_rt: v.p_as_rt,
    p_cd_rt: v.p_cd_rt,
    p_cr_rt: v.p_cr_rt,
    p_cr_vi: v.p_cr_vi,
    p_pb_rt: v.p_pb_rt,
    p_hg_rt: v.p_hg_rt,
    p_cl_rt: v.p_cl_rt,
    p_ef_nh3: undefined,
    p_app_method_options: v.p_app_method_options,
  }
}

216-224: Consider compensating for partial failure

If addFertilizer fails after addFertilizerToCatalogue succeeds, you may orphan a catalogue entry. Add cleanup (if API allows) or a server-side atomic endpoint.

Would you like me to check @svenvw/fdm-core for a delete/rollback API or propose a small wrapper on the server that performs both operations transactionally?

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx (5)

27-35: Fix meta description typo

Small Dutch typo: “Bekij” → “Bekijk”.

Apply this diff:

-        {
-            name: "description",
-            content: "Bekij de details van deze meststof",
-        },
+        {
+            name: "description",
+            content: "Bekijk de details van deze meststof",
+        },

95-100: Guard against undefined fertilizer names in options

Same as the custom route: ensure a safe label.

Apply this diff:

-        const fertilizerOptions = fertilizers.map((fertilizer) => {
-            return {
-                p_id: fertilizer.p_id,
-                p_name_nl: fertilizer.p_name_nl,
-            }
-        })
+        const fertilizerOptions = fertilizers.map((fertilizer) => ({
+            p_id: fertilizer.p_id,
+            p_name_nl: fertilizer.p_name_nl ?? "",
+        }))

118-123: Fix stale JSDoc block

Comment doesn’t reflect this route’s purpose (new-from-template within wizard).

Apply this diff:

-/**
- * Renders the layout for managing farm settings.
- *
- * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields.
- * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster.
- */
+/**
+ * Route: create a new fertilizer from an existing template within the field fertilizer manage flow.
+ * Renders FarmNewFertilizerBlock and redirects back to manage on success.
+ */

25-26: Prefer app alias for calendar import

Align with other imports using "~".

Apply this diff:

-import { FarmNewFertilizerBlock } from "../components/blocks/fertilizer/new-fertilizer-page"
-import { getCalendar } from "../lib/calendar"
+import { FarmNewFertilizerBlock } from "~/components/blocks/fertilizer/new-fertilizer-page"
+import { getCalendar } from "~/lib/calendar"

156-209: Deduplicate payload mapping (same as custom route)

Extract and reuse buildCataloguePayload to keep the field list consistent across routes.

Apply this diff:

-        const p_id_catalogue = await addFertilizerToCatalogue(
-            fdm,
-            session.principal_id,
-            b_id_farm,
-            {
-                p_name_nl: formValues.p_name_nl,
-                p_name_en: formValues.p_name_en,
-                p_description: formValues.p_description,
-                p_type: formValues.p_type,
-                p_dm: formValues.p_dm,
-                p_density: formValues.p_density,
-                p_om: formValues.p_om,
-                p_a: formValues.p_a,
-                p_hc: formValues.p_hc,
-                p_eom: formValues.p_eom,
-                p_eoc: formValues.p_eoc,
-                p_c_rt: formValues.p_c_rt,
-                p_c_of: formValues.p_c_of,
-                p_c_if: formValues.p_c_if,
-                p_c_fr: formValues.p_c_fr,
-                p_cn_of: formValues.p_cn_of,
-                p_n_rt: formValues.p_n_rt,
-                p_n_if: formValues.p_n_if,
-                p_n_of: formValues.p_n_of,
-                p_n_wc: formValues.p_n_wc,
-                p_no3_rt: formValues.p_no3_rt,
-                p_nh4_rt: formValues.p_nh4_rt,
-                p_p_rt: formValues.p_p_rt,
-                p_k_rt: formValues.p_k_rt,
-                p_mg_rt: formValues.p_mg_rt,
-                p_ca_rt: formValues.p_ca_rt,
-                p_ne: formValues.p_ne,
-                p_s_rt: formValues.p_s_rt,
-                p_s_wc: formValues.p_s_wc,
-                p_cu_rt: formValues.p_cu_rt,
-                p_zn_rt: formValues.p_zn_rt,
-                p_na_rt: formValues.p_na_rt,
-                p_si_rt: formValues.p_si_rt,
-                p_b_rt: formValues.p_b_rt,
-                p_mn_rt: formValues.p_mn_rt,
-                p_ni_rt: formValues.p_ni_rt,
-                p_fe_rt: formValues.p_fe_rt,
-                p_mo_rt: formValues.p_mo_rt,
-                p_co_rt: formValues.p_co_rt,
-                p_as_rt: formValues.p_as_rt,
-                p_cd_rt: formValues.p_cd_rt,
-                p_cr_rt: formValues.p_cr_rt,
-                p_cr_vi: formValues.p_cr_vi,
-                p_pb_rt: formValues.p_pb_rt,
-                p_hg_rt: formValues.p_hg_rt,
-                p_cl_rt: formValues.p_cl_rt,
-                p_ef_nh3: undefined,
-                p_app_method_options: formValues.p_app_method_options,
-            },
-        )
+        const p_id_catalogue = await addFertilizerToCatalogue(
+            fdm,
+            session.principal_id,
+            b_id_farm,
+            buildCataloguePayload(formValues),
+        )

Re-use the helper outlined in the other comment.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 883611e and 4174369.

📒 Files selected for processing (23)
  • .changeset/spicy-knives-pump.md (1 hunks)
  • fdm-app/app/components/blocks/fertilizer-applications/card.tsx (5 hunks)
  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx (3 hunks)
  • fdm-app/app/components/blocks/fertilizer/fertilizer-page.tsx (1 hunks)
  • fdm-app/app/components/blocks/fertilizer/index-page.tsx (1 hunks)
  • fdm-app/app/components/blocks/fertilizer/new-custom-fertilizer-page.tsx (1 hunks)
  • fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx (1 hunks)
  • fdm-app/app/components/blocks/fertilizer/new-index-page.tsx (1 hunks)
  • fdm-app/app/components/blocks/fertilizer/new-layout.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.$p_id.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx (2 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.tsx (2 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx (2 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx (2 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx (2 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx (2 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx (2 hunks)
  • fdm-app/app/store/field-fertilizer-form.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (14)
📚 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.$b_id.tsx
  • fdm-app/app/components/blocks/fertilizer/new-index-page.tsx
  • fdm-app/app/components/blocks/fertilizer/new-custom-fertilizer-page.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx
  • fdm-app/app/components/blocks/fertilizer/fertilizer-page.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx
  • fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx
  • fdm-app/app/components/blocks/fertilizer/new-layout.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx
  • fdm-app/app/components/blocks/fertilizer/index-page.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.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.$b_id.tsx
  • fdm-app/app/components/blocks/fertilizer/new-custom-fertilizer-page.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx
  • fdm-app/app/components/blocks/fertilizer/fertilizer-page.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx
  • fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx
  • fdm-app/app/components/blocks/fertilizer/new-layout.tsx
  • fdm-app/app/components/blocks/fertilizer/index-page.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx
📚 Learning: 2025-06-02T10:31:27.097Z
Learnt from: SvenVw
PR: SvenVw/fdm#151
File: fdm-app/app/routes/signin._index.tsx:101-101
Timestamp: 2025-06-02T10:31:27.097Z
Learning: In fdm-app/app/routes/signin._index.tsx, the redirect destinations are intentionally inconsistent by design: the component defaults new sign-ins to "/welcome" (line 101) while the loader redirects authenticated users to "/farm" (line 80) and the action uses "/farm" as fallback (line 434). This creates appropriate user flows where new users complete their profile via the welcome page, while existing authenticated users bypass it and go directly to the main application.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.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.$b_id_farm.$calendar.field.$b_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx
  • fdm-app/app/components/blocks/fertilizer/new-layout.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.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 comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.

Applied to files:

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

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.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/components/blocks/fertilizer-applications/form.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/card.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.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/components/blocks/fertilizer-applications/form.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/card.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.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/components/blocks/fertilizer-applications/form.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.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/components/blocks/fertilizer-applications/form.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/card.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.tsx
🧬 Code graph analysis (16)
fdm-app/app/components/blocks/fertilizer/new-index-page.tsx (1)
fdm-core/src/db/schema.ts (1)
  • fertilizers (131-139)
fdm-app/app/components/blocks/fertilizer/new-custom-fertilizer-page.tsx (1)
fdm-app/app/components/blocks/fertilizer/form.tsx (1)
  • FertilizerForm (55-278)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage._index.tsx (3)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx (1)
  • loader (55-172)
fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx (2)
  • loader (25-81)
  • FarmFertilizersIndexPage (83-87)
fdm-app/app/components/blocks/fertilizer/index-page.tsx (1)
  • FarmFertilizersIndexBlock (26-64)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx (2)
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx (3)
  • loader (34-123)
  • FarmFertilizerPage (131-135)
  • action (137-222)
fdm-app/app/components/blocks/fertilizer/new-custom-fertilizer-page.tsx (1)
  • FarmNewCustomFertilizerBlock (22-137)
fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx (1)
fdm-app/app/components/blocks/fertilizer/form.tsx (1)
  • FertilizerForm (55-278)
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (4)
fdm-app/app/components/blocks/fertilizer-applications/types.d.tsx (1)
  • FertilizerOption (12-19)
fdm-app/app/store/field-fertilizer-form.tsx (1)
  • useFieldFertilizerFormStore (24-46)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.$p_id.tsx (1)
  • action (133-172)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx (1)
  • action (130-230)
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx (1)
fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx (1)
  • FarmNewFertilizerBlock (23-89)
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx (3)
fdm-app/app/components/blocks/fertilizer/fertilizer-page.tsx (1)
  • FarmFertilizerBlock (38-133)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx (1)
  • FarmFertilizerBlock (83-99)
fdm-app/app/components/blocks/fertilizer/new-layout.tsx (1)
  • FarmNewFertilizerLayout (20-59)
fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx (2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage._index.tsx (1)
  • FarmFertilizersIndexPage (84-99)
fdm-app/app/components/blocks/fertilizer/index-page.tsx (1)
  • FarmFertilizersIndexBlock (26-64)
fdm-app/app/components/blocks/fertilizer/new-layout.tsx (5)
fdm-app/app/components/blocks/header/action.tsx (1)
  • HeaderActionProps (6-10)
fdm-app/app/components/blocks/header/base.tsx (1)
  • Header (6-29)
fdm-app/app/components/blocks/header/farm.tsx (1)
  • HeaderFarm (15-76)
fdm-app/app/components/blocks/header/fertilizer.tsx (1)
  • HeaderFertilizer (15-86)
fdm-app/app/components/blocks/farm/farm-title.tsx (1)
  • FarmTitle (15-36)
fdm-app/app/components/blocks/fertilizer-applications/card.tsx (1)
fdm-app/app/store/field-fertilizer-form.tsx (1)
  • useFieldFertilizerFormStore (24-46)
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx (1)
fdm-app/app/components/blocks/fertilizer/new-index-page.tsx (1)
  • NewFertilizerIndex (9-54)
fdm-app/app/components/blocks/fertilizer/index-page.tsx (7)
fdm-app/app/components/blocks/header/action.tsx (1)
  • HeaderActionProps (6-10)
fdm-app/app/components/blocks/header/base.tsx (1)
  • Header (6-29)
fdm-app/app/components/blocks/header/farm.tsx (1)
  • HeaderFarm (15-76)
fdm-app/app/components/blocks/header/fertilizer.tsx (1)
  • HeaderFertilizer (15-86)
fdm-app/app/components/blocks/farm/farm-title.tsx (1)
  • FarmTitle (15-36)
fdm-app/app/components/blocks/fertilizer/table.tsx (1)
  • DataTable (30-132)
fdm-app/app/components/blocks/fertilizer/columns.tsx (1)
  • columns (31-118)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new._index.tsx (2)
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx (1)
  • loader (8-23)
fdm-app/app/components/blocks/fertilizer/new-index-page.tsx (1)
  • NewFertilizerIndex (9-54)
fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.tsx (2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.$p_id.tsx (1)
  • FarmFertilizerPage (122-131)
fdm-app/app/components/blocks/fertilizer/fertilizer-page.tsx (1)
  • FarmFertilizerBlock (38-133)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx (2)
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx (2)
  • loader (27-75)
  • FarmFertilizerBlock (83-94)
fdm-app/app/components/blocks/fertilizer/new-layout.tsx (1)
  • FarmNewFertilizerLayout (20-59)
🔇 Additional comments (27)
.changeset/spicy-knives-pump.md (1)

1-6: Clear changeset documentation.

The changeset properly documents the UX improvement allowing users to navigate directly from the field fertilizer form to the fertilizer management page and return when done.

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

9-9: LGTM! Appropriate import addition.

The useLocation import is correctly added to support the conditional rendering logic.


186-188: Use explicit route matching instead of a loose substring check

File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.tsx (lines 186–188) — currently: if (location.pathname.includes("fertilizer/manage")) return . Ripgrep search did not complete, so other substring collisions could not be ruled out.

Recommendations:

  • Minimal fix: if (location.pathname.includes("/fertilizer/manage")) return
  • Preferred: use react-router exact matching (matchPath / useMatch) for the full route, e.g. matchPath({ path: '/farm/:b_id_farm/:calendar/field/:b_id/fertilizer/manage', end: false }, location.pathname)
fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx (2)

8-8: LGTM! Clean refactor to use the UI block.

The route has been properly refactored to delegate UI rendering to the FarmFertilizersIndexBlock component, with appropriate type imports and component usage.

Also applies to: 13-13


83-87: LGTM! Component properly renamed and simplified.

The component is appropriately renamed from FarmFertilizersBlock to FarmFertilizersIndexPage and correctly uses the new block component.

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

84-98: LGTM! Proper route component implementation.

The component correctly uses the Route.ComponentProps pattern with proper typing and renders the FarmFertilizersIndexBlock with an appropriate backlink action.

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

122-131: LGTM! Clean route component implementation.

The component properly delegates rendering to the FarmFertilizerBlock and provides the appropriate backlink for navigation.

fdm-app/app/store/field-fertilizer-form.tsx (3)

1-19: LGTM! Clean store interface design.

The store interface is well-structured with clear method signatures for save, load, and delete operations. The use of composite keys (farm/field) ensures proper data isolation.


24-46: LGTM! Proper Zustand implementation with persistence.

The store implementation correctly uses Zustand with sessionStorage persistence. The use of sessionStorage is appropriate for form data that should persist during the session but not across browser restarts.


28-30: Avoid overwriting existing DB entry in save — merge instead.

Couldn't verify usage in the repo (ripgrep returned no searchable files in the sandbox). If save can be called with partial formData, merge with the existing entry to avoid losing fields:

 save(b_id_farm, b_id, formData) {
-  const db = { ...get().db, [makeId(b_id_farm, b_id)]: formData }
+  const existingData = get().db[makeId(b_id_farm, b_id)] || {}
+  const db = { ...get().db, [makeId(b_id_farm, b_id)]: { ...existingData, ...formData } }
   set({ db })
 },
fdm-app/app/components/blocks/fertilizer/index-page.tsx (1)

40-44: Verify fertilizerOptions type compatibility.

loaderData.fertilizers is typed as Fertilizer (fdm-core/src/fertilizer.d.ts); HeaderFertilizerOption was not found in the repo — ensure HeaderFertilizer accepts Fertilizer or map/cast loaderData.fertilizers to HeaderFertilizerOption[] before passing.
Location: fdm-app/app/components/blocks/fertilizer/index-page.tsx (lines 40–44)

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

112-133: Nice UX: inline “Beheren” with Combobox.

Good placement and preventDefault to avoid form submit on click.

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

16-16: Good modularization: delegate UI to FarmNewCustomFertilizerBlock.

Import and usage look correct and align with the new block-based pattern.


131-135: LGTM: simple wrapper rendering the block.

Keeps route thin; matches loader/action contract.

fdm-app/app/components/blocks/fertilizer-applications/card.tsx (3)

177-187: Smart: auto-open dialog when persisted form data exists.

This nicely resumes the wizard flow after returning from management.


206-209: Minor UI polish reads well.

Dialog title/layout changes improve spacing without behavior change.


277-285: Nice feedback on destructive action.

Showing a spinner and disabled state during delete avoids double-submits.

fdm-app/app/components/blocks/fertilizer/new-index-page.tsx (1)

1-54: Clean, accessible index with clear CTAs.

Structure and routing look good; keys and fallbacks are correct.

fdm-app/app/components/blocks/fertilizer/new-custom-fertilizer-page.tsx (1)

29-79: Defaults mapping looks consistent.

Comprehensive coverage of fields, aligned with schema.

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

83-93: LGTM: wrapper + backlink wiring look correct

Outlet inside FarmNewFertilizerLayout with backlink /farm/${params.b_id_farm}/fertilizers matches the farm-level navigation.

fdm-app/app/components/blocks/fertilizer/new-layout.tsx (1)

31-57: LGTM: layout composition is clean and reusable

HeaderFarm/HeaderFertilizer + FarmTitle + children composition is clear; backlink/action props are applied correctly.

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

25-29: LGTM: delegation to NewFertilizerIndex

Route thin wrapper pattern is good.

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

126-127: LGTM: switched to shared block

Using FarmNewFertilizerBlock keeps the route lean.

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

87-95: LGTM: wizard back/forward links

Action and backlink correctly target the application step and manage list.

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

122-131: LGTM: shared block and backlink

Thin wrapper with backlink ../fertilizers matches the list route.

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

4-5: Path alias '@/app' configured — no action required.
fdm-app/tsconfig.json defines "paths": { "@/": ["./"], "~/": ["./app/"] } so imports like '@/app/...' resolve to ./app/....

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

221-226: Good redirect target for wizard flow

Redirect back to .../fertilizer/manage ensures the application page can revalidate on return. Nice.

If the manage page uses a fetcher or deferred loader, confirm it revalidates after this redirect so the dropdown shows the new item.

Comment thread fdm-app/app/components/blocks/fertilizer-applications/form.tsx
Comment thread fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx (1)

77-78: Default enum_multi array to a safe empty array

Ensure consistent type for p_app_method_options to avoid surprises in validation/serialization.

-            p_app_method_options: fertilizer.p_app_method_options,
+            p_app_method_options: fertilizer.p_app_method_options ?? [],
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (1)

69-81: Rehydrate Date fields from session store; trim deps.

p_app_date will be a string here; DatePicker expects a Date. Also avoid function deps to keep the effect stable.

Apply:

-    useEffect(() => {
-        if (b_id_farm && b_id) {
-            const savedFormValues = fieldFertilizerFormStore.load(
-                b_id_farm,
-                b_id,
-            )
-            if (savedFormValues) {
-                for (const [k, v] of Object.entries(savedFormValues)) {
-                    form.setValue(k as any, v)
-                }
-            }
-        }
-    }, [b_id_farm, b_id, form.setValue, fieldFertilizerFormStore.load])
+    useEffect(() => {
+        if (!b_id_farm || !b_id) return
+        const savedFormValues = fieldFertilizerFormStore.load(b_id_farm, b_id)
+        if (savedFormValues) {
+            for (const [k, v] of Object.entries(savedFormValues)) {
+                const value =
+                    k === "p_app_date" && v ? new Date(v as any) : (v as any)
+                form.setValue(k as any, value)
+            }
+        }
+        // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [b_id_farm, b_id])
🧹 Nitpick comments (11)
fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx (2)

29-79: Reduce drift: derive defaultValues from parameter metadata

Hard-coding every field is error-prone when schema/parameters evolve. Build defaultValues from fertilizerParameters, defaulting enum_multi to [] and others to undefined.

+import { useMemo } from "react"
@@
-    const form = useRemixForm<z.infer<typeof FormSchema>>({
-        mode: "onTouched",
-        resolver: zodResolver(FormSchema),
-        defaultValues: {
-            p_name_nl: "",
-            p_type: fertilizer.p_type,
-            p_dm: fertilizer.p_dm,
-            p_density: fertilizer.p_density,
-            p_om: fertilizer.p_om,
-            p_a: fertilizer.p_a,
-            p_hc: fertilizer.p_hc,
-            p_eom: fertilizer.p_eom,
-            p_eoc: fertilizer.p_eoc,
-            p_c_rt: fertilizer.p_c_rt,
-            p_c_of: fertilizer.p_c_of,
-            p_c_if: fertilizer.p_c_if,
-            p_c_fr: fertilizer.p_c_fr,
-            p_cn_of: fertilizer.p_cn_of,
-            p_n_rt: fertilizer.p_n_rt,
-            p_n_if: fertilizer.p_n_if,
-            p_n_of: fertilizer.p_n_of,
-            p_n_wc: fertilizer.p_n_wc,
-            p_no3_rt: fertilizer.p_no3_rt,
-            p_nh4_rt: fertilizer.p_nh4_rt,
-            p_p_rt: fertilizer.p_p_rt,
-            p_k_rt: fertilizer.p_k_rt,
-            p_mg_rt: fertilizer.p_mg_rt,
-            p_ca_rt: fertilizer.p_ca_rt,
-            p_ne: fertilizer.p_ne,
-            p_s_rt: fertilizer.p_s_rt,
-            p_s_wc: fertilizer.p_s_wc,
-            p_cu_rt: fertilizer.p_cu_rt,
-            p_zn_rt: fertilizer.p_zn_rt,
-            p_na_rt: fertilizer.p_na_rt,
-            p_si_rt: fertilizer.p_si_rt,
-            p_b_rt: fertilizer.p_b_rt,
-            p_mn_rt: fertilizer.p_mn_rt,
-            p_ni_rt: fertilizer.p_ni_rt,
-            p_fe_rt: fertilizer.p_fe_rt,
-            p_mo_rt: fertilizer.p_mo_rt,
-            p_co_rt: fertilizer.p_co_rt,
-            p_as_rt: fertilizer.p_as_rt,
-            p_cd_rt: fertilizer.p_cd_rt,
-            p_cr_rt: fertilizer.p_cr_rt,
-            p_cr_vi: fertilizer.p_cr_vi,
-            p_pb_rt: fertilizer.p_pb_rt,
-            p_hg_rt: fertilizer.p_hg_rt,
-            p_cl_rt: fertilizer.p_cl_rt,
-            p_app_method_options: fertilizer.p_app_method_options ?? [],
-        },
-    })
+    const defaultValues = useMemo<Partial<z.infer<typeof FormSchema>>>(() => {
+        const base = Object.fromEntries(
+            fertilizerParameters
+                .filter(
+                    (p) => p.parameter !== "p_source" && p.parameter !== "p_id_catalogue",
+                )
+                .map((p) => {
+                    const v = (fertilizer as any)[p.parameter]
+                    return [
+                        p.parameter,
+                        p.type === "enum_multi" ? (Array.isArray(v) ? v : []) : v ?? undefined,
+                    ]
+                }),
+        ) as Partial<z.infer<typeof FormSchema>>
+        return { p_name_nl: "", ...base }
+    }, [fertilizer, fertilizerParameters])
+
+    const form = useRemixForm<z.infer<typeof FormSchema>>({
+        mode: "onTouched",
+        resolver: zodResolver(FormSchema),
+        defaultValues,
+    })

17-21: Docstring clarity

“existing farm” seems off; this block seeds a new fertilizer from an existing fertilizer/template within the wizard.

- * Renders the new fertilizer form based off of an existing farm.
+ * Renders the "new fertilizer" form seeded from an existing fertilizer/template.
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx (5)

46-49: Trim unused loader work (drop getSession/getFertilizers if not needed)

On the “new custom” page, these values aren’t used by FarmNewCustomFertilizerBlock (per app/components/blocks/fertilizer/new-custom-fertilizer-page.tsx). Removing them reduces loader time and payload.

Apply if unused:

@@
-        // Get the session
-        const session = await getSession(request)
@@
-        // Get the available fertilizers
-        const fertilizers = await getFertilizers(
-            fdm,
-            session.principal_id,
-            b_id_farm,
-        )
-        const fertilizerOptions = fertilizers.map((fertilizer) => {
-            return {
-                p_id: fertilizer.p_id,
-                p_name_nl: fertilizer.p_name_nl,
-            }
-        })
+        // No template list needed for the "new custom" page
@@
-        return {
-            fertilizerOptions: fertilizerOptions,
-            fertilizer: fertilizer,
-            fertilizerParameters: fertilizerParameters,
-        }
+        return { fertilizer, fertilizerParameters }

And in imports:

-import {
-    addFertilizer,
-    addFertilizerToCatalogue,
-    getFertilizerParametersDescription,
-    getFertilizers,
-} from "@svenvw/fdm-core"
+import {
+    addFertilizer,
+    addFertilizerToCatalogue,
+    getFertilizerParametersDescription,
+} from "@svenvw/fdm-core"

Also applies to: 102-120


52-100: Deduplicate default fertilizer shape via a helper

This literal is large and duplicated in app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx. Extract a typed factory to prevent drift.

Example:

-        const fertilizer = {
-            p_id: undefined, // Added p_id
-            p_source: b_id_farm,
-            ...
-            p_app_method_options: [],
-        }
+        const fertilizer = buildEmptyCustomFertilizer(b_id_farm)

New helper (suggested location: app/lib/fertilizer.ts):

export function buildEmptyCustomFertilizer(b_id_farm: string) {
  return {
    p_id: undefined,
    p_source: b_id_farm,
    p_name_nl: "",
    p_type: undefined,
    p_dm: undefined,
    p_density: undefined,
    p_om: undefined,
    p_a: undefined,
    p_hc: undefined,
    p_eom: undefined,
    p_eoc: undefined,
    p_c_rt: undefined,
    p_c_of: undefined,
    p_c_if: undefined,
    p_c_fr: undefined,
    p_cn_of: undefined,
    p_n_rt: undefined,
    p_n_if: undefined,
    p_n_of: undefined,
    p_n_wc: undefined,
    p_no3_rt: undefined,
    p_nh4_rt: undefined,
    p_p_rt: undefined,
    p_k_rt: undefined,
    p_mg_rt: undefined,
    p_ca_rt: undefined,
    p_ne: undefined,
    p_s_rt: undefined,
    p_s_wc: undefined,
    p_cu_rt: undefined,
    p_zn_rt: undefined,
    p_na_rt: undefined,
    p_si_rt: undefined,
    p_b_rt: undefined,
    p_mn_rt: undefined,
    p_ni_rt: undefined,
    p_fe_rt: undefined,
    p_mo_rt: undefined,
    p_co_rt: undefined,
    p_as_rt: undefined,
    p_cd_rt: undefined,
    p_cr_rt: undefined,
    p_cr_vi: undefined,
    p_pb_rt: undefined,
    p_hg_rt: undefined,
    p_cl_rt: undefined,
    p_app_method_options: [],
  }
}

126-136: Fix stale doc comment

Comment describes a layout with sidebar/outlet, but the component only renders FarmNewCustomFertilizerBlock.

-/**
- * Renders the layout for managing farm settings.
- *
- * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields.
- * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster.
- */
+/**
+ * New custom fertilizer page within the field fertilizer wizard.
+ * Renders the form block for creating a custom fertilizer.
+ */

142-151: Return 400 consistently for missing params in action

Loader throws a 400 via data(). Action should mirror that instead of throwing generic Errors that may surface as 500s.

Apply:

-        if (!b_id_farm) {
-            throw new Error("missing: b_id_farm")
-        }
+        if (!b_id_farm) {
+            throw data("invalid: b_id_farm", { status: 400, statusText: "invalid: b_id_farm" })
+        }
@@
-        if (!b_id) {
-            throw new Error("missing: b_id")
-        }
+        if (!b_id) {
+            throw data("invalid: b_id", { status: 400, statusText: "invalid: b_id" })
+        }

211-213: Drop explicit undefined

Passing p_ef_nh3: undefined is redundant and can lead to accidental schema drift. Omit it.

-                p_ef_nh3: undefined,
                 p_app_method_options: formValues.p_app_method_options,
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (4)

61-66: Don’t wipe p_app_method on initial rehydrate; only clear if invalid for new fertilizer.

Current effect clears saved method when p_id is set from store. Keep it if compatible.

Apply:

-    useEffect(() => {
-        if (p_id) {
-            form.setValue("p_app_method", "")
-        }
-    }, [p_id, form.setValue])
+    useEffect(() => {
+        if (!p_id) return
+        const current = form.getValues("p_app_method")
+        const allowed =
+            options.find((o) => o.value === p_id)?.applicationMethodOptions?.map((o) => o.value) ?? []
+        if (!current || !allowed.includes(current as any)) {
+            form.setValue("p_app_method", undefined as any)
+        }
+        // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [p_id, options])

83-89: Make “Beheren” a non-submit button.

Inside a form, defaults to submit. You call preventDefault, but set type="button" for robustness/a11y.

Apply:

-    function handleManageFertilizers(e: MouseEvent<HTMLButtonElement>) {
+    function handleManageFertilizers(e: MouseEvent<HTMLButtonElement>) {
         e.preventDefault()
         if (b_id_farm && b_id) {
             fieldFertilizerFormStore.save(b_id_farm, b_id, form.getValues())
         }
         navigate("./manage")
     }

And in JSX:

-                                <Button
+                                <Button
                                     variant="secondary"
                                     className="ml-2"
+                                    type="button"
                                     onClick={handleManageFertilizers}
                                 >

91-94: Only clear store on valid submit.

Currently deletes saved values even when client validation fails. Hook into RHF’s valid path.

Apply:

-    function handleSubmit(e: FormEvent<HTMLFormElement>) {
-        fieldFertilizerFormStore.delete(b_id_farm, b_id)
-        form.handleSubmit(e)
-    }
+    const onValidSubmit = () => {
+        fieldFertilizerFormStore.delete(b_id_farm, b_id)
+    }

And:

-                onSubmit={handleSubmit}
+                onSubmit={form.handleSubmit(onValidSubmit)}

Also applies to: 101-101


107-128: Tailwind classes: use grow (not grow-1); simplify button container.

grow-1 isn’t a core utility; use grow. Use flex-none to keep the button from stretching instead of the complex selector.

Apply:

-                        <div className="flex flex-row items-end [&>*]:grow-1">
+                        <div className="flex flex-row items-end [&>*]:grow">
                             <Combobox
 ...
-                            <div className="py-2 [&.py-2]:grow-0">
+                            <div className="py-2 flex-none">
                                 <Button
                                     variant="secondary"
                                     className="ml-2"
                                     onClick={handleManageFertilizers}
+                                    type="button"
                                 >
                                     Beheren
                                 </Button>
                             </div>

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4174369 and c58244e.

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

Applied to files:

  • fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx
🧬 Code graph analysis (2)
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (2)
fdm-app/app/components/blocks/fertilizer-applications/types.d.tsx (1)
  • FertilizerOption (12-19)
fdm-app/app/store/field-fertilizer-form.tsx (1)
  • useFieldFertilizerFormStore (24-46)
fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx (1)
fdm-app/app/components/blocks/fertilizer/form.tsx (1)
  • FertilizerForm (55-278)
🔇 Additional comments (9)
fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx (2)

81-87: Overall: solid integration

Clean handoff to FertilizerForm with typed RHF setup and editable flag. No functional blockers spotted here.


5-9: Unify import aliases or verify config for "@"

Mixed aliases ("" and "@") can break builds if tsconfig/vite aliases aren’t both defined. Prefer one (typically "" in Remix), or confirm "@/*" is mapped.

Apply if "~" is the project standard:

-} from "@/app/components/blocks/fertilizer/form"
+} from "~/components/blocks/fertilizer/form"

Run to verify alias mappings exist:

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

24-33: LGTM: Meta content

Title/description read well and match the page intent.


225-230: LGTM: Redirect path matches wizard manage route

Redirecting to /farm/{farm}/{calendar}/field/{b_id}/fertilizer/manage aligns with the nested flow in the PR objectives.

Please confirm the “Back to Applications/Done” control navigates back with a normal navigation (not replace) so the application page loader re-runs and refreshes the dropdown.


7-13: Verify import source: Remix vs react-router

Confirm that importing types/hooks from "react-router" is intentional across the app. In Remix apps, the norm is "@remix-run/node" for server types and "@remix-run/react" for hooks. If you’re standardizing on React Router’s data APIs directly, keep it consistent repo‑wide.

Run to audit import usage:


225-230: Minor: toast message safety

The toast includes user-provided p_name_nl. Ensure your toast renderer escapes/does not render HTML to avoid XSS if names contain angle brackets.

If unsure, search your toast UI component for any use of dangerouslySetInnerHTML or HTML parsing.

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

2-2: Type-only React event imports — good.

Keeps bundle lean and avoids value import conflicts.


5-5: RRv7 Form/useNavigate import — correct for this repo.

Matches our v7 pattern (Form from "react-router").


36-44: New required IDs in props — verify all call sites updated.

Ensure every <FertilizerApplicationForm …> passes b_id_farm and b_id.

Run:

@BoraIneviNMI BoraIneviNMI requested a review from SvenVw September 19, 2025 14:54
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: 9

Caution

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

⚠️ Outside diff range comments (1)
fdm-app/app/components/blocks/fertilizer-applications/card.tsx (1)

168-177: Dialog may auto‑reopen after submit due to store values.

You close the dialog when navigation becomes idle, but you don’t clear the saved form; the auto‑open effect then reopens the modal, causing a flicker/loop.

-    const [isDialogOpen, setIsDialogOpen] = useState(false)
-    const previousNavigationState = useRef(navigation.state)
+    const [isDialogOpen, setIsDialogOpen] = useState(false)
+    const previousNavigationState = useRef(navigation.state)
+    const closedBySubmitRef = useRef(false)
@@
-    useEffect(() => {
+    const fieldFertilizerFormStore = useFieldFertilizerFormStore()
+    useEffect(() => {
         const wasNotIdle = previousNavigationState.current !== "idle"
         const isIdle = navigation.state === "idle"
 
         if (wasNotIdle && isIdle) {
-            setIsDialogOpen(false)
+            // Close after submit and clear persisted draft to prevent auto‑reopen
+            setIsDialogOpen(false)
+            closedBySubmitRef.current = true
+            if (params.b_id_farm && b_id_or_b_lu_catalogue) {
+                fieldFertilizerFormStore.delete(
+                    params.b_id_farm,
+                    b_id_or_b_lu_catalogue,
+                )
+            }
         }
 
         previousNavigationState.current = navigation.state
     }, [navigation.state])
 
-    const fieldFertilizerFormStore = useFieldFertilizerFormStore()
     const savedFormValues =
         params.b_id_farm &&
         b_id_or_b_lu_catalogue &&
         fieldFertilizerFormStore.load(params.b_id_farm, b_id_or_b_lu_catalogue)
     useEffect(() => {
-        if (!isDialogOpen && savedFormValues) {
+        if (!isDialogOpen && savedFormValues && !closedBySubmitRef.current) {
             setIsDialogOpen(true)
         }
     }, [isDialogOpen, savedFormValues])
@@
-    function handleDialogOpenChange(state: boolean) {
+    function handleDialogOpenChange(state: boolean) {
         if (!state && params.b_id_farm && b_id_or_b_lu_catalogue) {
             fieldFertilizerFormStore.delete(
                 params.b_id_farm,
                 b_id_or_b_lu_catalogue,
             )
+            // User closed explicitly, don't immediately auto‑open again
+            closedBySubmitRef.current = true
         }
 
         setIsDialogOpen(state)
     }

Also applies to: 179-189, 192-201

🧹 Nitpick comments (10)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx (1)

22-22: Minor: object shorthand in return

Style consistency with manage._index.tsx.

-return { b_id_farm: b_id_farm, fertilizers: fertilizers }
+return { b_id_farm, fertilizers }
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage._index.tsx (2)

16-24: Fix Dutch meta description copy

Typo and phrasing.

 export const meta: MetaFunction = () => {
   return [
     { title: `Meststoffen | ${clientConfig.name}` },
     {
       name: "description",
-      content: "Bekij de lijst van meststoffen beschikbaar.",
+      content: "Bekijk de lijst van beschikbare meststoffen.",
     },
   ]
 }

58-64: Avoid variable shadowing of “farm”

Shadowing the earlier farm constant is confusing; rename the mapper param.

-const farmOptions = farms.map((farm) => {
-    return {
-        b_id_farm: farm.b_id_farm,
-        b_name_farm: farm.b_name_farm,
-    }
-})
+const farmOptions = farms.map((f) => ({
+    b_id_farm: f.b_id_farm,
+    b_name_farm: f.b_name_farm,
+}))
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.$p_id.tsx (2)

29-31: Typo in meta description.

"Bekij" → "Bekijk".

-            content: "Bekij de details van deze meststof",
+            content: "Bekijk de details van deze meststof",

122-131: Type loader data and fix backlink path.

  • Add typeof loader to useLoaderData for type safety.
  • Backlink should go one level up (../) from .../manage/$p_id to .../manage.
-export default function FarmFertilizerPage() {
-    const loaderData = useLoaderData()
+export default function FarmFertilizerPage() {
+    const loaderData = useLoaderData<typeof loader>()
@@
-        <FarmFertilizerBlock
-            loaderData={loaderData}
-            backlink={"../fertilizer/manage"}
-        />
+        <FarmFertilizerBlock loaderData={loaderData} backlink={".."} />
fdm-app/app/components/blocks/fertilizer-applications/card.tsx (1)

7-7: Path alias inconsistency.

Elsewhere the repo uses "" for app-root imports. Using "@/..." may depend on custom TS config and can break. Align to "".

-import { useFieldFertilizerFormStore } from "@/app/store/field-fertilizer-form"
+import { useFieldFertilizerFormStore } from "~/app/store/field-fertilizer-form"
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx (1)

31-33: Typo in meta description.

-            content: "Bekij de details van deze meststof",
+            content: "Bekijk de details van deze meststof",
fdm-app/app/store/field-fertilizer-form.tsx (3)

27-34: Use functional set to avoid stale reads during rapid updates.

Derive next state from the current state provided to set.

-            save(b_id_farm, b_id_or_b_lu_catalogue, formData) {
-                const db = {
-                    ...get().db,
-                    [makeId(b_id_farm, b_id_or_b_lu_catalogue)]: formData,
-                }
-                set({ db })
-            },
+            save(b_id_farm, b_id_or_b_lu_catalogue, formData) {
+                const key = makeId(b_id_farm, b_id_or_b_lu_catalogue)
+                set(state => ({
+                    db: { ...state.db, [key]: formData },
+                }))
+            },

21-23: Consider delimiter safety in composite keys.

If IDs can contain “/”, collisions occur. Use a delimiter guaranteed absent or encode.

-function makeId(b_id_farm: string, b_id: string) {
-    return `${b_id_farm}/${b_id}`
-}
+function makeId(b_id_farm: string, b_id: string) {
+    return `${encodeURIComponent(b_id_farm)}|${encodeURIComponent(b_id)}`
+}

1-1: Nit: rename file to .ts (no JSX).

Keeps filetype accurate.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ad3770 and a56b79d.

📒 Files selected for processing (9)
  • fdm-app/app/components/blocks/fertilizer-applications/card.tsx (5 hunks)
  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx (3 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.$p_id.tsx (1 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx (1 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.custom.tsx (1 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.tsx (1 hunks)
  • fdm-app/app/store/field-fertilizer-form.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx
🧰 Additional context used
🧠 Learnings (9)
📚 Learning: 2024-12-19T13:20:44.152Z
Learnt from: SvenVw
PR: SvenVw/fdm#23
File: fdm-app/app/routes/app.addfarm.new.tsx:15-17
Timestamp: 2024-12-19T13:20:44.152Z
Learning: Authentication for the “app.addfarm.new” route is already handled globally in “fdm-app/app/routes/app.tsx,” automatically redirecting unauthenticated users to the SignIn page.

Applied to files:

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

Applied to files:

  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage._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.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.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.fertilizers.$b_lu_catalogue.manage.$p_id.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.custom.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage._index.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.fertilizers.$b_lu_catalogue.manage.new.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.custom.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage._index.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 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.fertilizers.$b_lu_catalogue.manage.new.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/components/blocks/fertilizer-applications/card.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/components/blocks/fertilizer-applications/card.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/components/blocks/fertilizer-applications/card.tsx
🔇 Additional comments (13)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx (1)

1-4: Verify TS path alias setup for "@" before merging

If "@" isn’t configured in tsconfig paths, the route won’t compile. Script checks aliases and usage.

fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage._index.tsx (1)

92-96: Verify back-navigation target and loader re-run on return

Ensure the target route exists and its loader refreshes the dropdown when returning from Manage.

Manual: From the wizard, add/edit a fertilizer via Manage, click “Terug naar bemesting toevoegen”, verify the application route loader runs (new fertilizer appears in the dropdown without hard refresh).

fdm-app/app/components/blocks/fertilizer-applications/card.tsx (2)

271-275: Date formatting: ensure p_app_date is a Date.

If it’s a string, date-fns/format expects a Date/number. Convert via new Date(...) or ensure type upstream.

Would you confirm the type of application.p_app_date? If it’s a string in ISO format, consider:

- format(application.p_app_date, "yyyy-MM-dd")
+ format(new Date(application.p_app_date), "yyyy-MM-dd")

222-237: Good: form receives explicit route context props.

Passing b_id_farm and b_id_or_b_lu_catalogue down prevents implicit global lookups and makes the form self‑sufficient.

fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.tsx (2)

59-65: LGTM: stable mapping for farm options.

Defensive fallback for b_name_farm avoids undefined in UI lists.


84-92: Backlink/action routes look correct for wizard return.

Targets application step and manage index as required by the PR objectives.

If there are guards (loaders) on the destination routes requiring fresh data, confirm they re-run on navigation to refresh dropdowns.

fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx (1)

221-226: Nice: redirect back to manage with success toast.

Matches the wizard flow and provides immediate feedback.

fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.custom.tsx (2)

52-101: Default fertilizer stub: good shape, keep p_id undefined.

This matches the “new custom fertilizer” flow and avoids accidental edits.


225-231: Redirect target aligns with flow.

Returns to manage list with toast; consistent with other routes.

fdm-app/app/store/field-fertilizer-form.tsx (4)

46-46: Confirm values are JSON‑serializable.

If FormSchema includes Dates/Maps, persistence will stringify unexpectedly.


24-49: Overall: solid, minimal store API enabling in‑wizard persistence.

Hook shape and per‑farm/key scoping look good.

Ensure the fertilizer application loader invalidates/refreshes after returning from Manage flows so the dropdown reflects new entries.


4-6: Import FormSchema as a value to use z.infer.

import type removes the value; typeof FormSchema then fails.

-import type { FormSchema } from "../components/blocks/fertilizer-applications/formschema"
+import { FormSchema } from "../components/blocks/fertilizer-applications/formschema"

Run to verify the path/export casing:


18-18: Rename method from delete to remove to avoid reserved-word ergonomics.

delete is awkward to destructure; rename API to remove.

-    delete(b_id_farm: string, b_id_or_b_lu_catalogue: string): void
+    remove(b_id_farm: string, b_id_or_b_lu_catalogue: string): void
@@
-            delete(b_id_farm, b_id_or_b_lu_catalogue) {
-                const db = { ...get().db }
-                delete db[makeId(b_id_farm, b_id_or_b_lu_catalogue)]
-                set({ db })
-            },
+            remove(b_id_farm, b_id_or_b_lu_catalogue) {
+                const key = makeId(b_id_farm, b_id_or_b_lu_catalogue)
+                set(state => {
+                    const db = { ...state.db }
+                    delete db[key]
+                    return { db }
+                })
+            },

Check call sites:

Also applies to: 38-42

Comment on lines +84 to +86
const fertilizer = await getFertilizer(fdm, p_id)
const fertilizerParameters = getFertilizerParametersDescription()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle not found fertilizer to avoid 500s.

getFertilizer may return null/undefined; subsequent access to fields will throw.

-        const fertilizer = await getFertilizer(fdm, p_id)
+        const fertilizer = await getFertilizer(fdm, p_id)
+        if (!fertilizer) {
+            throw data("not found: p_id", { status: 404, statusText: "not found: p_id" })
+        }
📝 Committable suggestion

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

Suggested change
const fertilizer = await getFertilizer(fdm, p_id)
const fertilizerParameters = getFertilizerParametersDescription()
const fertilizer = await getFertilizer(fdm, p_id)
if (!fertilizer) {
throw data("not found: p_id", { status: 404, statusText: "not found: p_id" })
}
const fertilizerParameters = getFertilizerParametersDescription()
🤖 Prompt for AI Agents
In
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.$p_id.tsx
around lines 84-86, getFertilizer may return null/undefined but the code
immediately uses its fields; add a null check after awaiting getFertilizer and
handle the missing case to avoid a 500. If fertilizer is falsy, return/throw a
404 response (or redirect to a safe page like the fertilizers list) and avoid
further property access; otherwise proceed to call
getFertilizerParametersDescription and the rest of the logic.

Comment on lines +1 to +7
import { getFertilizers } from "@svenvw/fdm-core"
import type { LoaderFunctionArgs } from "react-router"
import { useLoaderData } from "react-router"
import { NewFertilizerIndex } from "@/app/components/blocks/fertilizer/new-index-page"
import { getSession } from "~/lib/auth.server"
import { fdm } from "~/lib/fdm.server"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Align loader with error-handling pattern and enforce farm ACL check

This route throws a raw Error and skips verifying farm access. Mirror the pattern used in manage._index.tsx: use data + handleLoaderError and verify the farm exists for the principal to avoid information disclosure and inconsistent errors.

-import { getFertilizers } from "@svenvw/fdm-core"
-import type { LoaderFunctionArgs } from "react-router"
-import { useLoaderData } from "react-router"
+import { getFarm, getFertilizers } from "@svenvw/fdm-core"
+import { data, type LoaderFunctionArgs } from "react-router"
+import { useLoaderData } from "react-router"
 import { NewFertilizerIndex } from "@/app/components/blocks/fertilizer/new-index-page"
 import { getSession } from "~/lib/auth.server"
 import { fdm } from "~/lib/fdm.server"
+import { handleLoaderError } from "~/lib/error"
 
 export async function loader({ request, params }: LoaderFunctionArgs) {
-    const { b_id_farm } = params
-    if (!b_id_farm) {
-        throw new Error("Farm ID is required")
-    }
-
-    const session = await getSession(request)
-
-    const fertilizers = await getFertilizers(
-        fdm,
-        session.principal_id,
-        b_id_farm,
-    )
-
-    return { b_id_farm: b_id_farm, fertilizers: fertilizers }
+    try {
+        const { b_id_farm } = params
+        if (!b_id_farm) {
+            throw data("invalid: b_id_farm", { status: 400, statusText: "invalid: b_id_farm" })
+        }
+
+        const session = await getSession(request)
+
+        // Enforce access check and consistent 404
+        const farm = await getFarm(fdm, session.principal_id, b_id_farm)
+        if (!farm) {
+            throw data("not found: b_id_farm", { status: 404, statusText: "not found: b_id_farm" })
+        }
+
+        const fertilizers = await getFertilizers(fdm, session.principal_id, b_id_farm)
+        return { b_id_farm, fertilizers }
+    } catch (error) {
+        throw handleLoaderError(error)
+    }
 }

Also applies to: 8-23

🤖 Prompt for AI Agents
In
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx
around lines 1-23, the loader currently throws a raw Error and does not verify
that the current principal has access to the requested farm; update the loader
to follow the same pattern as manage._index.tsx by performing the farm ACL check
via the fdm helper (ensure you verify the farm exists and the session principal
is authorized), fetch fertilizers and other data into a single data object, wrap
async work in try/catch and pass errors to the shared handleLoaderError helper
instead of throwing raw Errors, and return the data object via the loader so the
route uses the unified error-handling and access-control pattern.

Comment thread fdm-app/app/store/field-fertilizer-form.tsx Outdated
Comment on lines +45 to +47
name: "field-fertilizer-form", // name of the item in the storage (must be unique)
storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
},
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Sep 22, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Harden persist storage for SSR (sessionStorage is undefined on server).

Guard storage access to avoid SSR crashes in Remix; add a memory fallback.

-import { createJSONStorage, persist } from "zustand/middleware"
+import { createJSONStorage, persist, type StateStorage } from "zustand/middleware"
@@
         {
             name: "field-fertilizer-form", // name of the item in the storage (must be unique)
-            storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
+            storage: createJSONStorage(() => (typeof window !== "undefined" ? sessionStorage : memoryStorage)),
+            version: 1,
         },

Add this helper near the top of the file (outside the shown range):

const memoryStorage: StateStorage = {
  getItem: () => null,
  setItem: () => void 0,
  removeItem: () => void 0,
}

Also applies to: 3-3

🤖 Prompt for AI Agents
In fdm-app/app/store/field-fertilizer-form.tsx around lines 45 to 47,
sessionStorage is referenced directly which will be undefined during SSR; add a
memoryStorage fallback object near the top of the file (with getItem returning
null, setItem and removeItem no-ops) and change the storage creation to use
createJSONStorage(() => typeof sessionStorage !== "undefined" ? sessionStorage :
memoryStorage) so SSR won't crash; apply the same change to the other occurrence
noted (lines 3-3) as well.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

@SvenVw You're absolutely right! Looking at the existing store patterns in your codebase, I can see that other Zustand stores don't include SSR hardening for storage access. Since this follows the same pattern as your existing stores and you've confirmed this is client-side only usage, the SSR guard is not needed.

The recommendation was overly cautious - in your specific Remix setup, if other stores work fine without this pattern, then this one should too. My apologies for the unnecessary suggestion.

🐰 tilts ears thoughtfully


🏁 Script executed:

#!/bin/bash
# Find existing Zustand stores in the fdm-app to compare patterns
fd -e tsx -e ts . fdm-app/app/store/

# Look at their content to see how they handle storage
find fdm-app/app/store -name "*.ts" -o -name "*.tsx" | head -10 | xargs cat

Length of output: 4539


🏁 Script executed:

#!/bin/bash
# Search for other uses of createJSONStorage or sessionStorage in the codebase
rg -n "createJSONStorage|sessionStorage|localStorage" fdm-app/app/

# Also search for other persist configurations
rg -n "persist\(" fdm-app/app/ -A 5 -B 2

Length of output: 3312


🏁 Script executed:

#!/bin/bash
# Look specifically for Zustand store patterns
rg -n "create.*persist" fdm-app/app/ -A 10 -B 2

Length of output: 1912


@SvenVw You're absolutely right! Looking at the existing store patterns in your codebase, I can see that the changelog.ts store uses the exact same pattern:

storage: createJSONStorage(() => localStorage),

And the codebase has direct sessionStorage usage in other places (like the atlas fields route) without any SSR guards. Since your existing stores work fine without this pattern and you've confirmed this is client-side only usage, the SSR guard is indeed not needed.

My recommendation was overly cautious for your specific setup. Your new store follows the established patterns in the FDM application perfectly.

🐰 ears perk up in acknowledgment


✏️ Learnings added
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.

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.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
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.

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.

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 (6)
fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx (1)

28-33: Remove redundant key on inner element

The list caller supplies the key; the inner NavLink key is unnecessary.

Apply this diff:

-        <NavLink
-            key={fertilizer.p_id}
+        <NavLink
             to={`${fertilizer.p_id}`}
             className="block"
         >
fdm-app/app/components/blocks/header/create-farm.tsx (2)

68-86: Avoid nested anchors: remove outer Link wrapper

Link (renders ) wrapping BreadcrumbLink (also an ) creates invalid nested anchors and hurts a11y/keyboard nav.

Apply this diff:

-                    {currentPath.match(/manage/) ? (
-                        <>
-                            <Link to="..">
-                                <BreadcrumbItem className="hidden md:block">
-                                    <BreadcrumbLink href="..">
-                                        Bemesting
-                                    </BreadcrumbLink>
-                                </BreadcrumbItem>
-                            </Link>
+                    {currentPath.match(/manage/) ? (
+                        <>
+                            <BreadcrumbItem className="hidden md:block">
+                                <BreadcrumbLink href="..">
+                                    Bemesting
+                                </BreadcrumbLink>
+                            </BreadcrumbItem>
                             <BreadcrumbSeparator />
                             <BreadcrumbItem className="hidden md:block">
                                 <BreadcrumbLink>Meststoffen</BreadcrumbLink>
                             </BreadcrumbItem>
                         </>
                     ) : (

Additionally, consider matching both singular and plural in the surrounding condition so this branch also applies in the wizard step:

  • Change the fertilizers condition to /fertilizer(s)?/ to support paths like “…/fertilizer/manage …”.

1-1: Clean up unused import after refactor

If you remove the Link wrapper as suggested, drop Link from imports.

Apply this diff:

-import { Link, useLocation } from "react-router"
+import { useLocation } from "react-router"
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx (2)

11-16: Use a typed error response for missing farm id

Throwing Error forces a 500. Use data(...) with a 400 to align with other loaders.

Apply this diff:

-import type { LoaderFunctionArgs } from "react-router"
-import { useLoaderData } from "react-router"
+import type { LoaderFunctionArgs } from "react-router"
+import { data, useLoaderData } from "react-router"
@@
     const { b_id_farm } = params
     if (!b_id_farm) {
-        throw new Error("Farm ID is required")
+        throw data("invalid: b_id_farm", { status: 400, statusText: "invalid: b_id_farm" })
     }

35-35: Type the loader data usage

Adds safety and IDE help; matches patterns elsewhere.

Apply this diff:

-    const { fertilizers } = useLoaderData()
+    const { fertilizers } = useLoaderData<typeof loader>()
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.tsx (1)

2-9: Prefer route-aware matching (useMatch) over substring checks

Using useMatch for the manage branch is more robust than pathname.includes, avoiding false positives and future path drift.

Apply this diff to the imports:

 import {
     data,
     type LoaderFunctionArgs,
     type MetaFunction,
     Outlet,
     useLoaderData,
-    useLocation,
+    useMatch,
 } from "react-router"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b31ba4 and b7b58e1.

📒 Files selected for processing (10)
  • fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx (1 hunks)
  • fdm-app/app/components/blocks/header/create-farm.tsx (2 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx (2 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx (2 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.tsx (1 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
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.
📚 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/components/blocks/fertilizer/new-fertilizer.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx
  • fdm-app/app/components/blocks/header/create-farm.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/fertilizer/new-fertilizer.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx
  • fdm-app/app/components/blocks/header/create-farm.tsx
📚 Learning: 2024-12-19T13:20:44.152Z
Learnt from: SvenVw
PR: SvenVw/fdm#23
File: fdm-app/app/routes/app.addfarm.new.tsx:15-17
Timestamp: 2024-12-19T13:20:44.152Z
Learning: Authentication for the “app.addfarm.new” route is already handled globally in “fdm-app/app/routes/app.tsx,” automatically redirecting unauthenticated users to the SignIn page.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.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.fertilizers.new.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.tsx
  • fdm-app/app/components/blocks/header/create-farm.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 comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.

Applied to files:

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

Applied to files:

  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.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.create.$b_id_farm.$calendar.fertilizers.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/routes/farm.$b_id_farm.fertilizers.new._index.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/routes/farm.$b_id_farm.fertilizers.new._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/components/blocks/header/create-farm.tsx
🧬 Code graph analysis (2)
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx (2)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx (2)
  • FarmFertilizerBlock (87-126)
  • loader (31-79)
fdm-app/app/components/blocks/header/base.tsx (1)
  • Header (6-29)
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx (1)
fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx (2)
  • CustomFertilizerButton (5-20)
  • BasedOffFertilizerButton (22-46)
🔇 Additional comments (6)
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx (2)

18-18: Type-safe route props import looks good

Using Route.ComponentProps tightens types for params and aligns with the codebase patterns.


87-98: Good: dynamic absolute navigation path

Switching the Header action to /farm/${params.b_id_farm}/fertilizers avoids fragile relative paths from nested routes.

Confirm the farm fertilizers overview route is indeed /farm/:b_id_farm/fertilizers for consistency across all entry points.

fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx (2)

2-2: Correct router import

Importing Link/NavLink from react-router matches the project conventions.


5-20: LGTM: clear CTA for custom fertilizer

Simple, accessible card-as-link pattern with clear copy.

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

39-47: Minor: avoid duplicate keys

Parent supplies key; inner component also sets a key on its NavLink. Consider removing the inner key (see component suggestion).

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

82-86: Gate "manage" branch via useMatch; also verify parent loader revalidation

Replace the substring check with a precise useMatch for /farm/create/:b_id_farm/:calendar/fertilizers/manage/* to avoid accidental matches and ensure the parent loader re-runs when leaving manage so the dropdown refreshes.

-    const location = useLocation()
-
-    if (location.pathname.includes("/manage")) {
-        return <Outlet />
-    }
+    const isManage = useMatch(
+        "/farm/create/:b_id_farm/:calendar/fertilizers/manage/*"
+    )
+    if (isManage) {
+        return <Outlet />
+    }

Verification: repository search returned no fertilizers.manage route files or explicit revalidation hooks — confirm the manage route path exists and that the parent loader revalidates on navigation.

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator Author

One thing that can be done is to move the [Terug naar overzicht] in the farm fertilizers index page to the same place as other newly added index routes.

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 (3)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx (2)

27-35: Update meta description for fertilizer template selection context.

The current meta description "Bekijk de details van deze meststof" (View the details of this fertilizer) doesn't accurately reflect this route's purpose, which is selecting an existing fertilizer as a template for creating a new one within the farm creation wizard.

Apply this diff to align the meta description with the route's function:

 export const meta: MetaFunction = () => {
     return [
-        { title: `Meststof | ${clientConfig.name}` },
+        { title: `Meststof als sjabloon | ${clientConfig.name}` },
         {
             name: "description",
-            content: "Bekijk de details van deze meststof",
+            content: "Selecteer een meststof als sjabloon om een nieuwe meststof te maken voor dit bedrijf",
         },
     ]
 }

118-123: Update component documentation to match actual functionality.

The JSDoc comment describes a generic farm settings layout but this component specifically handles fertilizer template selection within the farm creation wizard.

Apply this diff to update the documentation:

 /**
- * Renders the layout for managing farm settings.
+ * Renders the fertilizer template selection page within the farm creation wizard.
  *
- * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields.
- * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster.
+ * This component allows users to select an existing fertilizer as a template for creating
+ * a new custom fertilizer. It displays the fertilizer form pre-populated with the template values.
  */
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx (1)

32-32: Fix typo in meta description.

Minor typographical error in the Dutch text.

Apply this diff to fix the typo:

-            content: "Bekij de details van deze meststof",
+            content: "Bekijk de details van deze meststof",
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef13797 and 8d49369.

📒 Files selected for processing (9)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx (2 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx (2 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx (2 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx (1 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.tsx (1 hunks)
  • fdm-app/app/store/field-fertilizer-form.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new._index.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx
  • fdm-app/app/store/field-fertilizer-form.tsx
🧰 Additional context used
🧠 Learnings (20)
📓 Common learnings
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.
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.
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.
📚 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.fertilizers.new.$p_id.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.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.fertilizers.new.$p_id.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.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.fertilizers.new.$p_id.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.fertilizers.new.$p_id.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.fertilizers.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._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.fertilizers.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._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.fertilizers.new.$p_id.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.fertilizers.new.$p_id.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx
📚 Learning: 2024-12-19T13:20:44.152Z
Learnt from: SvenVw
PR: SvenVw/fdm#23
File: fdm-app/app/routes/app.addfarm.new.tsx:15-17
Timestamp: 2024-12-19T13:20:44.152Z
Learning: Authentication for the “app.addfarm.new” route is already handled globally in “fdm-app/app/routes/app.tsx,” automatically redirecting unauthenticated users to the SignIn page.

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.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.$b_id.fertilizer.manage.new.$p_id.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 comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx
🧬 Code graph analysis (3)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx (3)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.tsx (2)
  • meta (19-28)
  • loader (30-62)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.custom.tsx (3)
  • meta (24-33)
  • loader (35-124)
  • action (138-234)
fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx (1)
  • FarmNewFertilizerBlock (22-88)
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx (3)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new._index.tsx (1)
  • NewFertilizerIndexPage (34-51)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx (1)
  • NewFertilizerIndexPage (34-51)
fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx (2)
  • CustomFertilizerButton (5-20)
  • BasedOffFertilizerButton (22-42)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx (3)
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx (3)
  • loader (36-115)
  • FarmFertilizerPage (123-127)
  • action (129-218)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx (3)
  • loader (35-124)
  • FarmFertilizerPage (132-136)
  • action (138-234)
fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx (1)
  • FarmNewFertilizerBlock (22-88)
🔇 Additional comments (15)
fdm-app/app/routes/farm.$b_id_farm.fertilizers._index.tsx (2)

22-22: LGTM - Type import for route component props.

The Route.ComponentProps type from the generated route types is correctly imported to type the component props, providing type safety for params, loaderData, and other route-related props.


92-94: Component properly typed with Route.ComponentProps.

The component correctly accepts Route.ComponentProps which provides type safety for params and other route-related props. The params destructuring aligns with React Router v7's typed route component pattern.

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

1-1: LGTM - Combined import for type and function.

The Fertilizer type and getFertilizers function are correctly imported together from the fdm-core package.


3-7: LGTM - Proper imports for React Router hooks and new fertilizer components.

The imports are clean and correctly sourced from react-router for the hook and from the new fertilizer component module for the button components.


28-33: LGTM - Clear component documentation.

The JSDoc comment provides a clear description of the component's functionality and expected behavior.


34-35: Component name updated correctly.

The function name change from NewFertilizerIndex to NewFertilizerIndexPage provides better clarity and consistency with other page components in the codebase.


39-46: LGTM - Consistent implementation with other routes.

The component implementation correctly uses the new CustomFertilizerButton and BasedOffFertilizerButton components, which matches the pattern used in similar routes like farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new._index.tsx. This provides consistency across the fertilizer management flows.

fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx (1)

85-87: Add authorization check for getFertilizer call.

Based on React Router patterns for authentication, the loader should verify user authorization before accessing resources. From the retrieved learnings, getFertilizer does not perform authorization checks, unlike getFertilizers which includes a checkPermission call. This means getFertilizer(fdm, p_id) can potentially return fertilizer details for any fertilizer ID without validating user permissions.

Add authorization validation before retrieving the fertilizer:

 // Get selected fertilizer
-const fertilizer = await getFertilizer(fdm, p_id)
+const fertilizer = await getFertilizer(fdm, p_id)
+// Verify user has permission to access this fertilizer
+const userFertilizers = await getFertilizers(fdm, session.principal_id, b_id_farm)
+const hasAccess = userFertilizers.some(f => f.p_id === p_id)
+if (!hasAccess) {
+    throw data("not found: p_id", {
+        status: 404,
+        statusText: "not found: p_id",
+    })
+}
fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx (2)

19-19: LGTM! Proper block component import.

The import of FarmNewFertilizerBlock follows the established pattern for migrating form rendering to dedicated block components.


123-127: LGTM! Clean delegation to block component.

The refactor successfully moves form rendering logic from inline code to the FarmNewFertilizerBlock component, maintaining clean separation of concerns. This follows the established pattern shown in other similar routes.

fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx (5)

1-25: LGTM! Complete and appropriate imports.

All necessary imports are present, including the field-specific calendar utilities (getCalendar) that differentiate this route from the simpler farm-level route.


32-32: Fix typo in meta description.

Minor typographical error in the Dutch text.

Apply this diff to fix the typo:

-            content: "Bekijk de details van deze meststof",
+            content: "Bekijk de details van deze meststof",

124-128: LGTM! Proper block component delegation.

The component correctly delegates rendering to FarmNewFertilizerBlock, following the established pattern for form block components.


85-87: Critical security vulnerability - unauthorized fertilizer access.

The getFertilizer(fdm, p_id) call lacks proper authorization checks, potentially allowing users to access fertilizer templates from farms they don't have permission to view. Based on my analysis of the @svenvw/fdm-core functions:

  • getFertilizers includes checkPermission to verify farm access
  • getFertilizer has NO authorization checks and queries directly by p_id

This creates a security vulnerability where malicious users could craft URLs to access unauthorized fertilizer data.

Apply this diff to ensure proper authorization:

-        // Get selected fertilizer
-        const fertilizer = await getFertilizer(fdm, p_id)
-        const fertilizerParameters = getFertilizerParametersDescription()
-
-        // Get the available fertilizers
-        const fertilizers = await getFertilizers(
-            fdm,
-            session.principal_id,
-            b_id_farm,
-        )
-        const fertilizerOptions = fertilizers.map((fertilizer) => {
-            return {
-                p_id: fertilizer.p_id,
-                p_name_nl: fertilizer.p_name_nl,
-            }
-        })
+        // Get the available fertilizers (scoped to current user & farm)
+        const fertilizers = await getFertilizers(
+            fdm,
+            session.principal_id,
+            b_id_farm,
+        )
+        // Find selected template within the authorized set
+        const fertilizer = fertilizers.find((f) => f.p_id === p_id)
+        if (!fertilizer) {
+            throw data("not found: p_id", {
+                status: 404,
+                statusText: "not found: p_id",
+            })
+        }
+        const fertilizerParameters = getFertilizerParametersDescription()
+        const fertilizerOptions = fertilizers.map((f) => ({
+            p_id: f.p_id,
+            p_name_nl: f.p_name_nl,
+        }))

221-226: Redirect is fine — fertilizer dropdown will refresh on return

The fertilizer application route's loader (fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx) fetches fertilizers and returns fertilizerOptions, and there are no shouldRevalidate/unstable_shouldReload hooks; navigating back to /farm/{b_id_farm}/{calendar}/field/{b_id}/fertilizer will rerun the loader and refresh the dropdown.

@coderabbitai coderabbitai Bot added the branch:development Issue only affecting development, not the main branch (yet) label 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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d49369 and f1a3b4f.

📒 Files selected for processing (1)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
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.
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.
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.
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.
📚 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.$b_id.fertilizer.manage.new.tsx
📚 Learning: 2024-12-19T13:20:44.152Z
Learnt from: SvenVw
PR: SvenVw/fdm#23
File: fdm-app/app/routes/app.addfarm.new.tsx:15-17
Timestamp: 2024-12-19T13:20:44.152Z
Learning: Authentication for the “app.addfarm.new” route is already handled globally in “fdm-app/app/routes/app.tsx,” automatically redirecting unauthenticated users to the SignIn page.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.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.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.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.field.$b_id.fertilizer.manage.new.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/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.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.$b_id.fertilizer.manage.new.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 comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx
🧬 Code graph analysis (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx (4)
fdm-app/app/components/blocks/header/base.tsx (1)
  • Header (6-29)
fdm-app/app/components/blocks/header/farm.tsx (1)
  • HeaderFarm (15-76)
fdm-app/app/components/blocks/header/field.tsx (1)
  • HeaderField (16-92)
fdm-app/app/components/blocks/farm/farm-title.tsx (1)
  • FarmTitle (15-36)

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator Author

BoraIneviNMI commented Sep 25, 2025

Maybe I could use the same routes for /farm/b_id_farm/calendar/field/(field)/fertilizer/manage/new$(?fieldIds) Parts in parentheses will be optional.

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 (4)
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (1)

75-98: Stabilize rehydration effect deps to avoid unnecessary re-runs.

Depending on function identities can cause extra runs; keep to stable keys.

- }, [
-     b_id_farm,
-     b_id_or_b_lu_catalogue,
-     form.setValue,
-     fieldFertilizerFormStore.load,
- ])
+ }, [b_id_farm, b_id_or_b_lu_catalogue])
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new._index.tsx (1)

11-26: Wrap loader with centralized error handling for consistency.

Other routes use handleLoaderError; align this one for consistent responses/logging.

-export async function loader({ request, params }: LoaderFunctionArgs) {
-    const { b_id_farm } = params
-    if (!b_id_farm) {
-        throw new Error("Farm ID is required")
-    }
-    const session = await getSession(request)
-    const fertilizers = await getFertilizers(fdm, session.principal_id, b_id_farm)
-    return { b_id_farm: b_id_farm, fertilizers: fertilizers }
-}
+export async function loader({ request, params }: LoaderFunctionArgs) {
+    try {
+        const { b_id_farm } = params
+        if (!b_id_farm) {
+            throw new Error("Farm ID is required")
+        }
+        const session = await getSession(request)
+        const fertilizers = await getFertilizers(
+            fdm,
+            session.principal_id,
+            b_id_farm,
+        )
+        return { b_id_farm, fertilizers }
+    } catch (error) {
+        throw handleLoaderError(error)
+    }
+}
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.tsx (1)

113-116: Use BreadcrumbItem for “Percelen” to keep breadcrumb semantics/styles.

Wrap the plain text in a BreadcrumbItem and add its import.

-import { BreadcrumbLink, BreadcrumbSeparator } from "~/components/ui/breadcrumb"
+import {
+    BreadcrumbItem,
+    BreadcrumbLink,
+    BreadcrumbSeparator,
+} from "~/components/ui/breadcrumb"
-                <BreadcrumbSeparator />
-                Percelen
-                <BreadcrumbSeparator />
+                <BreadcrumbSeparator />
+                <BreadcrumbItem>Percelen</BreadcrumbItem>
+                <BreadcrumbSeparator />

Also applies to: 11-13

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

146-154: Validate presence of fieldIds in action to avoid redirecting with null.

Your loaders require fieldIds; mirror that in the action for robustness.

 export async function action({ request, params }: ActionFunctionArgs) {
     try {
         const searchParams = new URL(request.url).searchParams
         const b_id_farm = params.b_id_farm
 
         if (!b_id_farm) {
             throw new Error("missing: b_id_farm")
         }
+        if (!searchParams.has("fieldIds")) {
+            throw data("missing: fieldIds", {
+                status: 400,
+                statusText: "missing: fieldIds",
+            })
+        }
-        return redirectWithSuccess(
-            `/farm/${b_id_farm}/${calendar}/field/fertilizer?fieldIds=${searchParams.get("fieldIds")}`,
+        return redirectWithSuccess(
+            `/farm/${b_id_farm}/${calendar}/field/fertilizer?fieldIds=${searchParams.get("fieldIds")}`,
             {
                 message: `${formValues.p_name_nl} is toegevoegd! 🎉`,
             },
         )

Also applies to: 228-233

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1a3b4f and 580442d.

📒 Files selected for processing (7)
  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx (4 hunks)
  • fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new._index.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
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.
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.
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.
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.
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.
📚 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/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.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.fertilizer.manage.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new._index.tsx
📚 Learning: 2024-12-19T13:20:44.152Z
Learnt from: SvenVw
PR: SvenVw/fdm#23
File: fdm-app/app/routes/app.addfarm.new.tsx:15-17
Timestamp: 2024-12-19T13:20:44.152Z
Learning: Authentication for the “app.addfarm.new” route is already handled globally in “fdm-app/app/routes/app.tsx,” automatically redirecting unauthenticated users to the SignIn page.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new._index.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.$b_id_farm.$calendar.field.fertilizer.manage.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.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.fertilizer.manage.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.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 comprehensive farm layout system has been created in `components/custom/farm-layouts/` with `BaseFarmLayout` and `FarmSidebarLayout` components. The system supports both simple and sidebar-based layouts while maintaining consistent header and farm selection functionality across all farm routes.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx
📚 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-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx
📚 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-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.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.fertilizer.manage.new.custom.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx
🧬 Code graph analysis (4)
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (3)
fdm-app/app/components/blocks/fertilizer-applications/types.d.tsx (1)
  • FertilizerOption (12-19)
fdm-app/app/store/field-fertilizer-form.tsx (1)
  • useFieldFertilizerFormStore (24-49)
fdm-app/app/components/custom/combobox.tsx (1)
  • Combobox (41-158)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx (1)
fdm-core/src/db/schema.ts (1)
  • fertilizers (131-139)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx (4)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx (3)
  • meta (61-69)
  • loader (71-184)
  • action (426-471)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx (3)
  • meta (24-33)
  • loader (35-132)
  • action (146-237)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.tsx (2)
  • meta (21-30)
  • loader (32-88)
fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx (1)
  • FarmNewFertilizerBlock (22-88)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new._index.tsx (1)
fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx (2)
  • CustomFertilizerButton (5-28)
  • BasedOffFertilizerButton (30-58)
🔇 Additional comments (6)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer._index.tsx (1)

402-406: Prop wiring to persist/restore form state looks good.

Passing b_id_farm and fieldIds (as b_id_or_b_lu_catalogue) aligns with the store’s composite keying and enables round‑trip persistence.

Please verify that fieldIds is always present when the form renders (you already gate on fieldAmount > 0), so the store key remains stable across navigation.

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

99-108: Post‑submit cleanup is correct.

Clearing persisted draft only after successful submit prevents data loss on validation errors.


137-137: Replace invalid Tailwind class [&>*]:grow-1.

Use [&>*]:grow; grow-1 is not a valid utility.

- <div className="flex flex-row items-end [&>*]:grow-1">
+ <div className="flex flex-row items-end [&>*]:grow">

149-149: Remove invalid arbitrary variant [&.py-2]:grow-0.

Use the utility directly.

- <div className="py-2 [&.py-2]:grow-0">
+ <div className="py-2 grow-0">
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx (2)

37-124: Loader structure and validations look solid.

Param checks, farm existence, and fieldIds requirement mirror other routes correctly.


138-241: Action flow is correct and consistent.

Catalogue creation, linking to farm, and success redirect align with the intended UX.

Comment on lines +110 to +124
function handleManageFertilizers(e: MouseEvent<HTMLButtonElement>) {
e.preventDefault()
if (b_id_farm && b_id_or_b_lu_catalogue) {
fieldFertilizerFormStore.save(
b_id_farm,
b_id_or_b_lu_catalogue,
form.getValues(),
)
}
navigate(
searchParams.has("fieldIds")
? `./manage/new?fieldIds=${searchParams.get("fieldIds")}`
: "./manage/new",
)
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Sep 25, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix nested interactive element in TooltipTrigger; make the button non-submit and drop preventDefault.

Using TooltipTrigger without asChild wraps a button, creating nested buttons. Also set type="button" to avoid accidental submit; you can then remove preventDefault and the MouseEvent import.

Apply:

- import type { MouseEvent } from "react"
+ // (no MouseEvent import needed)

- function handleManageFertilizers(e: MouseEvent<HTMLButtonElement>) {
-     e.preventDefault()
+ function handleManageFertilizers() {
      if (b_id_farm && b_id_or_b_lu_catalogue) {
          fieldFertilizerFormStore.save(
              b_id_farm,
              b_id_or_b_lu_catalogue,
              form.getValues(),
          )
      }
      navigate(
          searchParams.has("fieldIds")
              ? `./manage/new?fieldIds=${searchParams.get("fieldIds")}`
              : "./manage/new",
      )
  }
- <Tooltip>
-     <TooltipTrigger>
-         <Button
-             variant="secondary"
-             className="ml-2"
-             onClick={handleManageFertilizers}
-         >
+ <Tooltip>
+     <TooltipTrigger asChild>
+         <Button
+             type="button"
+             variant="secondary"
+             className="ml-2"
+             onClick={handleManageFertilizers}
+         >
              Voeg nieuwe toe
          </Button>
      </TooltipTrigger>

Also applies to: 150-165, 2-2

🤖 Prompt for AI Agents
In fdm-app/app/components/blocks/fertilizer-applications/form.tsx around lines
110-124 (also apply same fix at 150-165 and top imports), the handler currently
takes a MouseEvent, calls e.preventDefault(), and the button is wrapped by
TooltipTrigger (causing nested interactive elements) — change the button to
type="button", remove the preventDefault call and the MouseEvent parameter (make
the handler parameterless), and remove the MouseEvent import; also update the
TooltipTrigger usage to use asChild (or otherwise avoid wrapping a native
button) so the button is not nested inside another interactive element.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

e.preventDefault() was needed to prevent remix form from submitting. I don't know why clicking the button submits. Maybe you can look into it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@boraini You're absolutely right! The button defaults to type="submit" when inside a form, which is why it triggers form submission and requires preventDefault().

My suggestion addresses exactly this issue - by explicitly setting type="button", the button won't submit the form anymore, eliminating the need for preventDefault(). So the fix I proposed actually solves both problems:

  1. Form submission issue: type="button" prevents automatic form submission
  2. Nested interactive elements: asChild on TooltipTrigger fixes the accessibility issue

The reason clicking the button submits is HTML's default behavior - any <button> inside a <form> defaults to type="submit" unless explicitly told otherwise. Setting type="button" is the cleaner solution than using preventDefault().

🐰 ears perked up with understanding


✏️ Learnings added
Learnt from: boraini
PR: SvenVw/fdm#272
File: fdm-app/app/components/blocks/fertilizer-applications/form.tsx:110-124
Timestamp: 2025-09-25T12:42:28.012Z
Learning: In HTML forms, buttons default to type="submit" unless explicitly set to type="button", which causes form submission when clicked inside a form element. Using type="button" eliminates the need for preventDefault() calls in event handlers.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
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.

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.

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

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

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

73-97: Hydration effect deps: avoid unstable function deps; keep deps stable.

The effect will re-run more than necessary because it depends on method identities (form.setValue, fieldFertilizerFormStore.load). Limit deps to the keys driving hydration.

Apply:

-    }, [
-        b_id_farm,
-        b_id_or_b_lu_catalogue,
-        form.setValue,
-        fieldFertilizerFormStore.load,
-    ])
+    }, [b_id_farm, b_id_or_b_lu_catalogue]) // keep stable; avoid fn deps

99-109: Post‑submit cleanup: add guard and remove unstable deps.

Guard on both IDs and drop method identity from deps to avoid unnecessary re-runs.

Apply:

-    useEffect(() => {
-        if (form.formState.isSubmitSuccessful) {
-            fieldFertilizerFormStore.delete(b_id_farm, b_id_or_b_lu_catalogue)
-        }
-    }, [
-        form.formState.isSubmitSuccessful,
-        b_id_farm,
-        b_id_or_b_lu_catalogue,
-        fieldFertilizerFormStore.delete,
-    ])
+    useEffect(() => {
+        if (
+            form.formState.isSubmitSuccessful &&
+            b_id_farm &&
+            b_id_or_b_lu_catalogue
+        ) {
+            fieldFertilizerFormStore.delete(b_id_farm, b_id_or_b_lu_catalogue)
+        }
+    }, [form.formState.isSubmitSuccessful, b_id_farm, b_id_or_b_lu_catalogue]) // keep deps stable

2-2: Make manage button non‑submit; drop preventDefault and MouseEvent import.

Buttons in forms default to submit; set type="button" instead of calling preventDefault, and simplify the handler.

Apply:

-import type { MouseEvent } from "react"
+// (no MouseEvent import needed)
-    function handleManageFertilizers(e: MouseEvent<HTMLButtonElement>) {
-        e.preventDefault()
+    function handleManageFertilizers() {
         if (b_id_farm && b_id_or_b_lu_catalogue) {
             fieldFertilizerFormStore.save(
                 b_id_farm,
                 b_id_or_b_lu_catalogue,
                 form.getValues(),
             )
         }
         navigate(
             searchParams.has("fieldIds")
                 ? `./manage/new?fieldIds=${searchParams.get("fieldIds")}`
                 : "./manage/new",
         )
     }
-                                        <Button
+                                        <Button
+                                            type="button"
                                             variant="secondary"
                                             className="ml-2"
                                             onClick={handleManageFertilizers}
                                         >

Also applies to: 110-124, 152-156

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 580442d and 0cd860a.

📒 Files selected for processing (2)
  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx (4 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.tsx
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
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.
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.
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.
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.
📚 Learning: 2025-09-25T12:42:28.012Z
Learnt from: boraini
PR: SvenVw/fdm#272
File: fdm-app/app/components/blocks/fertilizer-applications/form.tsx:110-124
Timestamp: 2025-09-25T12:42:28.012Z
Learning: In HTML forms, buttons default to type="submit" unless explicitly set to type="button", which causes form submission when clicked inside a form element. Using type="button" eliminates the need for preventDefault() calls in event handlers.

Applied to files:

  • fdm-app/app/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.tsx
🧬 Code graph analysis (1)
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (3)
fdm-app/app/components/blocks/fertilizer-applications/types.d.tsx (1)
  • FertilizerOption (12-19)
fdm-app/app/store/field-fertilizer-form.tsx (1)
  • useFieldFertilizerFormStore (24-49)
fdm-app/app/components/custom/combobox.tsx (1)
  • Combobox (41-158)
🔇 Additional comments (4)
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (4)

5-5: Good: Using React Router v7 Form/APIs correctly.

Importing Form, useNavigate, and useSearchParams from react-router aligns with the project’s v7 usage.


151-151: Good: TooltipTrigger uses asChild to avoid nested interactive elements.

Prevents nested buttons and improves accessibility.


137-137: Fix invalid Tailwind class grow-1.

Use grow instead; grow-1 isn’t a valid utility and has no effect.

Apply:

-                        <div className="flex flex-row items-end [&>*]:grow-1">
+                        <div className="flex flex-row items-end [&>*]:grow">

149-149: Remove invalid arbitrary variant [&.py-2]:grow-0.

Use plain grow-0; the selector is invalid here and won’t apply.

Apply:

-                            <div className="py-2 [&.py-2]:grow-0">
+                            <div className="py-2 grow-0">

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

76-99: Tighten hydration effect deps; avoid function deps to prevent spurious reruns.

Remove form.setValue and fieldFertilizerFormStore.load from the deps; run only when the keys change.

-    }, [
-        b_id_farm,
-        b_id_or_b_lu_catalogue,
-        form.setValue,
-        fieldFertilizerFormStore.load,
-    ])
+    }, [b_id_farm, b_id_or_b_lu_catalogue])
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2054c32 and fd623cc.

📒 Files selected for processing (1)
  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx (4 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
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.
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.
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.
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.
📚 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/components/blocks/fertilizer-applications/form.tsx
📚 Learning: 2025-09-25T12:42:28.012Z
Learnt from: boraini
PR: SvenVw/fdm#272
File: fdm-app/app/components/blocks/fertilizer-applications/form.tsx:110-124
Timestamp: 2025-09-25T12:42:28.012Z
Learning: In HTML forms, buttons default to type="submit" unless explicitly set to type="button", which causes form submission when clicked inside a form element. Using type="button" eliminates the need for preventDefault() calls in event handlers.

Applied to files:

  • fdm-app/app/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.tsx
🧬 Code graph analysis (1)
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (3)
fdm-app/app/components/blocks/fertilizer-applications/types.d.tsx (1)
  • FertilizerOption (12-19)
fdm-app/app/store/field-fertilizer-form.tsx (1)
  • useFieldFertilizerFormStore (24-49)
fdm-app/app/components/custom/combobox.tsx (1)
  • Combobox (41-158)
🔇 Additional comments (5)
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (5)

137-137: Flex growth fix looks good.

[&>*]:grow is the correct Tailwind utility. Nice catch.


152-160: Correct TooltipTrigger usage and button type.

Using asChild and type="button" avoids nested interactive elements and accidental submits. LGTM.


149-149: Fix invalid Tailwind selector [&.py-2]:grow-0.

Use plain grow-0; the current arbitrary variant is invalid and has no effect.

-                            <div className="py-2 [&.py-2]:grow-0">
+                            <div className="py-2 grow-0">

2-2: Remove unused MouseEvent import and handler param.

Button is type="button", so no submit to prevent. Drop the unused event to avoid lint/noUnusedVars and dead code.

-import type { MouseEvent } from "react"
+// (no MouseEvent import needed)
-    function handleManageFertilizers(e: MouseEvent<HTMLButtonElement>) {
+    function handleManageFertilizers() {
         if (b_id_farm && b_id_or_b_lu_catalogue) {
             fieldFertilizerFormStore.save(
                 b_id_farm,
                 b_id_or_b_lu_catalogue,
                 form.getValues(),
             )
         }
         navigate(
             searchParams.has("fieldIds")
                 ? `./manage/new?fieldIds=${searchParams.get("fieldIds")}`
                 : "./manage/new",
         )
     }

Also applies to: 111-124


142-147: No action required: ComboboxProps.label accepts ReactNode. Label is already typed as ReactNode in ComboboxProps.

Comment on lines +100 to +109
useEffect(() => {
if (form.formState.isSubmitSuccessful) {
fieldFertilizerFormStore.delete(b_id_farm, b_id_or_b_lu_catalogue)
}
}, [
form.formState.isSubmitSuccessful,
b_id_farm,
b_id_or_b_lu_catalogue,
fieldFertilizerFormStore.delete,
])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Guard delete-after-submit and simplify deps.

Avoid deleting with undefined keys; also drop the function dep.

-    useEffect(() => {
-        if (form.formState.isSubmitSuccessful) {
-            fieldFertilizerFormStore.delete(b_id_farm, b_id_or_b_lu_catalogue)
-        }
-    }, [
-        form.formState.isSubmitSuccessful,
-        b_id_farm,
-        b_id_or_b_lu_catalogue,
-        fieldFertilizerFormStore.delete,
-    ])
+    useEffect(() => {
+        if (
+            form.formState.isSubmitSuccessful &&
+            b_id_farm &&
+            b_id_or_b_lu_catalogue
+        ) {
+            fieldFertilizerFormStore.delete(b_id_farm, b_id_or_b_lu_catalogue)
+        }
+    }, [form.formState.isSubmitSuccessful, b_id_farm, b_id_or_b_lu_catalogue])
🤖 Prompt for AI Agents
In fdm-app/app/components/blocks/fertilizer-applications/form.tsx around lines
100 to 109, the useEffect currently calls fieldFertilizerFormStore.delete even
when b_id_farm or b_id_or_b_lu_catalogue may be undefined and includes the
delete function in the dependency array; guard the call so it only runs when
form.formState.isSubmitSuccessful is true and both keys are defined
(non-null/undefined) before invoking delete, and simplify the deps to
[form.formState.isSubmitSuccessful, b_id_farm, b_id_or_b_lu_catalogue] (remove
fieldFertilizerFormStore.delete).

Comment thread .changeset/spicy-knives-pump.md Outdated
BoraIneviNMI and others added 3 commits September 26, 2025 15:03
Co-authored-by: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
Signed-off-by: BoraIneviNMI <bora.inevi@nmi-agro.nl>
@SvenVw SvenVw merged commit 438f597 into development Sep 29, 2025
9 of 10 checks passed
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (11)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx (5)

27-35: Meta text mismatches the page intent (“new from template”, not “details”).
Update title/description to reflect creation flow.

 export const meta: MetaFunction = () => {
   return [
-    { title: `Meststof | ${clientConfig.name}` },
+    { title: `Nieuwe meststof | ${clientConfig.name}` },
     {
       name: "description",
-      content: "Bekijk de details van deze meststof",
+      content: "Maak een nieuwe meststof op basis van een sjabloon",
     },
   ]
 }

143-149: Standardize action param validation to typed 400 responses.
Use data(...) like in the loader for consistent error handling.

-  if (!b_id_farm) {
-    throw new Error("missing: b_id_farm")
-  }
+  if (!b_id_farm) {
+    throw data("missing: b_id_farm", { status: 400, statusText: "missing: b_id_farm" })
+  }

-  if (!p_id) {
-    throw new Error("missing: p_id")
-  }
+  if (!p_id) {
+    throw data("missing: p_id", { status: 400, statusText: "missing: p_id" })
+  }

218-220: Avoid sending explicit undefined in mutation payload.
Drop p_ef_nh3 when it’s not set; most clients/servers ignore missing optional fields but may treat explicit undefined differently.

-                p_ef_nh3: undefined,
                 p_app_method_options: formValues.p_app_method_options,

232-237: Build redirect query with URLSearchParams to ensure correct encoding.
Safer for comma‑separated fieldIds and future params.

-        return redirectWithSuccess(
-            `/farm/${b_id_farm}/${calendar}/field/fertilizer?fieldIds=${searchParams.get("fieldIds")}&p_id=${new_p_id}`,
-            {
-                message: `${formValues.p_name_nl} is toegevoegd! 🎉`,
-            },
-        )
+        const qs = new URLSearchParams({
+          fieldIds: searchParams.get("fieldIds")!,
+          p_id: String(new_p_id),
+        }).toString()
+        return redirectWithSuccess(
+          `/farm/${b_id_farm}/${calendar}/field/fertilizer?${qs}`,
+          { message: `${formValues.p_name_nl} is toegevoegd! 🎉` },
+        )

110-120: Optionally include fieldIds in loaderData.
If the UI block builds “Back/Done” links server‑side, exposing fieldIds avoids re‑parsing search params in the client. Harmless if unused.

         return {
           farm: farm,
           p_id: p_id,
           b_id_farm: b_id_farm,
           farmOptions: farmOptions,
           fertilizerOptions: fertilizerOptions,
           fertilizer: fertilizer,
           editable: true,
           fertilizerParameters: fertilizerParameters,
+          fieldIds: searchParams.get("fieldIds")!,
         }
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx (6)

1-9: Clean up imports after the auth fix.

getFertilizer becomes unused—remove it.

 import {
     addFertilizer,
     addFertilizerToCatalogue,
     getFarm,
     getFarms,
-    getFertilizer,
     getFertilizerParametersDescription,
     getFertilizers,
 } from "@svenvw/fdm-core"

37-56: Align loader param validation with action (validate b_lu_catalogue).

Action checks b_lu_catalogue, loader doesn’t. Add the same guard for consistency and clearer error semantics.

         const b_id_farm = params.b_id_farm
         if (!b_id_farm) {
             throw data("invalid: b_id_farm", {
                 status: 400,
                 statusText: "invalid: b_id_farm",
             })
         }
 
+        // Validate catalogue segment
+        const b_lu_catalogue = params.b_lu_catalogue
+        if (!b_lu_catalogue) {
+            throw data("invalid: b_lu_catalogue", {
+                status: 400,
+                statusText: "invalid: b_lu_catalogue",
+            })
+        }
+
         // Get the fertilizer id
         const p_id = params.p_id

27-35: Meta copy: make consistent with “add from template” flow.

Use add-oriented text like other “new” routes; optional: derive title from selected template.

 export const meta: MetaFunction = () => {
     return [
-        { title: `Meststof | ${clientConfig.name}` },
+        { title: `Meststof toevoegen | ${clientConfig.name}` },
         {
             name: "description",
-            content: "Bekijk de details van deze meststof",
+            content: "Maak een nieuwe meststof op basis van een sjabloon.",
         },
     ]
 }

144-146: p_id is not used in action—drop the check or use it.

Keeping an unused mandatory param check is misleading.

-        if (!p_id) {
-            throw new Error("missing: p_id")
-        }

206-208: Avoid sending undefined fields in payloads.

p_ef_nh3: undefined is redundant; omit the key to prevent accidental schema mismatches.

-                p_ef_nh3: undefined,

69-84: Remove unused farmOptions and its getFarms call from this loader
FarmNewFertilizerBlock only consumes fertilizer, fertilizerParameters, and editable, so loading and mapping farms here adds unnecessary latency.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b8c2c2 and cd8fce5.

📒 Files selected for processing (8)
  • .changeset/spicy-knives-pump.md (1 hunks)
  • fdm-app/app/components/blocks/fertilizer-applications/form.tsx (4 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx (1 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx (1 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.custom.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • .changeset/spicy-knives-pump.md
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.custom.tsx
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
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.
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.
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.
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.
📚 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/components/blocks/fertilizer-applications/form.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx
📚 Learning: 2025-09-25T12:42:28.012Z
Learnt from: boraini
PR: SvenVw/fdm#272
File: fdm-app/app/components/blocks/fertilizer-applications/form.tsx:110-124
Timestamp: 2025-09-25T12:42:28.012Z
Learning: In HTML forms, buttons default to type="submit" unless explicitly set to type="button", which causes form submission when clicked inside a form element. Using type="button" eliminates the need for preventDefault() calls in event handlers.

Applied to files:

  • fdm-app/app/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.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/components/blocks/fertilizer-applications/form.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.fertilizer.manage.new.$p_id.tsx
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx
📚 Learning: 2024-12-19T13:20:44.152Z
Learnt from: SvenVw
PR: SvenVw/fdm#23
File: fdm-app/app/routes/app.addfarm.new.tsx:15-17
Timestamp: 2024-12-19T13:20:44.152Z
Learning: Authentication for the “app.addfarm.new” route is already handled globally in “fdm-app/app/routes/app.tsx,” automatically redirecting unauthenticated users to the SignIn page.

Applied to files:

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

Applied to files:

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

Applied to files:

  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx
🧬 Code graph analysis (3)
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (3)
fdm-app/app/components/blocks/fertilizer-applications/types.d.tsx (1)
  • FertilizerOption (12-19)
fdm-app/app/store/field-fertilizer-form.tsx (1)
  • useFieldFertilizerFormStore (24-49)
fdm-app/app/components/custom/combobox.tsx (1)
  • Combobox (41-158)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx (4)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx (2)
  • meta (27-35)
  • loader (37-116)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx (2)
  • meta (24-33)
  • loader (35-132)
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.tsx (2)
  • meta (21-30)
  • loader (32-88)
fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx (1)
  • FarmNewFertilizerBlock (22-88)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx (2)
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.tsx (2)
  • meta (19-28)
  • loader (30-62)
fdm-app/app/components/blocks/fertilizer/new-fertilizer-page.tsx (1)
  • FarmNewFertilizerBlock (22-88)
🔇 Additional comments (10)
fdm-app/app/components/blocks/fertilizer-applications/form.tsx (5)

119-132: Remove unused MouseEvent param/import; handler doesn’t need event.

Button is type="button", so the handler can be parameterless. Clean up the signature and import.
[ suggest_recommended_refactor ]

-    function handleManageFertilizers(e: MouseEvent<HTMLButtonElement>) {
+    function handleManageFertilizers() {

Also update imports (outside this range):

-import type { MouseEvent } from "react"

76-99: Stabilize hydration effect deps; drop function refs to avoid needless re-runs.

Including form.setValue and fieldFertilizerFormStore.load causes the effect to re-run if identities change. Keys are sufficient.

-    }, [
-        b_id_farm,
-        b_id_or_b_lu_catalogue,
-        form.setValue,
-        fieldFertilizerFormStore.load,
-    ])
+    }, [b_id_farm, b_id_or_b_lu_catalogue])

159-169: Nice: TooltipTrigger asChild + type=button.

This fixes nested interactive elements and avoids accidental form submits. Good alignment with our UI patterns.


108-117: Guard delete-after-submit and simplify deps.

Avoid deleting with undefined keys and remove function dep from the array.

-    useEffect(() => {
-        if (form.formState.isSubmitSuccessful) {
-            fieldFertilizerFormStore.delete(b_id_farm, b_id_or_b_lu_catalogue)
-        }
-    }, [
-        form.formState.isSubmitSuccessful,
-        b_id_farm,
-        b_id_or_b_lu_catalogue,
-        fieldFertilizerFormStore.delete,
-    ])
+    useEffect(() => {
+        if (
+            form.formState.isSubmitSuccessful &&
+            b_id_farm &&
+            b_id_or_b_lu_catalogue
+        ) {
+            fieldFertilizerFormStore.delete(b_id_farm, b_id_or_b_lu_catalogue)
+        }
+    }, [form.formState.isSubmitSuccessful, b_id_farm, b_id_or_b_lu_catalogue])

157-157: Fix invalid Tailwind selector.

[&.py-2]:grow-0 is not valid; use grow-0.

-                            <div className="py-2 [&.py-2]:grow-0">
+                            <div className="py-2 grow-0">
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx (4)

232-234: Redirect target aligns with the agreed field workflow.
Using ${calendar}/field/fertilizer matches the design choice to start from the field overview and ensures the application loader re‑runs. Nice.
Based on learnings


132-136: LGTM on composition.
Simple passthrough to FarmNewFertilizerBlock keeps the route thin and consistent with sibling pages.


93-97: 404 when template fertilizer not found.
getFertilizer may return null/undefined; without a guard the UI can break on missing fields.

   // Get selected fertilizer
-  const fertilizer = await getFertilizer(fdm, p_id)
+  const fertilizer = await getFertilizer(fdm, p_id)
+  if (!fertilizer) {
+    throw data("not found: p_id", {
+      status: 404,
+      statusText: "not found: p_id",
+    })
+  }
⛔ Skipped due to learnings
Learnt from: BoraIneviNMI
PR: SvenVw/fdm#272
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.$p_id.tsx:151-164
Timestamp: 2025-09-23T10:02:32.123Z
Learning: The getFertilizer function from svenvw/fdm-core throws an exception if the fertilizer doesn't exist, rather than returning null or undefined.
Learnt from: BoraIneviNMI
PR: SvenVw/fdm#272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.546Z
Learning: The getFertilizer function in svenvw/fdm-core does not perform authorization checks, unlike getFertilizers which includes a checkPermission call to verify farm access. This means getFertilizer(fdm, p_id) can potentially return fertilizer details for any fertilizer ID without validating user permissions.
Learnt from: BoraIneviNMI
PR: SvenVw/fdm#272
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx:85-101
Timestamp: 2025-09-24T14:02:48.546Z
Learning: Both getFertilizer and getFertilizers functions in svenvw/fdm-core perform authorization checks using the user's principal_id to verify farm access before returning fertilizer data.
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.
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.

10-16: Bump remix-toast to ≥3.3.0 in package.json
The project currently declares remix-toast@^3.2.0, which doesn’t include the redirectWithSuccess API used here—upgrade to at least version 3.3.0.

⛔ Skipped due to learnings
Learnt from: SvenVw
PR: SvenVw/fdm#16
File: fdm-app/package.json:40-40
Timestamp: 2024-12-16T10:48:27.598Z
Learning: In the `fdm-app/package.json` file, `remix-toast` and `sonner` are not duplicate dependencies. `remix-toast` manages toast generation and handling within the Remix framework, while `sonner` provides the actual implementation and UI of the toast notifications. They serve different purposes and are intended to be used together in the project.
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.
Learnt from: SvenVw
PR: SvenVw/fdm#132
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.access.tsx:54-68
Timestamp: 2025-04-29T11:28:44.181Z
Learning: In React Router v7, the `json()` function has been replaced with `data()` for creating responses in loaders and actions.
Learnt from: SvenVw
PR: SvenVw/fdm#132
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.access.tsx:54-68
Timestamp: 2025-04-29T11:28:44.181Z
Learning: In React Router v7, the `json()` function has been deprecated and removed. Instead, either return plain JavaScript objects directly from loaders/actions, or use the `data()` function for responses with custom status codes and headers.
fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx (1)

85-101: Critical: Unauthorized data access via direct getFertilizer (bypasses permission checks).

Per learnings, getFertilizer(fdm, p_id) performs no authorization; fetching it directly can leak details of fertilizers outside the user/farm scope. Authorize by deriving the selected fertilizer from the permitted set returned by getFertilizers(fdm, principal, b_id_farm) and 404 when absent. Also keeps a single source for fertilizers used to build options. Based on learnings.

-        // Get selected fertilizer
-        const fertilizer = await getFertilizer(fdm, p_id)
-        const fertilizerParameters = getFertilizerParametersDescription()
-
-        // Get the available fertilizers
-        const fertilizers = await getFertilizers(
-            fdm,
-            session.principal_id,
-            b_id_farm,
-        )
+        // Authorize: only allow templates that belong to the accessible set for this farm/user
+        const fertilizers = await getFertilizers(
+            fdm,
+            session.principal_id,
+            b_id_farm,
+        )
+        const fertilizer = fertilizers.find((f) => f.p_id === p_id)
+        if (!fertilizer) {
+            throw data("not found: p_id", {
+                status: 404,
+                statusText: "not found: p_id",
+            })
+        }
+        const fertilizerParameters = getFertilizerParametersDescription()

Comment on lines +161 to +168
<Button
type="button"
variant="secondary"
className="ml-2"
onClick={handleManageFertilizers}
>
<Plus />
</Button>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add an accessible name to the icon-only button.

Icon-only controls need an accessible label; tooltips don’t provide one for SR. Add aria-label and mark the icon decorative.

-                                        <Button
+                                        <Button
+                                            aria-label="Voeg meststof toe"
                                             type="button"
                                             variant="secondary"
                                             className="ml-2"
                                             onClick={handleManageFertilizers}
                                         >
-                                            <Plus />
+                                            <Plus aria-hidden="true" />
                                         </Button>
📝 Committable suggestion

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

Suggested change
<Button
type="button"
variant="secondary"
className="ml-2"
onClick={handleManageFertilizers}
>
<Plus />
</Button>
<Button
aria-label="Voeg meststof toe"
type="button"
variant="secondary"
className="ml-2"
onClick={handleManageFertilizers}
>
<Plus aria-hidden="true" />
</Button>
🤖 Prompt for AI Agents
In fdm-app/app/components/blocks/fertilizer-applications/form.tsx around lines
161–168, the icon-only Button lacks an accessible name; add an aria-label (e.g.,
aria-label="Add fertilizer") to the Button to provide a screen-reader label, and
mark the Plus icon decorative by setting it to aria-hidden="true" (and
focusable="false" if it's an SVG) so the icon is ignored by assistive tech.

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 fdm-data

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Integrate Fertilizer Management into Farm Creation Wizard

3 participants