Added uploading of image with warehouse artefact logging

This commit is contained in:
Tim Howitz 2025-06-03 16:04:23 +01:00
parent 5ae30b4178
commit 74aea1a86a
2 changed files with 60 additions and 13 deletions

View File

@ -1,14 +1,22 @@
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import { apiAuthMiddleware } from "@utils/apiAuthMiddleware"; import { apiAuthMiddleware } from "@utils/apiAuthMiddleware";
import { prisma } from "@utils/prisma"; import { prisma } from "@utils/prisma";
import { writeFile } from "fs/promises";
import { join } from "path";
export async function POST(request: NextRequest) { export async function POST(request: NextRequest) {
try { try {
const body = await request.json(); const formData = await request.formData();
const { name, type, description, location, earthquakeCode, warehouseLocation } = body; 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(); const authResult = await apiAuthMiddleware();
if ("user" in authResult === false) return authResult; // Handle error response if ("user" in authResult === false) return authResult;
const { user } = authResult; const { user } = authResult;
@ -22,6 +30,15 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: "Earthquake code not found" }, { status: 400 }); 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({ await prisma.artefact.create({
data: { data: {
name, name,
@ -29,7 +46,7 @@ export async function POST(request: NextRequest) {
description, description,
earthquakeId: linkedEarthquake.id, earthquakeId: linkedEarthquake.id,
warehouseArea: warehouseLocation, warehouseArea: warehouseLocation,
imageName: "NoImageFound.PNG", imageName,
creatorId: user.id, creatorId: user.id,
}, },
}); });

View File

@ -77,7 +77,6 @@ function FilterInput({
); );
} }
// Modal Component for Logging Artefact
function LogModal({ onClose }: { onClose: () => void }) { function LogModal({ onClose }: { onClose: () => void }) {
const [name, setName] = useState(""); const [name, setName] = useState("");
const [type, setType] = useState(""); const [type, setType] = useState("");
@ -88,6 +87,7 @@ function LogModal({ onClose }: { onClose: () => void }) {
const [isRequired, setIsRequired] = useState(true); const [isRequired, setIsRequired] = useState(true);
const [error, setError] = useState(""); const [error, setError] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [image, setImage] = useState<File | null>(null);
const handleOverlayClick = (e: { target: any; currentTarget: any }) => { const handleOverlayClick = (e: { target: any; currentTarget: any }) => {
if (e.target === e.currentTarget) { if (e.target === e.currentTarget) {
@ -95,7 +95,21 @@ function LogModal({ onClose }: { onClose: () => void }) {
} }
}; };
// todo add uploading image const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
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() { async function handleLog() {
if (!name || !type || !description || !location || !earthquakeCode || !warehouseLocation) { if (!name || !type || !description || !location || !earthquakeCode || !warehouseLocation) {
setError("All fields are required."); setError("All fields are required.");
@ -107,13 +121,19 @@ function LogModal({ onClose }: { onClose: () => void }) {
} }
setIsSubmitting(true); setIsSubmitting(true);
try { try {
await axios.post("/api/warehouse/log", { const formData = new FormData();
name, formData.append("name", name);
type, formData.append("type", type);
description, formData.append("description", description);
location, formData.append("location", location);
earthquakeCode, formData.append("earthquakeCode", earthquakeCode);
warehouseLocation, 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}`); alert(`Logged ${name} to storage: ${warehouseLocation}`);
@ -187,6 +207,14 @@ function LogModal({ onClose }: { onClose: () => void }) {
aria-label="Storage Location" aria-label="Storage Location"
disabled={isSubmitting} disabled={isSubmitting}
/> />
<input
type="file"
accept="image/jpeg,image/png"
onChange={handleImageChange}
className="w-full p-2 border border-neutral-300 rounded-md placeholder-neutral-400 focus:ring-2 focus:ring-blue-500"
aria-label="Artefact Image"
disabled={isSubmitting}
/>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<input <input
type="checkbox" type="checkbox"
@ -357,6 +385,8 @@ function EditModal({ artefact, onClose }: { artefact: ExtendedArtefact; onClose:
const [error, setError] = useState(""); const [error, setError] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
// todo add display of image
const handleOverlayClick = (e: { target: any; currentTarget: any }) => { const handleOverlayClick = (e: { target: any; currentTarget: any }) => {
if (e.target === e.currentTarget) { if (e.target === e.currentTarget) {
onClose(); onClose();