This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { errorResponse, getJsonBody, jsonResponse } from "@/lib/api-helpers";
|
||||
import { scrapePartInfo } from "@/lib/scrape";
|
||||
|
||||
export async function GET(req: Request) {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const q = searchParams.get("q")?.trim() ?? "";
|
||||
const carId = searchParams.get("carId");
|
||||
const includeAll = searchParams.get("all") === "true";
|
||||
const includeGlobal = searchParams.get("includeGlobal") === "true";
|
||||
|
||||
const term = q.toLowerCase();
|
||||
const cid = carId ? Number(carId) : undefined;
|
||||
const parts = await prisma.part.findMany({
|
||||
where: {
|
||||
AND: [
|
||||
q
|
||||
? {
|
||||
OR: [
|
||||
{ name: { contains: term } },
|
||||
{ manufacturer: { contains: term } },
|
||||
],
|
||||
}
|
||||
: {},
|
||||
carId
|
||||
? includeGlobal
|
||||
? { OR: [{ carId: cid }, { isGlobal: true }] }
|
||||
: { carId: cid }
|
||||
: includeAll
|
||||
? {}
|
||||
: { isGlobal: true },
|
||||
],
|
||||
},
|
||||
include: { car: true },
|
||||
orderBy: { updatedAt: "desc" },
|
||||
take: 100,
|
||||
});
|
||||
|
||||
return jsonResponse(parts);
|
||||
}
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const body = await getJsonBody(req);
|
||||
if (!body) return errorResponse("Invalid JSON");
|
||||
const {
|
||||
name,
|
||||
manufacturer,
|
||||
supplierLink,
|
||||
url,
|
||||
purchasePrice,
|
||||
notes,
|
||||
carId,
|
||||
orderId,
|
||||
isGlobal,
|
||||
} = body;
|
||||
if (!name) return errorResponse("name is required");
|
||||
|
||||
const normalizedName = name.trim();
|
||||
const carIdNum = carId ? Number(carId) : null;
|
||||
|
||||
// Prevent duplicate part names per car and avoid global/private collisions.
|
||||
if (carIdNum) {
|
||||
const duplicate = await prisma.part.findFirst({
|
||||
where: {
|
||||
name: { equals: normalizedName },
|
||||
OR: [{ carId: carIdNum }, { isGlobal: true }],
|
||||
},
|
||||
});
|
||||
if (duplicate && duplicate.name.toLowerCase() === normalizedName.toLowerCase()) {
|
||||
return errorResponse(
|
||||
`A part named "${duplicate.name}" already exists for this car or globally.`,
|
||||
409,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const part = await prisma.part.create({
|
||||
data: {
|
||||
name: normalizedName,
|
||||
manufacturer,
|
||||
supplierLink,
|
||||
url,
|
||||
purchasePrice: Number(purchasePrice) || 0,
|
||||
notes: notes || "",
|
||||
carId: carIdNum,
|
||||
orderId: orderId ? Number(orderId) : null,
|
||||
// Private by default. User must explicitly mark global.
|
||||
isGlobal: isGlobal === true || isGlobal === "true",
|
||||
},
|
||||
include: { car: true },
|
||||
});
|
||||
|
||||
return jsonResponse(part, 201);
|
||||
}
|
||||
|
||||
export async function PATCH(req: Request) {
|
||||
const body = await getJsonBody(req);
|
||||
if (!body) return errorResponse("Invalid JSON");
|
||||
const { id, ...data } = body;
|
||||
if (!id) return errorResponse("id is required");
|
||||
|
||||
if (data.purchasePrice !== undefined) data.purchasePrice = Number(data.purchasePrice) || 0;
|
||||
if (data.sellPrice !== undefined) data.sellPrice = Number(data.sellPrice) || 0;
|
||||
if (data.qty !== undefined) data.qty = Number(data.qty) || 0;
|
||||
if (data.isGlobal !== undefined) data.isGlobal = data.isGlobal === true || data.isGlobal === "true";
|
||||
|
||||
if (data.name) {
|
||||
const target = await prisma.part.findUnique({ where: { id: Number(id) } });
|
||||
if (target) {
|
||||
const carIdNum = data.carId !== undefined ? Number(data.carId) || null : target.carId;
|
||||
const duplicate = await prisma.part.findFirst({
|
||||
where: {
|
||||
id: { not: target.id },
|
||||
name: { equals: data.name.trim() },
|
||||
OR: [{ carId: carIdNum }, { isGlobal: true }],
|
||||
},
|
||||
});
|
||||
if (
|
||||
duplicate &&
|
||||
duplicate.name.toLowerCase() === data.name.trim().toLowerCase()
|
||||
) {
|
||||
return errorResponse(
|
||||
`A part named "${duplicate.name}" already exists for this car or globally.`,
|
||||
409,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updated = await prisma.part.update({
|
||||
where: { id: Number(id) },
|
||||
data,
|
||||
include: { car: true },
|
||||
});
|
||||
|
||||
return jsonResponse(updated);
|
||||
}
|
||||
Reference in New Issue
Block a user