From 74aea1a86a40aeb431538060abd566e43976cc14 Mon Sep 17 00:00:00 2001 From: Tim Howitz Date: Tue, 3 Jun 2025 16:04:23 +0100 Subject: [PATCH] Added uploading of image with warehouse artefact logging --- src/app/api/warehouse/log/route.ts | 25 +++++++++++++--- src/app/warehouse/page.tsx | 48 ++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/src/app/api/warehouse/log/route.ts b/src/app/api/warehouse/log/route.ts index dc35fe2..dabedb5 100644 --- a/src/app/api/warehouse/log/route.ts +++ b/src/app/api/warehouse/log/route.ts @@ -1,14 +1,22 @@ import { NextRequest, NextResponse } from "next/server"; import { apiAuthMiddleware } from "@utils/apiAuthMiddleware"; import { prisma } from "@utils/prisma"; +import { writeFile } from "fs/promises"; +import { join } from "path"; export async function POST(request: NextRequest) { try { - const body = await request.json(); - const { name, type, description, location, earthquakeCode, warehouseLocation } = body; + const formData = await request.formData(); + const name = formData.get("name") as string; + const type = formData.get("type") as string; + const description = formData.get("description") as string; + const location = formData.get("location") as string; + const earthquakeCode = formData.get("earthquakeCode") as string; + const warehouseLocation = formData.get("warehouseLocation") as string; + const image = formData.get("image") as File | null; const authResult = await apiAuthMiddleware(); - if ("user" in authResult === false) return authResult; // Handle error response + if ("user" in authResult === false) return authResult; const { user } = authResult; @@ -22,6 +30,15 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: "Earthquake code not found" }, { status: 400 }); } + let imageName = "NoImageFound.PNG"; + if (image) { + const buffer = Buffer.from(await image.arrayBuffer()); + const extension = image.type === "image/jpeg" ? "jpg" : "png"; + imageName = `${name}-${new Date().toLocaleDateString("en-GB")}.${extension}`; + const imagePath = join(process.cwd(), "public", "uploads", imageName); + await writeFile(imagePath, buffer); + } + await prisma.artefact.create({ data: { name, @@ -29,7 +46,7 @@ export async function POST(request: NextRequest) { description, earthquakeId: linkedEarthquake.id, warehouseArea: warehouseLocation, - imageName: "NoImageFound.PNG", + imageName, creatorId: user.id, }, }); diff --git a/src/app/warehouse/page.tsx b/src/app/warehouse/page.tsx index e01a26c..c1f22ec 100644 --- a/src/app/warehouse/page.tsx +++ b/src/app/warehouse/page.tsx @@ -77,7 +77,6 @@ function FilterInput({ ); } -// Modal Component for Logging Artefact function LogModal({ onClose }: { onClose: () => void }) { const [name, setName] = useState(""); const [type, setType] = useState(""); @@ -88,6 +87,7 @@ function LogModal({ onClose }: { onClose: () => void }) { const [isRequired, setIsRequired] = useState(true); const [error, setError] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); + const [image, setImage] = useState(null); const handleOverlayClick = (e: { target: any; currentTarget: any }) => { if (e.target === e.currentTarget) { @@ -95,7 +95,21 @@ function LogModal({ onClose }: { onClose: () => void }) { } }; - // todo add uploading image + const handleImageChange = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files[0]) { + const file = e.target.files[0]; + if (file.size > 5 * 1024 * 1024) { + setError("Image size must be less than 5MB"); + return; + } + if (!["image/jpeg", "image/png"].includes(file.type)) { + setError("Only JPEG or PNG images are allowed"); + return; + } + setImage(file); + } + }; + async function handleLog() { if (!name || !type || !description || !location || !earthquakeCode || !warehouseLocation) { setError("All fields are required."); @@ -107,13 +121,19 @@ function LogModal({ onClose }: { onClose: () => void }) { } setIsSubmitting(true); try { - await axios.post("/api/warehouse/log", { - name, - type, - description, - location, - earthquakeCode, - warehouseLocation, + const formData = new FormData(); + formData.append("name", name); + formData.append("type", type); + formData.append("description", description); + formData.append("location", location); + formData.append("earthquakeCode", earthquakeCode); + formData.append("warehouseLocation", warehouseLocation); + if (image) { + formData.append("image", image); + } + + await axios.post("/api/warehouse/log", formData, { + headers: { "Content-Type": "multipart/form-data" }, }); alert(`Logged ${name} to storage: ${warehouseLocation}`); @@ -187,6 +207,14 @@ function LogModal({ onClose }: { onClose: () => void }) { aria-label="Storage Location" disabled={isSubmitting} /> +
{ if (e.target === e.currentTarget) { onClose();