Skip to content

Move map when the selected field changes#263

Merged
SvenVw merged 6 commits into
developmentfrom
FDM251
Sep 22, 2025
Merged

Move map when the selected field changes#263
SvenVw merged 6 commits into
developmentfrom
FDM251

Conversation

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator

@BoraIneviNMI BoraIneviNMI commented Sep 11, 2025

  • *Bug Fixes
    • While reviewing newly created fields or viewing individual fields on the map, the map now centers to the field when a new field is selected.

Summary by CodeRabbit

  • New Features

    • Map now auto-centers and adjusts zoom to the selected field when switching fields, reviewing newly created fields, or viewing an individual field for clearer, field-focused navigation.
  • Chores

    • Added a changeset entry to publish a patch release.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Sep 11, 2025

🦋 Changeset detected

Latest commit: ca7f0f1

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 Patch

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

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 11, 2025

Walkthrough

Adds imperative map fitting on viewState changes in two route components by introducing a Mapbox mapRef and useEffect to call fitBounds. Also adds a changeset entry for a patch release describing the new auto-centering behavior.

Changes

Cohort / File(s) Summary
Map auto-fit via mapRef & useEffect
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.atlas.tsx, fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id._index.tsx
Import useRef/useEffect and MapRef; create mapRef = useRef<MapRef>(null); attach ref={mapRef} to MapGL; add useEffect to call mapRef.current?.fitBounds(viewState.bounds, viewState.fitBoundsOptions) on mount and when viewState changes.
Changeset
.changeset/quick-parents-grin.md
Add patch changeset for @svenvw/fdm-app documenting that the map now recenters/fits to a newly selected field.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant RC as React Component
  participant VS as viewState
  participant UE as useEffect
  participant M as Mapbox Map (mapRef)

  Note over RC: Initial render
  RC->>VS: compute bounds & fitBoundsOptions
  RC->>M: mount MapGL with ref
  UE->>M: fitBounds(VS.bounds, VS.fitBoundsOptions)
  M-->>RC: viewport adjusted

  Note over RC: On field selection change
  U->>RC: select field
  RC->>VS: recompute bounds/options
  UE->>M: fitBounds(updated bounds, options)
  M-->>RC: viewport adjusted
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • gerardhros
  • SvenVw

Pre-merge checks (3 passed)

✅ 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 "Move map when the selected field changes" is a clear, concise single-sentence summary that directly describes the PR's primary change (centering the map when a new field is selected); it is relevant to the changeset and readable for teammates scanning history.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Poem

A hop, a skip, a map re-centers true,
I nudge the bounds—fit neatly into view.
Fields align where carrots might be found,
With gentle refs, the atlas turns around.
tap-tap—fitBounds! says this jaunty bun 🥕🗺️

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86470a9 and d6644e8.

