From 20b4d93b7796f36d3166f8e116e57f326f1867dd Mon Sep 17 00:00:00 2001 From: StefanWout Date: Wed, 20 Nov 2024 17:17:27 +0000 Subject: [PATCH] disabled the reduce energy consumption option --- .../components/RemoteAssessmentModal.tsx | 480 ++++++++++++++++++ 1 file changed, 480 insertions(+) create mode 100644 src/app/portfolio/[slug]/components/RemoteAssessmentModal.tsx diff --git a/src/app/portfolio/[slug]/components/RemoteAssessmentModal.tsx b/src/app/portfolio/[slug]/components/RemoteAssessmentModal.tsx new file mode 100644 index 0000000..38ea37a --- /dev/null +++ b/src/app/portfolio/[slug]/components/RemoteAssessmentModal.tsx @@ -0,0 +1,480 @@ +"use client"; + +import { Dialog, Transition, Menu } from "@headlessui/react"; +import { useState, Fragment, useEffect, useMemo } from "react"; +import { Input } from "@/app/shadcn_components/ui/input"; +import { Button } from "@/app/shadcn_components/ui/button"; +import { Float } from "@headlessui-float/react"; +import { ChevronDownIcon } from "@heroicons/react/20/solid"; +import { useMutation } from "@tanstack/react-query"; +import { useSession } from "next-auth/react"; + +type Option = { + label: string; + value: string; + disabled: boolean; +}; + +type DropdownProps = { + options: Option[]; + selectedOption: string; + onSelectOption: (option: Option) => void; +}; + +const selecthousingTypeOptions = [ + { + label: "Social", + value: "Social", + disabled: false, + }, + { + label: "Private", + value: "Private", + disabled: false, + }, +]; + +const selectGoalOptions = [ + { + label: "Increase EPC", + value: "Increase EPC", + disabled: false, + }, + { + label: "Reduce energy consumption", + value: "Reduce energy consumption", + disabled: true, + }, +]; + +const goalValueOptions = [ + { + label: "C", + value: "C", + disabled: false, + }, + { + label: "B", + value: "B", + disabled: false, + }, + { + label: "A", + value: "A", + disabled: false, + }, +]; + +export function SelectDropdown({ + options, + selectedOption, + onSelectOption, +}: DropdownProps) { + return ( + + + + {selectedOption || "Select an option"} + + + + {options.map((option) => ( + + {({ active }) => ( + + )} + + ))} + + + + + ); +} + +async function uploadCsvToS3({ + presignedUrl, + file, +}: { + presignedUrl: string; + file: Blob; +}) { + try { + const response = await fetch(presignedUrl, { + method: "PUT", + body: file, + headers: { "Content-Type": "text/csv" }, + }); + + if (!response.ok) { + console.error(response); + throw new Error("Network response was not ok"); + } + } catch (error) { + console.error(error); + throw new Error("Upload failed."); + } + + return { success: true }; +} + +async function generatePresignedUrl({ + userId, + portfolioId, + fileKey, +}: { + userId: string; + portfolioId: string; + fileKey: string; +}) { + // fileKey is a location in S3 where we want to upload the file + const response = await fetch("/api/upload/csv", { + method: "POST", + body: JSON.stringify({ + userId, + portfolioId, + fileKey, + }), + }); + + if (!response.ok) { + throw new Error("Failed to generate presigned url"); + } + + return response.json(); +} + +function generateS3Keys(userId: string, portfolioId: string) { + const timestamp = new Date().toISOString().replace(/[:.-]/g, ""); + const assetListFileKey = `${userId}/${portfolioId}/${timestamp}/asset_list.csv`; + const valuationDataFileKey = `${userId}/${portfolioId}/${timestamp}/valuation_data.csv`; + return { assetListFileKey, valuationDataFileKey }; +} + +type GenericObject = Record; + +const convertToCSV = (data: T[]): string => { + // Get headers (keys from the first object) + const headers = Object.keys(data[0]) as (keyof T)[]; + + // Create CSV rows + const rows = data.map((row) => + headers.map((header) => row[header]).join(",") + ); + + // Combine headers and rows into CSV string + return [headers.join(","), ...rows].join("\n"); +}; + +function useCreateRemoteAssessment({ + portfolioId, + uprn, + addressLineOne, + postcode, +}: { + portfolioId: string; + uprn: number | null; + addressLineOne: string; + postcode: string; +}) { + // 1) We want to upload the asset data. To do this, we format the asset data, generate a presigned URL, and upload the data to S3. + // 2) We then want to upload valuation data. To do this, we format the valuation data, generate a presigned URL, and upload the data to S3. + // 3) Trigger the engine!!!! This is an api at /api/plan/trigger with our body that we looked at in Miro + + // Set up the mutation with react-query, to generate a presigned URL + + const session = useSession(); + const userId = String(session.data?.user.dbId); + + const { assetListFileKey, valuationDataFileKey } = useMemo( + () => generateS3Keys(userId, portfolioId), + [userId, portfolioId] + ); + + const { + mutate: mutateUploadAssetList, + isLoading: uploadAssetListIsLoading, + isError: uploadAssetListIsError, + } = useMutation(uploadCsvToS3, { + onSuccess: (data) => { + console.log("WAS IT A SUCCESS?", data.success); + console.log("TRIGGERING THE ENGINE"); + // This is where we trigger the engine!!! + const body = { + trigger_file_path: assetListFileKey, + }; + // engine API call goes here + }, + onError: (error) => { + console.error(error); + }, + }); + + const { + mutate: mutatePresignedUrl, + isLoading: presignedUrlIsLoading, + isError: presignedUrlIsError, + } = useMutation(generatePresignedUrl, { + onSuccess: (data) => { + console.log(data.url); + // On success, upload to that URL!!!! + const assetList = [ + { + uprn: uprn, + address: addressLineOne, + postcode: postcode, + }, + ]; + const assetListCsvString = convertToCSV(assetList); + const assetListCsv = new Blob([assetListCsvString], { + type: "text/csv", + }); + + mutateUploadAssetList({ presignedUrl: data.url, file: assetListCsv }); + }, + onError: (error) => { + console.error(error); + }, + }); + + function handleSubmit() { + mutatePresignedUrl({ userId, portfolioId, fileKey: assetListFileKey }); + console.log("SUCCESS"); // This is where we would want to trigger some kind of use feedback + } + + return { + handleSubmit, + presignedUrlIsLoading, + presignedUrlIsError, + }; +} + +export default function RemoteAssessmentModal({ + portfolioId, + isOpen, + setIsOpen, +}: { + isOpen: boolean; + setIsOpen: (isOpen: boolean) => void; + portfolioId: string; +}) { + const [scenario, setScenario] = useState(undefined); + const [housingType, sethousingType] = useState(""); + const [selectedGoal, setSelectedGoal] = useState(""); + const [goalValue, setGoalValue] = useState(""); + const [addressLineOne, setAddressLineOne] = useState(""); + const [postcode, setPostcode] = useState(""); + const [uprn, setUprn] = useState(null); + const [valuation, setValuation] = useState(""); + const [buttonDisabled, setButtonDisabled] = useState(true); + + function handleScenarioChange(event: React.ChangeEvent) { + setScenario(event.target.value); + } + + function handleAddressLineOneChange( + event: React.ChangeEvent + ) { + setAddressLineOne(event.target.value); + } + + function handlePostcodeChange(event: React.ChangeEvent) { + setPostcode(event.target.value); + } + + function handleUprnChange(event: React.ChangeEvent) { + setUprn(Number(event.target.value)); + } + + function handleValuationChange(event: React.ChangeEvent) { + setValuation(event.target.value); + } + + const { handleSubmit, presignedUrlIsLoading, presignedUrlIsError } = + useCreateRemoteAssessment({ + portfolioId, + uprn, + addressLineOne, + postcode, + }); + + useEffect(() => { + function handleButtonDisabled(): boolean { + return !( + scenario && + selectedGoal && + housingType && + addressLineOne && + postcode && + uprn && + valuation + ); + } + + setButtonDisabled(handleButtonDisabled()); + }, [ + scenario, + selectedGoal, + housingType, + addressLineOne, + postcode, + uprn, + valuation, + ]); + + return ( + <> + + setIsOpen(false)} + > + +
+ + +
+
+ + + + {scenario} + +
+ Scenario Name + +
+
+ + sethousingType(option.value)} + /> +
+
+ + setSelectedGoal(option.value)} + /> + {selectedGoal === "Increase EPC" && ( +
+ + { + setGoalValue(option.value); + }} + /> +
+ )} +
+
+ Address Line 1 + +
+
+ Postcode + +
+
+ UPRN + +
+
+ Valuation + +
+
+ +
+ +
+
+
+
+
+
+ + ); +}