mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-30 12:55:02 +00:00
Implemented functionality on plan page
This commit is contained in:
parent
8de210dd00
commit
f03883738a
4 changed files with 169 additions and 60 deletions
|
|
@ -1,16 +1,11 @@
|
|||
import { Fragment } from "react";
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
import { ChevronDownIcon } from "@heroicons/react/20/solid";
|
||||
|
||||
type Option = {
|
||||
label: string;
|
||||
value: string;
|
||||
cost: number;
|
||||
};
|
||||
import { PartOption } from "@/types/parts";
|
||||
|
||||
type DropdownProps = {
|
||||
options: Option[];
|
||||
onSelectOption: (option: Option) => void;
|
||||
options: PartOption[];
|
||||
onSelectOption: (option: PartOption) => void;
|
||||
selectedOption: string;
|
||||
};
|
||||
|
||||
|
|
@ -39,7 +34,7 @@ export default function PartDropdown({
|
|||
>
|
||||
<Menu.Items className="origin-bottom left-0 w-full rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||
{options.map((option) => (
|
||||
<Menu.Item key={option.value}>
|
||||
<Menu.Item key={option.part}>
|
||||
{({ active }) => (
|
||||
<button
|
||||
className={`${
|
||||
|
|
|
|||
|
|
@ -2,16 +2,21 @@ import { Dialog, Transition } from "@headlessui/react";
|
|||
import { Dispatch, Fragment, SetStateAction, useState } from "react";
|
||||
import { TanButton } from "../Buttons";
|
||||
import PartDropdown from "./PartDropdown";
|
||||
import { formatNumber } from "@/app/utils";
|
||||
import type { Part, PartOption } from "@/types/parts";
|
||||
|
||||
type ParModalProps = {
|
||||
type PartModalProps = {
|
||||
title: string;
|
||||
isOpen: boolean;
|
||||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||
options: { label: string; value: string; cost: number }[];
|
||||
options: PartOption[];
|
||||
selectedOption: string;
|
||||
setSelectedOption: Dispatch<SetStateAction<string>>;
|
||||
cost: number;
|
||||
setCost: Dispatch<SetStateAction<number>>;
|
||||
parts: Part[];
|
||||
setParts: Dispatch<SetStateAction<Part[]>>;
|
||||
partIndex: number;
|
||||
setTotalCost: Dispatch<SetStateAction<string>>;
|
||||
setWorkHours: Dispatch<SetStateAction<number>>;
|
||||
};
|
||||
|
||||
export default function PartModal({
|
||||
|
|
@ -21,18 +26,31 @@ export default function PartModal({
|
|||
options,
|
||||
selectedOption,
|
||||
setSelectedOption,
|
||||
cost,
|
||||
setCost,
|
||||
}: ParModalProps) {
|
||||
parts,
|
||||
setParts,
|
||||
partIndex,
|
||||
setTotalCost,
|
||||
setWorkHours,
|
||||
}: PartModalProps) {
|
||||
function handleModalSubmit() {
|
||||
// Right now the dropdown is setting the state on the selected option so we might not need
|
||||
setSelectedOption(optionInput);
|
||||
setCost(costInput);
|
||||
// recalculate total cost
|
||||
parts[partIndex].cost = costInput;
|
||||
setTotalCost(formatNumber(parts.reduce((sum, part) => sum + part.cost, 0)));
|
||||
|
||||
// recalculata total hours
|
||||
parts[partIndex].workHours = hoursInput;
|
||||
setWorkHours(parts.reduce((sum, part) => sum + part.workHours, 0));
|
||||
setParts(parts);
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
const [costInput, setCostInput] = useState<number>(cost);
|
||||
const [costInput, setCostInput] = useState<number>(parts[partIndex].cost);
|
||||
const [optionInput, setOptionInput] = useState<string>(selectedOption);
|
||||
const [hoursInput, setHoursInput] = useState<number>(
|
||||
parts[partIndex].workHours
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -80,10 +98,11 @@ export default function PartModal({
|
|||
<PartDropdown
|
||||
options={options}
|
||||
onSelectOption={(option) => {
|
||||
setOptionInput(option.value);
|
||||
setOptionInput(option.part);
|
||||
setCostInput(option.cost);
|
||||
setHoursInput(option.workHours);
|
||||
}}
|
||||
selectedOption={selectedOption}
|
||||
selectedOption={optionInput}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,53 +1,74 @@
|
|||
import { useState } from "react";
|
||||
import { Dispatch, SetStateAction, useState } from "react";
|
||||
import PartModal from "./PartModal";
|
||||
import { formatNumber } from "@/app/utils";
|
||||
import type { Part, PartOption } from "@/types/parts";
|
||||
|
||||
type PlanPartProps = {
|
||||
partIndex: number;
|
||||
title: string;
|
||||
cost: number;
|
||||
co2Emissions: number;
|
||||
co2Reduction: number;
|
||||
workHours: number;
|
||||
parts: Part[];
|
||||
setParts: Dispatch<SetStateAction<Part[]>>;
|
||||
setTotalCost: Dispatch<SetStateAction<string>>;
|
||||
setWorkHours: Dispatch<SetStateAction<number>>;
|
||||
};
|
||||
|
||||
export default function PlanPart({
|
||||
partIndex,
|
||||
title,
|
||||
cost,
|
||||
co2Emissions,
|
||||
co2Reduction,
|
||||
workHours,
|
||||
parts,
|
||||
setParts,
|
||||
setTotalCost,
|
||||
setWorkHours,
|
||||
}: PlanPartProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [partCost, setPartCost] = useState(cost);
|
||||
const [partCost, setPartCost] = useState(parts[partIndex].cost);
|
||||
|
||||
// These are temporary options for the demo
|
||||
const options = [
|
||||
const options: PartOption[] = [
|
||||
{
|
||||
label: "option 1: £" + formatNumber(cost + 0.05 * cost),
|
||||
value: "option 1",
|
||||
part: "option 1",
|
||||
cost: cost + 0.05 * cost,
|
||||
co2Reduction: co2Reduction - 0.05 * co2Reduction,
|
||||
workHours: workHours + 0.05 * workHours,
|
||||
},
|
||||
{
|
||||
label: "option 2: £" + formatNumber(cost + 0.1 * cost),
|
||||
value: "option 2",
|
||||
part: "option 2",
|
||||
cost: cost + 0.1 * cost,
|
||||
co2Reduction: co2Reduction - 0.1 * co2Reduction,
|
||||
workHours: workHours + 0.1 * workHours,
|
||||
},
|
||||
{
|
||||
label: "option 3: £" + formatNumber(cost + 0.15 * cost),
|
||||
value: "option 3",
|
||||
part: "option 3",
|
||||
cost: cost + 0.15 * cost,
|
||||
co2Reduction: co2Reduction - 0.15 * co2Reduction,
|
||||
workHours: workHours + 0.15 * workHours,
|
||||
},
|
||||
{
|
||||
label: "option 4: £" + formatNumber(cost + 0.2 * cost),
|
||||
value: "option 4",
|
||||
part: "option 4",
|
||||
cost: cost + 0.2 * cost,
|
||||
co2Reduction: co2Reduction - 0.2 * co2Reduction,
|
||||
workHours: workHours + 0.2 * workHours,
|
||||
},
|
||||
{
|
||||
label: "option 5: £" + formatNumber(cost + 0.25 * cost),
|
||||
value: "option 5",
|
||||
part: "option 5",
|
||||
cost: cost + 0.25 * cost,
|
||||
co2Reduction: co2Reduction - 0.25 * co2Reduction,
|
||||
workHours: workHours + 0.25 * workHours,
|
||||
},
|
||||
];
|
||||
|
||||
const [selectedOption, setSelectedOption] = useState(options[0].value);
|
||||
const [selectedOption, setSelectedOption] = useState(options[0].part);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -59,13 +80,13 @@ export default function PlanPart({
|
|||
<h2 className="flex-1 text-lg font-bold mb-2 text-left">{title}</h2>
|
||||
<div>{selectedOption}</div>
|
||||
<div className="flex-1 text-center">
|
||||
<p>Cost: £{partCost}</p>
|
||||
<p>Cost: £{parts[partIndex].cost}</p>
|
||||
</div>
|
||||
<div className="flex-1 text-center">
|
||||
<p>CO2 Reduction: {co2Emissions}</p>
|
||||
<p>CO2 Reduction: {parts[partIndex].co2Reduction}</p>
|
||||
</div>
|
||||
<div className="flex-1 text-center">
|
||||
<p>Work Hours: {workHours}</p>
|
||||
<p>Work Hours: {parts[partIndex].workHours}</p>
|
||||
</div>
|
||||
<PartModal
|
||||
title={title}
|
||||
|
|
@ -74,8 +95,11 @@ export default function PlanPart({
|
|||
options={options}
|
||||
selectedOption={selectedOption}
|
||||
setSelectedOption={setSelectedOption}
|
||||
cost={partCost}
|
||||
setCost={setPartCost}
|
||||
parts={parts}
|
||||
setParts={setParts}
|
||||
partIndex={partIndex}
|
||||
setTotalCost={setTotalCost}
|
||||
setWorkHours={setWorkHours}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import EditEpctargetModal from "@/app/components/property/EditEpcTargetModal";
|
|||
import Link from "next/link";
|
||||
import BudgetModal from "@/app/components/plan/BudgetModal";
|
||||
import { formatNumber } from "@/app/utils";
|
||||
import { Part } from "@/types/parts";
|
||||
|
||||
export default function Plan({
|
||||
params,
|
||||
|
|
@ -27,46 +28,110 @@ export default function Plan({
|
|||
|
||||
// Temp config for the demo
|
||||
const partsConfig = [
|
||||
{ part: "Roof", cost: 1200, co2Emissions: 50, workHours: 20 },
|
||||
{ part: "Walls", cost: 900, co2Emissions: 50, workHours: 20 },
|
||||
{ part: "Floors", cost: 4300, co2Emissions: 50, workHours: 20 },
|
||||
{ part: "Window Glazing", cost: 6000, co2Emissions: 50, workHours: 20 },
|
||||
{
|
||||
part: "Roof",
|
||||
option: "option 1",
|
||||
cost: 1200,
|
||||
co2Reduction: 50,
|
||||
workHours: 20,
|
||||
},
|
||||
{
|
||||
part: "Walls",
|
||||
option: "option 1",
|
||||
cost: 900,
|
||||
co2Reduction: 50,
|
||||
workHours: 20,
|
||||
},
|
||||
{
|
||||
part: "Floors",
|
||||
option: "option 1",
|
||||
cost: 4300,
|
||||
co2Reduction: 50,
|
||||
workHours: 20,
|
||||
},
|
||||
{
|
||||
part: "Window Glazing",
|
||||
option: "option 1",
|
||||
cost: 6000,
|
||||
co2Reduction: 50,
|
||||
workHours: 20,
|
||||
},
|
||||
{
|
||||
part: "Window Draughproofing",
|
||||
option: "option 1",
|
||||
cost: 10,
|
||||
co2Emissions: 50,
|
||||
co2Reduction: 50,
|
||||
workHours: 20,
|
||||
},
|
||||
{
|
||||
part: "Door Insulation",
|
||||
option: "option 1",
|
||||
cost: 250,
|
||||
co2Reduction: 50,
|
||||
workHours: 20,
|
||||
},
|
||||
{ part: "Door Insulation", cost: 250, co2Emissions: 50, workHours: 20 },
|
||||
{
|
||||
part: "Door Draughproofing",
|
||||
option: "option 1",
|
||||
cost: 70,
|
||||
co2Emissions: 50,
|
||||
co2Reduction: 50,
|
||||
workHours: 20,
|
||||
},
|
||||
{
|
||||
part: "Heating",
|
||||
option: "option 1",
|
||||
cost: 2300,
|
||||
co2Reduction: 50,
|
||||
workHours: 20,
|
||||
},
|
||||
{
|
||||
part: "Hot Water",
|
||||
option: "option 1",
|
||||
cost: 5290,
|
||||
co2Reduction: 50,
|
||||
workHours: 20,
|
||||
},
|
||||
{
|
||||
part: "Solar Panels",
|
||||
option: "option 1",
|
||||
cost: 1300,
|
||||
co2Reduction: 50,
|
||||
workHours: 20,
|
||||
},
|
||||
{
|
||||
part: "Solar Hot Water",
|
||||
option: "option 1",
|
||||
cost: 1000,
|
||||
co2Reduction: 50,
|
||||
workHours: 20,
|
||||
},
|
||||
{
|
||||
part: "Lighting",
|
||||
option: "option 1",
|
||||
cost: 20,
|
||||
co2Reduction: 50,
|
||||
workHours: 20,
|
||||
},
|
||||
{ part: "Heating", cost: 2300, co2Emissions: 50, workHours: 20 },
|
||||
{ part: "Hot Water", cost: 5290, co2Emissions: 50, workHours: 20 },
|
||||
{ part: "Solar Panels", cost: 1300, co2Emissions: 50, workHours: 20 },
|
||||
{ part: "Solar Hot Water", cost: 1000, co2Emissions: 50, workHours: 20 },
|
||||
{ part: "Lighting", cost: 20, co2Emissions: 50, workHours: 20 },
|
||||
{
|
||||
part: "Chimneys/ Open Fire Places",
|
||||
option: "option 1",
|
||||
cost: 350,
|
||||
co2Emissions: 50,
|
||||
co2Reduction: 50,
|
||||
workHours: 20,
|
||||
},
|
||||
];
|
||||
|
||||
const totalWorkHours = partsConfig.reduce(
|
||||
(sum, part) => sum + part.workHours,
|
||||
0
|
||||
);
|
||||
|
||||
const partsTotalCost = partsConfig.reduce((sum, part) => sum + part.cost, 0);
|
||||
// Manage state of the selected parts - start with partsConfig
|
||||
const [parts, setParts] = useState<Part[]>(partsConfig);
|
||||
|
||||
const [budget, setBudget] = useState<number | "Not set">("Not set");
|
||||
const [totalCost, setTotalCost] = useState(partsTotalCost);
|
||||
const [installTime, setInstallTime] = useState(totalWorkHours);
|
||||
const [totalCost, setTotalCost] = useState<string>(
|
||||
formatNumber(parts.reduce((sum, part) => sum + part.cost, 0))
|
||||
);
|
||||
const [workHours, setWorkHours] = useState(
|
||||
parts.reduce((sum, part) => sum + part.workHours, 0)
|
||||
);
|
||||
|
||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||
const [isBudgetModalOpen, setIsBudgetModalOpen] = useState(false);
|
||||
const [targetEpcRating, setTargetEpcRating] = useState<EpcRating | "">(
|
||||
|
|
@ -92,6 +157,7 @@ export default function Plan({
|
|||
}
|
||||
|
||||
const propertyData = data.rows.filter((row) => row["lmk-key"] === lmkKey)[0];
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className="max-w-6xl mx-auto p-6 flex flex-col items-center">
|
||||
|
|
@ -101,14 +167,19 @@ export default function Plan({
|
|||
</div>
|
||||
<div className="flex w-full">
|
||||
<div className="w-3/4 pr-4">
|
||||
{partsConfig.map((part, index) => {
|
||||
{parts.map((part, index) => {
|
||||
return (
|
||||
<PlanPart
|
||||
key={index}
|
||||
partIndex={index}
|
||||
title={part.part}
|
||||
cost={part.cost}
|
||||
co2Emissions={part.co2Emissions}
|
||||
co2Reduction={part.co2Reduction}
|
||||
workHours={part.workHours}
|
||||
parts={parts}
|
||||
setParts={setParts}
|
||||
setTotalCost={setTotalCost}
|
||||
setWorkHours={setWorkHours}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
|
@ -143,7 +214,7 @@ export default function Plan({
|
|||
</Link>
|
||||
<li className="px-2 mb-2">Total cost: £{totalCost}</li>
|
||||
<li className="px-2 mb-2">
|
||||
Installation Time: {installTime} hours
|
||||
Installation Time: {workHours} hours
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue