- {loaderData.errorMessage ? (
-
-
-
-
- Helaas is het niet mogelijk om je
- gebruiksnormen uit te rekenen
-
-
-
-
-
- Er is onverwacht wat misgegaan.
- Probeer opnieuw of neem contact op
- met Ondersteuning en deel de
- volgende foutmelding:
+ return (
+
+
+
+ Helaas, nog geen gebruiksnormen beschikbaar
+ voor {loaderData.calendar}
+
+
+ Op dit moment kunnen we alleen nog de
+ gebruiksnormen voor 2025 berekenen en
+ weergeven.
-
-
- {JSON.stringify(
- {
- message:
- loaderData.errorMessage,
- page: page,
- timestamp: new Date(),
- },
- null,
- 2,
- )}
-
-
+
+ Ga naar 2025
+
-
-
-
- ) : loaderData.farmNorms && loaderData.fieldNorms ? (
- <>
-
-
-
- >
- ) : (
-
-
-
- Helaas, nog geen gebruiksnormen beschikbaar
- voor {loaderData.calendar}
-
-
- Op dit moment kunnen we alleen nog de
- gebruiksnormen voor 2025 berekenen en
- weergeven.
-
-
- Ga naar 2025
-
-
-
- )}
-
+
+ )
+ }}
+
+
)
From 1bee89d42266ade528c67a8732951771c0b4894f Mon Sep 17 00:00:00 2001
From: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
Date: Wed, 23 Jul 2025 16:33:49 +0200
Subject: [PATCH 04/10] feat: show card skeletons when data is loading for
balance
---
.../components/blocks/balance/skeletons.tsx | 77 +++
..._farm.$calendar.balance.nitrogen.$b_id.tsx | 490 ++++++++---------
...farm.$calendar.balance.nitrogen._index.tsx | 494 ++++++++----------
3 files changed, 540 insertions(+), 521 deletions(-)
create mode 100644 fdm-app/app/components/blocks/balance/skeletons.tsx
diff --git a/fdm-app/app/components/blocks/balance/skeletons.tsx b/fdm-app/app/components/blocks/balance/skeletons.tsx
new file mode 100644
index 000000000..d1b2299de
--- /dev/null
+++ b/fdm-app/app/components/blocks/balance/skeletons.tsx
@@ -0,0 +1,77 @@
+import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "~/components/ui/card"
+import { Skeleton } from "~/components/ui/skeleton"
+
+export function NitrogenBalanceCardSkeleton() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export function NitrogenBalanceChartSkeleton() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export function NitrogenBalanceFieldsSkeleton() {
+ return (
+
+
+
+
+
+
+
+ {[...Array(4)].map((_, i) => (
+
+ ))}
+
+
+
+ )
+}
+
+export function NitrogenBalanceFallback() {
+ return (
+
+ )
+}
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
index 4b9eee720..73a991f04 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
@@ -23,7 +23,7 @@ import {
} from "react-router"
import { NitrogenBalanceChart } from "~/components/blocks/balance/nitrogen-chart"
import NitrogenBalanceDetails from "~/components/blocks/balance/nitrogen-details"
-import { LoadingSpinner } from "~/components/custom/loadingspinner"
+import { NitrogenBalanceFallback } from "~/components/blocks/balance/skeletons"
import { Button } from "~/components/ui/button"
import {
Card,
@@ -33,8 +33,9 @@ import {
CardHeader,
CardTitle,
} from "~/components/ui/card"
-import { Skeleton } from "~/components/ui/skeleton"
import { getSession } from "~/lib/auth.server"
+import { Suspense } from "react"
+import { Await } from "react-router"
import { getTimeframe } from "~/lib/calendar"
import { clientConfig } from "~/lib/config"
import { fdm } from "~/lib/fdm.server"
@@ -106,11 +107,11 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
await calculateNitrogenBalance(nitrogenBalanceInput)
nitrogenBalanceResult = nitrogenBalanceResult.fields.find(
- (field) => field.b_id === b_id,
+ (field: NitrogenBalanceNumeric['fields'][number]) => field.b_id === b_id,
)
nitrogenBalanceInput = nitrogenBalanceInput.fields.find(
- (field) => field.field.b_id === b_id,
+ (field: typeof nitrogenBalanceInput.fields[number]) => field.field.b_id === b_id,
)
} catch (error) {
errorMessage = String(error).replace("Error: ", "")
@@ -130,8 +131,6 @@ export default function FarmBalanceNitrogenFieldBlock() {
const location = useLocation()
const navigation = useNavigation()
const page = location.pathname
- const isLoading = navigation.state === "loading"
-
const calendar = useCalendarStore((state) => state.calendar)
const {
@@ -144,265 +143,238 @@ export default function FarmBalanceNitrogenFieldBlock() {
return (
- {nitrogenBalanceResult ? (
- <>
-
-
-
-
- Overschot / Doel (Perceel)
-
-
-
-
-
- {isLoading ? (
-
- ) : (
-
-
- {`${nitrogenBalanceResult.balance} / ${nitrogenBalanceResult.target}`}
-
- {nitrogenBalanceResult.balance <=
- nitrogenBalanceResult.target ? (
-
- ) : (
-
- )}
-
- )}
-
-
- kg N / ha
-
-
-
-
-
-
- Aanvoer
-
-
-
-
-
- {isLoading ? (
-
- ) : (
- nitrogenBalanceResult.supply.total
- )}
-
-
- kg N / ha
-
-
-
-
-
-
- Afvoer
-
-
-
-
-
- {isLoading ? (
-
- ) : (
- nitrogenBalanceResult.removal.total
- )}
+
}>
+
+ {(resolvedNitrogenBalanceResult) => {
+ if (!nitrogenBalanceInput) {
+ return (
+
+
+
+ Ongeldig jaar
+
+
+
+
+ Dit perceel was niet in gebruik voor dit
+ jaar. Als dit perceel wel in gebruik was,
+ werk dan de startdatum bij in de
+ perceelsinstelling.
+
+
+
+
+
+ Naar perceelsinstelling
+
+
+
-
- kg N / ha
-
-
-
-
-
-
- Emissie
-
-
-
-
-
- {isLoading ? (
-
- ) : (
- nitrogenBalanceResult.volatilization
- .total
- )}
-
-
- kg N / ha
-
-
-
-
-
-
-
- Balans
-
- De stikstofbalans voor {field.b_name} van{" "}
- {farm.b_name_farm}. De balans is het
- verschil tussen de totale aanvoer, afvoer en
- emissie van stikstof. Een positieve balans
- betekent een overschot aan stikstof, een
- negatieve balans een tekort.
-
-
-
- {isLoading ? (
-
-
-
- ) : (
-
- )}
-
-
-
-
- Posten
-
-
-
-
- {isLoading ? (
- [...Array(5)].map((_, index) => {
- return (
-
-
-
-
-
-
+ )
+ }
+
+ if (errorMessage) {
+ return (
+
+
+
+
+ Helaas is het niet mogelijk om je balans uit te
+ rekenen
+
+
+
+ {!errorMessage ? (
+
+
+ Er is een onbekende fout opgetreden.
+ Probeer opnieuw of neem contact op met
+ Ondersteuning.
+
+
+ ) : errorMessage.match(
+ /Missing required soil parameters/,
+ ) ? (
+
+
+ Voor niet alle percelen zijn de
+ benodigde bodemparameters bekend:
+
+
+
+ {errorMessage.match(/a_n_rt/) ? (
+ Totaal stikstofgehalte
+ ) : null}
+ {errorMessage.match(
+ /b_soiltype_agr/,
+ ) ? (
+ Agrarisch bodemtype
+ ) : null}
+ {errorMessage.match(
+ /a_c_of|a_som_loi/,
+ ) ? (
+ Organische stofgehalte
+ ) : null}
+
+
+ ) : (
+
+
+ Er is helaas wat misgegaan. Probeer
+ opnieuw of neem contact op met
+ Ondersteuning en deel de volgende
+ foutmelding:
+
+
+
+ {JSON.stringify(
+ {
+ message: errorMessage,
+ page: page,
+ timestamp: new Date(),
+ },
+ null,
+ 2,
+ )}
+
- )
- })
- ) : (
-
- )}
-
-
-
-
- >
- ) : !nitrogenBalanceInput ? (
-
-
-
- Ongeldig jaar
-
-
-
-
- Dit perceel was niet in gebruik voor dit
- jaar. Als dit perceel wel in gebruik was,
- werk dan de startdatum bij in de
- perceelsinstelling.
-
-
-
-
-
- Naar perceelsinstelling
-
-
-
-
- ) : (
-
-
-
-
- Helaas is het niet mogelijk om je balans uit te
- rekenen
-
-
-
- {!errorMessage ? (
-
-
- Er is een onbekende fout opgetreden.
- Probeer opnieuw of neem contact op met
- Ondersteuning.
-
+ )}
+
+
- ) : errorMessage.match(
- /Missing required soil parameters/,
- ) ? (
-
-
- Voor niet alle percelen zijn de
- benodigde bodemparameters bekend:
-
-
-
- {errorMessage.match(/a_n_rt/) ? (
- Totaal stikstofgehalte
- ) : null}
- {errorMessage.match(
- /b_soiltype_agr/,
- ) ? (
- Agrarisch bodemtype
- ) : null}
- {errorMessage.match(
- /a_c_of|a_som_loi/,
- ) ? (
- Organische stofgehalte
- ) : null}
-
+ )
+ }
+
+ return (
+ <>
+
+
+
+
+ Overschot / Doel (Perceel)
+
+
+
+
+
+
+
+ {`${resolvedNitrogenBalanceResult.balance} / ${resolvedNitrogenBalanceResult.target}`}
+
+ {resolvedNitrogenBalanceResult.balance <=
+ resolvedNitrogenBalanceResult.target ? (
+
+ ) : (
+
+ )}
+
+
+
+ kg N / ha
+
+
+
+
+
+
+ Aanvoer
+
+
+
+
+
+ {resolvedNitrogenBalanceResult.supply.total}
+
+
+ kg N / ha
+
+
+
+
+
+
+ Afvoer
+
+
+
+
+
+ {resolvedNitrogenBalanceResult.removal.total}
+
+
+ kg N / ha
+
+
+
+
+
+
+ Emissie
+
+
+
+
+
+ {resolvedNitrogenBalanceResult.volatilization.total}
+
+
+ kg N / ha
+
+
+
- ) : (
-
-
- Er is helaas wat misgegaan. Probeer
- opnieuw of neem contact op met
- Ondersteuning en deel de volgende
- foutmelding:
-
-
-
- {JSON.stringify(
- {
- message: errorMessage,
- page: page,
- timestamp: new Date(),
- },
- null,
- 2,
- )}
-
-
+
+
+
+ Balans
+
+ De stikstofbalans voor {field.b_name} van{" "}
+ {farm.b_name_farm}. De balans is het
+ verschil tussen de totale aanvoer, afvoer en
+ emissie van stikstof. Een positieve balans
+ betekent een overschot aan stikstof, een
+ negatieve balans een tekort.
+
+
+
+
+
+
+
+
+ Posten
+
+
+
+
+
+
+
+
- )}
-
-
-
- )}
+ >
+ )
+ }}
+
+
)
}
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
index 840185fa7..dd0090177 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
@@ -22,7 +22,7 @@ import {
useNavigation,
} from "react-router"
import { NitrogenBalanceChart } from "~/components/blocks/balance/nitrogen-chart"
-import { LoadingSpinner } from "~/components/custom/loadingspinner"
+import { NitrogenBalanceFallback } from "~/components/blocks/balance/skeletons"
import {
Card,
CardContent,
@@ -30,8 +30,9 @@ import {
CardHeader,
CardTitle,
} from "~/components/ui/card"
-import { Skeleton } from "~/components/ui/skeleton"
import { getSession } from "~/lib/auth.server"
+import { Suspense } from "react"
+import { Await } from "react-router"
import { getTimeframe } from "~/lib/calendar"
import { clientConfig } from "~/lib/config"
import { fdm } from "~/lib/fdm.server"
@@ -109,276 +110,245 @@ export default function FarmBalanceNitrogenOverviewBlock() {
const location = useLocation()
const navigation = useNavigation()
const page = location.pathname
- const isLoading = navigation.state === "loading"
-
const { nitrogenBalanceResult, farm, fields, errorMessage } = loaderData
const fieldsMap = new Map(fields.map((f) => [f.b_id, f]))
return (
- {nitrogenBalanceResult ? (
- <>
-
-
-
-
- Overschot / Doel (Bedrijf)
-
-
-
-
-
- {isLoading ? (
-
- ) : (
-
-
- {`${nitrogenBalanceResult.balance} / ${nitrogenBalanceResult.target}`}
-
- {nitrogenBalanceResult.balance <=
- nitrogenBalanceResult.target ? (
-
+
}>
+
+ {(resolvedNitrogenBalanceResult) => {
+ if (errorMessage) {
+ return (
+
+
+
+
+ Helaas is het niet mogelijk om je balans uit te
+ rekenen
+
+
+
+ {!errorMessage ? (
+
+
+ Er is een onbekende fout opgetreden.
+ Probeer opnieuw of neem contact op met
+ Ondersteuning.
+
+
+ ) : errorMessage.match(
+ /Missing required soil parameters/,
+ ) ? (
+
+
+ Voor niet alle percelen zijn de
+ benodigde bodemparameters bekend:
+
+
+
+ {errorMessage.match(/a_n_rt/) ? (
+ Totaal stikstofgehalte
+ ) : null}
+ {errorMessage.match(
+ /b_soiltype_agr/,
+ ) ? (
+ Agrarisch bodemtype
+ ) : null}
+ {errorMessage.match(
+ /a_c_of|a_som_loi/,
+ ) ? (
+ Organische stofgehalte
+ ) : null}
+
+
) : (
-
+
+
+ Er is helaas wat misgegaan. Probeer
+ opnieuw of neem contact op met
+ Ondersteuning en deel de volgende
+ foutmelding:
+
+
+
+ {JSON.stringify(
+ {
+ message: errorMessage,
+ page: page,
+ timestamp: new Date(),
+ },
+ null,
+ 2,
+ )}
+
+
+
)}
-
- )}
-
-
- kg N / ha
-
-
-
-
-
-
- Aanvoer
-
-
-
-
-
- {isLoading ? (
-
- ) : (
- nitrogenBalanceResult.supply
- )}
-
-
- kg N / ha
-
-
-
-
-
-
- Afvoer
-
-
-
-
-
- {isLoading ? (
-
- ) : (
- nitrogenBalanceResult.removal
- )}
+
+
-
- kg N / ha
-
-
-
-
-
-
- Emissie
-
-
-
-
-
- {isLoading ? (
-
- ) : (
- nitrogenBalanceResult.volatilization
- )}
-
-
- kg N / ha
-
-
-
-
-
-
-
- Balans
-
- De stikstofbalans voor alle percelen van{" "}
- {farm.b_name_farm}. De balans is het
- verschil tussen de totale aanvoer, afvoer en
- emissie van stikstof. Een positieve balans
- betekent een overschot aan stikstof, een
- negatieve balans een tekort.
-
-
-
- {isLoading ? (
-
-
-
- ) : (
-
- )}
- {/* */}
-
-
-
-
- Percelen
-
-
-
-
- {isLoading
- ? fields.map((field) => {
- return (
-
- )
- })
- : nitrogenBalanceResult.fields.map(
- (field) => {
- const fieldData =
- fieldsMap.get(field.b_id)
- return (
-
- {field.balance <=
- field.target ? (
-
- ) : (
-
- )}
+ )
+ }
-
-
-
- {
- fieldData?.b_name
- }
-
-
-
- {
- fieldData?.b_area
- }{" "}
- ha
-
-
-
- {field.balance} /{" "}
- {field.target}
-
-
- )
- },
- )}
-
-
-
-
- >
- ) : (
-
-
-
-
- Helaas is het niet mogelijk om je balans uit te
- rekenen
-
-
-
- {!errorMessage ? (
-
-
- Er is een onbekende fout opgetreden.
- Probeer opnieuw of neem contact op met
- Ondersteuning.
-
-
- ) : errorMessage.match(
- /Missing required soil parameters/,
- ) ? (
-
-
- Voor niet alle percelen zijn de
- benodigde bodemparameters bekend:
-
-
-
- {errorMessage.match(/a_n_rt/) ? (
- Totaal stikstofgehalte
- ) : null}
- {errorMessage.match(
- /b_soiltype_agr/,
- ) ? (
- Agrarisch bodemtype
- ) : null}
- {errorMessage.match(
- /a_c_of|a_som_loi/,
- ) ? (
- Organische stofgehalte
- ) : null}
-
+ return (
+ <>
+
+
+
+
+ Overschot / Doel (Bedrijf)
+
+
+
+
+
+
+
+ {`${resolvedNitrogenBalanceResult.balance} / ${resolvedNitrogenBalanceResult.target}`}
+
+ {resolvedNitrogenBalanceResult.balance <=
+ resolvedNitrogenBalanceResult.target ? (
+
+ ) : (
+
+ )}
+
+
+
+ kg N / ha
+
+
+
+
+
+
+ Aanvoer
+
+
+
+
+
+ {resolvedNitrogenBalanceResult.supply}
+
+
+ kg N / ha
+
+
+
+
+
+
+ Afvoer
+
+
+
+
+
+ {resolvedNitrogenBalanceResult.removal}
+
+
+ kg N / ha
+
+
+
+
+
+
+ Emissie
+
+
+
+
+
+ {resolvedNitrogenBalanceResult.volatilization}
+
+
+ kg N / ha
+
+
+
- ) : (
-
-
- Er is helaas wat misgegaan. Probeer
- opnieuw of neem contact op met
- Ondersteuning en deel de volgende
- foutmelding:
-
-
-
- {JSON.stringify(
- {
- message: errorMessage,
- page: page,
- timestamp: new Date(),
- },
- null,
- 2,
- )}
-
-
+
+
+
+ Balans
+
+ De stikstofbalans voor alle percelen van{" "}
+ {farm.b_name_farm}. De balans is het
+ verschil tussen de totale aanvoer, afvoer en
+ emissie van stikstof. Een positieve balans
+ betekent een overschot aan stikstof, een
+ negatieve balans een tekort.
+
+
+
+
+
+
+
+
+ Percelen
+
+
+
+
+ {resolvedNitrogenBalanceResult.fields.map(
+ (field: NitrogenBalanceNumeric['fields'][number]) => {
+ const fieldData =
+ fieldsMap.get(field.b_id)
+ return (
+
+ {field.balance <=
+ field.target ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {
+ fieldData?.b_name
+ }
+
+
+
+ {
+ fieldData?.b_area
+ }{" "}
+ ha
+
+
+
+ {field.balance} /{" "}
+ {field.target}
+
+
+ )
+ },
+ )}
+
+
+
- )}
-
-
-
- )}
+ >
+ )
+ }}
+
+
)
}
From 21517f8f99ee6466898df0455f3b28f22612664b Mon Sep 17 00:00:00 2001
From: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
Date: Wed, 23 Jul 2025 16:43:55 +0200
Subject: [PATCH 05/10] refactor: remove not needed navigation state check
---
...d_farm.$calendar.nutrient_advice.$b_id.tsx | 200 ++++++++----------
1 file changed, 93 insertions(+), 107 deletions(-)
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.nutrient_advice.$b_id.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.nutrient_advice.$b_id.tsx
index 1358ba662..89a978cab 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.nutrient_advice.$b_id.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.nutrient_advice.$b_id.tsx
@@ -13,7 +13,6 @@ import {
type LoaderFunctionArgs,
type MetaFunction,
useLoaderData,
- useNavigation,
} from "react-router"
import { NutrientCard } from "~/components/blocks/nutrient-advice/cards"
import {
@@ -155,7 +154,6 @@ export default function FieldNutrientAdviceBlock() {
fertilizers,
calendar,
} = useLoaderData()
- const navigation = useNavigation()
const primaryNutrients = nutrientsDescription.filter(
(item: NutrientDescription) => item.type === "primary",
@@ -181,41 +179,37 @@ export default function FieldNutrientAdviceBlock() {
- {navigation.state === "loading" ? (
-
- ) : (
-
- {primaryNutrients.map(
- (nutrient: NutrientDescription) => (
-
}
- >
-
- {(nutrientAdvice) => (
-
- )}
-
-
- ),
- )}
-
- )}
+
+ {primaryNutrients.map(
+ (nutrient: NutrientDescription) => (
+
}
+ >
+
+ {(nutrientAdvice) => (
+
+ )}
+
+
+ ),
+ )}
+
@@ -249,41 +243,37 @@ export default function FieldNutrientAdviceBlock() {
- {navigation.state === "loading" ? (
-
- ) : (
-
- {secondaryNutrients.map(
- (nutrient: NutrientDescription) => (
-
}
- >
-
- {(nutrientAdvice) => (
-
- )}
-
-
- ),
- )}
-
- )}
+
+ {secondaryNutrients.map(
+ (nutrient: NutrientDescription) => (
+
}
+ >
+
+ {(nutrientAdvice) => (
+
+ )}
+
+
+ ),
+ )}
+
@@ -298,41 +288,37 @@ export default function FieldNutrientAdviceBlock() {
- {navigation.state === "loading" ? (
-
- ) : (
-
- {traceNutrients.map(
- (nutrient: NutrientDescription) => (
-
}
- >
-
- {(nutrientAdvice) => (
-
- )}
-
-
- ),
- )}
-
- )}
+
+ {traceNutrients.map(
+ (nutrient: NutrientDescription) => (
+
}
+ >
+
+ {(nutrientAdvice) => (
+
+ )}
+
+
+ ),
+ )}
+
From b7d95e0e6a63bb8bf65db331efeb908e975853d5 Mon Sep 17 00:00:00 2001
From: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
Date: Wed, 23 Jul 2025 16:45:40 +0200
Subject: [PATCH 06/10] chore: add changeset
---
.changeset/giant-files-yell.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 .changeset/giant-files-yell.md
diff --git a/.changeset/giant-files-yell.md b/.changeset/giant-files-yell.md
new file mode 100644
index 000000000..6d41f3dac
--- /dev/null
+++ b/.changeset/giant-files-yell.md
@@ -0,0 +1,5 @@
+---
+"@svenvw/fdm-app": minor
+---
+
+Add for pages with calculations (.e.g., nutrient advice, norms and balance) placeholders with skeletons so that user sees the page already and is notified that the content will arrive shortly
From 0145296770bfba4936e652a52f1b0a415b784c76 Mon Sep 17 00:00:00 2001
From: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
Date: Thu, 24 Jul 2025 10:41:08 +0200
Subject: [PATCH 07/10] fix: import
---
fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
index 8452c26cf..26e27cd18 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx
@@ -51,6 +51,7 @@ import { handleActionError, handleLoaderError } from "~/lib/error";
import { fdm } from "~/lib/fdm.server";
import { extractFormValuesFromRequest } from "~/lib/form";
import { useCalendarStore } from "~/store/calendar";
+import { clientConfig } from "~/lib/config";
// Meta
export const meta: MetaFunction = () => {
From f5f44f39b16ad9f3a4881d83e1bafd8d77cffd87 Mon Sep 17 00:00:00 2001
From: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
Date: Thu, 24 Jul 2025 10:41:46 +0200
Subject: [PATCH 08/10] refactor: improve implementation of suspense
---
..._farm.$calendar.balance.nitrogen.$b_id.tsx | 66 ++++++++-----------
1 file changed, 28 insertions(+), 38 deletions(-)
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
index 73a991f04..c0b99a990 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
@@ -92,53 +92,42 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
const field = await getField(fdm, session.principal_id, b_id)
// Collect input data for nutrient balance calculation
- let nitrogenBalanceInput = await collectInputForNitrogenBalance(
+ const nitrogenBalancePromise = collectInputForNitrogenBalance(
fdm,
session.principal_id,
b_id_farm,
timeframe,
String(process.env.FDM_PUBLIC_DATA_URL),
- )
-
- let nitrogenBalanceResult = null as NitrogenBalanceNumeric | null
- let errorMessage = null as string | null
- try {
- nitrogenBalanceResult =
- await calculateNitrogenBalance(nitrogenBalanceInput)
-
- nitrogenBalanceResult = nitrogenBalanceResult.fields.find(
- (field: NitrogenBalanceNumeric['fields'][number]) => field.b_id === b_id,
- )
-
- nitrogenBalanceInput = nitrogenBalanceInput.fields.find(
- (field: typeof nitrogenBalanceInput.fields[number]) => field.field.b_id === b_id,
- )
- } catch (error) {
- errorMessage = String(error).replace("Error: ", "")
- }
+ ).then(async (input) => {
+ const result = await calculateNitrogenBalance(input);
+ return {
+ input: input.fields.find((field: { field: { b_id: string } }) => field.field.b_id === b_id),
+ result: result.fields.find((field: { b_id: string }) => field.b_id === b_id),
+ errorMessage: null
+ };
+ }).catch(error => ({
+ input: null,
+ result: null,
+ errorMessage: String(error).replace("Error: ", "")
+ }));
return {
- nitrogenBalanceInput: nitrogenBalanceInput,
- nitrogenBalanceResult: nitrogenBalanceResult,
+ nitrogenBalanceResult: nitrogenBalancePromise,
field: field,
farm: farm,
- errorMessage: errorMessage,
}
}
export default function FarmBalanceNitrogenFieldBlock() {
const loaderData = useLoaderData()
const location = useLocation()
- const navigation = useNavigation()
const page = location.pathname
const calendar = useCalendarStore((state) => state.calendar)
const {
- nitrogenBalanceInput,
nitrogenBalanceResult,
field,
farm,
- errorMessage,
} = loaderData
return (
@@ -146,7 +135,8 @@ export default function FarmBalanceNitrogenFieldBlock() {
}>
{(resolvedNitrogenBalanceResult) => {
- if (!nitrogenBalanceInput) {
+ const { input, result, errorMessage } = resolvedNitrogenBalanceResult
+ if (!input) {
return (
@@ -262,10 +252,10 @@ export default function FarmBalanceNitrogenFieldBlock() {
- {`${resolvedNitrogenBalanceResult.balance} / ${resolvedNitrogenBalanceResult.target}`}
+ {`${result.balance} / ${result.target}`}
- {resolvedNitrogenBalanceResult.balance <=
- resolvedNitrogenBalanceResult.target ? (
+ {result.balance <=
+ result.target ? (
) : (
@@ -286,7 +276,7 @@ export default function FarmBalanceNitrogenFieldBlock() {
- {resolvedNitrogenBalanceResult.supply.total}
+ {result.supply.total}
kg N / ha
@@ -302,7 +292,7 @@ export default function FarmBalanceNitrogenFieldBlock() {
- {resolvedNitrogenBalanceResult.removal.total}
+ {result.removal.total}
kg N / ha
@@ -318,7 +308,7 @@ export default function FarmBalanceNitrogenFieldBlock() {
- {resolvedNitrogenBalanceResult.volatilization.total}
+ {result.volatilization.total}
kg N / ha
@@ -341,15 +331,15 @@ export default function FarmBalanceNitrogenFieldBlock() {
@@ -363,8 +353,8 @@ export default function FarmBalanceNitrogenFieldBlock() {
From 012bdd76b6fe62ebb84491fe6a2f04794ebb2810 Mon Sep 17 00:00:00 2001
From: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
Date: Thu, 24 Jul 2025 10:49:40 +0200
Subject: [PATCH 09/10] refactor: prevent repeated promise resolution
---
...d_farm.$calendar.nutrient_advice.$b_id.tsx | 92 +++++++++----------
1 file changed, 46 insertions(+), 46 deletions(-)
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.nutrient_advice.$b_id.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.nutrient_advice.$b_id.tsx
index 89a978cab..823dbbdd3 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.nutrient_advice.$b_id.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.nutrient_advice.$b_id.tsx
@@ -180,21 +180,21 @@ export default function FieldNutrientAdviceBlock() {
- {primaryNutrients.map(
- (nutrient: NutrientDescription) => (
-
}
- >
-
- {(nutrientAdvice) => (
+ (
+
+ ))}
+ >
+
+ {(nutrientAdvice) =>
+ primaryNutrients.map(
+ (nutrient: NutrientDescription) => (
- )}
-
-
- ),
- )}
+ ),
+ )
+ }
+
+
@@ -244,21 +244,21 @@ export default function FieldNutrientAdviceBlock() {
- {secondaryNutrients.map(
- (nutrient: NutrientDescription) => (
-
}
- >
-
- {(nutrientAdvice) => (
+ (
+
+ ))}
+ >
+
+ {(nutrientAdvice) =>
+ secondaryNutrients.map(
+ (nutrient: NutrientDescription) => (
- )}
-
-
- ),
- )}
+ ),
+ )
+ }
+
+
@@ -289,21 +289,21 @@ export default function FieldNutrientAdviceBlock() {
- {traceNutrients.map(
- (nutrient: NutrientDescription) => (
-
}
- >
-
- {(nutrientAdvice) => (
+ (
+
+ ))}
+ >
+
+ {(nutrientAdvice) =>
+ traceNutrients.map(
+ (nutrient: NutrientDescription) => (
- )}
-
-
- ),
- )}
+ ),
+ )
+ }
+
+
)
-}
+}
\ No newline at end of file
From 37b80b8c5490a03d19578beb353c552bd1251207 Mon Sep 17 00:00:00 2001
From: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
Date: Thu, 24 Jul 2025 11:02:31 +0200
Subject: [PATCH 10/10] docs: improve comment
---
..._farm.$calendar.balance.nitrogen.$b_id.tsx | 135 ++++++++++--------
1 file changed, 79 insertions(+), 56 deletions(-)
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
index c0b99a990..83e05ba32 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
@@ -91,25 +91,32 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
// Get details of field
const field = await getField(fdm, session.principal_id, b_id)
- // Collect input data for nutrient balance calculation
- const nitrogenBalancePromise = collectInputForNitrogenBalance(
+ // Return promise directly for React Router v7 Suspense pattern
+ const nitrogenBalancePromise = collectInputForNitrogenBalance(
fdm,
session.principal_id,
b_id_farm,
timeframe,
String(process.env.FDM_PUBLIC_DATA_URL),
- ).then(async (input) => {
- const result = await calculateNitrogenBalance(input);
- return {
- input: input.fields.find((field: { field: { b_id: string } }) => field.field.b_id === b_id),
- result: result.fields.find((field: { b_id: string }) => field.b_id === b_id),
- errorMessage: null
- };
- }).catch(error => ({
- input: null,
- result: null,
- errorMessage: String(error).replace("Error: ", "")
- }));
+ )
+ .then(async (input) => {
+ const result = await calculateNitrogenBalance(input)
+ return {
+ input: input.fields.find(
+ (field: { field: { b_id: string } }) =>
+ field.field.b_id === b_id,
+ ),
+ result: result.fields.find(
+ (field: { b_id: string }) => field.b_id === b_id,
+ ),
+ errorMessage: null,
+ }
+ })
+ .catch((error) => ({
+ input: null,
+ result: null,
+ errorMessage: String(error).replace("Error: ", ""),
+ }))
return {
nitrogenBalanceResult: nitrogenBalancePromise,
@@ -124,18 +131,15 @@ export default function FarmBalanceNitrogenFieldBlock() {
const page = location.pathname
const calendar = useCalendarStore((state) => state.calendar)
- const {
- nitrogenBalanceResult,
- field,
- farm,
- } = loaderData
+ const { nitrogenBalanceResult, field, farm } = loaderData
return (
}>
{(resolvedNitrogenBalanceResult) => {
- const { input, result, errorMessage } = resolvedNitrogenBalanceResult
+ const { input, result, errorMessage } =
+ resolvedNitrogenBalanceResult
if (!input) {
return (
@@ -146,9 +150,11 @@ export default function FarmBalanceNitrogenFieldBlock() {
- Dit perceel was niet in gebruik voor dit
- jaar. Als dit perceel wel in gebruik was,
- werk dan de startdatum bij in de
+ Dit perceel was niet in
+ gebruik voor dit jaar. Als
+ dit perceel wel in gebruik
+ was, werk dan de startdatum
+ bij in de
perceelsinstelling.
@@ -157,7 +163,9 @@ export default function FarmBalanceNitrogenFieldBlock() {
- Naar perceelsinstelling
+
+ Naar perceelsinstelling
+
@@ -171,59 +179,76 @@ export default function FarmBalanceNitrogenFieldBlock() {
- Helaas is het niet mogelijk om je balans uit te
- rekenen
+ Helaas is het niet mogelijk om
+ je balans uit te rekenen
{!errorMessage ? (
- Er is een onbekende fout opgetreden.
- Probeer opnieuw of neem contact op met
- Ondersteuning.
+ Er is een onbekende fout
+ opgetreden. Probeer
+ opnieuw of neem contact
+ op met Ondersteuning.
) : errorMessage.match(
- /Missing required soil parameters/,
- ) ? (
+ /Missing required soil parameters/,
+ ) ? (
- Voor niet alle percelen zijn de
- benodigde bodemparameters bekend:
+ Voor niet alle percelen
+ zijn de benodigde
+ bodemparameters bekend:
- {errorMessage.match(/a_n_rt/) ? (
- Totaal stikstofgehalte
+ {errorMessage.match(
+ /a_n_rt/,
+ ) ? (
+
+ Totaal
+ stikstofgehalte
+
) : null}
{errorMessage.match(
/b_soiltype_agr/,
) ? (
- Agrarisch bodemtype
+
+ Agrarisch
+ bodemtype
+
) : null}
{errorMessage.match(
/a_c_of|a_som_loi/,
) ? (
- Organische stofgehalte
+
+ Organische
+ stofgehalte
+
) : null}
) : (
- Er is helaas wat misgegaan. Probeer
- opnieuw of neem contact op met
- Ondersteuning en deel de volgende
+ Er is helaas wat
+ misgegaan. Probeer
+ opnieuw of neem contact
+ op met Ondersteuning en
+ deel de volgende
foutmelding:
{JSON.stringify(
{
- message: errorMessage,
+ message:
+ errorMessage,
page: page,
- timestamp: new Date(),
+ timestamp:
+ new Date(),
},
null,
2,
@@ -321,26 +346,24 @@ export default function FarmBalanceNitrogenFieldBlock() {
Balans
- De stikstofbalans voor {field.b_name} van{" "}
- {farm.b_name_farm}. De balans is het
- verschil tussen de totale aanvoer, afvoer en
- emissie van stikstof. Een positieve balans
- betekent een overschot aan stikstof, een
- negatieve balans een tekort.
+ De stikstofbalans voor{" "}
+ {field.b_name} van{" "}
+ {farm.b_name_farm}. De balans is
+ het verschil tussen de totale
+ aanvoer, afvoer en emissie van
+ stikstof. Een positieve balans
+ betekent een overschot aan
+ stikstof, een negatieve balans
+ een tekort.