mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-08 11:37:25 +00:00
split out uvalue columns and recommendations modal
This commit is contained in:
parent
cc6ea6c73c
commit
30962b20cf
1 changed files with 2 additions and 197 deletions
|
|
@ -1,204 +1,9 @@
|
|||
"use client";
|
||||
import { ComponentRecommendation } from "@/app/db/schema/recommendations";
|
||||
import { Dispatch, Fragment, SetStateAction, useState } from "react";
|
||||
import { Dispatch, SetStateAction, useState } from "react";
|
||||
import { formatNumber } from "@/app/utils";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import RecommendationTable from "@/app/components/building-passport/RecommendationTable";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { Checkbox } from "@/app/shadcn_components/ui/checkbox";
|
||||
import { RecommendationMetricMap } from "@/types/recommendations";
|
||||
import { sumRecommendationMetricMap } from "@/app/portfolio/[slug]/building-passport/[propertyId]/recommendations/utils";
|
||||
|
||||
export const uvalueColumns: ColumnDef<ComponentRecommendation>[] = [
|
||||
{
|
||||
accessorKey: "description",
|
||||
header: "Description",
|
||||
},
|
||||
{
|
||||
accessorKey: "estimatedCost",
|
||||
header: "Estimated Cost",
|
||||
cell: ({ row }) => {
|
||||
return <div>£{formatNumber(row.getValue("estimatedCost"))}</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "newUValue",
|
||||
header: "New U-Value",
|
||||
},
|
||||
{
|
||||
accessorKey: "default",
|
||||
id: "default",
|
||||
header: "Selected?",
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => {
|
||||
if (value === true && !row.getIsSelected()) {
|
||||
row.toggleSelected(true);
|
||||
}
|
||||
}}
|
||||
aria-label="Select row"
|
||||
/>
|
||||
),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
];
|
||||
|
||||
interface RecommendationModalProps {
|
||||
title: string;
|
||||
isOpen: boolean;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
recommendationData: ComponentRecommendation[];
|
||||
setCardComponent: Dispatch<SetStateAction<ComponentRecommendation>>;
|
||||
setCostMap: Dispatch<SetStateAction<RecommendationMetricMap>>;
|
||||
costMap: RecommendationMetricMap;
|
||||
setTotalEstimatedCost: Dispatch<SetStateAction<number>>;
|
||||
sapMap: RecommendationMetricMap;
|
||||
setSapMap: Dispatch<SetStateAction<RecommendationMetricMap>>;
|
||||
setTotalSapPoints: Dispatch<SetStateAction<number>>;
|
||||
}
|
||||
|
||||
export function RecommendationModal({
|
||||
title,
|
||||
isOpen = false,
|
||||
setIsOpen,
|
||||
recommendationData,
|
||||
setCardComponent,
|
||||
setCostMap,
|
||||
costMap,
|
||||
setTotalEstimatedCost,
|
||||
sapMap,
|
||||
setSapMap,
|
||||
setTotalSapPoints,
|
||||
}: RecommendationModalProps) {
|
||||
const [saveButtonDisabled, setSaveButtonDisabled] = useState(true);
|
||||
|
||||
// Find the row where default is true
|
||||
const [defaultRowIndex, setDefaultRowIndex] = useState(
|
||||
recommendationData.findIndex((d) => d.default)
|
||||
);
|
||||
|
||||
// Initialise the state with the default row index
|
||||
const [rowSelection, setRowSelection] = useState(
|
||||
defaultRowIndex !== -1 ? { [defaultRowIndex]: true } : {}
|
||||
);
|
||||
|
||||
function closeModal() {
|
||||
setIsOpen(false);
|
||||
// If the user closes the modal, re-set the state of the row selection to the default, since nothing has changed
|
||||
setRowSelection({ [defaultRowIndex]: true });
|
||||
}
|
||||
|
||||
function saveChanges() {
|
||||
// disable the button to prevent multiple clicks
|
||||
// TODO: Add a loading state to show we're saving
|
||||
setSaveButtonDisabled(true);
|
||||
setIsOpen(false);
|
||||
// Update the card component data
|
||||
const newIndex = parseInt(Object.keys(rowSelection)[0]);
|
||||
|
||||
setCardComponent(recommendationData[newIndex]);
|
||||
// Set the default index
|
||||
setDefaultRowIndex(newIndex);
|
||||
// Update the cost map
|
||||
const newCostMap = {
|
||||
...costMap,
|
||||
[title]: recommendationData[newIndex].estimatedCost,
|
||||
};
|
||||
setCostMap(newCostMap);
|
||||
// update the cost sum
|
||||
setTotalEstimatedCost(sumRecommendationMetricMap(newCostMap));
|
||||
|
||||
// Update the sap map
|
||||
const newSapMap = {
|
||||
...sapMap,
|
||||
[title]: recommendationData[newIndex].sapPoints,
|
||||
};
|
||||
setSapMap(newSapMap);
|
||||
// update the sap sum
|
||||
console.log("setTotalSapPoints", setTotalSapPoints);
|
||||
setTotalSapPoints(sumRecommendationMetricMap(newSapMap));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className="w-full max-w-screen-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-brandblue mb-3"
|
||||
>
|
||||
{title}
|
||||
</Dialog.Title>
|
||||
<RecommendationTable
|
||||
data={recommendationData}
|
||||
columns={uvalueColumns}
|
||||
defaultRowIndex={defaultRowIndex}
|
||||
rowSelection={rowSelection}
|
||||
setRowSelection={setRowSelection}
|
||||
setSaveButtonDisabled={setSaveButtonDisabled}
|
||||
/>
|
||||
<div className="mt-4 flex justify-end gap-2">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex justify-center rounded-md border border-transparent hover:text-red-600 bg-gray-200 px-4 py-2 text-sm font-medium text-red-600 hover:bg-gray-300 focus:outline-none focus-visible:ring-2 focus-visible:none focus-visible:ring-offset-2"
|
||||
onClick={() => {
|
||||
setRowSelection({});
|
||||
}}
|
||||
>
|
||||
Remove Recommendation
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex justify-center rounded-md border border-transparent bg-blue-100 px-4 py-2 text-sm font-medium text-blue-900 hover:bg-blue-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
onClick={closeModal}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex justify-center rounded-md border border-transparent bg-brandblue px-4 py-2 text-sm font-medium text-gray-100 hover:bg-hoverblue focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-brandblue"
|
||||
onClick={saveChanges}
|
||||
disabled={saveButtonDisabled}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</>
|
||||
);
|
||||
}
|
||||
import RecommendationModal from "./RecommendationModal";
|
||||
|
||||
const selectionStyling =
|
||||
"shadow active:shadow active:bg-brandmidblue w-full border rounded p-4 cursor-pointer text-gray-900 bg-gray-100 hover:bg-hoverblue hover:text-gray-100 transition-colors rounded-md flex flex-col justify-start";
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue