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. 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.tsx b/fdm-app/app/components/blocks/fertilizer/new-fertilizer.tsx index 64523ee4b..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" @@ -7,8 +8,8 @@ export function CustomFertilizerButton() { return ( Bouwplan - {currentPath.match(/manage/) ? ( + {currentPath.match(/new/) ? ( <> - + Bemesting diff --git a/fdm-app/app/components/blocks/sidebar/apps.tsx b/fdm-app/app/components/blocks/sidebar/apps.tsx index 15058ec01..6dc1ab817 100644 --- a/fdm-app/app/components/blocks/sidebar/apps.tsx +++ b/fdm-app/app/components/blocks/sidebar/apps.tsx @@ -6,7 +6,7 @@ import { MapIcon, Scale, } from "lucide-react" -import { NavLink, useLocation } from "react-router" +import { NavLink, useLocation, useSearchParams } from "react-router" import { useCalendarStore } from "@/app/store/calendar" import { useFarmStore } from "@/app/store/farm" import { Badge } from "~/components/ui/badge" @@ -24,9 +24,12 @@ export function SidebarApps() { const farmId = useFarmStore((state) => 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 isCreateFarmWizard = location.pathname.includes("farm/create") + const [searchParams] = useSearchParams() + const isCreateFarmWizard = + location.pathname.includes("farm/create") || + 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 c3fce2dec..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,9 +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 isCreateFarmWizard = location.pathname.includes("farm/create") + const [searchParams] = useSearchParams() + const isCreateFarmWizard = + location.pathname.includes("farm/create") || + 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..150dc036b --- /dev/null +++ b/fdm-app/app/lib/url-utils.ts @@ -0,0 +1,71 @@ +/** + * 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() +} + +/** + * 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.$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..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,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 { isOfOrigin, modifySearchParams } from "~/lib/url-utils" export const meta: MetaFunction = () => { return [ @@ -138,6 +139,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 +207,7 @@ export async function action({ request, params }: ActionFunctionArgs) { }, ) - await addFertilizer( + const p_new_id = await addFertilizer( fdm, session.principal_id, p_id_catalogue, @@ -210,9 +216,16 @@ export async function action({ request, params }: ActionFunctionArgs) { undefined, ) - return redirectWithSuccess(`/farm/${b_id_farm}/fertilizers`, { - message: `${formValues.p_name_nl} is toegevoegd! 🎉`, - }) + return redirectWithSuccess( + 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! 🎉`, + }, + ) } 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 268588b65..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,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 { isOfOrigin, modifySearchParams } from "~/lib/url-utils" export const meta: MetaFunction = () => { return [ @@ -142,6 +143,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 +211,7 @@ export async function action({ request, params }: ActionFunctionArgs) { }, ) - await addFertilizer( + const p_new_id = await addFertilizer( fdm, session.principal_id, p_id_catalogue, @@ -214,9 +220,16 @@ export async function action({ request, params }: ActionFunctionArgs) { undefined, ) - return redirectWithSuccess(`/farm/${b_id_farm}/fertilizers`, { - message: `${formValues.p_name_nl} is toegevoegd! 🎉`, - }) + return redirectWithSuccess( + 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! 🎉`, + }, + ) } catch (error) { throw handleActionError(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..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 @@ -5,7 +5,9 @@ import { type MetaFunction, Outlet, useLoaderData, + useSearchParams, } from "react-router" +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" @@ -15,6 +17,7 @@ import { getSession } from "~/lib/auth.server" 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" export const meta: MetaFunction = () => { @@ -39,6 +42,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) @@ -85,16 +100,33 @@ 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 (
{ - 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 ( - -
- -
-
- -
- -
-
-
- ) -}