diff --git a/client/src/containers/projects/form/cell-value.tsx b/client/src/containers/projects/form/cell-value.tsx index c4f35194..92407b5e 100644 --- a/client/src/containers/projects/form/cell-value.tsx +++ b/client/src/containers/projects/form/cell-value.tsx @@ -1,5 +1,9 @@ +import { ComponentProps } from "react"; + import { useFormContext } from "react-hook-form"; +import { cn } from "@/lib/utils"; + import { CreateCustomProjectForm } from "@/containers/projects/form/setup"; import { @@ -12,8 +16,12 @@ import { Input } from "@/components/ui/input"; export default function CellValue({ name, + className, + hasUnit = false, }: { name: keyof CreateCustomProjectForm; + className?: ComponentProps["className"]; + hasUnit?: boolean; }) { const form = useFormContext(); @@ -26,7 +34,7 @@ export default function CellValue({ const { value, ...restField } = field; return ( - + diff --git a/client/src/containers/projects/form/index.tsx b/client/src/containers/projects/form/index.tsx index dfb01452..ecbd8862 100644 --- a/client/src/containers/projects/form/index.tsx +++ b/client/src/containers/projects/form/index.tsx @@ -1,10 +1,20 @@ +import { useFormContext } from "react-hook-form"; + +import { ACTIVITY } from "@shared/entities/activity.enum"; + import AssumptionsProjectForm from "@/containers/projects/form/assumptions"; import CostInputsOverridesProjectForm from "@/containers/projects/form/cost-inputs-overrides"; -import SetupProjectForm from "@/containers/projects/form/setup"; +import RestorationPlanProjectForm from "@/containers/projects/form/restoration-plan"; +import SetupProjectForm, { + CreateCustomProjectForm, +} from "@/containers/projects/form/setup"; import { Card } from "@/components/ui/card"; export default function ProjectForm({ onSubmit }: { onSubmit: () => void }) { + const form = useFormContext(); + const { activity } = form.getValues(); + return (
@@ -17,6 +27,11 @@ export default function ProjectForm({ onSubmit }: { onSubmit: () => void }) { + {activity === ACTIVITY.RESTORATION && ( + + + + )}
); diff --git a/client/src/containers/projects/form/restoration-plan/columns.tsx b/client/src/containers/projects/form/restoration-plan/columns.tsx new file mode 100644 index 00000000..357a34db --- /dev/null +++ b/client/src/containers/projects/form/restoration-plan/columns.tsx @@ -0,0 +1,40 @@ +import { createColumnHelper } from "@tanstack/react-table"; + +import CellValue from "@/containers/projects/form/cell-value"; +import { DataColumnDef } from "@/containers/projects/form/cost-inputs-overrides/constants"; +import { CreateCustomProjectForm } from "@/containers/projects/form/setup"; + +import { Label } from "@/components/ui/label"; + +export type RestorationPlanFormProperty = { + year: number; + annualHectaresRestored: number; +}; + +const columnHelper = createColumnHelper(); + +export const COLUMNS = [ + columnHelper.accessor("year", { + header: () => Year, + cell: (props) => { + return ; + }, + }), + columnHelper.accessor("annualHectaresRestored", { + header: () => ( + Annual hectares restored / year + ), + cell: (props) => ( + + ), + // size: 75, + maxSize: 55, + // cell: (props) => { + // return ; + // }, + }), +]; diff --git a/client/src/containers/projects/form/restoration-plan/index.tsx b/client/src/containers/projects/form/restoration-plan/index.tsx new file mode 100644 index 00000000..d132aee6 --- /dev/null +++ b/client/src/containers/projects/form/restoration-plan/index.tsx @@ -0,0 +1,167 @@ +import { useMemo } from "react"; + +import { useFormContext } from "react-hook-form"; + +import { ASSUMPTIONS_NAME_TO_DTO_MAP } from "@shared/schemas/assumptions/assumptions.enums"; +import { + flexRender, + getCoreRowModel, + useReactTable, +} from "@tanstack/react-table"; + +import { client } from "@/lib/query-client"; +import { queryKeys } from "@/lib/query-keys"; + +import { AssumptionsFormProperty } from "@/containers/projects/form/assumptions/columns"; +import { COLUMNS } from "@/containers/projects/form/restoration-plan/columns"; +import { CreateCustomProjectForm } from "@/containers/projects/form/setup"; + +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; + +export default function RestorationPlanProjectForm() { + const form = useFormContext(); + + const v = form.getValues(); + console.log(v); + + const { + ecosystem, + activity, + assumptions: { projectLength }, + } = form.getValues(); + + const { queryKey } = queryKeys.customProjects.assumptions({ + ecosystem, + activity, + }); + const { data: defaultRestorationProjectLength } = + client.customProjects.getDefaultAssumptions.useQuery( + queryKey, + { + query: { ecosystem, activity }, + }, + { + queryKey, + select: (data) => + Number( + data.body.data.find( + (assumption) => assumption.name === "Restoration project length", + )?.value, + ), + }, + ); + + const totalYears = projectLength + ? Number(projectLength) + : defaultRestorationProjectLength; + + console.log({ totalYears }); + + const DATA = useMemo( + () => + Array.from({ + length: (totalYears as NonNullable) + 2, + }) + .map((_, i) => ({ + year: i - 1, + annualHectaresRestored: 0, + })) + .filter(({ year }) => year != 0), + [totalYears], + ); + + const table = useReactTable({ + data: DATA, + columns: COLUMNS, + getCoreRowModel: getCoreRowModel(), + }); + + return ( + + + +
+
+

Restoration Plan

+ + optional + +
+

+ Overrides annual hectares inputs +

+
+
+ + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+
+ ); +} diff --git a/client/src/containers/projects/form/setup/index.tsx b/client/src/containers/projects/form/setup/index.tsx index e9f27520..3a6c56c4 100644 --- a/client/src/containers/projects/form/setup/index.tsx +++ b/client/src/containers/projects/form/setup/index.tsx @@ -96,8 +96,6 @@ export default function SetupProjectForm() { disabled: isDisabled(ecosystem), })); - console.log(form.formState.errors); - return ( diff --git a/client/src/containers/projects/new/index.tsx b/client/src/containers/projects/new/index.tsx index efb51754..1904e861 100644 --- a/client/src/containers/projects/new/index.tsx +++ b/client/src/containers/projects/new/index.tsx @@ -74,6 +74,14 @@ export const onSubmit = (data: CreateCustomProjectForm) => { // @ts-expect-error fix later plantingSuccessRate: plantingSuccessRate / 100, }), + ...(originalValues.activity === ACTIVITY.RESTORATION && { + restorationYearlyBreakdown: [ + ...new Set(originalValues.parameters.restorationYearlyBreakdown), + ].map((hectareas, index) => ({ + year: index, + annualHectaresRestored: hectareas, + })), + }), }, assumptions: { ...Object.keys(originalValues.assumptions ?? {}).reduce( @@ -128,7 +136,7 @@ export default function CreateCustomProject() { resolver: zodResolver(CreateCustomProjectSchema), defaultValues: { projectName: "test", - activity: ACTIVITY.CONSERVATION, + activity: ACTIVITY.RESTORATION, ecosystem: ECOSYSTEM.SEAGRASS, countryCode: countryOptions?.[0]?.value, projectSizeHa: 20, diff --git a/shared/schemas/custom-projects/create-custom-project.schema.ts b/shared/schemas/custom-projects/create-custom-project.schema.ts index 17e887cc..6ea2857e 100644 --- a/shared/schemas/custom-projects/create-custom-project.schema.ts +++ b/shared/schemas/custom-projects/create-custom-project.schema.ts @@ -62,6 +62,7 @@ export const RestorationCustomProjectSchema = z plantingSuccessRate: z.preprocess(parseNumber, z.number().nonnegative({ message: 'Planting Success Rate should be a non-negative number', })), + restorationYearlyBreakdown: z.array(z.preprocess(parseNumber, z.number()).optional()).optional(), }) .superRefine((data, ctx) => { if ( @@ -82,7 +83,9 @@ export const AssumptionsSchema = z.object({ restorationRate: z.preprocess(parseNumber, z.number().positive()).optional(), carbonPriceIncrease: z.preprocess(parseNumber, z.number().positive()).optional(), buffer: z.preprocess(parseNumber, z.number().positive()).optional(), - projectLength: z.preprocess(parseNumber, z.number().positive()).optional(), + projectLength: z.preprocess(parseNumber, z.number().positive().min(1).max(40, { + message: 'Project Length should be between 1 and 40 years', + })).optional(), }); export const InputCostsSchema = z.object({