📒 Files selected for processing (3)
  • .changeset/quick-parents-grin.md (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.atlas.tsx (3 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id._index.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • .changeset/quick-parents-grin.md
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.atlas.tsx
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch FDM251

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

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.92%. Comparing base (883611e) to head (ca7f0f1).
⚠️ Report is 7 commits behind head on development.

Additional details and impacted files
@@             Coverage Diff              @@
##           development     #263   +/-   ##
============================================
  Coverage        92.92%   92.92%           
============================================
  Files               79       79           
  Lines            12926    12926           
  Branches          1266     1266           
============================================
  Hits             12012    12012           
  Misses             912      912           
  Partials             2        2           
Flag Coverage Δ
fdm-calculator 94.36% <ø> (ø)
fdm-core 91.90% <ø> (ø)
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 branch:development Issue only affecting development, not the main branch (yet) bug Something isn't working fdm-app labels Sep 11, 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: 5

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

253-256: Prevent over-firing of fitBounds on unrelated renders.

Depend on stable keys for bounds/options to avoid redundant refits.

-    useEffect(() => {
-        mapRef.current?.fitBounds(viewState.bounds, viewState.fitBoundsOptions)
-    }, [viewState])
+    useEffect(() => {
+        mapRef.current?.fitBounds(viewState.bounds, viewState.fitBoundsOptions)
+    }, [
+        JSON.stringify(viewState.bounds),
+        JSON.stringify(viewState.fitBoundsOptions),
+    ])
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.atlas.tsx (1)

114-116: Stabilize effect triggers for map fitting.

Avoid repeated fits from identity-changed viewState.

-    useEffect(() => {
-        mapRef.current?.fitBounds(viewState.bounds, viewState.fitBoundsOptions)
-    }, [viewState])
+    useEffect(() => {
+        mapRef.current?.fitBounds(viewState.bounds, viewState.fitBoundsOptions)
+    }, [
+        JSON.stringify(viewState.bounds),
+        JSON.stringify(viewState.fitBoundsOptions),
+    ])
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33b7994 and 86470a9.

📒 Files selected for processing (3)
  • .changeset/quick-parents-grin.md (1 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.atlas.tsx (3 hunks)
  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id._index.tsx (2 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2024-11-25T14:42:26.660Z
Learnt from: SvenVw
PR: SvenVw/fdm#6
File: fdm-app/app/components/blocks/field-map.tsx:0-0
Timestamp: 2024-11-25T14:42:26.660Z
Learning: In `fdm-app/app/components/blocks/field-map.tsx`, explicit cleanup of Mapbox GL resources is not necessary, as `react-map-gl` handles it automatically upon component unmount, and `MapRef` does not have a `remove` method.

Applied to files:

  • fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.atlas.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A shared layout component `FarmLayoutBase` has been created in `components/custom/farm-layout-base.tsx` to maintain consistency across farm-related pages. The component handles farm selection dropdown, breadcrumb navigation, and provides a common layout structure.

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.atlas.tsx

Comment thread .changeset/quick-parents-grin.md Outdated
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.atlas.tsx Outdated
mapStyle={loaderData.mapboxStyle}
mapboxAccessToken={loaderData.mapboxToken}
interactiveLayerIds={[id]}
ref={mapRef}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Sep 11, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Guarantee first fit when the map is ready.

Call fitBounds on onLoad to cover the initial render under ClientOnly.

-                            ref={mapRef}
+                            ref={mapRef}
+                            onLoad={() =>
+                                mapRef.current?.fitBounds(
+                                    viewState.bounds,
+                                    viewState.fitBoundsOptions
+                                )
+                            }
📝 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
ref={mapRef}
ref={mapRef}
onLoad={() =>
mapRef.current?.fitBounds(
viewState.bounds,
viewState.fitBoundsOptions
)
}
🤖 Prompt for AI Agents
In fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.atlas.tsx around
line 143, the map ref is set but the initial viewport may not be fit because the
map is rendered inside ClientOnly; add an onLoad handler (or pass an onLoad prop
to the map component) that, when the map instance is available, calls fitBounds
with the same bounds you use elsewhere (compute bounds from markers or stored
bbox) so the first render is correctly framed; ensure the handler uses
mapRef.current (or the provided instance) and only runs once when the map loads.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Why isn't adding ...viewState is enough to get the map centered on the initially selected field?

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.

@BoraIneviNMI You're absolutely right! Spreading ...viewState should be sufficient to center the map on the initially selected field.

The getViewState(fields) function computes the appropriate initial positioning (center, zoom, bounds) from the field geometry, and when you spread these props onto the MapGL component with {...viewState}, it sets the initial viewport correctly.

My suggestion for the onLoad handler was based on a potential edge case where the map might not respect the initial bounds due to timing issues with the ClientOnly wrapper, but if ...viewState is working properly for initial positioning, then the onLoad handler would indeed be redundant.

The useEffect with fitBounds is primarily for handling dynamic updates when the viewState changes (like when switching between different fields), not for the initial render.

Are you experiencing any issues with the initial centering, or is the current implementation with ...viewState working as expected for your use case?


🧠 Learnings used
Learnt from: SvenVw
PR: SvenVw/fdm#6
File: fdm-app/app/components/blocks/field-map.tsx:0-0
Timestamp: 2024-11-25T14:42:26.660Z
Learning: In `fdm-app/app/components/blocks/field-map.tsx`, explicit cleanup of Mapbox GL resources is not necessary, as `react-map-gl` handles it automatically upon component unmount, and `MapRef` does not have a `remove` method.

Learnt from: SvenVw
PR: SvenVw/fdm#67
File: fdm-app/app/components/custom/atlas/atlas.d.tsx:8-8
Timestamp: 2025-01-31T14:29:37.599Z
Learning: In the Atlas component's MapFieldsProps interface, mapStyle is intentionally restricted to "mapbox://styles/mapbox/satellite-streets-v12" as it's currently the only supported style option.

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.

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

No I wasn't experiencing any issues. Thanks for the suggestion though.

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.

@BoraIneviNMI Perfect! You're absolutely right - the ...viewState approach is clean and sufficient for your use case. Thanks for the clarification, and I'm glad the implementation is working smoothly! 🐰

  (\   /)
 ( ._. )
o_(")(")  *nods understandingly*

🧠 Learnings used
Learnt from: SvenVw
PR: SvenVw/fdm#6
File: fdm-app/app/components/blocks/field-map.tsx:0-0
Timestamp: 2024-11-25T14:42:26.660Z
Learning: In `fdm-app/app/components/blocks/field-map.tsx`, explicit cleanup of Mapbox GL resources is not necessary, as `react-map-gl` handles it automatically upon component unmount, and `MapRef` does not have a `remove` method.

Learnt from: SvenVw
PR: SvenVw/fdm#67
File: fdm-app/app/components/custom/atlas/atlas.d.tsx:8-8
Timestamp: 2025-01-31T14:29:37.599Z
Learning: In the Atlas component's MapFieldsProps interface, mapStyle is intentionally restricted to "mapbox://styles/mapbox/satellite-streets-v12" as it's currently the only supported style option.

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.

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

Comment thread fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id._index.tsx Outdated
mapStyle={loaderData.mapboxStyle}
mapboxAccessToken={loaderData.mapboxToken}
interactiveLayerIds={[id]}
ref={mapRef}
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

Ensure initial fit after map mounts (SSR-safe).

With the container gate, the first effect may run before the map exists. Trigger a fit on onLoad.

-                                ref={mapRef}
+                                ref={mapRef}
+                                onLoad={() =>
+                                    mapRef.current?.fitBounds(
+                                        viewState.bounds,
+                                        viewState.fitBoundsOptions
+                                    )
+                                }
📝 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
ref={mapRef}
ref={mapRef}
onLoad={() =>
mapRef.current?.fitBounds(
viewState.bounds,
viewState.fitBoundsOptions
)
}
🤖 Prompt for AI Agents
In fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id._index.tsx
around line 389 (ref={mapRef}), the initial fit can run before the map exists
due to the container gate; add an onLoad handler on the map component that runs
when the map instance is ready and performs the fit/fitBounds (checking
mapRef.current or using the map instance passed to onLoad), so the initial
viewport is applied only after the map mounts (SSR-safe) and remove/avoid
relying solely on the first effect to perform the initial fit.

Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw left a comment

Choose a reason for hiding this comment

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

Thanks! I checked it and it works perfectly.

@SvenVw SvenVw merged commit 7d2b233 into development Sep 22, 2025
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

branch:development Issue only affecting development, not the main branch (yet) bug Something isn't working fdm-app

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Map does not zoom to newly selected field when switching from field at farm create wizard

2 participants