Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions app/api/cart/bulk/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// *********************
// Role: Bulk Cart Operations API Route
// Purpose: Handles bulk operations for the shopping cart
// Endpoints:
// - POST: Adds all materials from a project to the cart
// Features:
// - Fetches all products from a project
// - Replaces current cart contents with project materials
// - Maintains quantities from project specifications
// Security: Requires authenticated session
// *********************

import { NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import prisma from "@/utils/db";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";

// Type declaration for global cart storage
declare global {
var cartItems: { [key: string]: Array<{ productId: string; quantity: number }> };
}

// Initialize global cart storage if not exists
if (!global.cartItems) {
global.cartItems = {};
}

// POST handler: Add all project materials to cart
export async function POST(request: Request) {
try {
// Verify user authentication
const session = await getServerSession(authOptions);
if (!session?.user) {
return new NextResponse("Unauthorized", { status: 401 });
}

// Parse request body for project ID
const body = await request.json();
const { projectId } = body;

if (!projectId) {
return new NextResponse("Project ID is required", { status: 400 });
}

// Fetch all products associated with the project
const projectProducts = await prisma.projectProduct.findMany({
where: {
projectId: projectId
},
include: {
product: true
}
});

if (!projectProducts.length) {
return new NextResponse("Project has no products", { status: 400 });
}

// Ensure cart storage is initialized
if (!global.cartItems) {
global.cartItems = {};
}

// Initialize or clear user's cart
const userEmail = session.user.email as string;
if (!global.cartItems[userEmail]) {
global.cartItems[userEmail] = [];
}

// Transform project products to cart format
const cartItems = projectProducts.map(pp => ({
productId: pp.productId,
quantity: pp.quantity
}));

// Replace current cart contents with project materials
global.cartItems[userEmail] = cartItems;

return NextResponse.json({ success: true });
} catch (error) {
console.error("[CART_BULK_POST]", error);
return new NextResponse("Internal error", { status: 500 });
}
}
108 changes: 108 additions & 0 deletions app/api/cart/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// *********************
// Role: Cart API Route Handler
// Purpose: Manages shopping cart operations
// Endpoints:
// - GET: Retrieves all items in the user's cart with product details
// - POST: Adds a new item to the cart
// - DELETE: Clears the user's cart
// Storage: Uses global server-side storage keyed by user email
// Security: Requires authenticated session
// *********************

import { NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import prisma from "@/utils/db";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";

// GET handler: Retrieve cart items with product details
export async function GET() {
try {
// Verify user authentication
const session = await getServerSession(authOptions);
if (!session) {
return new NextResponse("Unauthorized", { status: 401 });
}

// Get cart items from session storage
const cartItems = global.cartItems?.[session.user?.email as string] || [];

// Fetch full product details for all cart items
const products = await prisma.product.findMany({
where: {
id: {
in: cartItems.map(item => item.productId)
}
}
});

// Combine cart quantities with product details
const cartWithDetails = cartItems.map(item => {
const product = products.find(p => p.id === item.productId);
return {
...item,
product
};
});

return NextResponse.json(cartWithDetails);
} catch (error) {
console.error("[CART_GET]", error);
return new NextResponse("Internal error", { status: 500 });
}
}

// POST handler: Add new item to cart
export async function POST(request: Request) {
try {
// Verify user authentication
const session = await getServerSession(authOptions);
if (!session) {
return new NextResponse("Unauthorized", { status: 401 });
}

// Parse request body for product details
const body = await request.json();
const { productId, quantity } = body;

// Initialize global cart storage if needed
if (!global.cartItems) {
global.cartItems = {};
}

// Initialize user's cart array if needed
const userEmail = session.user?.email as string;
if (!global.cartItems[userEmail]) {
global.cartItems[userEmail] = [];
}

// Add new item to user's cart
global.cartItems[userEmail].push({ productId, quantity });

return NextResponse.json({ success: true });
} catch (error) {
console.error("[CART_POST]", error);
return new NextResponse("Internal error", { status: 500 });
}
}

// DELETE handler: Clear all items from cart
export async function DELETE() {
try {
// Verify user authentication
const session = await getServerSession(authOptions);
if (!session) {
return new NextResponse("Unauthorized", { status: 401 });
}

// Reset user's cart to empty array
const userEmail = session.user?.email as string;
if (global.cartItems?.[userEmail]) {
global.cartItems[userEmail] = [];
}

return new NextResponse(null, { status: 204 });
} catch (error) {
console.error("[CART_DELETE]", error);
return new NextResponse("Internal error", { status: 500 });
}
}
114 changes: 114 additions & 0 deletions app/api/contractor/materials/templates/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
import prisma from "@/utils/db";

// GET /api/contractor/materials/templates
export async function GET() {
try {
const session = await getServerSession(authOptions);
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const templates = await prisma.materialTemplate.findMany();
return NextResponse.json(templates);
} catch (error) {
console.error("Error fetching material templates:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
}

// POST /api/contractor/materials/templates
export async function POST(request: Request) {
try {
const session = await getServerSession(authOptions);
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const body = await request.json();
const { name, description, unit } = body;

const template = await prisma.materialTemplate.create({
data: {
name,
description,
unit,
},
});

return NextResponse.json(template);
} catch (error) {
console.error("Error creating material template:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
}

// PUT /api/contractor/materials/templates/[templateId]
export async function PUT(request: Request) {
try {
const session = await getServerSession(authOptions);
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const body = await request.json();
const { templateId, name, description, unit } = body;

const template = await prisma.materialTemplate.update({
where: { id: templateId },
data: {
name,
description,
unit,
},
});

return NextResponse.json(template);
} catch (error) {
console.error("Error updating material template:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
}

// DELETE /api/contractor/materials/templates/[templateId]
export async function DELETE(request: Request) {
try {
const session = await getServerSession(authOptions);
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

const { searchParams } = new URL(request.url);
const templateId = searchParams.get("templateId");

if (!templateId) {
return NextResponse.json(
{ error: "Template ID is required" },
{ status: 400 }
);
}

await prisma.materialTemplate.delete({
where: { id: templateId },
});

return NextResponse.json({ message: "Template deleted successfully" });
} catch (error) {
console.error("Error deleting material template:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
}
Loading