From 9541d9f24754965b0a32d0c30ccd1634ec3f8f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20Bora=20=C4=B0nevi?= Date: Tue, 11 Nov 2025 14:23:30 +0100 Subject: [PATCH 1/8] Use returnUrl in the fertilizers/new route to get rid of all the other copies of the route --- .../blocks/fertilizer-applications/form.tsx | 4 +- .../fertilizer/new-fertilizer-page-header.tsx | 132 ++++++++++ .../blocks/fertilizer/new-fertilizer.tsx | 8 +- .../components/blocks/header/create-farm.tsx | 9 +- ...ield.$b_id.fertilizer.manage.new.$p_id.tsx | 231 ----------------- ...eld.$b_id.fertilizer.manage.new._index.tsx | 51 ---- ...eld.$b_id.fertilizer.manage.new.custom.tsx | 236 ----------------- ...ndar.field.$b_id.fertilizer.manage.new.tsx | 169 ------------ ...ndar.field.fertilizer.manage.new.$p_id.tsx | 242 ------------------ ...dar.field.fertilizer.manage.new._index.tsx | 51 ---- ...dar.field.fertilizer.manage.new.custom.tsx | 239 ----------------- ....$calendar.field.fertilizer.manage.new.tsx | 141 ---------- .../farm.$b_id_farm.fertilizers.new.$p_id.tsx | 17 +- ...farm.$b_id_farm.fertilizers.new.custom.tsx | 17 +- .../farm.$b_id_farm.fertilizers.new.tsx | 63 +++-- ...izers.$b_lu_catalogue.manage.new.$p_id.tsx | 231 ----------------- ...zers.$b_lu_catalogue.manage.new._index.tsx | 51 ---- ...zers.$b_lu_catalogue.manage.new.custom.tsx | 236 ----------------- ...fertilizers.$b_lu_catalogue.manage.new.tsx | 97 ------- 19 files changed, 212 insertions(+), 2013 deletions(-) create mode 100644 fdm-app/app/components/blocks/fertilizer/new-fertilizer-page-header.tsx delete mode 100644 fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx delete mode 100644 fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new._index.tsx delete mode 100644 fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx delete mode 100644 fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx delete mode 100644 fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx delete mode 100644 fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new._index.tsx delete mode 100644 fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx delete mode 100644 fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.tsx delete mode 100644 fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.$p_id.tsx delete mode 100644 fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx delete mode 100644 fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.custom.tsx delete mode 100644 fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.tsx diff --git a/fdm-app/app/components/blocks/fertilizer-applications/form.tsx b/fdm-app/app/components/blocks/fertilizer-applications/form.tsx index 2f4e939f8..36d5c8722 100644 --- a/fdm-app/app/components/blocks/fertilizer-applications/form.tsx +++ b/fdm-app/app/components/blocks/fertilizer-applications/form.tsx @@ -148,9 +148,7 @@ export function FertilizerApplicationForm({ ) } navigate( - searchParams.has("fieldIds") - ? `./manage/new?fieldIds=${searchParams.get("fieldIds")}` - : "./manage/new", + `/farm/${b_id_farm}/fertilizers/new?returnUrl=${encodeURIComponent(`${location.pathname}${location.search}`)}`, ) } diff --git a/fdm-app/app/components/blocks/fertilizer/new-fertilizer-page-header.tsx b/fdm-app/app/components/blocks/fertilizer/new-fertilizer-page-header.tsx new file mode 100644 index 000000000..82acfc03b --- /dev/null +++ b/fdm-app/app/components/blocks/fertilizer/new-fertilizer-page-header.tsx @@ -0,0 +1,132 @@ +import { useLoaderData, useParams, useSearchParams } from "react-router" +import { Header } from "~/components/blocks/header/base" +import { HeaderFarmCreate } from "~/components/blocks/header/create-farm" +import { HeaderFarm } from "~/components/blocks/header/farm" +import { HeaderFertilizer } from "~/components/blocks/header/fertilizer" +import { HeaderField } from "~/components/blocks/header/field" +import { BreadcrumbLink, BreadcrumbSeparator } from "~/components/ui/breadcrumb" + +/** + * Returns the appropriate new fertilizer page header based on the `returnUrl`\ + * search param. + * + * `b_id_farm` is assumed to be in the route params for the farm fertilizer + * route. + * + * `b_name_farm`, `farmOptions`, and `fieldOptions` are assumed to exist in the + * loader data when needed for the route. + * + * If `returnUrl` is present as a search param, the application will return here + * when the fertilizer creation is complete or the user clicks the button in + * this component. + */ +export function NewFertilizerPageHeader() { + const [searchParams] = useSearchParams() + const params = useParams() + const { b_name_farm, farmOptions, fieldOptions } = useLoaderData() + const returnUrl = searchParams.get("returnUrl") ?? "" + const parsedReturnUrl = returnUrl + ? new URL( + returnUrl.startsWith("/") + ? `http://localhost${returnUrl}` + : returnUrl, + ) + : null + + const singleFieldMatch = returnUrl.match( + /farm\/([^/]*)\/([^/]*)\/field\/([^/]*)\/fertilizer/, + ) + if (singleFieldMatch) { + const [_, b_id_farm, calendar, b_id] = singleFieldMatch + return ( +
+ + + + Nieuwe meststof + +
+ ) + } + + const multipleFieldsMatch = returnUrl.match( + /farm\/([^/]*)\/([^/]*)\/field\/fertilizer/, + ) + if (multipleFieldsMatch) { + const [_, b_id_farm, calendar] = multipleFieldsMatch + return ( +
+ + + Percelen + + + Bemesting toevoegen + + + + Nieuwe meststof + +
+ ) + } + + const createMatch = /farm\/create/.test(returnUrl) + if (createMatch) { + return ( +
+ +
+ ) + } + + const { b_id_farm } = params + return ( +
+ + +
+ ) +} diff --git a/fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx b/fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx index 64523ee4b..855919f44 100644 --- a/fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx +++ b/fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx @@ -7,8 +7,8 @@ export function CustomFertilizerButton() { return ( Bouwplan - {currentPath.match(/manage/) ? ( + {currentPath.match(/new/) ? ( <> - + Bemesting diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx deleted file mode 100644 index 85b4cd074..000000000 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.$p_id.tsx +++ /dev/null @@ -1,231 +0,0 @@ -import { - addFertilizer, - addFertilizerToCatalogue, - getFarm, - getFarms, - getFertilizer, - getFertilizerParametersDescription, - getFertilizers, -} from "@svenvw/fdm-core" -import { - type ActionFunctionArgs, - data, - type LoaderFunctionArgs, - type MetaFunction, - useLoaderData, -} from "react-router" -import { redirectWithSuccess } from "remix-toast" -import { FormSchema } from "~/components/blocks/fertilizer/formschema" -import { FarmNewFertilizerBlock } from "~/components/blocks/fertilizer/new-fertilizer-page" -import { getSession } from "~/lib/auth.server" -import { getCalendar } from "~/lib/calendar" -import { clientConfig } from "~/lib/config" -import { handleActionError, handleLoaderError } from "~/lib/error" -import { fdm } from "~/lib/fdm.server" -import { extractFormValuesFromRequest } from "~/lib/form" - -export const meta: MetaFunction = () => { - return [ - { title: `Meststof | ${clientConfig.name}` }, - { - name: "description", - content: "Bekijk de details van deze meststof", - }, - ] -} - -export async function loader({ request, params }: LoaderFunctionArgs) { - try { - // Get the farm id - const b_id_farm = params.b_id_farm - if (!b_id_farm) { - throw data("invalid: b_id_farm", { - status: 400, - statusText: "invalid: b_id_farm", - }) - } - - // Get the fertilizer id - const p_id = params.p_id - if (!p_id) { - throw data("invalid: p_id", { - status: 400, - statusText: "invalid: p_id", - }) - } - - // Get the session - const session = await getSession(request) - - // Get details of farm - const farm = await getFarm(fdm, session.principal_id, b_id_farm) - if (!farm) { - throw data("not found: b_id_farm", { - status: 404, - statusText: "not found: b_id_farm", - }) - } - - // Get a list of possible farms of the user - const farms = await getFarms(fdm, session.principal_id) - if (!farms || farms.length === 0) { - throw data("not found: farms", { - status: 404, - statusText: "not found: farms", - }) - } - - const farmOptions = farms.map((farm) => { - return { - b_id_farm: farm.b_id_farm, - b_name_farm: farm.b_name_farm, - } - }) - - // Get selected fertilizer - const fertilizer = await getFertilizer(fdm, p_id) - const fertilizerParameters = getFertilizerParametersDescription() - - // Get the available fertilizers - const fertilizers = await getFertilizers( - fdm, - session.principal_id, - b_id_farm, - ) - const fertilizerOptions = fertilizers.map((fertilizer) => { - return { - p_id: fertilizer.p_id, - p_name_nl: fertilizer.p_name_nl, - } - }) - - // Return user information from loader - return { - farm: farm, - p_id: p_id, - b_id_farm: b_id_farm, - farmOptions: farmOptions, - fertilizerOptions: fertilizerOptions, - fertilizer: fertilizer, - editable: true, - fertilizerParameters: fertilizerParameters, - } - } catch (error) { - throw handleLoaderError(error) - } -} - -/** - * Renders the layout for managing farm settings. - * - * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields. - * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster. - */ -export default function FarmFertilizerPage() { - const loaderData = useLoaderData() - - return -} - -export async function action({ request, params }: ActionFunctionArgs) { - try { - const b_id_farm = params.b_id_farm - const b_id = params.b_id - const p_id = params.p_id - - if (!b_id_farm) { - throw new Error("missing: b_id_farm") - } - - if (!b_id) { - throw new Error("missing: b_id") - } - - if (!p_id) { - throw new Error("missing: p_id") - } - - const calendar = getCalendar(params) - - const session = await getSession(request) - const formValues = await extractFormValuesFromRequest( - request, - FormSchema, - ) - - const p_id_catalogue = await addFertilizerToCatalogue( - fdm, - session.principal_id, - b_id_farm, - { - p_name_nl: formValues.p_name_nl, - p_name_en: formValues.p_name_en, - p_description: formValues.p_description, - p_type: null, - p_type_rvo: formValues.p_type_rvo, - p_dm: formValues.p_dm, - p_density: formValues.p_density, - p_om: formValues.p_om, - p_a: formValues.p_a, - p_hc: formValues.p_hc, - p_eom: formValues.p_eom, - p_eoc: formValues.p_eoc, - p_c_rt: formValues.p_c_rt, - p_c_of: formValues.p_c_of, - p_c_if: formValues.p_c_if, - p_c_fr: formValues.p_c_fr, - p_cn_of: formValues.p_cn_of, - p_n_rt: formValues.p_n_rt, - p_n_if: formValues.p_n_if, - p_n_of: formValues.p_n_of, - p_n_wc: formValues.p_n_wc, - p_no3_rt: formValues.p_no3_rt, - p_nh4_rt: formValues.p_nh4_rt, - p_p_rt: formValues.p_p_rt, - p_k_rt: formValues.p_k_rt, - p_mg_rt: formValues.p_mg_rt, - p_ca_rt: formValues.p_ca_rt, - p_ne: formValues.p_ne, - p_s_rt: formValues.p_s_rt, - p_s_wc: formValues.p_s_wc, - p_cu_rt: formValues.p_cu_rt, - p_zn_rt: formValues.p_zn_rt, - p_na_rt: formValues.p_na_rt, - p_si_rt: formValues.p_si_rt, - p_b_rt: formValues.p_b_rt, - p_mn_rt: formValues.p_mn_rt, - p_ni_rt: formValues.p_ni_rt, - p_fe_rt: formValues.p_fe_rt, - p_mo_rt: formValues.p_mo_rt, - p_co_rt: formValues.p_co_rt, - p_as_rt: formValues.p_as_rt, - p_cd_rt: formValues.p_cd_rt, - p_cr_rt: formValues.p_cr_rt, - p_cr_vi: formValues.p_cr_vi, - p_pb_rt: formValues.p_pb_rt, - p_hg_rt: formValues.p_hg_rt, - p_cl_rt: formValues.p_cl_rt, - p_ef_nh3: undefined, - p_app_method_options: formValues.p_app_method_options, - }, - ) - - const new_p_id = await addFertilizer( - fdm, - session.principal_id, - p_id_catalogue, - b_id_farm, - undefined, - undefined, - ) - - return redirectWithSuccess( - `/farm/${b_id_farm}/${calendar}/field/${b_id}/fertilizer?p_id=${new_p_id}`, - { - message: `${formValues.p_name_nl} is toegevoegd! 🎉`, - }, - ) - } catch (error) { - throw handleActionError(error) - } -} diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new._index.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new._index.tsx deleted file mode 100644 index 08f08cc74..000000000 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new._index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { type Fertilizer, getFertilizers } from "@svenvw/fdm-core" -import type { LoaderFunctionArgs } from "react-router" -import { useLoaderData } from "react-router" -import { - BasedOffFertilizerButton, - CustomFertilizerButton, -} from "~/components/blocks/fertilizer/new-fertilizer" -import { getSession } from "~/lib/auth.server" -import { fdm } from "~/lib/fdm.server" - -export async function loader({ request, params }: LoaderFunctionArgs) { - const { b_id_farm } = params - if (!b_id_farm) { - throw new Error("Farm ID is required") - } - - const session = await getSession(request) - - const fertilizers = await getFertilizers( - fdm, - session.principal_id, - b_id_farm, - ) - - return { b_id_farm: b_id_farm, fertilizers: fertilizers } -} - -/** - * Renders the new fertilizer wizard start page - * - * This component includes a button that can be used to fill everything from scratch. - * Below that it includes a button for each existing fertilizer which the user can click to fill in the new fertilizer form values on the next page based on the corresponding fertilizer. - */ -export default function NewFertilizerIndexPage() { - const { fertilizers } = useLoaderData() - - return ( -
- -

Of baseer op een meststof

-
- {fertilizers.map((fertilizer: Fertilizer) => ( - - ))} -
-
- ) -} diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx deleted file mode 100644 index e5284b9d4..000000000 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.custom.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import { - addFertilizer, - addFertilizerToCatalogue, - getFertilizerParametersDescription, - getFertilizers, -} from "@svenvw/fdm-core" -import { - type ActionFunctionArgs, - data, - type LoaderFunctionArgs, - type MetaFunction, - useLoaderData, -} from "react-router" -import { redirectWithSuccess } from "remix-toast" -import { FormSchema } from "~/components/blocks/fertilizer/formschema" -import { FarmNewCustomFertilizerBlock } from "~/components/blocks/fertilizer/new-custom-fertilizer-page" -import { getSession } from "~/lib/auth.server" -import { getCalendar } from "~/lib/calendar" -import { clientConfig } from "~/lib/config" -import { handleActionError, handleLoaderError } from "~/lib/error" -import { fdm } from "~/lib/fdm.server" -import { extractFormValuesFromRequest } from "~/lib/form" - -export const meta: MetaFunction = () => { - return [ - { title: `Meststof toevoegen | ${clientConfig.name}` }, - { - name: "description", - content: - "Voeg een meststof toe om deze te gebruiken op dit bedrijf.", - }, - ] -} - -export async function loader({ request, params }: LoaderFunctionArgs) { - try { - // Get the farm id - const b_id_farm = params.b_id_farm - if (!b_id_farm) { - throw data("invalid: b_id_farm", { - status: 400, - statusText: "invalid: b_id_farm", - }) - } - - // Get the session - const session = await getSession(request) - - // Get selected fertilizer - const fertilizerParameters = getFertilizerParametersDescription() - - const fertilizer = { - p_id: undefined, // Added p_id - p_source: b_id_farm, - p_name_nl: "", - p_type: undefined, - p_type_rvo: undefined, - p_dm: undefined, - p_density: undefined, - p_om: undefined, - p_a: undefined, - p_hc: undefined, - p_eom: undefined, - p_eoc: undefined, - p_c_rt: undefined, - p_c_of: undefined, - p_c_if: undefined, - p_c_fr: undefined, - p_cn_of: undefined, - p_n_rt: undefined, - p_n_if: undefined, - p_n_of: undefined, - p_n_wc: undefined, - p_no3_rt: undefined, - p_nh4_rt: undefined, - p_p_rt: undefined, - p_k_rt: undefined, - p_mg_rt: undefined, - p_ca_rt: undefined, - p_ne: undefined, - p_s_rt: undefined, - p_s_wc: undefined, - p_cu_rt: undefined, - p_zn_rt: undefined, - p_na_rt: undefined, - p_si_rt: undefined, - p_b_rt: undefined, - p_mn_rt: undefined, - p_ni_rt: undefined, - p_fe_rt: undefined, - p_mo_rt: undefined, - p_co_rt: undefined, - p_as_rt: undefined, - p_cd_rt: undefined, - p_cr_rt: undefined, - p_cr_vi: undefined, - p_pb_rt: undefined, - p_hg_rt: undefined, - p_cl_rt: undefined, - p_app_method_options: [], - } - - // Get the available fertilizers - const fertilizers = await getFertilizers( - fdm, - session.principal_id, - b_id_farm, - ) - const fertilizerOptions = fertilizers.map((fertilizer) => { - return { - p_id: fertilizer.p_id, - p_name_nl: fertilizer.p_name_nl, - } - }) - - // Return user information from loader - return { - fertilizerOptions: fertilizerOptions, - fertilizer: fertilizer, - fertilizerParameters: fertilizerParameters, - } - } catch (error) { - throw handleLoaderError(error) - } -} - -/** - * Renders the layout for managing farm settings. - * - * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields. - * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster. - */ -export default function FarmFertilizerPage() { - const loaderData = useLoaderData() - - return -} - -export async function action({ request, params }: ActionFunctionArgs) { - try { - const b_id_farm = params.b_id_farm - - if (!b_id_farm) { - throw new Error("missing: b_id_farm") - } - - const b_id = params.b_id - - if (!b_id) { - throw new Error("missing: b_id") - } - - const session = await getSession(request) - const calendar = getCalendar(params) - - const formValues = await extractFormValuesFromRequest( - request, - FormSchema, - ) - - const p_id_catalogue = await addFertilizerToCatalogue( - fdm, - session.principal_id, - b_id_farm, - { - p_name_nl: formValues.p_name_nl, - p_name_en: formValues.p_name_en, - p_description: formValues.p_description, - p_type: null, - p_type_rvo: formValues.p_type_rvo, - p_dm: formValues.p_dm, - p_density: formValues.p_density, - p_om: formValues.p_om, - p_a: formValues.p_a, - p_hc: formValues.p_hc, - p_eom: formValues.p_eom, - p_eoc: formValues.p_eoc, - p_c_rt: formValues.p_c_rt, - p_c_of: formValues.p_c_of, - p_c_if: formValues.p_c_if, - p_c_fr: formValues.p_c_fr, - p_cn_of: formValues.p_cn_of, - p_n_rt: formValues.p_n_rt, - p_n_if: formValues.p_n_if, - p_n_of: formValues.p_n_of, - p_n_wc: formValues.p_n_wc, - p_no3_rt: formValues.p_no3_rt, - p_nh4_rt: formValues.p_nh4_rt, - p_p_rt: formValues.p_p_rt, - p_k_rt: formValues.p_k_rt, - p_mg_rt: formValues.p_mg_rt, - p_ca_rt: formValues.p_ca_rt, - p_ne: formValues.p_ne, - p_s_rt: formValues.p_s_rt, - p_s_wc: formValues.p_s_wc, - p_cu_rt: formValues.p_cu_rt, - p_zn_rt: formValues.p_zn_rt, - p_na_rt: formValues.p_na_rt, - p_si_rt: formValues.p_si_rt, - p_b_rt: formValues.p_b_rt, - p_mn_rt: formValues.p_mn_rt, - p_ni_rt: formValues.p_ni_rt, - p_fe_rt: formValues.p_fe_rt, - p_mo_rt: formValues.p_mo_rt, - p_co_rt: formValues.p_co_rt, - p_as_rt: formValues.p_as_rt, - p_cd_rt: formValues.p_cd_rt, - p_cr_rt: formValues.p_cr_rt, - p_cr_vi: formValues.p_cr_vi, - p_pb_rt: formValues.p_pb_rt, - p_hg_rt: formValues.p_hg_rt, - p_cl_rt: formValues.p_cl_rt, - p_ef_nh3: undefined, - p_app_method_options: formValues.p_app_method_options, - }, - ) - - const new_p_id = await addFertilizer( - fdm, - session.principal_id, - p_id_catalogue, - b_id_farm, - undefined, - undefined, - ) - - return redirectWithSuccess( - `/farm/${b_id_farm}/${calendar}/field/${b_id}/fertilizer?p_id=${new_p_id}`, - { - message: `${formValues.p_name_nl} is toegevoegd! 🎉`, - }, - ) - } catch (error) { - throw handleActionError(error) - } -} diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx deleted file mode 100644 index cddd8e4bc..000000000 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new.tsx +++ /dev/null @@ -1,169 +0,0 @@ -import { getFarm, getFarms, getField, getFields } from "@svenvw/fdm-core" -import { - data, - type LoaderFunctionArgs, - type MetaFunction, - Outlet, - useLoaderData, -} from "react-router" -import { FarmTitle } from "~/components/blocks/farm/farm-title" -import { Header } from "~/components/blocks/header/base" -import { HeaderFarm } from "~/components/blocks/header/farm" -import { HeaderField } from "~/components/blocks/header/field" -import { BreadcrumbLink } from "~/components/ui/breadcrumb" -import { SidebarInset } from "~/components/ui/sidebar" -import { getSession } from "~/lib/auth.server" -import { getTimeframe } from "~/lib/calendar" -import { clientConfig } from "~/lib/config" -import { handleLoaderError } from "~/lib/error" -import { fdm } from "~/lib/fdm.server" -import type { Route } from "./+types/farm.$b_id_farm.$calendar.field.$b_id.fertilizer.manage.new" - -export const meta: MetaFunction = () => { - return [ - { title: `Meststof toevoegen | ${clientConfig.name}` }, - { - name: "description", - content: - "Voeg een meststof toe om deze te gebruiken op dit bedrijf.", - }, - ] -} - -export async function loader({ request, params }: LoaderFunctionArgs) { - try { - // Get the farm id - const b_id_farm = params.b_id_farm - if (!b_id_farm) { - throw data("invalid: b_id_farm", { - status: 400, - statusText: "invalid: b_id_farm", - }) - } - - // Get the field id - const b_id = params.b_id - if (!b_id) { - throw data("invalid: b_id", { - status: 400, - statusText: "invalid: b_id", - }) - } - - // Get the session - const session = await getSession(request) - - // Get the calendar - const timeframe = getTimeframe(params) - - // Get details of farm - const farm = await getFarm(fdm, session.principal_id, b_id_farm) - if (!farm) { - throw data("not found: b_id_farm", { - status: 404, - statusText: "not found: b_id_farm", - }) - } - - const field = await getField(fdm, session.principal_id, b_id) - if (!field) { - throw data("not found: b_id", { - status: 404, - statusText: "not found: b_id", - }) - } - - // Get a list of possible farms of the user - const farms = await getFarms(fdm, session.principal_id) - if (!farms || farms.length === 0) { - throw data("not found: farms", { - status: 404, - statusText: "not found: farms", - }) - } - - const farmOptions = farms.map((farm) => { - return { - b_id_farm: farm.b_id_farm, - b_name_farm: farm.b_name_farm || "", - } - }) - - // Get the fields to be selected - const fields = await getFields( - fdm, - session.principal_id, - b_id_farm, - timeframe, - ) - const fieldOptions = fields.map((field) => { - if (!field?.b_id || !field?.b_name) { - throw new Error("Invalid field data structure") - } - return { - b_id: field.b_id, - b_name: field.b_name, - b_area: Math.round(field.b_area * 10) / 10, - } - }) - - // Return user information from loader - return { - farm: farm, - b_id_farm: b_id_farm, - b_id: b_id, - farmOptions: farmOptions, - fieldOptions: fieldOptions, - } - } catch (error) { - throw handleLoaderError(error) - } -} - -/** - * Renders the layout for managing farm settings. - * - * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields. - * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster. - */ -export default function FarmFertilizerBlock({ params }: Route.ComponentProps) { - const loaderData = useLoaderData() - - return ( - -
- - - - Nieuwe meststof - -
-
- -
- -
-
-
- ) -} diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx deleted file mode 100644 index da74625b0..000000000 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.$p_id.tsx +++ /dev/null @@ -1,242 +0,0 @@ -import { - addFertilizer, - addFertilizerToCatalogue, - getFarm, - getFarms, - getFertilizer, - getFertilizerParametersDescription, - getFertilizers, -} from "@svenvw/fdm-core" -import { - type ActionFunctionArgs, - data, - type LoaderFunctionArgs, - type MetaFunction, - useLoaderData, -} from "react-router" -import { redirectWithSuccess } from "remix-toast" -import { FormSchema } from "~/components/blocks/fertilizer/formschema" -import { FarmNewFertilizerBlock } from "~/components/blocks/fertilizer/new-fertilizer-page" -import { getSession } from "~/lib/auth.server" -import { getCalendar } from "~/lib/calendar" -import { clientConfig } from "~/lib/config" -import { handleActionError, handleLoaderError } from "~/lib/error" -import { fdm } from "~/lib/fdm.server" -import { extractFormValuesFromRequest } from "~/lib/form" - -export const meta: MetaFunction = () => { - return [ - { title: `Meststof | ${clientConfig.name}` }, - { - name: "description", - content: "Bekijk de details van deze meststof", - }, - ] -} - -export async function loader({ request, params }: LoaderFunctionArgs) { - try { - // Get the farm id - const b_id_farm = params.b_id_farm - if (!b_id_farm) { - throw data("invalid: b_id_farm", { - status: 400, - statusText: "invalid: b_id_farm", - }) - } - - // Get the fertilizer id - const p_id = params.p_id - if (!p_id) { - throw data("invalid: p_id", { - status: 400, - statusText: "invalid: p_id", - }) - } - - const searchParams = new URL(request.url).searchParams - if (!searchParams.has("fieldIds")) { - throw data("missing: fieldIds", { - status: 400, - statusText: "missing: fieldIds", - }) - } - - // Get the session - const session = await getSession(request) - - // Get details of farm - const farm = await getFarm(fdm, session.principal_id, b_id_farm) - if (!farm) { - throw data("not found: b_id_farm", { - status: 404, - statusText: "not found: b_id_farm", - }) - } - - // Get a list of possible farms of the user - const farms = await getFarms(fdm, session.principal_id) - if (!farms || farms.length === 0) { - throw data("not found: farms", { - status: 404, - statusText: "not found: farms", - }) - } - - const farmOptions = farms.map((farm) => { - return { - b_id_farm: farm.b_id_farm, - b_name_farm: farm.b_name_farm, - } - }) - - // Get selected fertilizer - const fertilizer = await getFertilizer(fdm, p_id) - const fertilizerParameters = getFertilizerParametersDescription() - - // Get the available fertilizers - const fertilizers = await getFertilizers( - fdm, - session.principal_id, - b_id_farm, - ) - const fertilizerOptions = fertilizers.map((fertilizer) => { - return { - p_id: fertilizer.p_id, - p_name_nl: fertilizer.p_name_nl, - } - }) - - // Return user information from loader - return { - farm: farm, - p_id: p_id, - b_id_farm: b_id_farm, - farmOptions: farmOptions, - fertilizerOptions: fertilizerOptions, - fertilizer: fertilizer, - editable: true, - fertilizerParameters: fertilizerParameters, - } - } catch (error) { - throw handleLoaderError(error) - } -} - -/** - * Renders the layout for managing farm settings. - * - * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields. - * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster. - */ -export default function FarmFertilizerPage() { - const loaderData = useLoaderData() - - return -} - -export async function action({ request, params }: ActionFunctionArgs) { - try { - const b_id_farm = params.b_id_farm - const p_id = params.p_id - - if (!b_id_farm) { - throw new Error("missing: b_id_farm") - } - - if (!p_id) { - throw new Error("missing: p_id") - } - - const searchParams = new URL(request.url).searchParams - if (!searchParams.has("fieldIds")) { - throw data("missing: fieldIds", { - status: 400, - statusText: "missing: fieldIds", - }) - } - - const calendar = getCalendar(params) - - const session = await getSession(request) - const formValues = await extractFormValuesFromRequest( - request, - FormSchema, - ) - - const p_id_catalogue = await addFertilizerToCatalogue( - fdm, - session.principal_id, - b_id_farm, - { - p_name_nl: formValues.p_name_nl, - p_name_en: formValues.p_name_en, - p_description: formValues.p_description, - p_type: null, - p_type_rvo: formValues.p_type_rvo, - p_dm: formValues.p_dm, - p_density: formValues.p_density, - p_om: formValues.p_om, - p_a: formValues.p_a, - p_hc: formValues.p_hc, - p_eom: formValues.p_eom, - p_eoc: formValues.p_eoc, - p_c_rt: formValues.p_c_rt, - p_c_of: formValues.p_c_of, - p_c_if: formValues.p_c_if, - p_c_fr: formValues.p_c_fr, - p_cn_of: formValues.p_cn_of, - p_n_rt: formValues.p_n_rt, - p_n_if: formValues.p_n_if, - p_n_of: formValues.p_n_of, - p_n_wc: formValues.p_n_wc, - p_no3_rt: formValues.p_no3_rt, - p_nh4_rt: formValues.p_nh4_rt, - p_p_rt: formValues.p_p_rt, - p_k_rt: formValues.p_k_rt, - p_mg_rt: formValues.p_mg_rt, - p_ca_rt: formValues.p_ca_rt, - p_ne: formValues.p_ne, - p_s_rt: formValues.p_s_rt, - p_s_wc: formValues.p_s_wc, - p_cu_rt: formValues.p_cu_rt, - p_zn_rt: formValues.p_zn_rt, - p_na_rt: formValues.p_na_rt, - p_si_rt: formValues.p_si_rt, - p_b_rt: formValues.p_b_rt, - p_mn_rt: formValues.p_mn_rt, - p_ni_rt: formValues.p_ni_rt, - p_fe_rt: formValues.p_fe_rt, - p_mo_rt: formValues.p_mo_rt, - p_co_rt: formValues.p_co_rt, - p_as_rt: formValues.p_as_rt, - p_cd_rt: formValues.p_cd_rt, - p_cr_rt: formValues.p_cr_rt, - p_cr_vi: formValues.p_cr_vi, - p_pb_rt: formValues.p_pb_rt, - p_hg_rt: formValues.p_hg_rt, - p_cl_rt: formValues.p_cl_rt, - p_ef_nh3: undefined, - p_app_method_options: formValues.p_app_method_options, - }, - ) - - const new_p_id = await addFertilizer( - fdm, - session.principal_id, - p_id_catalogue, - b_id_farm, - undefined, - undefined, - ) - - return redirectWithSuccess( - `/farm/${b_id_farm}/${calendar}/field/fertilizer?fieldIds=${searchParams.get("fieldIds")}&p_id=${new_p_id}`, - { - message: `${formValues.p_name_nl} is toegevoegd! 🎉`, - }, - ) - } catch (error) { - throw handleActionError(error) - } -} diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new._index.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new._index.tsx deleted file mode 100644 index 08f08cc74..000000000 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new._index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { type Fertilizer, getFertilizers } from "@svenvw/fdm-core" -import type { LoaderFunctionArgs } from "react-router" -import { useLoaderData } from "react-router" -import { - BasedOffFertilizerButton, - CustomFertilizerButton, -} from "~/components/blocks/fertilizer/new-fertilizer" -import { getSession } from "~/lib/auth.server" -import { fdm } from "~/lib/fdm.server" - -export async function loader({ request, params }: LoaderFunctionArgs) { - const { b_id_farm } = params - if (!b_id_farm) { - throw new Error("Farm ID is required") - } - - const session = await getSession(request) - - const fertilizers = await getFertilizers( - fdm, - session.principal_id, - b_id_farm, - ) - - return { b_id_farm: b_id_farm, fertilizers: fertilizers } -} - -/** - * Renders the new fertilizer wizard start page - * - * This component includes a button that can be used to fill everything from scratch. - * Below that it includes a button for each existing fertilizer which the user can click to fill in the new fertilizer form values on the next page based on the corresponding fertilizer. - */ -export default function NewFertilizerIndexPage() { - const { fertilizers } = useLoaderData() - - return ( -
- -

Of baseer op een meststof

-
- {fertilizers.map((fertilizer: Fertilizer) => ( - - ))} -
-
- ) -} diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx deleted file mode 100644 index 3352d968c..000000000 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.custom.tsx +++ /dev/null @@ -1,239 +0,0 @@ -import { - addFertilizer, - addFertilizerToCatalogue, - getFertilizerParametersDescription, - getFertilizers, -} from "@svenvw/fdm-core" -import { - type ActionFunctionArgs, - data, - type LoaderFunctionArgs, - type MetaFunction, - useLoaderData, -} from "react-router" -import { redirectWithSuccess } from "remix-toast" -import { FormSchema } from "~/components/blocks/fertilizer/formschema" -import { FarmNewCustomFertilizerBlock } from "~/components/blocks/fertilizer/new-custom-fertilizer-page" -import { getSession } from "~/lib/auth.server" -import { getCalendar } from "~/lib/calendar" -import { clientConfig } from "~/lib/config" -import { handleActionError, handleLoaderError } from "~/lib/error" -import { fdm } from "~/lib/fdm.server" -import { extractFormValuesFromRequest } from "~/lib/form" - -export const meta: MetaFunction = () => { - return [ - { title: `Meststof toevoegen | ${clientConfig.name}` }, - { - name: "description", - content: - "Voeg een meststof toe om deze te gebruiken op dit bedrijf.", - }, - ] -} - -export async function loader({ request, params }: LoaderFunctionArgs) { - try { - // Get the farm id - const b_id_farm = params.b_id_farm - if (!b_id_farm) { - throw data("invalid: b_id_farm", { - status: 400, - statusText: "invalid: b_id_farm", - }) - } - - const searchParams = new URL(request.url).searchParams - if (!searchParams.has("fieldIds")) { - throw data("missing: fieldIds", { - status: 400, - statusText: "missing: fieldIds", - }) - } - - // Get the session - const session = await getSession(request) - - // Get selected fertilizer - const fertilizerParameters = getFertilizerParametersDescription() - - const fertilizer = { - p_id: undefined, // Added p_id - p_source: b_id_farm, - p_name_nl: "", - p_type: undefined, - p_type_rvo: undefined, - p_dm: undefined, - p_density: undefined, - p_om: undefined, - p_a: undefined, - p_hc: undefined, - p_eom: undefined, - p_eoc: undefined, - p_c_rt: undefined, - p_c_of: undefined, - p_c_if: undefined, - p_c_fr: undefined, - p_cn_of: undefined, - p_n_rt: undefined, - p_n_if: undefined, - p_n_of: undefined, - p_n_wc: undefined, - p_no3_rt: undefined, - p_nh4_rt: undefined, - p_p_rt: undefined, - p_k_rt: undefined, - p_mg_rt: undefined, - p_ca_rt: undefined, - p_ne: undefined, - p_s_rt: undefined, - p_s_wc: undefined, - p_cu_rt: undefined, - p_zn_rt: undefined, - p_na_rt: undefined, - p_si_rt: undefined, - p_b_rt: undefined, - p_mn_rt: undefined, - p_ni_rt: undefined, - p_fe_rt: undefined, - p_mo_rt: undefined, - p_co_rt: undefined, - p_as_rt: undefined, - p_cd_rt: undefined, - p_cr_rt: undefined, - p_cr_vi: undefined, - p_pb_rt: undefined, - p_hg_rt: undefined, - p_cl_rt: undefined, - p_app_method_options: [], - } - - // Get the available fertilizers - const fertilizers = await getFertilizers( - fdm, - session.principal_id, - b_id_farm, - ) - const fertilizerOptions = fertilizers.map((fertilizer) => { - return { - p_id: fertilizer.p_id, - p_name_nl: fertilizer.p_name_nl, - } - }) - - // Return user information from loader - return { - fertilizerOptions: fertilizerOptions, - fertilizer: fertilizer, - fertilizerParameters: fertilizerParameters, - } - } catch (error) { - throw handleLoaderError(error) - } -} - -/** - * Renders the layout for managing farm settings. - * - * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields. - * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster. - */ -export default function FarmFertilizerPage() { - const loaderData = useLoaderData() - - return -} - -export async function action({ request, params }: ActionFunctionArgs) { - try { - const searchParams = new URL(request.url).searchParams - const b_id_farm = params.b_id_farm - - if (!b_id_farm) { - throw new Error("missing: b_id_farm") - } - - const session = await getSession(request) - const calendar = getCalendar(params) - - const formValues = await extractFormValuesFromRequest( - request, - FormSchema, - ) - - const p_id_catalogue = await addFertilizerToCatalogue( - fdm, - session.principal_id, - b_id_farm, - { - p_name_nl: formValues.p_name_nl, - p_name_en: formValues.p_name_en, - p_description: formValues.p_description, - p_type: null, - p_type_rvo: formValues.p_type_rvo, - p_dm: formValues.p_dm, - p_density: formValues.p_density, - p_om: formValues.p_om, - p_a: formValues.p_a, - p_hc: formValues.p_hc, - p_eom: formValues.p_eom, - p_eoc: formValues.p_eoc, - p_c_rt: formValues.p_c_rt, - p_c_of: formValues.p_c_of, - p_c_if: formValues.p_c_if, - p_c_fr: formValues.p_c_fr, - p_cn_of: formValues.p_cn_of, - p_n_rt: formValues.p_n_rt, - p_n_if: formValues.p_n_if, - p_n_of: formValues.p_n_of, - p_n_wc: formValues.p_n_wc, - p_no3_rt: formValues.p_no3_rt, - p_nh4_rt: formValues.p_nh4_rt, - p_p_rt: formValues.p_p_rt, - p_k_rt: formValues.p_k_rt, - p_mg_rt: formValues.p_mg_rt, - p_ca_rt: formValues.p_ca_rt, - p_ne: formValues.p_ne, - p_s_rt: formValues.p_s_rt, - p_s_wc: formValues.p_s_wc, - p_cu_rt: formValues.p_cu_rt, - p_zn_rt: formValues.p_zn_rt, - p_na_rt: formValues.p_na_rt, - p_si_rt: formValues.p_si_rt, - p_b_rt: formValues.p_b_rt, - p_mn_rt: formValues.p_mn_rt, - p_ni_rt: formValues.p_ni_rt, - p_fe_rt: formValues.p_fe_rt, - p_mo_rt: formValues.p_mo_rt, - p_co_rt: formValues.p_co_rt, - p_as_rt: formValues.p_as_rt, - p_cd_rt: formValues.p_cd_rt, - p_cr_rt: formValues.p_cr_rt, - p_cr_vi: formValues.p_cr_vi, - p_pb_rt: formValues.p_pb_rt, - p_hg_rt: formValues.p_hg_rt, - p_cl_rt: formValues.p_cl_rt, - p_ef_nh3: undefined, - p_app_method_options: formValues.p_app_method_options, - }, - ) - - const new_p_id = await addFertilizer( - fdm, - session.principal_id, - p_id_catalogue, - b_id_farm, - undefined, - undefined, - ) - - return redirectWithSuccess( - `/farm/${b_id_farm}/${calendar}/field/fertilizer?fieldIds=${searchParams.get("fieldIds")}&p_id=${new_p_id}`, - { - message: `${formValues.p_name_nl} is toegevoegd! 🎉`, - }, - ) - } catch (error) { - throw handleActionError(error) - } -} diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.tsx deleted file mode 100644 index 119989707..000000000 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.fertilizer.manage.new.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { getFarm, getFarms } from "@svenvw/fdm-core" -import { - data, - type LoaderFunctionArgs, - type MetaFunction, - Outlet, - useLoaderData, - useSearchParams, -} from "react-router" -import { FarmTitle } from "~/components/blocks/farm/farm-title" -import { Header } from "~/components/blocks/header/base" -import { HeaderFarm } from "~/components/blocks/header/farm" -import { BreadcrumbLink, BreadcrumbSeparator } from "~/components/ui/breadcrumb" -import { SidebarInset } from "~/components/ui/sidebar" -import { getSession } from "~/lib/auth.server" -import { clientConfig } from "~/lib/config" -import { handleLoaderError } from "~/lib/error" -import { fdm } from "~/lib/fdm.server" -import type { Route } from "./+types/farm.$b_id_farm.$calendar.field.fertilizer.manage.new" - -export const meta: MetaFunction = () => { - return [ - { title: `Meststof toevoegen | ${clientConfig.name}` }, - { - name: "description", - content: - "Voeg een meststof toe om deze te gebruiken op dit bedrijf.", - }, - ] -} - -export async function loader({ request, params }: LoaderFunctionArgs) { - try { - // Get the farm id - const b_id_farm = params.b_id_farm - if (!b_id_farm) { - throw data("invalid: b_id_farm", { - status: 400, - statusText: "invalid: b_id_farm", - }) - } - - // Get the field ids - const searchParams = new URL(request.url).searchParams - if (!searchParams.has("fieldIds")) { - throw data("missing: fieldIds", { - status: 400, - statusText: "missing: fieldIds", - }) - } - - // Get the session - const session = await getSession(request) - - // Get details of farm - const farm = await getFarm(fdm, session.principal_id, b_id_farm) - if (!farm) { - throw data("not found: b_id_farm", { - status: 404, - statusText: "not found: b_id_farm", - }) - } - - // Get a list of possible farms of the user - const farms = await getFarms(fdm, session.principal_id) - if (!farms || farms.length === 0) { - throw data("not found: farms", { - status: 404, - statusText: "not found: farms", - }) - } - - const farmOptions = farms.map((farm) => { - return { - b_id_farm: farm.b_id_farm, - b_name_farm: farm.b_name_farm || "", - } - }) - - // Return user information from loader - return { - b_id_farm: b_id_farm, - farmOptions: farmOptions, - } - } catch (error) { - throw handleLoaderError(error) - } -} - -/** - * Renders the layout for managing farm settings. - * - * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields. - * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster. - */ -export default function FarmFertilizerBlock({ params }: Route.ComponentProps) { - const loaderData = useLoaderData() - const [urlSearchParams] = useSearchParams() - - return ( - -
- - - Percelen - - - Bemesting toevoegen - - - - Nieuwe meststof - -
-
- -
- -
-
-
- ) -} diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx index 19d83b234..f49111bd3 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx @@ -138,6 +138,11 @@ export async function action({ request, params }: ActionFunctionArgs) { throw new Error("missing: p_id") } + const requestUrl = new URL(request.url) + const returnUrl = + requestUrl.searchParams.get("returnUrl") ?? + `/farm/${b_id_farm}/fertilizers` + const session = await getSession(request) const formValues = await extractFormValuesFromRequest( request, @@ -201,7 +206,7 @@ export async function action({ request, params }: ActionFunctionArgs) { }, ) - await addFertilizer( + const p_new_id = await addFertilizer( fdm, session.principal_id, p_id_catalogue, @@ -210,7 +215,15 @@ export async function action({ request, params }: ActionFunctionArgs) { undefined, ) - return redirectWithSuccess(`/farm/${b_id_farm}/fertilizers`, { + const parsedReturnUrl = new URL( + returnUrl.startsWith("/") + ? `http://localhost${returnUrl}` + : returnUrl, + ) + parsedReturnUrl.searchParams.set("p_id", p_new_id) + const modifiedReturnUrl = `${returnUrl.startsWith("/") ? "" : parsedReturnUrl.origin}${parsedReturnUrl.pathname}${parsedReturnUrl.search}${parsedReturnUrl.hash}` + + return redirectWithSuccess(modifiedReturnUrl, { message: `${formValues.p_name_nl} is toegevoegd! 🎉`, }) } catch (error) { diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx index 268588b65..50b0c69ee 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx @@ -142,6 +142,11 @@ export async function action({ request, params }: ActionFunctionArgs) { throw new Error("missing: b_id_farm") } + const requestUrl = new URL(request.url) + const returnUrl = + requestUrl.searchParams.get("returnUrl") ?? + `/farm/${b_id_farm}/fertilizers` + const session = await getSession(request) const formValues = await extractFormValuesFromRequest( request, @@ -205,7 +210,7 @@ export async function action({ request, params }: ActionFunctionArgs) { }, ) - await addFertilizer( + const p_id = await addFertilizer( fdm, session.principal_id, p_id_catalogue, @@ -214,7 +219,15 @@ export async function action({ request, params }: ActionFunctionArgs) { undefined, ) - return redirectWithSuccess(`/farm/${b_id_farm}/fertilizers`, { + const parsedReturnUrl = new URL( + returnUrl.startsWith("/") + ? `http://localhost${returnUrl}` + : returnUrl, + ) + parsedReturnUrl.searchParams.set("p_id", p_id) + const modifiedReturnUrl = `${returnUrl.startsWith("/") ? "" : parsedReturnUrl.origin}${parsedReturnUrl.pathname}${parsedReturnUrl.search}${parsedReturnUrl.hash}` + + return redirectWithSuccess(modifiedReturnUrl, { message: `${formValues.p_name_nl} is toegevoegd! 🎉`, }) } catch (error) { diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx index ab965f317..b35c861d2 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx @@ -1,17 +1,15 @@ -import { getFarm, getFarms } from "@svenvw/fdm-core" +import { getFarm, getFarms, getFields } from "@svenvw/fdm-core" import { data, type LoaderFunctionArgs, type MetaFunction, Outlet, - useLoaderData, } from "react-router" +import { NewFertilizerPageHeader } from "@/app/components/blocks/fertilizer/new-fertilizer-page-header" import { FarmTitle } from "~/components/blocks/farm/farm-title" -import { Header } from "~/components/blocks/header/base" -import { HeaderFarm } from "~/components/blocks/header/farm" -import { HeaderFertilizer } from "~/components/blocks/header/fertilizer" import { SidebarInset } from "~/components/ui/sidebar" import { getSession } from "~/lib/auth.server" +import { getTimeframe } from "~/lib/calendar" import { clientConfig } from "~/lib/config" import { handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" @@ -67,11 +65,46 @@ export async function loader({ request, params }: LoaderFunctionArgs) { } }) + // Try to load more data if the requestUrl is found + const requestUrl = new URL(request.url) + const returnUrl = + requestUrl.searchParams.get("returnUrl") ?? + `/farm/${b_id_farm}/fertilizers` + + let fieldOptions: { + b_id: string + b_name: string + b_area: number + }[] = [] + if (/farm\/[^/]*\/[^/]*\/field\/[^/]*\/fertilizer/.test(returnUrl)) { + const timeframe = getTimeframe(params) + const fields = await getFields( + fdm, + session.principal_id, + b_id_farm, + timeframe, + ) + fieldOptions = fields.map( + (field: Awaited>[number]) => { + if (!field?.b_id || !field?.b_name) { + throw new Error("Invalid field data structure") + } + return { + b_id: field.b_id, + b_name: field.b_name, + b_area: Math.round(field.b_area * 10) / 10, + } + }, + ) + } + // Return user information from loader return { farm: farm, b_id_farm: b_id_farm, + b_name_farm: farm.b_name_farm, farmOptions: farmOptions, + fieldOptions: fieldOptions, } } catch (error) { throw handleLoaderError(error) @@ -85,27 +118,9 @@ export async function loader({ request, params }: LoaderFunctionArgs) { * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster. */ export default function FarmFertilizerBlock({ params }: Route.ComponentProps) { - const loaderData = useLoaderData() - return ( -
- - -
+
{ - return [ - { title: `Meststof | ${clientConfig.name}` }, - { - name: "description", - content: "Bekijk de details van deze meststof", - }, - ] -} - -export async function loader({ request, params }: LoaderFunctionArgs) { - try { - // Get the farm id - const b_id_farm = params.b_id_farm - if (!b_id_farm) { - throw data("invalid: b_id_farm", { - status: 400, - statusText: "invalid: b_id_farm", - }) - } - - // Get the fertilizer id - const p_id = params.p_id - if (!p_id) { - throw data("invalid: p_id", { - status: 400, - statusText: "invalid: p_id", - }) - } - - // Get the session - const session = await getSession(request) - - // Get details of farm - const farm = await getFarm(fdm, session.principal_id, b_id_farm) - if (!farm) { - throw data("not found: b_id_farm", { - status: 404, - statusText: "not found: b_id_farm", - }) - } - - // Get a list of possible farms of the user - const farms = await getFarms(fdm, session.principal_id) - if (!farms || farms.length === 0) { - throw data("not found: farms", { - status: 404, - statusText: "not found: farms", - }) - } - - const farmOptions = farms.map((farm) => { - return { - b_id_farm: farm.b_id_farm, - b_name_farm: farm.b_name_farm, - } - }) - - // Get selected fertilizer - const fertilizer = await getFertilizer(fdm, p_id) - const fertilizerParameters = getFertilizerParametersDescription() - - // Get the available fertilizers - const fertilizers = await getFertilizers( - fdm, - session.principal_id, - b_id_farm, - ) - const fertilizerOptions = fertilizers.map((fertilizer) => { - return { - p_id: fertilizer.p_id, - p_name_nl: fertilizer.p_name_nl, - } - }) - - // Return user information from loader - return { - farm: farm, - p_id: p_id, - b_id_farm: b_id_farm, - farmOptions: farmOptions, - fertilizerOptions: fertilizerOptions, - fertilizer: fertilizer, - editable: true, - fertilizerParameters: fertilizerParameters, - } - } catch (error) { - throw handleLoaderError(error) - } -} - -/** - * Renders the layout for managing farm settings. - * - * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields. - * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster. - */ -export default function FarmFertilizerPage() { - const loaderData = useLoaderData() - - return -} - -export async function action({ request, params }: ActionFunctionArgs) { - try { - const b_id_farm = params.b_id_farm - const b_lu_catalogue = params.b_lu_catalogue - const p_id = params.p_id - - if (!b_id_farm) { - throw new Error("missing: b_id_farm") - } - - if (!b_lu_catalogue) { - throw new Error("missing: b_lu_catalogue") - } - - if (!p_id) { - throw new Error("missing: p_id") - } - - const calendar = getCalendar(params) - - const session = await getSession(request) - const formValues = await extractFormValuesFromRequest( - request, - FormSchema, - ) - - const p_id_catalogue = await addFertilizerToCatalogue( - fdm, - session.principal_id, - b_id_farm, - { - p_name_nl: formValues.p_name_nl, - p_name_en: formValues.p_name_en, - p_description: formValues.p_description, - p_type: null, - p_type_rvo: formValues.p_type_rvo, - p_dm: formValues.p_dm, - p_density: formValues.p_density, - p_om: formValues.p_om, - p_a: formValues.p_a, - p_hc: formValues.p_hc, - p_eom: formValues.p_eom, - p_eoc: formValues.p_eoc, - p_c_rt: formValues.p_c_rt, - p_c_of: formValues.p_c_of, - p_c_if: formValues.p_c_if, - p_c_fr: formValues.p_c_fr, - p_cn_of: formValues.p_cn_of, - p_n_rt: formValues.p_n_rt, - p_n_if: formValues.p_n_if, - p_n_of: formValues.p_n_of, - p_n_wc: formValues.p_n_wc, - p_no3_rt: formValues.p_no3_rt, - p_nh4_rt: formValues.p_nh4_rt, - p_p_rt: formValues.p_p_rt, - p_k_rt: formValues.p_k_rt, - p_mg_rt: formValues.p_mg_rt, - p_ca_rt: formValues.p_ca_rt, - p_ne: formValues.p_ne, - p_s_rt: formValues.p_s_rt, - p_s_wc: formValues.p_s_wc, - p_cu_rt: formValues.p_cu_rt, - p_zn_rt: formValues.p_zn_rt, - p_na_rt: formValues.p_na_rt, - p_si_rt: formValues.p_si_rt, - p_b_rt: formValues.p_b_rt, - p_mn_rt: formValues.p_mn_rt, - p_ni_rt: formValues.p_ni_rt, - p_fe_rt: formValues.p_fe_rt, - p_mo_rt: formValues.p_mo_rt, - p_co_rt: formValues.p_co_rt, - p_as_rt: formValues.p_as_rt, - p_cd_rt: formValues.p_cd_rt, - p_cr_rt: formValues.p_cr_rt, - p_cr_vi: formValues.p_cr_vi, - p_pb_rt: formValues.p_pb_rt, - p_hg_rt: formValues.p_hg_rt, - p_cl_rt: formValues.p_cl_rt, - p_ef_nh3: undefined, - p_app_method_options: formValues.p_app_method_options, - }, - ) - - const new_p_id = await addFertilizer( - fdm, - session.principal_id, - p_id_catalogue, - b_id_farm, - undefined, - undefined, - ) - - return redirectWithSuccess( - `/farm/create/${b_id_farm}/${calendar}/fertilizers/${b_lu_catalogue}?p_id=${new_p_id}`, - { - message: `${formValues.p_name_nl} is toegevoegd! 🎉`, - }, - ) - } catch (error) { - throw handleActionError(error) - } -} diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx deleted file mode 100644 index 08f08cc74..000000000 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new._index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { type Fertilizer, getFertilizers } from "@svenvw/fdm-core" -import type { LoaderFunctionArgs } from "react-router" -import { useLoaderData } from "react-router" -import { - BasedOffFertilizerButton, - CustomFertilizerButton, -} from "~/components/blocks/fertilizer/new-fertilizer" -import { getSession } from "~/lib/auth.server" -import { fdm } from "~/lib/fdm.server" - -export async function loader({ request, params }: LoaderFunctionArgs) { - const { b_id_farm } = params - if (!b_id_farm) { - throw new Error("Farm ID is required") - } - - const session = await getSession(request) - - const fertilizers = await getFertilizers( - fdm, - session.principal_id, - b_id_farm, - ) - - return { b_id_farm: b_id_farm, fertilizers: fertilizers } -} - -/** - * Renders the new fertilizer wizard start page - * - * This component includes a button that can be used to fill everything from scratch. - * Below that it includes a button for each existing fertilizer which the user can click to fill in the new fertilizer form values on the next page based on the corresponding fertilizer. - */ -export default function NewFertilizerIndexPage() { - const { fertilizers } = useLoaderData() - - return ( -
- -

Of baseer op een meststof

-
- {fertilizers.map((fertilizer: Fertilizer) => ( - - ))} -
-
- ) -} diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.custom.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.custom.tsx deleted file mode 100644 index e9e9d415d..000000000 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.custom.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import { - addFertilizer, - addFertilizerToCatalogue, - getFertilizerParametersDescription, - getFertilizers, -} from "@svenvw/fdm-core" -import { - type ActionFunctionArgs, - data, - type LoaderFunctionArgs, - type MetaFunction, - useLoaderData, -} from "react-router" -import { redirectWithSuccess } from "remix-toast" -import { FormSchema } from "~/components/blocks/fertilizer/formschema" -import { FarmNewCustomFertilizerBlock } from "~/components/blocks/fertilizer/new-custom-fertilizer-page" -import { getSession } from "~/lib/auth.server" -import { getCalendar } from "~/lib/calendar" -import { clientConfig } from "~/lib/config" -import { handleActionError, handleLoaderError } from "~/lib/error" -import { fdm } from "~/lib/fdm.server" -import { extractFormValuesFromRequest } from "~/lib/form" - -export const meta: MetaFunction = () => { - return [ - { title: `Meststof toevoegen | ${clientConfig.name}` }, - { - name: "description", - content: - "Voeg een meststof toe om deze te gebruiken op dit bedrijf.", - }, - ] -} - -export async function loader({ request, params }: LoaderFunctionArgs) { - try { - // Get the farm id - const b_id_farm = params.b_id_farm - if (!b_id_farm) { - throw data("invalid: b_id_farm", { - status: 400, - statusText: "invalid: b_id_farm", - }) - } - - // Get the session - const session = await getSession(request) - - // Get selected fertilizer - const fertilizerParameters = getFertilizerParametersDescription() - - const fertilizer = { - p_id: undefined, // Added p_id - p_source: b_id_farm, - p_name_nl: "", - p_type: undefined, - p_type_rvo: undefined, - p_dm: undefined, - p_density: undefined, - p_om: undefined, - p_a: undefined, - p_hc: undefined, - p_eom: undefined, - p_eoc: undefined, - p_c_rt: undefined, - p_c_of: undefined, - p_c_if: undefined, - p_c_fr: undefined, - p_cn_of: undefined, - p_n_rt: undefined, - p_n_if: undefined, - p_n_of: undefined, - p_n_wc: undefined, - p_no3_rt: undefined, - p_nh4_rt: undefined, - p_p_rt: undefined, - p_k_rt: undefined, - p_mg_rt: undefined, - p_ca_rt: undefined, - p_ne: undefined, - p_s_rt: undefined, - p_s_wc: undefined, - p_cu_rt: undefined, - p_zn_rt: undefined, - p_na_rt: undefined, - p_si_rt: undefined, - p_b_rt: undefined, - p_mn_rt: undefined, - p_ni_rt: undefined, - p_fe_rt: undefined, - p_mo_rt: undefined, - p_co_rt: undefined, - p_as_rt: undefined, - p_cd_rt: undefined, - p_cr_rt: undefined, - p_cr_vi: undefined, - p_pb_rt: undefined, - p_hg_rt: undefined, - p_cl_rt: undefined, - p_app_method_options: [], - } - - // Get the available fertilizers - const fertilizers = await getFertilizers( - fdm, - session.principal_id, - b_id_farm, - ) - const fertilizerOptions = fertilizers.map((fertilizer) => { - return { - p_id: fertilizer.p_id, - p_name_nl: fertilizer.p_name_nl, - } - }) - - // Return user information from loader - return { - fertilizerOptions: fertilizerOptions, - fertilizer: fertilizer, - fertilizerParameters: fertilizerParameters, - } - } catch (error) { - throw handleLoaderError(error) - } -} - -/** - * Renders the layout for managing farm settings. - * - * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields. - * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster. - */ -export default function FarmFertilizerPage() { - const loaderData = useLoaderData() - - return -} - -export async function action({ request, params }: ActionFunctionArgs) { - try { - const b_id_farm = params.b_id_farm - - if (!b_id_farm) { - throw new Error("missing: b_id_farm") - } - - const b_lu_catalogue = params.b_lu_catalogue - - if (!b_lu_catalogue) { - throw new Error("missing: b_lu_catalogue") - } - - const session = await getSession(request) - const calendar = getCalendar(params) - - const formValues = await extractFormValuesFromRequest( - request, - FormSchema, - ) - - const p_id_catalogue = await addFertilizerToCatalogue( - fdm, - session.principal_id, - b_id_farm, - { - p_name_nl: formValues.p_name_nl, - p_name_en: formValues.p_name_en, - p_description: formValues.p_description, - p_type: null, - p_type_rvo: formValues.p_type_rvo, - p_dm: formValues.p_dm, - p_density: formValues.p_density, - p_om: formValues.p_om, - p_a: formValues.p_a, - p_hc: formValues.p_hc, - p_eom: formValues.p_eom, - p_eoc: formValues.p_eoc, - p_c_rt: formValues.p_c_rt, - p_c_of: formValues.p_c_of, - p_c_if: formValues.p_c_if, - p_c_fr: formValues.p_c_fr, - p_cn_of: formValues.p_cn_of, - p_n_rt: formValues.p_n_rt, - p_n_if: formValues.p_n_if, - p_n_of: formValues.p_n_of, - p_n_wc: formValues.p_n_wc, - p_no3_rt: formValues.p_no3_rt, - p_nh4_rt: formValues.p_nh4_rt, - p_p_rt: formValues.p_p_rt, - p_k_rt: formValues.p_k_rt, - p_mg_rt: formValues.p_mg_rt, - p_ca_rt: formValues.p_ca_rt, - p_ne: formValues.p_ne, - p_s_rt: formValues.p_s_rt, - p_s_wc: formValues.p_s_wc, - p_cu_rt: formValues.p_cu_rt, - p_zn_rt: formValues.p_zn_rt, - p_na_rt: formValues.p_na_rt, - p_si_rt: formValues.p_si_rt, - p_b_rt: formValues.p_b_rt, - p_mn_rt: formValues.p_mn_rt, - p_ni_rt: formValues.p_ni_rt, - p_fe_rt: formValues.p_fe_rt, - p_mo_rt: formValues.p_mo_rt, - p_co_rt: formValues.p_co_rt, - p_as_rt: formValues.p_as_rt, - p_cd_rt: formValues.p_cd_rt, - p_cr_rt: formValues.p_cr_rt, - p_cr_vi: formValues.p_cr_vi, - p_pb_rt: formValues.p_pb_rt, - p_hg_rt: formValues.p_hg_rt, - p_cl_rt: formValues.p_cl_rt, - p_ef_nh3: undefined, - p_app_method_options: formValues.p_app_method_options, - }, - ) - - const new_p_id = await addFertilizer( - fdm, - session.principal_id, - p_id_catalogue, - b_id_farm, - undefined, - undefined, - ) - - return redirectWithSuccess( - `/farm/create/${b_id_farm}/${calendar}/fertilizers/${b_lu_catalogue}?p_id=${new_p_id}`, - { - message: `${formValues.p_name_nl} is toegevoegd! 🎉`, - }, - ) - } catch (error) { - throw handleActionError(error) - } -} diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.tsx deleted file mode 100644 index b79727516..000000000 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { getFarm } from "@svenvw/fdm-core" -import { - data, - type LoaderFunctionArgs, - type MetaFunction, - Outlet, - useLoaderData, -} from "react-router" -import { FarmTitle } from "~/components/blocks/farm/farm-title" -import { Header } from "~/components/blocks/header/base" -import { HeaderFarmCreate } from "~/components/blocks/header/create-farm" -import { SidebarInset } from "~/components/ui/sidebar" -import { getSession } from "~/lib/auth.server" -import { clientConfig } from "~/lib/config" -import { handleLoaderError } from "~/lib/error" -import { fdm } from "~/lib/fdm.server" -import type { Route } from "./+types/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.new" - -export const meta: MetaFunction = () => { - return [ - { title: `Meststof toevoegen | ${clientConfig.name}` }, - { - name: "description", - content: - "Voeg een meststof toe om deze te gebruiken op dit bedrijf.", - }, - ] -} - -export async function loader({ request, params }: LoaderFunctionArgs) { - try { - // Get the farm id - const b_id_farm = params.b_id_farm - if (!b_id_farm) { - throw data("invalid: b_id_farm", { - status: 400, - statusText: "invalid: b_id_farm", - }) - } - - // Get the session - const session = await getSession(request) - - // Get details of farm - const farm = await getFarm(fdm, session.principal_id, b_id_farm) - if (!farm) { - throw data("not found: b_id_farm", { - status: 404, - statusText: "not found: b_id_farm", - }) - } - - const b_name_farm = farm.b_name_farm - - // Return user information from loader - return { - b_name_farm: b_name_farm, - } - } catch (error) { - throw handleLoaderError(error) - } -} - -/** - * Renders the layout for managing farm settings. - * - * This component displays a sidebar that includes the farm header, navigation options, and a link to farm fields. - * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster. - */ -export default function FarmFertilizerBlock({ params }: Route.ComponentProps) { - const loaderData = useLoaderData() - - return ( - -
- -
-
- -
- -
-
-
- ) -} From 1feffff69680d7b63d781399d45fe1eaee422ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20Bora=20=C4=B0nevi?= Date: Tue, 11 Nov 2025 15:04:50 +0100 Subject: [PATCH 2/8] Add changeset --- .changeset/easy-boxes-sort.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/easy-boxes-sort.md diff --git a/.changeset/easy-boxes-sort.md b/.changeset/easy-boxes-sort.md new file mode 100644 index 000000000..a6fa672e3 --- /dev/null +++ b/.changeset/easy-boxes-sort.md @@ -0,0 +1,5 @@ +--- +"@svenvw/fdm-app": patch +--- + +The code for fertilizer management is made more maintainable by using only one route for all fertilizer application routes. From dafcac4120497879d4786df3533b8114855d42e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20Bora=20=C4=B0nevi?= Date: Tue, 11 Nov 2025 15:11:26 +0100 Subject: [PATCH 3/8] Gray out sidebar when coming to fertilizer management from the create wizard --- fdm-app/app/components/blocks/sidebar/apps.tsx | 4 +++- fdm-app/app/components/blocks/sidebar/farm.tsx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fdm-app/app/components/blocks/sidebar/apps.tsx b/fdm-app/app/components/blocks/sidebar/apps.tsx index 15058ec01..88460ec62 100644 --- a/fdm-app/app/components/blocks/sidebar/apps.tsx +++ b/fdm-app/app/components/blocks/sidebar/apps.tsx @@ -26,7 +26,9 @@ export function SidebarApps() { // Check if page contains `farm/create` in url const location = useLocation() - const isCreateFarmWizard = location.pathname.includes("farm/create") + const isCreateFarmWizard = + location.pathname.includes("farm/create") || + location.search.toLowerCase().includes("farm%2fcreate") let atlasLink: string | undefined if (isCreateFarmWizard) { diff --git a/fdm-app/app/components/blocks/sidebar/farm.tsx b/fdm-app/app/components/blocks/sidebar/farm.tsx index c3fce2dec..dc01c4e9b 100644 --- a/fdm-app/app/components/blocks/sidebar/farm.tsx +++ b/fdm-app/app/components/blocks/sidebar/farm.tsx @@ -39,7 +39,9 @@ export function SidebarFarm() { // Check if page contains `farm/create` in url const location = useLocation() - const isCreateFarmWizard = location.pathname.includes("farm/create") + const isCreateFarmWizard = + location.pathname.includes("farm/create") || + location.search.toLowerCase().includes("farm%2fcreate") // Set the farm link let farmLink: string From e4a08e54bc464545a2dc077a7bec5bdf2ec85f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20Bora=20=C4=B0nevi?= Date: Tue, 11 Nov 2025 16:32:06 +0100 Subject: [PATCH 4/8] Add url parsing utility and resolve some nitpicks --- .../fertilizer/new-fertilizer-page-header.tsx | 20 +++---- .../blocks/fertilizer/new-fertilizer.tsx | 5 +- .../app/components/blocks/sidebar/apps.tsx | 7 ++- .../app/components/blocks/sidebar/farm.tsx | 7 ++- fdm-app/app/lib/url-utils.ts | 55 +++++++++++++++++++ .../farm.$b_id_farm.fertilizers.new.$p_id.tsx | 18 +++--- ...farm.$b_id_farm.fertilizers.new.custom.tsx | 18 +++--- 7 files changed, 90 insertions(+), 40 deletions(-) create mode 100644 fdm-app/app/lib/url-utils.ts diff --git a/fdm-app/app/components/blocks/fertilizer/new-fertilizer-page-header.tsx b/fdm-app/app/components/blocks/fertilizer/new-fertilizer-page-header.tsx index 82acfc03b..80afe47cc 100644 --- a/fdm-app/app/components/blocks/fertilizer/new-fertilizer-page-header.tsx +++ b/fdm-app/app/components/blocks/fertilizer/new-fertilizer-page-header.tsx @@ -1,4 +1,5 @@ import { useLoaderData, useParams, useSearchParams } from "react-router" +import { getSearchParams } from "@/app/lib/url-utils" import { Header } from "~/components/blocks/header/base" import { HeaderFarmCreate } from "~/components/blocks/header/create-farm" import { HeaderFarm } from "~/components/blocks/header/farm" @@ -7,7 +8,7 @@ import { HeaderField } from "~/components/blocks/header/field" import { BreadcrumbLink, BreadcrumbSeparator } from "~/components/ui/breadcrumb" /** - * Returns the appropriate new fertilizer page header based on the `returnUrl`\ + * Returns the appropriate new fertilizer page header based on the `returnUrl` * search param. * * `b_id_farm` is assumed to be in the route params for the farm fertilizer @@ -24,17 +25,11 @@ export function NewFertilizerPageHeader() { const [searchParams] = useSearchParams() const params = useParams() const { b_name_farm, farmOptions, fieldOptions } = useLoaderData() - const returnUrl = searchParams.get("returnUrl") ?? "" - const parsedReturnUrl = returnUrl - ? new URL( - returnUrl.startsWith("/") - ? `http://localhost${returnUrl}` - : returnUrl, - ) - : null + const returnUrl = searchParams.get("returnUrl") || "" + const returnUrlSearchParams = getSearchParams(returnUrl) const singleFieldMatch = returnUrl.match( - /farm\/([^/]*)\/([^/]*)\/field\/([^/]*)\/fertilizer/, + /farm\/([^/]*)\/([^/]*)\/field\/([^/]*)\/fertilizer(?:\/|$|\?)/, ) if (singleFieldMatch) { const [_, b_id_farm, calendar, b_id] = singleFieldMatch @@ -55,6 +50,7 @@ export function NewFertilizerPageHeader() { fieldOptions={fieldOptions} b_id={b_id} /> + @@ -65,7 +61,7 @@ export function NewFertilizerPageHeader() { } const multipleFieldsMatch = returnUrl.match( - /farm\/([^/]*)\/([^/]*)\/field\/fertilizer/, + /farm\/([^/]*)\/([^/]*)\/field\/fertilizer(?:\/|$|\?)/, ) if (multipleFieldsMatch) { const [_, b_id_farm, calendar] = multipleFieldsMatch @@ -89,7 +85,7 @@ export function NewFertilizerPageHeader() { Nieuwe meststof diff --git a/fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx b/fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx index 855919f44..d4b1f6032 100644 --- a/fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx +++ b/fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx @@ -1,3 +1,4 @@ +import { modifySearchParams } from "@/app/lib/url-utils" import type { Fertilizer } from "@svenvw/fdm-core" import { Link, NavLink, useSearchParams } from "react-router" import { Card, CardContent } from "~/components/ui/card" @@ -8,7 +9,7 @@ export function CustomFertilizerButton() { state.farmId) const selectedCalendar = useCalendarStore((state) => state.calendar) - // Check if page contains `farm/create` in url + // Check if the page or its return page contains `farm/create` in url const location = useLocation() + const [searchParams] = useSearchParams() const isCreateFarmWizard = location.pathname.includes("farm/create") || - location.search.toLowerCase().includes("farm%2fcreate") + searchParams.get("returnUrl")?.includes("farm/create") let atlasLink: string | undefined if (isCreateFarmWizard) { diff --git a/fdm-app/app/components/blocks/sidebar/farm.tsx b/fdm-app/app/components/blocks/sidebar/farm.tsx index dc01c4e9b..d5ae03245 100644 --- a/fdm-app/app/components/blocks/sidebar/farm.tsx +++ b/fdm-app/app/components/blocks/sidebar/farm.tsx @@ -7,7 +7,7 @@ import { Square, } from "lucide-react" import { useState } from "react" -import { NavLink, useLocation } from "react-router" +import { NavLink, useLocation, useSearchParams } from "react-router" import { getCalendarSelection } from "@/app/lib/calendar" import { useCalendarStore } from "@/app/store/calendar" import { useFarmStore } from "@/app/store/farm" @@ -37,11 +37,12 @@ export function SidebarFarm() { const [isCalendarOpen, setIsCalendarOpen] = useState(false) const calendarSelection = getCalendarSelection() - // Check if page contains `farm/create` in url + // Check if the page or its return page contains `farm/create` in url const location = useLocation() + const [searchParams] = useSearchParams() const isCreateFarmWizard = location.pathname.includes("farm/create") || - location.search.toLowerCase().includes("farm%2fcreate") + searchParams.get("returnUrl")?.includes("farm/create") // Set the farm link let farmLink: string diff --git a/fdm-app/app/lib/url-utils.ts b/fdm-app/app/lib/url-utils.ts new file mode 100644 index 000000000..95a9495ac --- /dev/null +++ b/fdm-app/app/lib/url-utils.ts @@ -0,0 +1,55 @@ +/** + * Applies a function on the search params from a relative or absolute path, + * or a full URL, then returns the modified value. + * + * Note that it will treat input like `example.com/about` as + * `/example.com/about` since it looks for a http:// or https:// at the + * beginning to determine full URLs + * + * @param href relative or absolute path, or a full URL + * @param modifier function to be applied on the search params + * @returns a relative or absolute path, or a full URL, whichever format was + * provided + */ +export function modifySearchParams( + href: string, + modifier: (searchParams: URLSearchParams) => void, +): string { + const hasProtocol = + href.startsWith("http://") || href.startsWith("https://") + const hasSlash = href.startsWith("/") + const url = new URL( + hasProtocol + ? href + : hasSlash + ? `http://localhost${href}` + : `http://localhost/${href}`, + ) + modifier(url.searchParams) + const relativeToOrigin = `${url.pathname}${url.search}${url.hash}` + return hasProtocol + ? url.href + : hasSlash + ? relativeToOrigin + : relativeToOrigin.substring(1) +} + +/** + * Gets the search params from a relative or absolute path, or a full URL. + * + * Note that it will treat input like `example.com/about` as + * `/example.com/about` since it looks for a http:// or https:// at the + * beginning to determine full URLs + * + * @param href relative or absolute path, or a full URL + * @returns the search parameters, or empty search parameters if none was found + */ +export function getSearchParams(href: string) { + let searchParams: URLSearchParams | undefined + if (href.length > 0) { + modifySearchParams(href, (p) => { + searchParams = p + }) + } + return searchParams ?? new URLSearchParams() +} diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx index f49111bd3..3905a35c8 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx @@ -22,6 +22,7 @@ import { clientConfig } from "~/lib/config" import { handleActionError, handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" import { extractFormValuesFromRequest } from "~/lib/form" +import { modifySearchParams } from "~/lib/url-utils" export const meta: MetaFunction = () => { return [ @@ -215,17 +216,14 @@ export async function action({ request, params }: ActionFunctionArgs) { undefined, ) - const parsedReturnUrl = new URL( - returnUrl.startsWith("/") - ? `http://localhost${returnUrl}` - : returnUrl, + return redirectWithSuccess( + modifySearchParams(returnUrl, (searchParams) => + searchParams.set("p_id", p_new_id), + ), + { + message: `${formValues.p_name_nl} is toegevoegd! 🎉`, + }, ) - parsedReturnUrl.searchParams.set("p_id", p_new_id) - const modifiedReturnUrl = `${returnUrl.startsWith("/") ? "" : parsedReturnUrl.origin}${parsedReturnUrl.pathname}${parsedReturnUrl.search}${parsedReturnUrl.hash}` - - return redirectWithSuccess(modifiedReturnUrl, { - message: `${formValues.p_name_nl} is toegevoegd! 🎉`, - }) } catch (error) { throw handleActionError(error) } diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx index 50b0c69ee..4f6b2d768 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx @@ -19,6 +19,7 @@ import { clientConfig } from "~/lib/config" import { handleActionError, handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" import { extractFormValuesFromRequest } from "~/lib/form" +import { modifySearchParams } from "~/lib/url-utils" export const meta: MetaFunction = () => { return [ @@ -219,17 +220,14 @@ export async function action({ request, params }: ActionFunctionArgs) { undefined, ) - const parsedReturnUrl = new URL( - returnUrl.startsWith("/") - ? `http://localhost${returnUrl}` - : returnUrl, + return redirectWithSuccess( + modifySearchParams(returnUrl, (searchParams) => + searchParams.set("p_id", p_id), + ), + { + message: `${formValues.p_name_nl} is toegevoegd! 🎉`, + }, ) - parsedReturnUrl.searchParams.set("p_id", p_id) - const modifiedReturnUrl = `${returnUrl.startsWith("/") ? "" : parsedReturnUrl.origin}${parsedReturnUrl.pathname}${parsedReturnUrl.search}${parsedReturnUrl.hash}` - - return redirectWithSuccess(modifiedReturnUrl, { - message: `${formValues.p_name_nl} is toegevoegd! 🎉`, - }) } catch (error) { throw handleActionError(error) } From d209ed644828708b51b7a3bd00df667f0bccd1d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20Bora=20=C4=B0nevi?= Date: Tue, 11 Nov 2025 17:12:09 +0100 Subject: [PATCH 5/8] Resolve open redirect vulnerability --- fdm-app/app/lib/url-utils.ts | 16 ++++++++++++++++ .../farm.$b_id_farm.fertilizers.new.$p_id.tsx | 10 ++++++---- .../farm.$b_id_farm.fertilizers.new.custom.tsx | 12 +++++++----- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/fdm-app/app/lib/url-utils.ts b/fdm-app/app/lib/url-utils.ts index 95a9495ac..150dc036b 100644 --- a/fdm-app/app/lib/url-utils.ts +++ b/fdm-app/app/lib/url-utils.ts @@ -53,3 +53,19 @@ export function getSearchParams(href: string) { } return searchParams ?? new URLSearchParams() } + +/** + * Checks if the given URL-like might be a full URL, and if so, if it is of the + * origin given. No checks are performed if no protocol in the URL is detected. + * + * @param href URL-like + * @param origin origin, like `example.com` to check if full URL is detected + * @returns the validation result for full URLs, true if no full URL is found + */ +export function isOfOrigin(href: string, origin: string) { + try { + return !href.includes("://") || new URL(href).origin === origin + } catch { + return false + } +} diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx index 3905a35c8..357947a61 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx @@ -22,7 +22,7 @@ import { clientConfig } from "~/lib/config" import { handleActionError, handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" import { extractFormValuesFromRequest } from "~/lib/form" -import { modifySearchParams } from "~/lib/url-utils" +import { isOfOrigin, modifySearchParams } from "~/lib/url-utils" export const meta: MetaFunction = () => { return [ @@ -217,9 +217,11 @@ export async function action({ request, params }: ActionFunctionArgs) { ) return redirectWithSuccess( - modifySearchParams(returnUrl, (searchParams) => - searchParams.set("p_id", p_new_id), - ), + isOfOrigin(returnUrl, requestUrl.origin) + ? modifySearchParams(returnUrl, (searchParams) => + searchParams.set("p_id", p_new_id), + ) + : `/farm/${b_id_farm}/fertilizers`, { message: `${formValues.p_name_nl} is toegevoegd! 🎉`, }, diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx index 4f6b2d768..b00183724 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx @@ -19,7 +19,7 @@ import { clientConfig } from "~/lib/config" import { handleActionError, handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" import { extractFormValuesFromRequest } from "~/lib/form" -import { modifySearchParams } from "~/lib/url-utils" +import { isOfOrigin, modifySearchParams } from "~/lib/url-utils" export const meta: MetaFunction = () => { return [ @@ -211,7 +211,7 @@ export async function action({ request, params }: ActionFunctionArgs) { }, ) - const p_id = await addFertilizer( + const p_new_id = await addFertilizer( fdm, session.principal_id, p_id_catalogue, @@ -221,9 +221,11 @@ export async function action({ request, params }: ActionFunctionArgs) { ) return redirectWithSuccess( - modifySearchParams(returnUrl, (searchParams) => - searchParams.set("p_id", p_id), - ), + isOfOrigin(returnUrl, requestUrl.origin) + ? modifySearchParams(returnUrl, (searchParams) => + searchParams.set("p_id", p_new_id), + ) + : `/farm/${b_id_farm}/fertilizers`, { message: `${formValues.p_name_nl} is toegevoegd! 🎉`, }, From a62f18bfaa80ad6d07a31ed4951705d364a2b7a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20Bora=20=C4=B0nevi?= Date: Tue, 11 Nov 2025 17:20:51 +0100 Subject: [PATCH 6/8] Redirect in new fertilizer route in case of cross origin return url --- .../farm.$b_id_farm.fertilizers.new.tsx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx index b35c861d2..9261ca59b 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx @@ -13,7 +13,9 @@ import { getTimeframe } from "~/lib/calendar" import { clientConfig } from "~/lib/config" import { handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" +import { isOfOrigin } from "~/lib/url-utils" import type { Route } from "./+types/farm.$b_id_farm.fertilizers.new" +import { redirectWithError } from "remix-toast" export const meta: MetaFunction = () => { return [ @@ -37,6 +39,18 @@ export async function loader({ request, params }: LoaderFunctionArgs) { }) } + const requestUrl = new URL(request.url) + const returnUrl = + requestUrl.searchParams.get("returnUrl") ?? + `/farm/${b_id_farm}/fertilizers` + + if (!isOfOrigin(returnUrl, requestUrl.origin)) { + return redirectWithError( + `/farm/${b_id_farm}/fertilizers`, + `Return URL ${returnUrl} is niet ondersteund.`, + ) + } + // Get the session const session = await getSession(request) @@ -66,11 +80,6 @@ export async function loader({ request, params }: LoaderFunctionArgs) { }) // Try to load more data if the requestUrl is found - const requestUrl = new URL(request.url) - const returnUrl = - requestUrl.searchParams.get("returnUrl") ?? - `/farm/${b_id_farm}/fertilizers` - let fieldOptions: { b_id: string b_name: string From b193ebcf9d5bf6f952b6ff05bb165dc5512fd9dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mert=20Bora=20=C4=B0nevi?= Date: Tue, 11 Nov 2025 17:58:24 +0100 Subject: [PATCH 7/8] Use same header content for all fertilizer application pages --- .../fertilizer/new-fertilizer-page-header.tsx | 128 ------------------ .../farm.$b_id_farm.fertilizers.new.tsx | 45 +++++- 2 files changed, 42 insertions(+), 131 deletions(-) delete mode 100644 fdm-app/app/components/blocks/fertilizer/new-fertilizer-page-header.tsx diff --git a/fdm-app/app/components/blocks/fertilizer/new-fertilizer-page-header.tsx b/fdm-app/app/components/blocks/fertilizer/new-fertilizer-page-header.tsx deleted file mode 100644 index 80afe47cc..000000000 --- a/fdm-app/app/components/blocks/fertilizer/new-fertilizer-page-header.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { useLoaderData, useParams, useSearchParams } from "react-router" -import { getSearchParams } from "@/app/lib/url-utils" -import { Header } from "~/components/blocks/header/base" -import { HeaderFarmCreate } from "~/components/blocks/header/create-farm" -import { HeaderFarm } from "~/components/blocks/header/farm" -import { HeaderFertilizer } from "~/components/blocks/header/fertilizer" -import { HeaderField } from "~/components/blocks/header/field" -import { BreadcrumbLink, BreadcrumbSeparator } from "~/components/ui/breadcrumb" - -/** - * Returns the appropriate new fertilizer page header based on the `returnUrl` - * search param. - * - * `b_id_farm` is assumed to be in the route params for the farm fertilizer - * route. - * - * `b_name_farm`, `farmOptions`, and `fieldOptions` are assumed to exist in the - * loader data when needed for the route. - * - * If `returnUrl` is present as a search param, the application will return here - * when the fertilizer creation is complete or the user clicks the button in - * this component. - */ -export function NewFertilizerPageHeader() { - const [searchParams] = useSearchParams() - const params = useParams() - const { b_name_farm, farmOptions, fieldOptions } = useLoaderData() - const returnUrl = searchParams.get("returnUrl") || "" - const returnUrlSearchParams = getSearchParams(returnUrl) - - const singleFieldMatch = returnUrl.match( - /farm\/([^/]*)\/([^/]*)\/field\/([^/]*)\/fertilizer(?:\/|$|\?)/, - ) - if (singleFieldMatch) { - const [_, b_id_farm, calendar, b_id] = singleFieldMatch - return ( -
- - - - - Nieuwe meststof - -
- ) - } - - const multipleFieldsMatch = returnUrl.match( - /farm\/([^/]*)\/([^/]*)\/field\/fertilizer(?:\/|$|\?)/, - ) - if (multipleFieldsMatch) { - const [_, b_id_farm, calendar] = multipleFieldsMatch - return ( -
- - - Percelen - - - Bemesting toevoegen - - - - Nieuwe meststof - -
- ) - } - - const createMatch = /farm\/create/.test(returnUrl) - if (createMatch) { - return ( -
- -
- ) - } - - const { b_id_farm } = params - return ( -
- - -
- ) -} diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx index 9261ca59b..0e317d3df 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx @@ -4,9 +4,14 @@ import { type LoaderFunctionArgs, type MetaFunction, Outlet, + useLoaderData, + useSearchParams, } from "react-router" -import { NewFertilizerPageHeader } from "@/app/components/blocks/fertilizer/new-fertilizer-page-header" +import { redirectWithError } from "remix-toast" import { FarmTitle } from "~/components/blocks/farm/farm-title" +import { Header } from "~/components/blocks/header/base" +import { HeaderFarm } from "~/components/blocks/header/farm" +import { HeaderFertilizer } from "~/components/blocks/header/fertilizer" import { SidebarInset } from "~/components/ui/sidebar" import { getSession } from "~/lib/auth.server" import { getTimeframe } from "~/lib/calendar" @@ -15,7 +20,6 @@ import { handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" import { isOfOrigin } from "~/lib/url-utils" import type { Route } from "./+types/farm.$b_id_farm.fertilizers.new" -import { redirectWithError } from "remix-toast" export const meta: MetaFunction = () => { return [ @@ -127,9 +131,44 @@ export async function loader({ request, params }: LoaderFunctionArgs) { * It also renders a main section containing the farm title, description, nested routes via an Outlet, and a notification toaster. */ export default function FarmFertilizerBlock({ params }: Route.ComponentProps) { + const [searchParams] = useSearchParams() + const loaderData = useLoaderData() + + const returnUrl = searchParams.get("returnUrl") + const fieldsMatch = + returnUrl && + /farm\/[^/]+\/?[^/]+\/field\/[^/]+\/fertilizer(?:\/|$|\?)/.test( + returnUrl, + ) + const createMatch = returnUrl && /farm\/create/.test(returnUrl) + return ( - +
+ + +
Date: Wed, 12 Nov 2025 14:22:15 +0100 Subject: [PATCH 8/8] Remove loader code that was added to accomodate the varying header --- .../farm.$b_id_farm.fertilizers.new.tsx | 43 +++---------------- 1 file changed, 6 insertions(+), 37 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx index 0e317d3df..fd0d18090 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.tsx @@ -1,4 +1,4 @@ -import { getFarm, getFarms, getFields } from "@svenvw/fdm-core" +import { getFarm, getFarms } from "@svenvw/fdm-core" import { data, type LoaderFunctionArgs, @@ -14,7 +14,6 @@ import { HeaderFarm } from "~/components/blocks/header/farm" import { HeaderFertilizer } from "~/components/blocks/header/fertilizer" import { SidebarInset } from "~/components/ui/sidebar" import { getSession } from "~/lib/auth.server" -import { getTimeframe } from "~/lib/calendar" import { clientConfig } from "~/lib/config" import { handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" @@ -83,41 +82,11 @@ export async function loader({ request, params }: LoaderFunctionArgs) { } }) - // Try to load more data if the requestUrl is found - let fieldOptions: { - b_id: string - b_name: string - b_area: number - }[] = [] - if (/farm\/[^/]*\/[^/]*\/field\/[^/]*\/fertilizer/.test(returnUrl)) { - const timeframe = getTimeframe(params) - const fields = await getFields( - fdm, - session.principal_id, - b_id_farm, - timeframe, - ) - fieldOptions = fields.map( - (field: Awaited>[number]) => { - if (!field?.b_id || !field?.b_name) { - throw new Error("Invalid field data structure") - } - return { - b_id: field.b_id, - b_name: field.b_name, - b_area: Math.round(field.b_area * 10) / 10, - } - }, - ) - } - // Return user information from loader return { farm: farm, b_id_farm: b_id_farm, - b_name_farm: farm.b_name_farm, farmOptions: farmOptions, - fieldOptions: fieldOptions, } } catch (error) { throw handleLoaderError(error) @@ -132,12 +101,12 @@ export async function loader({ request, params }: LoaderFunctionArgs) { */ export default function FarmFertilizerBlock({ params }: Route.ComponentProps) { const [searchParams] = useSearchParams() - const loaderData = useLoaderData() + const loaderData = useLoaderData() const returnUrl = searchParams.get("returnUrl") const fieldsMatch = returnUrl && - /farm\/[^/]+\/?[^/]+\/field\/[^/]+\/fertilizer(?:\/|$|\?)/.test( + /farm\/[^/]+\/[^/]+\/field(?:\/[^/]+)?\/fertilizer(?:\/|$|\?)/.test( returnUrl, ) const createMatch = returnUrl && /farm\/create/.test(returnUrl) @@ -153,18 +122,18 @@ export default function FarmFertilizerBlock({ params }: Route.ComponentProps) { disabled: false, } : { - to: `/farm/${params.b_id_farm}/fertilizers`, + to: `/farm/${loaderData.b_id_farm}/fertilizers`, label: "Terug naar overzicht", disabled: false, } } >