diff --git a/src/app/components/Navbar.tsx b/src/app/components/Navbar.tsx index 2ee38b25..bb80afd3 100644 --- a/src/app/components/Navbar.tsx +++ b/src/app/components/Navbar.tsx @@ -43,6 +43,7 @@ function Nav({ userImage }: { userImage: string }) {
{makeLink("/home", "Home")} {makeLink("/due-considerations", "Due Considerations")} + {makeLink("/eco-spreadsheet", "Eco Spreadsheet")} {makeLink("/help", "Help")}
diff --git a/src/app/due-considerations/page.tsx b/src/app/due-considerations/page.tsx index 9a05cfdb..3af9848d 100644 --- a/src/app/due-considerations/page.tsx +++ b/src/app/due-considerations/page.tsx @@ -4,7 +4,6 @@ import { useMemo, useState } from "react"; import { SelectFolder } from "../components/due-considerations/SelectFolder"; import { Button } from "../shadcn_components/ui/button"; import { useSession } from "next-auth/react"; -import { useRouter } from "next/navigation"; import { useMutation } from "@tanstack/react-query"; import { Input } from "../shadcn_components/ui/input"; diff --git a/src/app/eco-spreadsheet/page.tsx b/src/app/eco-spreadsheet/page.tsx new file mode 100644 index 00000000..e4d752f8 --- /dev/null +++ b/src/app/eco-spreadsheet/page.tsx @@ -0,0 +1,256 @@ +"use client"; + +import { useMemo, useState } from "react"; +import { SelectFolder } from "../components/due-considerations/SelectFolder"; +import { Button } from "../shadcn_components/ui/button"; +import { useSession } from "next-auth/react"; +import { useMutation } from "@tanstack/react-query"; +import { Input } from "../shadcn_components/ui/input"; + +const Spinner = () => { + return ( +
+ ); +}; + +function generateEcoSpreadsheetS3Folder(userId: string) { + const timestamp = new Date().toISOString().replace(/[:.-]/g, ""); + const key = `${userId}/${timestamp}/`; + return key; +} + +async function postEcoSpreadsheet(userId: string, folderKey: string) { + // Triggers the eco spreadsheet process + const body = JSON.stringify({ + userId: userId, + folderKey: folderKey, + }); + + try { + const response = await fetch(`/api/eco-spreadsheet`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: body, + }); + + if (!response.ok) { + throw new Error("Network response was not ok"); + } + + // Handle the response as needed + const data = await response.json(); + return data; + } catch (error) { + console.error(error); + // Handle the error appropriately + } +} + +const useUploadFiles = ({ + files, + userId, + setDownloadUrl, +}: { + files: File[]; + userId: string; + setDownloadUrl: React.Dispatch>; +}) => { + const { mutate: mutateUploadFiles, isLoading: isUploadLoading } = useMutation( + uploadFilesToS3, + { + onSuccess: async () => { + console.log("Trigger the eco spreadsheet process"); + console.log("Folder key: ", folderKey); + const data = await postEcoSpreadsheet(userId, folderKey); + setDownloadUrl(data.download_url); + }, + onError: (error) => { + console.error(error); + }, + } + ); + + const { mutate, isLoading: isGeneratingUrlLoading } = useMutation( + generatePresignedUrls, + { + onSuccess: (data) => { + try { + const response = mutateUploadFiles({ + presignedUrls: data.urls, + files: files, + }); + return response; + } catch (error) { + console.error(error); + } + }, + onError: (error) => { + console.error(error); + }, + } + ); + + const [folderKey, setFolderKey] = useState(""); + + const handleUpload = (newFolderKey: string) => { + setFolderKey(newFolderKey); + mutate({ folderKey: newFolderKey, files: files }); + }; + + return { + handleUpload, + isGeneratingUrlLoading, + isUploadLoading, + }; +}; + +async function generatePresignedUrls({ + folderKey, + files, +}: { + folderKey: string; + files: File[]; +}) { + const body = JSON.stringify({ + files: files.map((file) => ({ + fileKey: folderKey + file.name, + contentType: file.type, + })), + }); + + const presignedResponse = await fetch("/api/upload/eco-spreadsheet", { + method: "POST", + body: body, + }); + + if (!presignedResponse.ok) { + throw new Error("Network response was not ok"); + } + const presignedUrls = await presignedResponse.json(); + return presignedUrls; +} + +async function uploadFilesToS3({ + presignedUrls, + files, +}: { + presignedUrls: string[]; + files: File[]; +}) { + await Promise.all( + files.map((file, index) => { + return fetch(presignedUrls[index], { + method: "PUT", + headers: { + "Content-Type": file.type, + }, + body: file, + }); + }) + ); +} + +export default function EcoSpreadsheetHome() { + const [files, setFiles] = useState([]); + const [buttonDisabled, setButtonDisabled] = useState(true); + const [uploadMessage, setUploadMessage] = useState(""); + const [downloadUrl, setDownloadUrl] = useState(""); + + const session = useSession(); + const userId = String(session.data?.user.dbId); + + const { handleUpload, isGeneratingUrlLoading, isUploadLoading } = + useUploadFiles({ + files, + userId, + setDownloadUrl, + }); + + const initiateUpload = () => { + setDownloadUrl(""); + const newFolderKey = generateEcoSpreadsheetS3Folder(userId); + handleUpload(newFolderKey); + }; + + function handleOnChange(e: React.ChangeEvent) { + if (e.target.files && e.target.files.length === 3) { + const filesArray = Array.from(e.target.files); + const extensions = filesArray.map((file) => + file.name.split(".").pop()?.toLowerCase() + ); + const names = filesArray.map((file) => file.name.toLowerCase()); + + if ( + extensions.includes("xml") && + extensions.includes(".pdf") && + names.includes("epr") && + names.includes("ventilation") + ) { + setFiles(filesArray); + setButtonDisabled(false); + setUploadMessage(""); + } else { + setUploadMessage( + "Please select the .xml, the epr and the ventilation and condition report" + ); + setButtonDisabled(true); + } + } else { + setUploadMessage("Please select exactly 3 files."); + setButtonDisabled(true); + } + } + + return ( +
+
+
Please select the following files:
+
    +
  • An xml
  • +
  • EPR pdf
  • +
  • Ventilation and Condition pdf
  • +
+
+ Additionally, you can upload an optional ECO spreadsheet if you wish + to add new records to an already populated spreadsheet +
+ +
+ Make sure these documents all relate to the same property +
+ +
+ +
+ +
+ +
+
{uploadMessage}
+ +
+
+ {isGeneratingUrlLoading || isUploadLoading ? ( + + ) : downloadUrl ? ( + + Download Eco Spreadsheet + + ) : null} +
+
+
+ ); +}