fixed form logic

This commit is contained in:
Khalim Conn-Kowlessar 2025-10-20 18:14:07 +00:00
parent ca63fa7c99
commit 6df5dc4b23
2 changed files with 262 additions and 48 deletions

View file

@ -50,11 +50,14 @@ export const uploadCsvSchema = baseFormSchema.extend({
if (val === "" || val === undefined) return undefined;
return Number(val);
}, z.number().min(0.1)),
budget: z.preprocess((val) => {
if (val === "" || val === undefined) return undefined;
if (val === null) return null;
return Number(val);
}, z.union([z.number(), z.null()]).optional()),
budget: z.preprocess(
(val) => {
if (val === "" || val === undefined) return undefined;
if (val === null) return null;
return Number(val);
},
z.union([z.number(), z.null()]).optional()
),
});
export type UploadCsvFormValues = z.infer<typeof uploadCsvSchema>;

View file

@ -1,27 +1,122 @@
"use client";
import { useState } from "react";
import { useState, useMemo } from "react";
import { useForm, FormProvider } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import {
FormField,
FormItem,
FormLabel,
FormControl,
FormMessage,
} from "@/app/shadcn_components/ui/form";
import { Card } from "@/app/shadcn_components/ui/card";
import { Input } from "@/app/shadcn_components/ui/input";
import { Button } from "@/app/shadcn_components/ui/button";
import { SelectDropdown } from "@/app/portfolio/[slug]/components/RemoteAssessmentDropdowns";
import {
SelectScenarioDropdown,
SelectDropdown,
ScenarioOption,
} from "@/app/portfolio/[slug]/components/RemoteAssessmentDropdowns";
import MeasuresCheckboxes from "@/app/portfolio/[slug]/components/MeasuresCheckboxes";
import { measuresList } from "@/app/db/schema/recommendations";
import { ScenarioSelect } from "@/app/db/schema/recommendations";
import {
RemoteAssessmentFormSchema,
type RemoteAssessmentFormValues,
} from "@/app/portfolio/[slug]/components/FormSchema";
const housingTypeOptions = [
{ label: "Social", value: "Social" },
{ label: "Private", value: "Private" },
];
// -------------------
// Component
// -------------------
export default function ScenarioSetup({
portfolioId,
scenarios,
disabled = false,
onSubmitScenario,
}: {
portfolioId: string;
scenarios: {
id: string;
name: string;
housingType: string;
goal: string;
goalValue: string | null;
}[];
scenarios: ScenarioSelect[];
disabled?: boolean;
onSubmitScenario?: (values: RemoteAssessmentFormValues) => void;
}) {
const NEW_SENTINEL = "__new__";
const [selectedScenario, setSelectedScenario] = useState<string | null>(null);
const [showMeasures, setShowMeasures] = useState(false);
const form = useForm<RemoteAssessmentFormValues>({
resolver: zodResolver(RemoteAssessmentFormSchema),
mode: "onChange",
defaultValues: {
scenario: "",
goal: "",
goalValue: "",
budget: undefined,
housingType: "Social",
addressLineOne: "",
postcode: "",
uprn: 0,
valuation: 0,
propertyType: null,
builtForm: null,
measures: measuresList,
},
});
const { setValue, watch, handleSubmit, formState } = form;
const values = watch();
const scenarioOptions: ScenarioOption[] = useMemo(
() =>
scenarios.map((s) => ({
label: s.name || "",
value: String(s.id) || "",
housingType: s.housingType || "",
goal: s.goal || "",
goalValue: s.goalValue || "",
})),
[scenarios]
);
function handleSelect(opt: ScenarioOption) {
setSelectedScenario(opt.value);
if (opt.value === NEW_SENTINEL) {
form.reset({
...form.getValues(),
scenario: "",
housingType: "",
goal: "",
goalValue: "",
budget: undefined,
valuation: undefined,
measures: measuresList, // all measures preselected
});
} else {
form.reset({
scenario: opt.label || "",
housingType: opt.housingType || "",
goal: opt.goal || "",
goalValue: opt.goalValue || "",
budget: undefined,
valuation: undefined,
measures: measuresList,
});
}
}
function onSubmit(data: RemoteAssessmentFormValues) {
if (onSubmitScenario) onSubmitScenario(data);
console.log("Submitted scenario data:", data);
}
return (
<Card
@ -35,42 +130,158 @@ export default function ScenarioSetup({
Step 2: Select or Create Scenario
</h2>
<SelectDropdown
options={[
{ label: "Create new scenario", value: "__new__" },
...scenarios.map((s) => ({
label: s.name,
value: s.id,
})),
]}
selectedOption={selectedScenario || ""}
onSelectOption={(opt) => setSelectedScenario(opt.value)}
/>
<FormProvider {...form}>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
<div>
<label className="block text-gray-800 text-sm font-medium mb-2">
Select Scenario
</label>
<SelectScenarioDropdown
selectedValue={selectedScenario}
onSelect={handleSelect}
scenarios={scenarioOptions}
/>
</div>
<div className="mt-6 space-y-3">
<Input
placeholder="Scenario name"
disabled={disabled}
className="border-brandbrown focus-visible:ring-brandbrown"
/>
<Input
placeholder="Budget (£)"
disabled={disabled}
className="border-brandbrown focus-visible:ring-brandbrown"
/>
<Input
placeholder="Valuation (optional)"
disabled={disabled}
className="border-brandbrown focus-visible:ring-brandbrown"
/>
</div>
{selectedScenario && (
<>
{/* Scenario Name + Housing Type */}
<div className="grid grid-cols-2 gap-4">
<FormField
control={form.control}
name="scenario"
render={({ field }) => (
<FormItem>
<FormLabel>Scenario Name</FormLabel>
<FormControl>
<Input
{...field}
disabled={selectedScenario !== NEW_SENTINEL}
placeholder="Enter scenario name"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button
className="mt-4 bg-brandbrown hover:bg-hoverblue"
disabled={disabled}
>
Save Scenario
</Button>
<FormField
control={form.control}
name="housingType"
render={({ field }) => (
<FormItem>
<FormLabel>Housing Type</FormLabel>
<FormControl>
{selectedScenario === NEW_SENTINEL ? (
<SelectDropdown
options={housingTypeOptions}
selectedOption={field.value}
onSelectOption={(opt) => field.onChange(opt.value)}
/>
) : (
<Input value={field.value} disabled />
)}
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Goal + EPC */}
<div className="grid grid-cols-2 gap-4">
<FormField
control={form.control}
name="goal"
render={({ field }) => (
<FormItem>
<FormLabel>Goal</FormLabel>
<FormControl>
{selectedScenario === NEW_SENTINEL ? (
<SelectDropdown
options={[
{
label: "Increasing EPC",
value: "Increasing EPC",
},
{
label: "Energy Savings",
value: "Energy Savings",
},
{
label: "Reducing CO₂ emissions",
value: "Reducing CO2 emissions",
},
]}
selectedOption={field.value}
onSelectOption={(opt) => field.onChange(opt.value)}
/>
) : (
<Input value={field.value} disabled />
)}
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{values.goal === "Increasing EPC" && (
<FormField
control={form.control}
name="goalValue"
render={({ field }) => (
<FormItem>
<FormLabel>Target EPC Rating</FormLabel>
<FormControl>
{selectedScenario === NEW_SENTINEL ? (
<SelectDropdown
options={[
{ label: "C", value: "C" },
{ label: "B", value: "B" },
{ label: "A", value: "A" },
]}
selectedOption={field.value || ""}
onSelectOption={(opt) =>
field.onChange(opt.value)
}
/>
) : (
<Input value={field.value || ""} disabled />
)}
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
</div>
{/* Measures Section */}
<div className="border-t pt-4 mt-6">
<button
type="button"
onClick={() => setShowMeasures(!showMeasures)}
className="flex items-center justify-between w-full text-sm font-medium text-gray-800"
>
<span>Measures</span>
<span>{showMeasures ? "" : "+"}</span>
</button>
{showMeasures && <MeasuresCheckboxes form={form} />}
</div>
<div className="flex justify-end pt-2">
<Button
type="submit"
className="bg-brandbrown text-white hover:bg-hoverblue"
disabled={!formState.isValid}
>
Save Scenario
</Button>
</div>
</>
)}
</form>
</FormProvider>
</Card>
);
}