diff --git a/client/src/containers/projects/form/setup/restoration-project-detail/index.tsx b/client/src/containers/projects/form/setup/restoration-project-detail/index.tsx index a23cc891..109d2f65 100644 --- a/client/src/containers/projects/form/setup/restoration-project-detail/index.tsx +++ b/client/src/containers/projects/form/setup/restoration-project-detail/index.tsx @@ -2,9 +2,15 @@ import * as React from "react"; import { useFormContext } from "react-hook-form"; -import { RESTORATION_ACTIVITY_SUBTYPE } from "@shared/entities/activity.enum"; +import { + ACTIVITY, + RESTORATION_ACTIVITY_SUBTYPE, +} from "@shared/entities/activity.enum"; import { SEQUESTRATION_RATE_TIER_TYPES } from "@shared/entities/carbon-inputs/sequestration-rate.entity"; +import { client } from "@/lib/query-client"; +import { queryKeys } from "@/lib/query-keys"; + import { CreateCustomProjectForm } from "@/containers/projects/form/setup"; import { Card } from "@/components/ui/card"; @@ -15,6 +21,7 @@ import { FormLabel, FormMessage, } from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { Select, SelectContent, @@ -26,98 +33,239 @@ import { export default function RestorationProjectDetails() { const form = useFormContext<CreateCustomProjectForm>(); + const { + ecosystem, + countryCode, + activity, + // @ts-expect-error fix later + parameters: { tierSelector }, + } = form.getValues(); + + const queryKey = queryKeys.customProjects.defaultActivityTypes({ + ecosystem, + countryCode, + }).queryKey; + + const { data } = client.customProjects.getActivityTypesDefaults.useQuery( + queryKey, + { query: { ecosystem, countryCode } }, + { + queryKey, + enabled: !!ecosystem && !!countryCode, + select: (response) => { + const { data } = response.body; + return data[activity as ACTIVITY.RESTORATION].sequestrationRate; + }, + }, + ); + return ( <Card variant="secondary"> - <div className="flex gap-3"> - <div className="basis-1/2 space-y-2"> + <div className="flex flex-col gap-4"> + <div className="flex gap-4"> + <div className="basis-1/2 space-y-2"> + <FormField + control={form.control} + name="parameters.restorationActivity" + render={() => ( + <FormItem className="basis-1/2"> + <FormLabel + tooltip={{ + title: "Project-specific emissions type", + content: "TBD", + }} + > + Restoration Activity type + </FormLabel> + <FormControl> + <Select + name="parameters.restorationActivity" + value={form.getValues("parameters.restorationActivity")} + onValueChange={async (v) => { + form.setValue( + "parameters.restorationActivity", + v as RESTORATION_ACTIVITY_SUBTYPE, + ); + await form.trigger("parameters.restorationActivity"); + }} + > + <SelectTrigger> + <SelectValue placeholder="Select restoration activity type" /> + </SelectTrigger> + <SelectContent> + {Object.values(RESTORATION_ACTIVITY_SUBTYPE)?.map( + (option) => ( + <SelectItem key={option} value={option}> + {option} + </SelectItem> + ), + )} + </SelectContent> + </Select> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + </div> + <div className="basis-1/2 space-y-2"> + <FormField + control={form.control} + name="parameters.restorationActivity" + render={() => ( + <FormItem className="basis-1/2"> + <FormLabel + tooltip={{ + title: "Project-specific emissions type", + content: "TBD", + }} + > + Sequestration Factor Used + </FormLabel> + <FormControl> + <Select + name="parameters.tierSelector" + value={form.getValues("parameters.tierSelector")} + onValueChange={async (v) => { + form.setValue( + "parameters.tierSelector", + v as SEQUESTRATION_RATE_TIER_TYPES, + ); + await form.trigger("parameters.tierSelector"); + }} + > + <SelectTrigger> + <SelectValue placeholder="Select sequestration tier" /> + </SelectTrigger> + <SelectContent> + {Object.values(SEQUESTRATION_RATE_TIER_TYPES)?.map( + (option) => ( + <SelectItem key={option} value={option}> + {option} + </SelectItem> + ), + )} + </SelectContent> + </Select> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + </div> + </div> + + <div className="flex gap-4"> <FormField - control={form.control} - name="parameters.restorationActivity" - render={() => ( - <FormItem className="basis-1/2"> + control={form?.control} + name="parameters.plantingSuccessRate" + render={({ field }) => ( + <FormItem className="basis-1/2 space-y-2"> <FormLabel tooltip={{ - title: "Project-specific emissions type", + title: "Project Specific Emission Factor", content: "TBD", }} > - Restoration Activity type + Project Specific Emission Factor </FormLabel> - <FormControl> - <Select - name="parameters.restorationActivity" - value={form.getValues("parameters.restorationActivity")} - onValueChange={async (v) => { - form.setValue( - "parameters.restorationActivity", - v as RESTORATION_ACTIVITY_SUBTYPE, - ); - await form.trigger("parameters.restorationActivity"); - }} - > - <SelectTrigger> - <SelectValue placeholder="Select restoration activity type" /> - </SelectTrigger> - <SelectContent> - {Object.values(RESTORATION_ACTIVITY_SUBTYPE)?.map( - (option) => ( - <SelectItem key={option} value={option}> - {option} - </SelectItem> - ), - )} - </SelectContent> - </Select> + <FormControl className="relative after:absolute after:right-6 after:inline-block after:text-sm after:text-muted-foreground after:content-['%']"> + <div className="relative flex flex-1 items-center"> + <Input + {...field} + className="w-full pr-12" + type="number" + min={0} + onChange={(v) => { + form.setValue( + "parameters.plantingSuccessRate", + Number(v.target.value), + ); + // await form.trigger("parameters.plantingSuccessRate"); + }} + readOnly + disabled + /> + </div> </FormControl> <FormMessage /> </FormItem> )} /> - </div> - <div className="basis-1/2 space-y-2"> - <FormField - control={form.control} - name="parameters.restorationActivity" - render={() => ( - <FormItem className="basis-1/2"> + + <> + {tierSelector === SEQUESTRATION_RATE_TIER_TYPES.TIER_1 && ( + <div className="basis-1/2 space-y-2"> <FormLabel tooltip={{ - title: "Project-specific emissions type", + title: "IPCC default value", content: "TBD", }} > - Sequestration Factor Used + IPCC default value </FormLabel> - <FormControl> - <Select - name="parameters.tierSelector" - value={form.getValues("parameters.tierSelector")} - onValueChange={async (v) => { - form.setValue( - "parameters.tierSelector", - v as SEQUESTRATION_RATE_TIER_TYPES, - ); - await form.trigger("parameters.tierSelector"); - }} - > - <SelectTrigger> - <SelectValue placeholder="Select sequestration tier" /> - </SelectTrigger> - <SelectContent> - {Object.values(SEQUESTRATION_RATE_TIER_TYPES)?.map( - (option) => ( - <SelectItem key={option} value={option}> - {option} - </SelectItem> - ), - )} - </SelectContent> - </Select> - </FormControl> - <FormMessage /> - </FormItem> + <div className="relative flex flex-1 items-center after:absolute after:right-6 after:inline-block after:text-sm after:text-muted-foreground after:content-['??']"> + <Input + className="w-full pr-32 text-muted-foreground" + disabled + readOnly + value={data?.tier1} + /> + </div> + </div> )} - /> + {tierSelector === SEQUESTRATION_RATE_TIER_TYPES.TIER_2 && ( + <div className="basis-1/2 space-y-2"> + <FormLabel + tooltip={{ + title: "Country-specific rate", + content: "TBD", + }} + > + Country-specific rate + </FormLabel> + <div className="relative flex flex-1 items-center after:absolute after:right-6 after:inline-block after:text-sm after:text-muted-foreground after:content-['??']"> + <Input + className="w-full pr-32 text-muted-foreground" + disabled + readOnly + value={data?.tier2} + /> + </div> + </div> + )} + {tierSelector === SEQUESTRATION_RATE_TIER_TYPES.TIER_3 && ( + <FormField + control={form?.control} + name="parameters.projectSpecificSequestrationRate" + render={({ field }) => ( + <FormItem className="basis-1/2 space-y-2"> + <FormLabel + tooltip={{ + title: "Project-specific sequestration rate", + content: "TBD", + }} + > + Project-specific sequestration rate + </FormLabel> + <FormControl className="relative after:absolute after:right-6 after:inline-block after:text-sm after:text-muted-foreground after:content-['%']"> + <div className="relative flex flex-1 items-center"> + <Input + {...field} + className="w-full pr-12" + type="number" + onChange={(v) => { + field.onChange(Number(v.target.value)); + }} + /> + </div> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + )} + </> </div> </div> </Card> diff --git a/client/src/containers/projects/new/index.tsx b/client/src/containers/projects/new/index.tsx index d5d9c0d5..23458205 100644 --- a/client/src/containers/projects/new/index.tsx +++ b/client/src/containers/projects/new/index.tsx @@ -134,6 +134,8 @@ export default function CreateCustomProject() { projectSpecificEmissionFactor: 0, emissionFactorSOC: 0, emissionFactorAGB: 0, + // @ts-expect-error fix later + plantingSuccessRate: 80, }, assumptions: { baselineReassessmentFrequency: undefined, diff --git a/shared/schemas/custom-projects/create-custom-project.schema.ts b/shared/schemas/custom-projects/create-custom-project.schema.ts index e4608363..17e887cc 100644 --- a/shared/schemas/custom-projects/create-custom-project.schema.ts +++ b/shared/schemas/custom-projects/create-custom-project.schema.ts @@ -56,28 +56,21 @@ export const RestorationCustomProjectSchema = z .object({ restorationActivity: z.nativeEnum(RESTORATION_ACTIVITY_SUBTYPE), tierSelector: z.nativeEnum(SEQUESTRATION_RATE_TIER_TYPES), - projectSpecificLossRate: z - .number({ message: "Project Specific Loss Rate should be a message" }) - .negative({ message: "Project Specific Loss Rate should be negative" }), + projectSpecificSequestrationRate: z + .number({ message: "Project Specific Rate should be a number" }), lossRateUsed: z.nativeEnum(LOSS_RATE_USED), + plantingSuccessRate: z.preprocess(parseNumber, z.number().nonnegative({ + message: 'Planting Success Rate should be a non-negative number', + })), }) .superRefine((data, ctx) => { if ( - data.lossRateUsed === LOSS_RATE_USED.PROJECT_SPECIFIC && - !data.projectSpecificLossRate + data.tierSelector === SEQUESTRATION_RATE_TIER_TYPES.TIER_3 && + !data.projectSpecificSequestrationRate ) { ctx.addIssue({ code: z.ZodIssueCode.custom, - message: `Project Specific Loss Rate is required when lossRateUsed is ${LOSS_RATE_USED.PROJECT_SPECIFIC}`, - }); - } - if ( - data.lossRateUsed === LOSS_RATE_USED.NATIONAL_AVERAGE && - data.projectSpecificLossRate - ) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: `Project Specific Loss Rate should not be provided when lossRateUsed is ${LOSS_RATE_USED.NATIONAL_AVERAGE}`, + message: 'Project Specific Rate is required', }); } });