Skip to content

Commit 022ae1f

Browse files
committed
Adde Projects Dashboar flow
1 parent 94f8b5a commit 022ae1f

File tree

34 files changed

+2092
-185
lines changed

34 files changed

+2092
-185
lines changed

app/api/cart/bulk/route.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// *********************
2+
// Role: Bulk Cart Operations API Route
3+
// Purpose: Handles bulk operations for the shopping cart
4+
// Endpoints:
5+
// - POST: Adds all materials from a project to the cart
6+
// Features:
7+
// - Fetches all products from a project
8+
// - Replaces current cart contents with project materials
9+
// - Maintains quantities from project specifications
10+
// Security: Requires authenticated session
11+
// *********************
12+
13+
import { NextResponse } from "next/server";
14+
import { getServerSession } from "next-auth";
15+
import prisma from "@/utils/db";
16+
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
17+
18+
// Type declaration for global cart storage
19+
declare global {
20+
var cartItems: { [key: string]: Array<{ productId: string; quantity: number }> };
21+
}
22+
23+
// Initialize global cart storage if not exists
24+
if (!global.cartItems) {
25+
global.cartItems = {};
26+
}
27+
28+
// POST handler: Add all project materials to cart
29+
export async function POST(request: Request) {
30+
try {
31+
// Verify user authentication
32+
const session = await getServerSession(authOptions);
33+
if (!session?.user) {
34+
return new NextResponse("Unauthorized", { status: 401 });
35+
}
36+
37+
// Parse request body for project ID
38+
const body = await request.json();
39+
const { projectId } = body;
40+
41+
if (!projectId) {
42+
return new NextResponse("Project ID is required", { status: 400 });
43+
}
44+
45+
// Fetch all products associated with the project
46+
const projectProducts = await prisma.projectProduct.findMany({
47+
where: {
48+
projectId: projectId
49+
},
50+
include: {
51+
product: true
52+
}
53+
});
54+
55+
if (!projectProducts.length) {
56+
return new NextResponse("Project has no products", { status: 400 });
57+
}
58+
59+
// Ensure cart storage is initialized
60+
if (!global.cartItems) {
61+
global.cartItems = {};
62+
}
63+
64+
// Initialize or clear user's cart
65+
const userEmail = session.user.email as string;
66+
if (!global.cartItems[userEmail]) {
67+
global.cartItems[userEmail] = [];
68+
}
69+
70+
// Transform project products to cart format
71+
const cartItems = projectProducts.map(pp => ({
72+
productId: pp.productId,
73+
quantity: pp.quantity
74+
}));
75+
76+
// Replace current cart contents with project materials
77+
global.cartItems[userEmail] = cartItems;
78+
79+
return NextResponse.json({ success: true });
80+
} catch (error) {
81+
console.error("[CART_BULK_POST]", error);
82+
return new NextResponse("Internal error", { status: 500 });
83+
}
84+
}

