Skip to content

Make productive filter work on rotation tables#590

Merged
SvenVw merged 11 commits into
developmentfrom
FDM589
Apr 29, 2026
Merged

Make productive filter work on rotation tables#590
SvenVw merged 11 commits into
developmentfrom
FDM589

Conversation

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator

@BoraIneviNMI BoraIneviNMI commented Apr 23, 2026

Bug fixes

  • The show productive only button can only update the fieldFilterStore, so now the rotation table also uses that filter store, not a separate one. Back then I was thinking they should use separate filter states but now I changed my mind. Although I still think the show productive button should be controllable.

Summary by CodeRabbit

  • Bug Fixes
    • Restored the "show-productive-only" control and ensured search terms & productive-only selection stay synchronized between field and rotation tables.
    • Table UI, selection and visible rows now update consistently when filters or search change.
    • Date inputs behavior improved: start dates are required where appropriate and submissions reflect correct field/group selections.
    • Crop-residue checkbox and variety selector now show aggregated, accurate values across grouped rows.
    • Parcel names, total area, fertilizer and harvest displays now reflect correct underlying field data.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

Warning

Rate limit exceeded

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

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

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

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

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

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

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 179bdc4a-2a75-4c1a-815b-c0770a256531

📥 Commits

Reviewing files that changed from the base of the PR and between a224f0d and 37b57bd.

📒 Files selected for processing (9)
  • fdm-app/app/components/blocks/rotation/columns.tsx
  • fdm-app/app/components/blocks/rotation/crop-residue-checkbox.tsx
  • fdm-app/app/components/blocks/rotation/date-selector.tsx
  • fdm-app/app/components/blocks/rotation/fertilizer-display.tsx
  • fdm-app/app/components/blocks/rotation/harvest-dates-display.tsx
  • fdm-app/app/components/blocks/rotation/table.tsx
  • fdm-app/app/components/blocks/rotation/variety-selector.tsx
  • fdm-app/app/components/custom/date-picker-v2.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.tsx
📝 Walkthrough

Walkthrough

Consolidates rotation filtering into the shared useFieldFilterStore, restores the rotation table’s productive-only control and synced search state, adds b_lu_catalogue to row data and types, and rewires the table to use a direct global filter function with updated typings.

Changes

Cohort / File(s) Summary
Changeset
​.changeset/ninety-pets-jog.md
Adds a patch release changeset documenting the rotation-table filter fix and synchronized search/productive-only state.
Table columns & types
fdm-app/app/components/blocks/rotation/columns.tsx
Updates CropRow/FieldRow typings (removes several crop-level fields, converts b_lu_variety to tuple array, adds b_lu_catalogue), changes column value derivation to aggregate from subRows, and adjusts memo dependencies.
Route loader / data transformation
fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.tsx
Stops computing crop-level aggregated fields (b_lu_start/b_lu_end, m_cropresidue, b_bufferstrip, aggregated b_lu_variety) and emits field rows with b_lu_variety as entries array and adds b_lu_catalogue.
Filter store & table logic
fdm-app/app/store/field-filter.ts, fdm-app/app/components/blocks/rotation/table.tsx
Removes useRotationFilterStore and makes table read from useFieldFilterStore; replaces TanStack FilterFn with an explicit globalFilterFn using store values, tightens table generics/typing and memoized data shape, and simplifies selection derivation.
Date handling / picker
fdm-app/app/components/blocks/rotation/date-selector.tsx, fdm-app/app/components/custom/date-picker-v2.tsx
TableDateSelector now accepts explicit value: Date[] and required prop; aggregates/sorts dates from subRows for non-field rows; DatePicker propagates required to Calendar.
Row UI components
fdm-app/app/components/blocks/rotation/crop-residue-checkbox.tsx, fdm-app/app/components/blocks/rotation/fertilizer-display.tsx, fdm-app/app/components/blocks/rotation/harvest-dates-display.tsx, fdm-app/app/components/blocks/rotation/variety-selector.tsx
All components now accept/operate on TanStack Row<RotationExtended> (or explicit value arrays) and derive field lists from row.original/row.subRows; logic for aggregation, memoization, and UI defaults updated accordingly.

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant UI as RotationTable
  participant Store as FieldFilterStore
  participant Loader as RouteLoader
  rect rgba(200,230,255,0.5)
    User->>UI: enter search / toggle productive-only
    UI->>Store: set searchTerms / showProductiveOnly
    Store-->>UI: current searchTerms & showProductiveOnly
    UI->>Loader: request transformed rows (includes b_lu_catalogue)
    Loader-->>UI: rows (fields + crop groups)
    UI->>UI: globalFilterFn(row.original, searchTerms, showProductiveOnly)
    UI-->>User: render filtered rows
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • nmi-agro/fdm#555 — Touches harvest-dates-display.tsx grouping/ordering logic; related to harvest-date refactor here.
  • nmi-agro/fdm#435 — Alters rotation table and filter/store usage; directly related to the decision to remove a rotation-specific store.
  • nmi-agro/fdm#430 — Also modifies rotation table filtering and integration with useFieldFilterStore; similar consolidation changes.

