Skip to content

Implement PDF export for Bemestingsplan#425

Merged
SvenVw merged 33 commits into
developmentfrom
FD413
Jan 27, 2026
Merged

Implement PDF export for Bemestingsplan#425
SvenVw merged 33 commits into
developmentfrom
FD413

Conversation

@SvenVw
Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw commented Jan 20, 2026

Summary by CodeRabbit

  • New Features

    • Downloadable "Bemestingsplan" PDF: multi-page report with cover, TOC, farm summary, per-field pages, charts, tables, bufferstrip handling, and sanitized filename.
    • In-app "Download bemestingsplan" action with progress indicator and success/error toasts.
    • Calendar-aware cultivation selection and planned-dose calculations reflected in the report.
    • Improved PDF layout, badges, cards and table components for clearer export presentation.
  • Chores

    • Added PDF rendering dependency and ensured public assets are available at runtime.

✏️ Tip: You can customize this high-level summary in your review settings.

Closes #413

@SvenVw SvenVw self-assigned this Jan 20, 2026
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jan 20, 2026

🦋 Changeset detected

Latest commit: a9b466d

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 Jan 20, 2026

Walkthrough

Adds a full Bemestingsplan PDF export: new React-PDF components, styles and types; server loader that gathers farm/ and field-level data and computes norms/advice/planned doses; client UI to download the PDF; dependency and Docker asset inclusion.

Changes

Cohort / File(s) Summary
Changeset
\.changeset/icy-kings-enjoy.md
Adds a changeset declaring a minor release for the PDF export feature.
PDF primitives
fdm-app/app/components/blocks/pdf/PdfBadge.tsx, fdm-app/app/components/blocks/pdf/PdfCard.tsx, fdm-app/app/components/blocks/pdf/PdfTable.tsx
New React(-PDF) building blocks: PdfBadge, PdfCard, PdfTable and table subcomponents (PdfTableHeader, PdfTableRow, PdfTableCell with weight prop).
Bemestingsplan PDF implementation
fdm-app/app/components/blocks/pdf/bemestingsplan/BemestingsplanPDF.tsx, .../styles.ts, .../types.d.ts
Adds data-driven multi-page PDF component, comprehensive PDF styles (pdfStyles) and BemestingsplanData type (cover, TOC, farm summary, per-field pages, totals, bufferstrips, charts, conditional assets).
Calculation integrations
fdm-app/app/integrations/calculator.ts, fdm-calculator/src/index.ts
Calendar-aware cultivation selection; getNutrientAdviceForField signature updated to accept calendar; new getPlannedDosesForField (fetches applications/fertilizers and computes doses); re-exports aggregateNorm*ToFarmLevel.
Server PDF route
fdm-app/app/routes/farm.$b_id_farm.$calendar.bemestingsplan[.]pdf.tsx
New loader: validates params, fetches farm/fields/data, computes norms/advice/planned doses per field, aggregates farm totals, embeds public assets as data URIs, streams rendered BemestingsplanPDF and sets Content-Disposition filename.
Client UI
fdm-app/app/routes/farm.$b_id_farm._index.tsx
Adds "Download bemestingsplan" card/button, loading state, fetch-based download handler, blob download flow, and toast success/error handling.
Deps & runtime assets
fdm-app/package.json, fdm-app/dockerfile
Adds @react-pdf/renderer dependency and copies public assets into production image so logos/cover can be embedded at runtime.

Sequence Diagram(s)

sequenceDiagram
    participant User as User (Browser)
    participant Client as Farm Dashboard (Client)
    participant Server as PDF Route Loader
    participant DB as Data Sources (fdm-core / integrations)
    participant PDF as BemestingsplanPDF

    User->>Client: Click "Download bemestingsplan"
    Client->>Client: set isGeneratingPdf = true
    Client->>Server: GET /farm/:b_id_farm/:calendar/bemestingsplan.pdf
    Server->>DB: Fetch farm, fields, cultivations, soil, applications, fertilizers
    Server->>Server: Compute norms, norm fillings, nutrient advice, planned doses per field
    Server->>Server: Aggregate farm-level totals and OM balance
    Server->>Server: Resolve public assets -> data URIs
    Server->>PDF: Render BemestingsplanPDF with assembled data
    PDF->>Server: Stream PDF bytes
    Server->>Client: Stream PDF response (blob)
    Client->>Client: Create temporary link and trigger download
    Client->>User: Save file and show success toast
    Client->>Client: set isGeneratingPdf = false
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR #274: modifies the same farm dashboard file (fdm-app/app/routes/farm.$b_id_farm._index.tsx) and may overlap UI additions.
  • PR #91: related to calculateDose/Dose API and schema changes used by the new planned-dose workflow.
  • PR #362: adjusts cultivation-selection logic (getDefaultCultivation) that the loader now uses for calendar-aware selection.

Suggested reviewers

  • BoraIneviNMI
  • gerardhros

Poem

🐰 I hopped through fields to fetch each plan,
Pages stacked tidy by one rabbit-hand.
Cover, totals, fields — all set to go,
Bemestingsplan delivered — neat and slow! 📄✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% 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 PR title clearly describes the main feature being added: PDF export functionality for the Bemestingsplan document. This directly aligns with the primary changeset objective.
Linked Issues check ✅ Passed The PR comprehensively implements all coding requirements from issue #413 including the download UI button, PDF generation with @react-pdf/renderer, required content sections (farm details, norms, advice, per-field reports), and data integration from fdm-calculator.
Out of Scope Changes check ✅ Passed All changes are directly scoped to issue #413 requirements: PDF export functionality, UI integration, component architecture, styling, data fetching, and asset management. No unrelated modifications detected.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch FD413

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 Jan 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.07%. Comparing base (3487a78) to head (a9b466d).
⚠️ Report is 34 commits behind head on development.

Additional details and impacted files
@@             Coverage Diff              @@
##           development     #425   +/-   ##
============================================
  Coverage        88.07%   88.07%           
============================================
  Files               91       91           
  Lines             4621     4621           
  Branches          1492     1492           
============================================
  Hits              4070     4070           
  Misses             551      551           
Flag Coverage Δ
fdm-calculator 88.76% <ø> (ø)
fdm-core 86.77% <ø> (ø)
fdm-data 92.12% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@SvenVw SvenVw changed the title FDM413 @coderabbitai Jan 26, 2026
@SvenVw SvenVw marked this pull request as ready for review January 26, 2026 16:10
@coderabbitai coderabbitai Bot changed the title @coderabbitai Implement PDF export for Bemestingsplan Jan 26, 2026
@coderabbitai coderabbitai Bot added branch:development Issue only affecting development, not the main branch (yet) enhancement New feature or request labels Jan 26, 2026
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

🤖 Fix all issues with AI agents
In `@fdm-app/app/components/blocks/pdf/bemestingsplan/BemestingsplanPDF.tsx`:
- Around line 433-444: In the BemestingsplanPDF React component (look for the
Text block that contains the Dutch disclaimer and references data.config.name),
fix the two typos where the English word "for" is used: change "for informatieve
doeleinden" to "voor informatieve doeleinden" and "for de naleving" to "voor de
naleving" so the disclaimer is correct Dutch; update the strings directly inside
the Text JSX where the current Dutch paragraph is rendered.
- Around line 295-333: The Table of Contents in BemestingsplanPDF.tsx is using
hardcoded page numbers inside the Link/Text blocks (see Link and Text usages for
"#farm-summary", "#fields-overview", "#fertilizer-totals"), which will become
incorrect as content changes; remove the hardcoded numeric <Text> nodes or
replace them with either empty placeholders or an indicator like "pagina
(approx.)" and/or a short note that numbers are approximate, and ensure the Link
components keep only the section labels (e.g., "Samenvatting bedrijf",
"Overzicht percelen", "Benodigde meststoffen") so navigation anchors remain but
no stale page numbers are shown.

