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}
+
+
+
+
+
+ );
+}