Suggested labels

bug, fdm-app, branch:development

Suggested reviewers

  • SvenVw
  • gerardhros

Poem

🐰 Hopping through rows and state so bright,
I stitched search and productive flags just right.
Varieties counted, dates sorted true,
Catalogues added — a fresh data view.
Cheers from a rabbit: filters snug and light!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary objective of the pull request: fixing the 'show productive only' filter to work on rotation tables by consolidating filter state.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch FDM589

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.

Comment thread fdm-app/app/components/blocks/rotation/table.tsx Fixed
@sentry
Copy link
Copy Markdown

sentry Bot commented Apr 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@BoraIneviNMI BoraIneviNMI linked an issue Apr 23, 2026 that may be closed by this pull request
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.

🧹 Nitpick comments (3)
fdm-app/app/components/blocks/rotation/table.tsx (3)

382-382: Nit: unnecessary optional chaining on fieldFilter.

useFieldFilterStore() always returns the store state, so fieldFilter?.searchTerms can be fieldFilter.searchTerms. Harmless, but slightly misleading about nullability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fdm-app/app/components/blocks/rotation/table.tsx` at line 382, The value prop
uses unnecessary optional chaining on fieldFilter
(value={fieldFilter?.searchTerms ?? ""}) even though useFieldFilterStore()
always returns a state object; update the expression to use
fieldFilter.searchTerms with the same fallback (e.g., value =
fieldFilter.searchTerms ?? "") to remove the misleading nullability. Locate the
usage in the component where useFieldFilterStore() is called and change the
value reference for the search input accordingly.

560-571: Redundant Row<MemoizedTData> casts.

row.getParentRow() already returns Row<MemoizedTData> | undefined given useReactTable<MemoizedTData> on Line 273, so the two as Row<MemoizedTData> casts are unnecessary and can be dropped.

♻️ Proposed cleanup
-                                            ((
-                                                row.getParentRow() as Row<MemoizedTData>
-                                            )?.subRows.length === 1
+                                            (row.getParentRow()?.subRows.length === 1
                                                 ? "shadow-[inset_0_1em_2em_-2em_#00000088,inset_0_-1em_2em_-2em_#00000088]"
                                                 : row.index === 0
                                                   ? "shadow-[inset_0_1em_2em_-2em_#00000088]"
                                                   : row.index ===
-                                                        (
-                                                            row.getParentRow() as Row<MemoizedTData>
-                                                        )?.subRows.length -
+                                                        (row.getParentRow()?.subRows.length ?? 0) -
                                                             1 &&
                                                     "shadow-[inset_0_-1em_2em_-2em_#00000088]"),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fdm-app/app/components/blocks/rotation/table.tsx` around lines 560 - 571,
Remove redundant type assertions for Row<MemoizedTData> in the conditional that
builds the shadow class string: `row.getParentRow() as Row<MemoizedTData>`
should be replaced with `row.getParentRow()` (no cast) in both occurrences;
update the expression that checks `?.subRows.length` and the subtraction
expression so they operate directly on `row.getParentRow()` to avoid unnecessary
`as Row<MemoizedTData>` casts—no other logic changes required (this is safe
because `useReactTable<MemoizedTData>` already types `getParentRow()` as
`Row<MemoizedTData> | undefined`).

67-100: Subscribe to individual store slices instead of the whole store.

useFieldFilterStore() with no selector subscribes this component to every field-filter state change (including farmId updates from syncFarm), causing unnecessary re-renders and re-running the rowSelection memo whenever any unrelated slice changes. It also means fieldFilter becomes a fresh object reference on every store update, which propagates into globalFilter: fieldFilter (line 315) and the useEffect dep fieldFilter.syncFarm (line 100).

Consider selecting only what's needed:

♻️ Proposed refactor
-    const fieldFilter = useFieldFilterStore()
+    const searchTerms = useFieldFilterStore((s) => s.searchTerms)
+    const showProductiveOnly = useFieldFilterStore((s) => s.showProductiveOnly)
+    const setSearchTerms = useFieldFilterStore((s) => s.setSearchTerms)
+    const syncFieldFilterFarm = useFieldFilterStore((s) => s.syncFarm)

Then use searchTerms / showProductiveOnly directly in the filter call and in globalFilter state, and pass syncFieldFilterFarm into the effect dep array (stable reference).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fdm-app/app/components/blocks/rotation/table.tsx` around lines 67 - 100, The
component currently calls useFieldFilterStore() with no selector, causing
full-store subscriptions and a new object reference on every update which
triggers unnecessary re-renders and stale deps; change to selecting only the
needed slices (e.g. select searchTerms and showProductiveOnly and the syncFarm
function) via useFieldFilterStore(selector) so you get stable references for the
sync function and primitive values for filters, then update usages: replace
globalFilter: fieldFilter with the selected searchTerms/showProductiveOnly
values and use the selected sync function (e.g. syncFieldFilterFarm) in the
useEffect dependency array instead of fieldFilter.syncFarm, and ensure
rowSelection memo only depends on those selected values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@fdm-app/app/components/blocks/rotation/table.tsx`:
- Line 382: The value prop uses unnecessary optional chaining on fieldFilter
(value={fieldFilter?.searchTerms ?? ""}) even though useFieldFilterStore()
always returns a state object; update the expression to use
fieldFilter.searchTerms with the same fallback (e.g., value =
fieldFilter.searchTerms ?? "") to remove the misleading nullability. Locate the
usage in the component where useFieldFilterStore() is called and change the
value reference for the search input accordingly.
- Around line 560-571: Remove redundant type assertions for Row<MemoizedTData>
in the conditional that builds the shadow class string: `row.getParentRow() as
Row<MemoizedTData>` should be replaced with `row.getParentRow()` (no cast) in
both occurrences; update the expression that checks `?.subRows.length` and the
subtraction expression so they operate directly on `row.getParentRow()` to avoid
unnecessary `as Row<MemoizedTData>` casts—no other logic changes required (this
is safe because `useReactTable<MemoizedTData>` already types `getParentRow()` as
`Row<MemoizedTData> | undefined`).
- Around line 67-100: The component currently calls useFieldFilterStore() with
no selector, causing full-store subscriptions and a new object reference on
every update which triggers unnecessary re-renders and stale deps; change to
selecting only the needed slices (e.g. select searchTerms and showProductiveOnly
and the syncFarm function) via useFieldFilterStore(selector) so you get stable
references for the sync function and primitive values for filters, then update
usages: replace globalFilter: fieldFilter with the selected
searchTerms/showProductiveOnly values and use the selected sync function (e.g.
syncFieldFilterFarm) in the useEffect dependency array instead of
fieldFilter.syncFarm, and ensure rowSelection memo only depends on those
selected values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a5ae35d8-9f96-4418-89f5-3d030bb7a9ef

📥 Commits

Reviewing files that changed from the base of the PR and between aec6106 and d867894.

📒 Files selected for processing (1)
  • fdm-app/app/components/blocks/rotation/table.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.

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

185-226: Tighten the component types instead of casting around them.

DataTable still advertises a generic TData, but this implementation now hard-depends on crop rows with nested field rows and needs both (data as CropRow[]) and columns as ColumnDef<MemoizedTData>[] to compile. Narrowing the props to the concrete rotation row shape would bring the new typing work back under compiler enforcement and make column/data drift fail earlier.

Also applies to: 273-276

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fdm-app/app/components/blocks/rotation/table.tsx` around lines 185 - 226, The
DataTable usage is relying on casts like (data as CropRow[]) and columns as
ColumnDef<MemoizedTData>[]; fix by tightening the component/prop typings so
DataTable's generic TData is the concrete rotation row shape instead of any —
update the component props (where memoizedData, data, and columns are declared)
to use CropRow and FieldRow (or a union type identical to MemoizedTData) as the
TData generic for DataTable and change ColumnDef types to ColumnDef<CropRow |
CropFieldRow> (or the exact MemoizedTData type) so you can remove the casts
around memoizedData, columns, and data; apply the same type tightening at the
other occurrence referenced (the block around memoizedData usage at lines
~273-276).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@fdm-app/app/components/blocks/rotation/table.tsx`:
- Around line 185-226: The DataTable usage is relying on casts like (data as
CropRow[]) and columns as ColumnDef<MemoizedTData>[]; fix by tightening the
component/prop typings so DataTable's generic TData is the concrete rotation row
shape instead of any — update the component props (where memoizedData, data, and
columns are declared) to use CropRow and FieldRow (or a union type identical to
MemoizedTData) as the TData generic for DataTable and change ColumnDef types to
ColumnDef<CropRow | CropFieldRow> (or the exact MemoizedTData type) so you can
remove the casts around memoizedData, columns, and data; apply the same type
tightening at the other occurrence referenced (the block around memoizedData
usage at lines ~273-276).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: abb87d5e-c847-4e1b-b5e7-5b9e9ec4e54c

📥 Commits

Reviewing files that changed from the base of the PR and between d867894 and 25c99f1.

📒 Files selected for processing (1)
  • fdm-app/app/components/blocks/rotation/table.tsx

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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/rotation/variety-selector.tsx (1)

53-80: ⚠️ Potential issue | 🟠 Major

Keep crop-row variety updates scoped to the visible field subset.

This submit path still uses row.original.fields for crop rows, while the displayed counts are now aggregated from row.subRows. In a filtered crop row, selecting a variety here will update hidden fields as well.

Suggested fix
-                                    const fieldIds = (
-                                        row.original.type === "crop"
-                                            ? row.original.fields
-                                            : [row.original]
-                                    )
+                                    const fieldIds = (
+                                        row.original.type === "crop"
+                                            ? (row.subRows ?? []).map(
+                                                  (fieldRow) =>
+                                                      fieldRow.original as FieldRow,
+                                              )
+                                            : [row.original]
+                                    )
                                         .map((field) =>
                                             encodeURIComponent(field.b_id),
                                         )
                                         .join(",")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@fdm-app/app/components/blocks/rotation/variety-selector.tsx` around lines 53
- 80, The submit currently builds fieldIds from row.original.fields which
updates hidden fields when the table is filtered; change the logic inside
onValueChange to use row.subRows (mapping each subRow.original.b_id) as the
source for crop-type rows instead of row.original.fields, and keep the fallback
for non-crop rows as [row.original]; then URL-encode and join those IDs exactly
as before before calling fetcher.submit (leave cultivationIds computation and
the fetcher.submit call unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@fdm-app/app/components/blocks/rotation/fertilizer-display.tsx`:
- Around line 52-65: The bug is that fieldIds is built from row.original.fields
instead of the already-filtered fields variable used for badges, causing edit
links to include hidden fields; fix by deriving fieldIds from fields (the
variable computed from row.original/type and row.subRows) — e.g., map the same
fields array to each field.b_id (keeping the same order/uniqueness logic as
fertilizers/uniqueFertilizers) so the fertilizer edit link only targets the
visible/filtered field subset.

---

Outside diff comments:
In `@fdm-app/app/components/blocks/rotation/variety-selector.tsx`:
- Around line 53-80: The submit currently builds fieldIds from
row.original.fields which updates hidden fields when the table is filtered;
change the logic inside onValueChange to use row.subRows (mapping each
subRow.original.b_id) as the source for crop-type rows instead of
row.original.fields, and keep the fallback for non-crop rows as [row.original];
then URL-encode and join those IDs exactly as before before calling
fetcher.submit (leave cultivationIds computation and the fetcher.submit call
unchanged).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 32a4421a-01fb-4842-85ef-8189d6284e34

📥 Commits

Reviewing files that changed from the base of the PR and between a224f0d and 80954c6.

📒 Files selected for processing (8)
  • fdm-app/app/components/blocks/rotation/columns.tsx
  • fdm-app/app/components/blocks/rotation/crop-residue-checkbox.tsx
  • fdm-app/app/components/blocks/rotation/date-selector.tsx
  • fdm-app/app/components/blocks/rotation/fertilizer-display.tsx
  • fdm-app/app/components/blocks/rotation/harvest-dates-display.tsx
  • fdm-app/app/components/blocks/rotation/variety-selector.tsx
  • fdm-app/app/components/custom/date-picker-v2.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • fdm-app/app/components/blocks/rotation/columns.tsx

Comment thread fdm-app/app/components/blocks/rotation/fertilizer-display.tsx Outdated
@BoraIneviNMI BoraIneviNMI requested a review from SvenVw April 29, 2026 07:24
@SvenVw SvenVw merged commit ddab948 into development Apr 29, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Buffer strip filtering doesn't work on the rotation table

2 participants