In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.bemestingsplan[.]pdf.tsx:
- Around line 265-291: The current mapping in the applications mapping block
assumes index alignment between applications and dosesResult.applications (using
idx), which can misalign data; instead, look up the corresponding dose by a
stable key (e.g., p_id or p_app_id) when building each application object — use
fertilizers.find as an example and create a doses lookup (map by p_id or the
application id present in dosesResult.applications) and then pull p_dose_*
values from that lookup, falling back to 0 when no matching dose exists; update
the code in the applications: applications.map(...) block to replace
dosesResult.applications[idx] with a safe lookup into that map.
- Around line 484-492: The Content-Disposition header builds a filename from
farm.b_name_farm which may contain non-ASCII or special characters; update the
response to emit both a safe ASCII filename and an RFC 5987 encoded filename*
parameter: generate a sanitized fallback (e.g., replace quotes/whitespace and
strip/normalize accents) for filename, produce a UTF-8 percent-encoded value for
filename* (using encodeURIComponent-like RFC5987 encoding), and set the header
to include both "filename" and "filename*" so Content-Disposition uses the
encoded name; adjust the code that constructs the header where new Response(...)
sets "Content-Disposition" and reference farm.b_name_farm, b_id_farm and
calendar when creating these two filename variants.
- Around line 444-457: The path construction currently assumes a top-level
"fdm-app" directory by using path.resolve(process.cwd(), "fdm-app", "public")
which breaks in Docker (WORKDIR is already /app/fdm-app) and also fails if
public isn't copied into the image; change the publicDir resolution to point to
the runtime app public folder (e.g., path.resolve(process.cwd(), "public") or
derive the app root dynamically) and update usages of publicDir, logoPath,
logoInverted and clientConfig.logo to use that corrected path; additionally add
a graceful fallback that checks file existence (fs.existsSync) and continues
without a logo if missing, and/or ensure the Docker build copies the public
assets into the image so logos are available in production.
🧹 Nitpick comments (5)
fdm-app/app/components/blocks/pdf/PdfBadge.tsx (1)

4-14: Consider stronger typing for the style prop.

The any type for style reduces type safety. The @react-pdf/renderer package exports a Style type that could be used here. However, I note this matches the pattern used in other PDF components (PdfCard, PdfTable) in this PR.

♻️ Optional type improvement
+import type { Style } from "@react-pdf/renderer"
 import { Text, View } from "@react-pdf/renderer"
 import { pdfStyles } from "./bemestingsplan/styles"

 export const PdfBadge = ({
     children,
     style,
 }: {
     children: string
-    style?: any
+    style?: Style
 }) => (
     <View style={[pdfStyles.badge, style]}>
         <Text>{children}</Text>
     </View>
 )
fdm-app/app/components/blocks/pdf/PdfCard.tsx (1)

4-10: Consider stronger typing for the style prop.

Using any for the style prop bypasses type checking. While react-pdf styles can be complex, you could improve type safety by using the library's type.

♻️ Suggested improvement
+import type { Style } from "@react-pdf/types"
 import { View } from "@react-pdf/renderer"
 import { pdfStyles } from "./bemestingsplan/styles"

 export const PdfCard = ({
     children,
     style,
 }: {
     children: React.ReactNode
-    style?: any
+    style?: Style
 }) => <View style={[pdfStyles.card, style]}>{children}</View>
fdm-app/app/components/blocks/pdf/bemestingsplan/styles.ts (1)

160-172: Hardcoded footer positioning may cause layout issues.

The top: 780 value assumes a specific page height (A4 = 842pt). This absolute positioning could cause the footer to overlap with content on pages with more data, or appear incorrectly if page size changes.

Consider using bottom positioning instead for more reliable footer placement.

♻️ Suggested fix
     footer: {
         position: "absolute",
-        top: 780,
+        bottom: 20,
         left: 40,
         right: 40,
         flexDirection: "row",
         justifyContent: "space-between",
         borderTopWidth: 1,
         borderTopColor: "#e2e8f0",
         paddingTop: 10,
         fontSize: 8,
         color: "#64748b",
     },
fdm-app/app/routes/farm.$b_id_farm._index.tsx (1)

132-164: Improve error handling and sanitize filename.

Two concerns with the download handler:

  1. The error handling discards the server's error details which would help debugging.
  2. The filename could contain special characters from the farm name that may cause issues.
♻️ Suggested improvements
     const handleDownloadPdf = async (e: React.MouseEvent) => {
         e.preventDefault()
         if (isGeneratingPdf) return

         setIsGeneratingPdf(true)
         toast.info("Bemestingsplan wordt gegenereerd", {
             description: "Dit kan enkele seconden duren...",
         })

         try {
             const response = await fetch(
                 `/farm/${loaderData.b_id_farm}/${calendar}/bemestingsplan.pdf`,
             )
-            if (!response.ok) throw new Error("Download failed")
+            if (!response.ok) {
+                const errorText = await response.text().catch(() => "")
+                throw new Error(`Download failed: ${response.status} ${errorText}`)
+            }

             const blob = await response.blob()
             const url = window.URL.createObjectURL(blob)
             const a = document.createElement("a")
             a.href = url
-            a.download = `Bemestingsplan_${loaderData.b_name_farm}_${calendar}.pdf`
+            // Sanitize filename by removing/replacing problematic characters
+            const safeFarmName = loaderData.b_name_farm.replace(/[<>:"/\\|?*]/g, "_")
+            a.download = `Bemestingsplan_${safeFarmName}_${calendar}.pdf`
             document.body.appendChild(a)
             a.click()
             window.URL.revokeObjectURL(url)
             document.body.removeChild(a)

             toast.success("Download voltooid")
         } catch (error) {
             console.error(error)
             toast.error("Er ging iets mis bij het genereren van de PDF")
         } finally {
             setIsGeneratingPdf(false)
         }
     }
fdm-app/app/components/blocks/pdf/bemestingsplan/BemestingsplanPDF.tsx (1)

86-101: External URL creates unnecessary availability risk; consider bundling a local image asset for resilience.

Unsplash images are licensed for use in generated PDFs without attribution or permission requirements. However, relying on an external URL introduces an availability dependency—if the image becomes unavailable or the connection fails, PDF generation could be impacted. Bundling a local asset eliminates this external dependency.

Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.bemestingsplan[.]pdf.tsx Outdated
…nPDF.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
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

🤖 Fix all issues with AI agents
In `@fdm-app/app/components/blocks/pdf/bemestingsplan/BemestingsplanPDF.tsx`:
- Around line 87-90: The Image component is using a hardcoded external Unsplash
URL which can break PDF generation; update the component (BemestingsplanPDF ->
the Image usage) to use a local bundled asset or a configurable image from
data.config (e.g., use data.config.bannerImage if provided, otherwise fall back
to a static local import/asset served from the app). Ensure the chosen image is
bundled or served from your app (import the file or reference /public) so PDF
generation doesn't rely on external HTTP, and keep the same style props
(width/height/objectFit) when replacing the src.
- Around line 1623-1631: The "p_dose_eoc" plannedKey used in BemestingsplanPDF
(the nutrient entry with key "om" / label "Organische koolstof (EOC)") does not
exist on field.planned per types.d.ts, causing field.planned["p_dose_eoc"] to be
undefined and planned values to show 0; fix by either (A) adding p_dose_eoc to
the field.planned type in types.d.ts and ensuring the data loader populates
field.planned.p_dose_eoc for each field, or (B) remove/omit the EOC entry from
the secondary nutrients list in BemestingsplanPDF (or switch it to use a
farm-level aggregate property) so you no longer access a non-existent key;
update any code referencing plannedKey "p_dose_eoc" and corresponding UI
rendering logic accordingly.
♻️ Duplicate comments (1)
fdm-app/app/components/blocks/pdf/bemestingsplan/BemestingsplanPDF.tsx (1)

295-333: Table of Contents has hardcoded page numbers.

This issue was previously flagged. The hardcoded page numbers (3, 4, 3) and the formula-based field page numbers (5 + i on line 369) won't adjust dynamically as content changes.

🧹 Nitpick comments (2)
fdm-app/app/components/blocks/pdf/bemestingsplan/BemestingsplanPDF.tsx (2)

668-820: Consider extracting crop overview logic to a separate component.

The IIFE computing productiveFields, bufferFields, getCrops, and rendering renderCropList is complex. Extracting this to a dedicated CropOverview component would improve readability and testability.


1016-1170: Potentially redundant Footer rendering.

The Footer component has fixed positioning (renders at absolute position on every page). Having <Footer /> at both line 1017 and line 1169 may cause duplicate rendering. Other pages in this document only include a single Footer.

♻️ Remove redundant Footer
         </PdfTable>
-            <Footer config={data.config} />
         </Page>

Comment thread fdm-app/app/components/blocks/pdf/bemestingsplan/BemestingsplanPDF.tsx Outdated
@coderabbitai coderabbitai Bot added dependencies Pull requests that update a dependency file fdm-calculator fdm-core labels Jan 27, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@fdm-app/app/components/blocks/pdf/bemestingsplan/BemestingsplanPDF.tsx`:
- Around line 1202-1203: Remove the duplicated Footer render inside the Page
start: in BemestingsplanPDF remove the earlier <Footer config={data.config} />
that sits immediately after <Page key={field.id} size="A4"
style={pdfStyles.page}> so only the existing fixed Footer (the one intended to
be rendered across pages) remains; locate usages of the Footer component within
the Page block and delete the initial/duplicate instance while keeping the fixed
Footer at the end.
- Around line 1042-1043: The Footer component is rendered twice in
BemestingsplanPDF (inside the Page with pdfStyles.page and again later), causing
duplicate fixed-position footers; remove the earlier Footer invocation located
immediately after <Page size="A4" orientation="landscape"
style={pdfStyles.page}> so only the single Footer rendered after the content
remains, and verify no other duplicate Footer calls exist in the
BemestingsplanPDF component.

In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.bemestingsplan[.]pdf.tsx:
- Around line 172-176: The return type of getNutrientAdviceForField must be
explicit so callers can access properties safely; update its signature in
integrations/calculator.ts to return Promise<ReturnType<typeof
getNutrientAdvice>> (or the specific exported interface from
`@svenvw/fdm-calculator` if available) instead of an implicit any, then adjust the
implementation to respect that type; lastly remove the defensive any casts and
fallback chain in the caller (where yearAdvice is computed) and access the typed
fields directly (e.g., result.data.year or result.year) relying on the declared
return type.
🧹 Nitpick comments (5)
fdm-app/app/components/blocks/pdf/bemestingsplan/styles.ts (2)

63-63: Remove leftover development comment.

The comment // ... rest of frontInfo appears to be a leftover from development and should be removed for cleaner code.

♻️ Suggested fix
     frontFarmName: {
         fontSize: 24,
         fontWeight: "bold",
         marginBottom: 8,
         color: "#FFFFFF",
     },
-    // ... rest of frontInfo
-
     frontInfo: {

161-173: Hardcoded footer position assumes A4 dimensions.

The top: 780 value is hardcoded based on A4 page height (~842pt) minus margins. This works for portrait A4 but will break for landscape orientation used in the Fields Overview page.

Consider using bottom positioning instead of top for more robust placement across different page orientations.

♻️ Suggested fix
     footer: {
         position: "absolute",
-        top: 780,
+        bottom: 20,
         left: 40,
         right: 40,
         flexDirection: "row",
         justifyContent: "space-between",
         borderTopWidth: 1,
         borderTopColor: "#e2e8f0",
         paddingTop: 10,
         fontSize: 8,
         color: "#64748b",
     },
fdm-app/app/routes/farm.$b_id_farm.$calendar.bemestingsplan[.]pdf.tsx (1)

73-361: Consider chunking for very large farms.

For farms with 300+ fields (as mentioned in requirements), executing all field data gathering in a single Promise.all could cause memory pressure. The current per-field error handling is good, but consider adding chunking for very large datasets.

Would you like me to generate a chunked implementation that processes fields in batches?

fdm-app/app/components/blocks/pdf/bemestingsplan/BemestingsplanPDF.tsx (2)

23-40: Redundant condition check in Footer component.

The showPageNumbers is checked at line 23 to return the entire footer, but then checked again at line 30 for the page number text. Since line 23 already guards the entire block, the inner check at line 30 is redundant.

♻️ Suggested fix
 const Footer = ({
     config,
     style,
     showPageNumbers = true,
 }: {
     config: { name: string }
     style?: any
     showPageNumbers?: boolean
 }) => {
-    if (showPageNumbers) {
-        return (
-            <View style={[pdfStyles.footer, style]} fixed>
-                <Text>
-                    {config.name} - Gegenereerd op{" "}
-                    {format(new Date(), "d MMMM yyyy", { locale: nl })}
-                </Text>
-                {showPageNumbers && (
-                    <Text
-                        render={({ pageNumber, totalPages }) =>
-                            `Pagina ${pageNumber} / ${totalPages}`
-                        }
-                    />
-                )}
-            </View>
-        )
-    }
-    return null
+    return (
+        <View style={[pdfStyles.footer, style]} fixed>
+            <Text>
+                {config.name} - Gegenereerd op{" "}
+                {format(new Date(), "d MMMM yyyy", { locale: nl })}
+            </Text>
+            {showPageNumbers && (
+                <Text
+                    render={({ pageNumber, totalPages }) =>
+                        `Pagina ${pageNumber} / ${totalPages}`
+                    }
+                />
+            )}
+        </View>
+    )
 }

140-147: Remove commented-out logo code.

This commented-out code block should either be removed or properly implemented. Leaving commented code in production reduces readability.

♻️ Suggested fix
                 <View style={{ marginBottom: 40, alignItems: "center" }}>
-                    {/* {data.config.logo ? (
-                        <Image src={data.config.logo} style={pdfStyles.frontLogo} />
-                    ) : data.config.logoInverted ? (
-                        <Image
-                            src={data.config.logoInverted}
-                            style={pdfStyles.frontLogo}
-                        />
-                    ) : null} */}
+                    {data.config.logoInverted && (
+                        <Image
+                            src={data.config.logoInverted}
+                            style={pdfStyles.frontLogo}
+                        />
+                    )}
                     <Text

Comment thread fdm-app/app/components/blocks/pdf/bemestingsplan/BemestingsplanPDF.tsx Outdated
Comment thread fdm-app/app/components/blocks/pdf/bemestingsplan/BemestingsplanPDF.tsx Outdated
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.bemestingsplan[.]pdf.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: 1

🤖 Fix all issues with AI agents
In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.bemestingsplan[.]pdf.tsx:
- Around line 164-167: Remove the redundant type assertion on b_centroid: since
field is typed as Field (returned from getFields) and Field.b_centroid is
already [number, number], delete the "as [number, number]" cast in the object
literal where b_centroid is assigned (the code referencing b_centroid for the
field variable). Ensure the property remains simply b_centroid: field.b_centroid
and that no other callers rely on that cast.
🧹 Nitpick comments (2)
fdm-app/app/components/blocks/pdf/bemestingsplan/BemestingsplanPDF.tsx (2)

140-147: Remove commented-out code or add a TODO explaining its purpose.

This commented-out logo rendering block appears to be dead code. If it's intended for future use, add a TODO comment explaining when/why it should be enabled. Otherwise, remove it to keep the codebase clean.


694-846: Consider extracting the crop aggregation logic into a helper function.

The IIFE containing crop aggregation logic (getCrops, renderCropList) spans 150+ lines inside JSX. While functional, extracting this to a separate component or helper would improve readability and testability.

Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.bemestingsplan[.]pdf.tsx Outdated
…pdf.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
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

🤖 Fix all issues with AI agents
In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.bemestingsplan[.]pdf.tsx:
- Around line 539-555: The ASCII fallback filename in the Content-Disposition
header is inconsistent with the encoded filename; update the `filename` value to
include the same "Bemestingsplan_${safeName}_${calendar}.pdf" prefix used when
building `encodedName` so both `safeName`/`encodedName` align; locate the header
construction around `safeName`, `encodedName` and the Response creation and
replace the current `filename="${safeName}.pdf"` with a filename string that
matches the encoded name pattern (using `safeName` and `calendar`).
- Around line 356-452: The farm OM average currently divides totalOmBalance by
totalArea, which treats missing f.omBalance as zero; update the aggregation to
compute omArea = sum of f.area for fields where f.omBalance exists (using
pdfFieldsData), use omArea as the divisor and in the conditional (replace checks
against totalArea for OM averaging), and keep totalOmBalance accumulation logic
in totalOmBalance; adjust farmOmBalance to use omArea > 0 ? totalOmBalance.* /
omArea : 0 so only fields with omBalance contribute to the per-hectare averages.

…pdf.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
@SvenVw SvenVw requested a review from BoraIneviNMI January 27, 2026 13:20
Copy link
Copy Markdown
Collaborator

@BoraIneviNMI BoraIneviNMI left a comment

Choose a reason for hiding this comment

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

It seems to work fine and looks great. I have tested some things and here is what I found:

  • The PDF renders good on the latest iOS.
  • It handles the calendar year properly.
  • If some error is thrown when an error occurs, the section that has given an error and other sections might show 0 instead of showing an error. The teeltplan shows an error but it might not be clear that it applies to the rest of the field's section.
  • Line numbers include the cover page. This is probably fine.

@SvenVw SvenVw merged commit f154ed0 into development Jan 27, 2026
10 of 11 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) dependencies Pull requests that update a dependency file enhancement New feature or request fdm-app fdm-calculator fdm-core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Export "Bemestingsplan" (Fertilizer Application Plan) to PDF

2 participants