diff --git a/src/app/api/energy-assessment-documents/route.ts b/src/app/api/energy-assessment-documents/route.ts new file mode 100644 index 00000000..89b9140d --- /dev/null +++ b/src/app/api/energy-assessment-documents/route.ts @@ -0,0 +1,49 @@ +// pages/api/get-presigned-url.ts +import S3 from "aws-sdk/clients/s3"; +import { NextRequest, NextResponse } from "next/server"; +import { z } from "zod"; + +const PresignedUrlBodySchema = z.object({ + fileKey: z.string(), +}); + +export async function POST(request: NextRequest) { + const body = await request.json(); + let validatedBody; + + try { + validatedBody = PresignedUrlBodySchema.parse(body); + } catch (error) { + console.error("Invalid input: ", error); + return new NextResponse(JSON.stringify({ msg: "Invalid input" }), { + status: 400, + }); + } + + try { + const s3 = new S3({ + signatureVersion: "v4", + region: process.env.PRESIGN_AWS_REGION, + accessKeyId: process.env.RETROFIT_ENERGY_ASSESSMENTS_AWS_ACCESS_KEY, + secretAccessKey: process.env.ENERGY_ASSESSMENTS_AWS_SECRET, + }); + + const { fileKey } = validatedBody; + + // Presigned URL is valid for 5 minutes + const preSignedUrl = await s3.getSignedUrl("getObject", { + Bucket: process.env.RETROFIT_ENERGY_ASSESSMENTS_BUCKET, + Key: fileKey, + Expires: 5 * 60, + }); + + return new NextResponse(JSON.stringify({ url: preSignedUrl }), { + status: 200, + }); + } catch (error) { + console.error(error); + return new NextResponse(JSON.stringify({ msg: "Internal server error" }), { + status: 500, + }); + } +} diff --git a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx index 49fae6e3..05d63182 100644 --- a/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx +++ b/src/app/portfolio/[slug]/building-passport/[propertyId]/energy-assessment/DocumentsTable.tsx @@ -8,13 +8,14 @@ import { TableRow, } from "@/app/shadcn_components/ui/table"; import { BrandBlueButton } from "@/app/components/Buttons"; +import { useMutation } from "@tanstack/react-query"; import { DocumentType } from "@/app/db/schema/energy_assessments"; // Use the type directly from the array type Document = { id: bigint; documentType: (typeof DocumentType)[number]; // Create a union type from the array - documentLocation: string; + documentLocation: string; // S3 file key uploadedAt: Date; }; @@ -35,6 +36,21 @@ const descriptions: { [key in (typeof DocumentType)[number]]: string } = { "Scenario Site Notes": "Site notes from the scenario analysis", }; +// Fetch the presigned URL from the API +async function generatePresignedUrl(fileKey: string) { + const response = await fetch("/api/energy-assessment-documents", { + method: "POST", + body: JSON.stringify({ fileKey }), + }); + + if (!response.ok) { + throw new Error("Failed to generate presigned URL"); + } + + const data = await response.json(); + return data.url; +} + export const DocumentsTable: React.FC = ({ documents, allowedTypes, @@ -43,38 +59,38 @@ export const DocumentsTable: React.FC = ({ allowedTypes.includes(doc.documentType) ); - // Track displayed descriptions - const displayedDescriptions: Record<(typeof DocumentType)[number], boolean> = - allowedTypes.reduce((acc, type) => { - acc[type] = false; - return acc; - }, {} as Record<(typeof DocumentType)[number], boolean>); - - const processedDocuments = relevantDocuments.map((doc) => { - const showDescription = !displayedDescriptions[doc.documentType]; - if (showDescription) { - displayedDescriptions[doc.documentType] = true; + // Mutation to handle the presigned URL generation + const { mutate: fetchPresignedUrl } = useMutation( + async (fileKey: string) => await generatePresignedUrl(fileKey), + { + onSuccess: (url) => { + window.open(url, "_blank"); // Open the file in a new tab + }, + onError: (error) => { + console.error("Error generating presigned URL:", error); + }, } - return { ...doc, showDescription }; - }); + ); + + const handleDownload = (documentLocation: string) => { + fetchPresignedUrl(documentLocation); // Generate URL and open in new tab + }; return ( - {processedDocuments.map((doc) => ( + {relevantDocuments.map((doc) => ( {doc.documentType} - {doc.showDescription ? descriptions[doc.documentType] : ""} + {descriptions[doc.documentType] || ""} - console.log(`Downloading ${doc.documentLocation}`) - } + onClick={() => handleDownload(doc.documentLocation)} // Call the download handler />