Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tiny-words-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@svenvw/fdm-app": minor
---

Improve the design of the error block to fit better in the page when shown
196 changes: 196 additions & 0 deletions fdm-app/app/components/blocks/header/automatic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import type { ReactNode } from "react"
import { type UIMatch, useLocation, useMatches, useParams } from "react-router"
import { useCalendarStore } from "@/app/store/calendar"
import { useFarmFieldOptionsStore } from "@/app/store/farm-field-options"
import type { FertilizerOption } from "../farm/farm"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix fertilizer options typing/import and align with HeaderFertilizer.

  • The import FertilizerOption path looks suspicious and the local fertilizerOptions is typed as unknown[], but HeaderFertilizer expects a concrete option shape. This will either break TS or mask bugs.

Apply this diff to type things correctly (assuming HeaderFertilizerOption is exported from ./fertilizer; if not, export it there or declare a local type with p_id/p_name_nl):

-import type { FertilizerOption } from "../farm/farm"
+import type { HeaderFertilizerOption } from "./fertilizer"
@@
-    let fertilizerOptions: unknown[] | undefined
+    let fertilizerOptions: HeaderFertilizerOption[] | undefined
@@
-                    fertilizerOptions={
+                    fertilizerOptions={
                         /\/new(\/|$)/.test(location.pathname)
                             ? []
                             : fertilizerOptions
                     }

If HeaderFertilizerOption is not exported yet, add this to fdm-app/app/components/blocks/header/fertilizer.tsx:

+export type HeaderFertilizerOption = {
+  p_id: string
+  p_name_nl: string | undefined | null
+}

Also applies to: 32-33, 114-122

🤖 Prompt for AI Agents
In fdm-app/app/components/blocks/header/automatic.tsx around lines 5 (and
likewise address lines 32-33 and 114-122), the import and typing for fertilizer
options are incorrect: replace the loose/unknown[] typing and the suspicious
import path with the concrete HeaderFertilizerOption type (imported from
./fertilizer); if HeaderFertilizerOption is not exported from
fdm-app/app/components/blocks/header/fertilizer.tsx, export it there (or declare
a local type with properties p_id and p_name_nl) and then update the import at
the top of automatic.tsx to import { HeaderFertilizerOption } from
"./fertilizer" and change fertilizerOptions' type from unknown[] to
HeaderFertilizerOption[] and ensure all usages match that shape so
HeaderFertilizer receives the correct typed options.

import { HeaderAtlas } from "./atlas"
import { HeaderBalance } from "./balance"
import { Header } from "./base"
import { HeaderFarmCreate } from "./create-farm"
import { HeaderFarm, type HeaderFarmOption } from "./farm"
import { HeaderFertilizer } from "./fertilizer"
import { HeaderField, type HeaderFieldOption } from "./field"
import { HeaderNorms } from "./norms"
import { HeaderNutrientAdvice } from "./nutrient-advice"

export default function HeaderAutomatic() {
interface LoaderDataCandidate {
b_name_farm?: string
farmOptions?: HeaderFarmOption[]
fieldOptions?: HeaderFieldOption[]
fertilizerOptions?: FertilizerOption[]
}
const matches = useMatches() as UIMatch<LoaderDataCandidate, unknown>[]
const params = useParams()
const location = useLocation()
const farmFieldOptionsStore = useFarmFieldOptionsStore()
const storedCalendar = useCalendarStore((s) => s.calendar)

// Find the farm and field options.
let farmOptions: HeaderFarmOption[] | undefined
let fieldOptions: HeaderFieldOption[] | undefined
let fertilizerOptions: unknown[] | undefined
let b_name_farm: string | undefined

for (const match of matches) {
if (match.loaderData) {
b_name_farm ??= match.loaderData.b_name_farm
farmOptions ??= match.loaderData.farmOptions
fieldOptions ??= match.loaderData.fieldOptions
fertilizerOptions ??= match.loaderData.fertilizerOptions
}
}

farmOptions ??= farmFieldOptionsStore.farmOptions
fieldOptions ??= farmFieldOptionsStore.fieldOptions
b_name_farm ??=
farmFieldOptionsStore.getFarmById(params.b_id_farm)?.b_name_farm ??
undefined

const calendar = params.calendar ?? storedCalendar

if (/\/create\//.test(location.pathname)) {
return (
<Header action={undefined}>
<HeaderFarmCreate b_name_farm={b_name_farm} />
</Header>
)
}

const variants: Record<string, () => ReactNode> = {
"routes/farm._index": () => (
<Header action={undefined}>
<HeaderFarm
b_id_farm={params.b_id_farm}
farmOptions={farmOptions}
/>
</Header>
),
"routes/farm.$b_id_farm.settings": () => (
<Header
action={{
to: `/farm/${params.b_id_farm}/${calendar}/field`,
label: "Naar percelen",
disabled: false,
}}
>
<HeaderFarm
b_id_farm={params.b_id_farm}
farmOptions={farmOptions}
/>
</Header>
),
"routes/farm.$b_id_farm.$calendar.field.new": () =>
variants["routes/farm.$b_id_farm.$calendar.field._index"](),
"routes/farm.$b_id_farm.$calendar.field.$b_id": () =>
variants["routes/farm.$b_id_farm.$calendar.field._index"](),
"routes/farm.$b_id_farm.$calendar.field._index": () => (
<Header
action={{
to: `/farm/${params.b_id_farm}`,
label: "Terug naar bedrijf",
disabled: false,
}}
>
<HeaderFarm
b_id_farm={params.b_id_farm}
farmOptions={farmOptions}
/>
<HeaderField
b_id_farm={params.b_id_farm}
fieldOptions={
/new\/?$/.test(location.pathname) ? [] : fieldOptions
}
b_id={params.b_id}
/>
</Header>
),
"routes/farm.$b_id_farm.fertilizers": () => (
<Header action={undefined}>
<HeaderFarm
b_id_farm={params.b_id_farm}
farmOptions={farmOptions}
/>
<HeaderFertilizer
b_id_farm={params.b_id_farm}
p_id={params.p_id}
fertilizerOptions={
/\/new(\/|$)/.test(location.pathname)
? []
: fertilizerOptions
}
/>
</Header>
),
"routes/farm.$b_id_farm.$calendar.atlas": () => {
const isFieldDetailsPage =
location.pathname.includes("/atlas/fields/") &&
location.pathname.split("/atlas/fields/")[1]?.includes(",")
let headerAction:
| { to: string; label: string; disabled: boolean }
| undefined
if (isFieldDetailsPage) {
headerAction = {
to: `/farm/${params.b_id_farm}/${calendar}/atlas/fields`,
label: "Terug",
disabled: false,
}
}
return (
<Header action={headerAction}>
<HeaderFarm
b_id_farm={params.b_id_farm}
farmOptions={farmOptions}
/>
<HeaderAtlas b_id_farm={params.b_id_farm} />
</Header>
)
},
"routes/farm.$b_id_farm.$calendar.balance.nitrogen": () => (
<Header action={undefined}>
<HeaderFarm
b_id_farm={params.b_id_farm}
farmOptions={farmOptions}
/>
<HeaderBalance
b_id_farm={params.b_id_farm}
b_id={params.b_id}
fieldOptions={fieldOptions}
/>
</Header>
),
"routes/farm.$b_id_farm.$calendar.nutrient_advice": () => (
<Header action={undefined}>
<HeaderFarm
b_id_farm={params.b_id_farm}
farmOptions={farmOptions}
/>
<HeaderNutrientAdvice
b_id_farm={params.b_id_farm}
b_id={params.b_id}
fieldOptions={fieldOptions}
/>
</Header>
),
"routes/farm.$b_id_farm.$calendar.norms": () => (
<Header action={undefined}>
<HeaderFarm
b_id_farm={params.b_id_farm}
farmOptions={farmOptions}
/>
<HeaderNorms b_id_farm={params.b_id_farm} />
</Header>
),
}

let chosenVariant: (() => ReactNode) | undefined

for (const match of matches) {
if (match.id in variants) {
chosenVariant = variants[match.id]
break
}
}

return chosenVariant ? chosenVariant() : null
}
14 changes: 13 additions & 1 deletion fdm-app/app/components/blocks/header/create-farm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { useLocation } from "react-router"
import { useEffect } from "react"
import { useLocation, useParams } from "react-router"
import { useFarmFieldOptionsStore } from "@/app/store/farm-field-options"
import {
BreadcrumbItem,
BreadcrumbLink,
Expand All @@ -11,6 +13,16 @@ export function HeaderFarmCreate({
b_name_farm: string | undefined | null
}) {
const location = useLocation()
const params = useParams()
const addFarmOptionToTheStore = useFarmFieldOptionsStore(
(s) => s.addFarmOption,
)
useEffect(() => {
if (params.b_id_farm && b_name_farm) {
addFarmOptionToTheStore(params.b_id_farm, b_name_farm)
}
}, [params.b_id_farm, b_name_farm, addFarmOptionToTheStore])

const currentPath = String(location.pathname)

return (
Expand Down
4 changes: 3 additions & 1 deletion fdm-app/app/components/blocks/header/farm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ChevronDown } from "lucide-react"
import { NavLink, useLocation } from "react-router"
import { useFarmFieldOptionsStore } from "@/app/store/farm-field-options"
import {
BreadcrumbItem,
BreadcrumbLink,
Expand All @@ -20,6 +21,7 @@ export function HeaderFarm({
farmOptions: HeaderFarmOption[]
}) {
const location = useLocation()

const currentPath = String(location.pathname)

return (
Expand Down Expand Up @@ -75,7 +77,7 @@ export function HeaderFarm({
)
}

type HeaderFarmOption = {
export type HeaderFarmOption = {
b_id_farm: string
b_name_farm: string | undefined | null
}
40 changes: 27 additions & 13 deletions fdm-app/app/components/blocks/header/fertilizer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ChevronDown } from "lucide-react"
import { NavLink, useLocation } from "react-router"
import { create } from "zustand"
import {
BreadcrumbItem,
BreadcrumbLink,
Expand All @@ -12,27 +13,38 @@ import {
DropdownMenuTrigger,
} from "~/components/ui/dropdown-menu"

const useFertilizerOptionsStore = create<{
fertilizerOptions: HeaderFertilizerOption[] | undefined
setFertilizerOptions(fertilizerOptions: HeaderFertilizerOption[]): void
}>((set) => ({
fertilizerOptions: undefined,
setFertilizerOptions(fertilizerOptions) {
set({ fertilizerOptions })
},
}))

export function HeaderFertilizer({
b_id_farm,
p_id,
fertilizerOptions,
}: {
b_id_farm: string
p_id: string | undefined
fertilizerOptions: HeaderFertilizerOption[]
fertilizerOptions: HeaderFertilizerOption[] | undefined
}) {
const location = useLocation()

const currentPath = String(location.pathname)

return (
<>
<BreadcrumbSeparator />
<BreadcrumbItem className="hidden md:block">
<BreadcrumbLink href={`/farm/${b_id_farm}/fertilizer`}>
<BreadcrumbLink href={`/farm/${b_id_farm}/fertilizers`}>
Meststof
</BreadcrumbLink>
</BreadcrumbItem>
{fertilizerOptions.length > 0 ? (
{fertilizerOptions && fertilizerOptions.length > 0 ? (
<>
<BreadcrumbSeparator />
<BreadcrumbItem>
Expand Down Expand Up @@ -70,16 +82,18 @@ export function HeaderFertilizer({
</BreadcrumbItem>
</>
) : (
<>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink
href={`/farm/${b_id_farm}/fertilizers/new`}
>
Nieuwe meststof
</BreadcrumbLink>
</BreadcrumbItem>
</>
fertilizerOptions && (
<>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink
href={`/farm/${b_id_farm}/fertilizers/new`}
>
Nieuwe meststof
</BreadcrumbLink>
</BreadcrumbItem>
</>
)
)}
</>
)
Expand Down
4 changes: 3 additions & 1 deletion fdm-app/app/components/blocks/header/field.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ChevronDown } from "lucide-react"
import { useEffect } from "react"
import { NavLink, useLocation } from "react-router"
import { useCalendarStore } from "@/app/store/calendar"
import { useFarmFieldOptionsStore } from "@/app/store/farm-field-options"
import {
BreadcrumbItem,
BreadcrumbLink,
Expand Down Expand Up @@ -91,7 +93,7 @@ export function HeaderField({
)
}

type HeaderFieldOption = {
export type HeaderFieldOption = {
b_id: string
b_name: string | undefined | null
}
Loading