app/api/cart/route.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// *********************
2+
// Role: Cart API Route Handler
3+
// Purpose: Manages shopping cart operations
4+
// Endpoints:
5+
// - GET: Retrieves all items in the user's cart with product details
6+
// - POST: Adds a new item to the cart
7+
// - DELETE: Clears the user's cart
8+
// Storage: Uses global server-side storage keyed by user email
9+
// Security: Requires authenticated session
10+
// *********************
11+
12+
import { NextResponse } from "next/server";
13+
import { getServerSession } from "next-auth";
14+
import prisma from "@/utils/db";
15+
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
16+
17+
// GET handler: Retrieve cart items with product details
18+
export async function GET() {
19+
try {
20+
// Verify user authentication
21+
const session = await getServerSession(authOptions);
22+
if (!session) {
23+
return new NextResponse("Unauthorized", { status: 401 });
24+
}
25+
26+
// Get cart items from session storage
27+
const cartItems = global.cartItems?.[session.user?.email as string] || [];
28+
29+
// Fetch full product details for all cart items
30+
const products = await prisma.product.findMany({
31+
where: {
32+
id: {
33+
in: cartItems.map(item => item.productId)
34+
}
35+
}
36+
});
37+
38+
// Combine cart quantities with product details
39+
const cartWithDetails = cartItems.map(item => {
40+
const product = products.find(p => p.id === item.productId);
41+
return {
42+
...item,
43+
product
44+
};
45+
});
46+
47+
return NextResponse.json(cartWithDetails);
48+
} catch (error) {
49+
console.error("[CART_GET]", error);
50+
return new NextResponse("Internal error", { status: 500 });
51+
}
52+
}
53+
54+
// POST handler: Add new item to cart
55+
export async function POST(request: Request) {
56+
try {
57+
// Verify user authentication
58+
const session = await getServerSession(authOptions);
59+
if (!session) {
60+
return new NextResponse("Unauthorized", { status: 401 });
61+
}
62+
63+
// Parse request body for product details
64+
const body = await request.json();
65+
const { productId, quantity } = body;
66+
67+
// Initialize global cart storage if needed
68+
if (!global.cartItems) {
69+
global.cartItems = {};
70+
}
71+
72+
// Initialize user's cart array if needed
73+
const userEmail = session.user?.email as string;
74+
if (!global.cartItems[userEmail]) {
75+
global.cartItems[userEmail] = [];
76+
}
77+
78+
// Add new item to user's cart
79+
global.cartItems[userEmail].push({ productId, quantity });
80+
81+
return NextResponse.json({ success: true });
82+
} catch (error) {
83+
console.error("[CART_POST]", error);
84+
return new NextResponse("Internal error", { status: 500 });
85+
}
86+
}
87+
88+
// DELETE handler: Clear all items from cart
89+
export async function DELETE() {
90+
try {
91+
// Verify user authentication
92+
const session = await getServerSession(authOptions);
93+
if (!session) {
94+
return new NextResponse("Unauthorized", { status: 401 });
95+
}
96+
97+
// Reset user's cart to empty array
98+
const userEmail = session.user?.email as string;
99+
if (global.cartItems?.[userEmail]) {
100+
global.cartItems[userEmail] = [];
101+
}
102+
103+
return new NextResponse(null, { status: 204 });
104+
} catch (error) {
105+
console.error("[CART_DELETE]", error);
106+
return new NextResponse("Internal error", { status: 500 });
107+
}
108+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { NextResponse } from "next/server";
2+
import { getServerSession } from "next-auth";
3+
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
4+
import prisma from "@/utils/db";
5+
6+
// GET /api/contractor/materials/templates
7+
export async function GET() {
8+
try {
9+
const session = await getServerSession(authOptions);
10+
if (!session) {
11+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
12+
}
13+
14+
const templates = await prisma.materialTemplate.findMany();
15+
return NextResponse.json(templates);
16+
} catch (error) {
17+
console.error("Error fetching material templates:", error);
18+
return NextResponse.json(
19+
{ error: "Internal server error" },
20+
{ status: 500 }
21+
);
22+
}
23+
}
24+
25+
// POST /api/contractor/materials/templates
26+
export async function POST(request: Request) {
27+
try {
28+
const session = await getServerSession(authOptions);
29+
if (!session) {
30+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
31+
}
32+
33+
const body = await request.json();
34+
const { name, description, unit } = body;
35+
36+
const template = await prisma.materialTemplate.create({
37+
data: {
38+
name,
39+
description,
40+
unit,
41+
},
42+
});
43+
44+
return NextResponse.json(template);
45+
} catch (error) {
46+
console.error("Error creating material template:", error);
47+
return NextResponse.json(
48+
{ error: "Internal server error" },
49+
{ status: 500 }
50+
);
51+
}
52+
}
53+
54+
// PUT /api/contractor/materials/templates/[templateId]
55+
export async function PUT(request: Request) {
56+
try {
57+
const session = await getServerSession(authOptions);
58+
if (!session) {
59+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
60+
}
61+
62+
const body = await request.json();
63+
const { templateId, name, description, unit } = body;
64+
65+
const template = await prisma.materialTemplate.update({
66+
where: { id: templateId },
67+
data: {
68+
name,
69+
description,
70+
unit,
71+
},
72+
});
73+
74+
return NextResponse.json(template);
75+
} catch (error) {
76+
console.error("Error updating material template:", error);
77+
return NextResponse.json(
78+
{ error: "Internal server error" },
79+
{ status: 500 }
80+
);
81+
}
82+
}
83+
84+
// DELETE /api/contractor/materials/templates/[templateId]
85+
export async function DELETE(request: Request) {
86+
try {
87+
const session = await getServerSession(authOptions);
88+
if (!session) {
89+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
90+
}
91+
92+
const { searchParams } = new URL(request.url);
93+
const templateId = searchParams.get("templateId");
94+
95+
if (!templateId) {
96+
return NextResponse.json(
97+
{ error: "Template ID is required" },
98+
{ status: 400 }
99+
);
100+
}
101+
102+
await prisma.materialTemplate.delete({
103+
where: { id: templateId },
104+
});
105+
106+
return NextResponse.json({ message: "Template deleted successfully" });
107+
} catch (error) {
108+
console.error("Error deleting material template:", error);
109+
return NextResponse.json(
110+
{ error: "Internal server error" },
111+
{ status: 500 }
112+
);
113+
}
114+
}

0 commit comments

Comments
 (0)