Skip to content

Commit 352ec3e

Browse files
authored
Merge pull request #55 from Shreyanshi210205/shreyanshi_api
feat(cart): implement cart functionality with add, update, remove, and checkout features
2 parents a12e628 + 9bc4b0a commit 352ec3e

File tree

5 files changed

+211
-2
lines changed

5 files changed

+211
-2
lines changed

backend/src/app.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import userRoutes from "./routes/user.routes";
88

99
// Consolidated Imports
1010
import productRoutes from "./routes/product.routes";
11+
import cartRoutes from "./routes/cartRoutes";
1112
import mongoose from "mongoose";
1213
import bcrypt from "bcryptjs";
1314
import cookieParser from "cookie-parser";
@@ -127,6 +128,7 @@ app.get("/protected", authenticate, (req: AuthRequest, res: Response) => {
127128
app.use('/api/health', healthRoutes);
128129
app.use("/api/products", productRoutes);
129130
app.use("/api/users", userRoutes);
131+
app.use("/api/cart", cartRoutes);
130132

131133
// Default
132134
app.get("/", (_req, res) => {
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import { Request, Response } from "express";
2+
import mongoose from "mongoose";
3+
import Cart, { CartItemInput } from "../models/cart.model";
4+
5+
6+
export const addItem = async (req:Request, res:Response): Promise<void> => {
7+
const userId = req.user?.id;
8+
if (!userId) {
9+
res.status(401).json({ message: "Unauthorized"}); return;
10+
}
11+
const {productId, name, price, quantity} = req.body as {
12+
productId: string;
13+
name: string;
14+
price: number;
15+
quantity: number;
16+
};
17+
18+
try {
19+
let cart = await Cart.findOne({ userId });
20+
if (!cart) cart = new Cart({ userId, items: [], totalPrice: 0 });
21+
const existingItem = cart.items.find(i => i.productId.toString() === productId);
22+
if (existingItem) {
23+
existingItem.quantity += quantity;
24+
} else {
25+
const newItem: CartItemInput = {
26+
productId: new mongoose.Types.ObjectId(productId),
27+
name,
28+
price,
29+
quantity,
30+
};
31+
cart.items.push(newItem as any);
32+
}
33+
cart.totalPrice = cart.items.reduce((sum, i) => sum + i.price * i.quantity, 0);
34+
await cart.save();
35+
res.status(200).json(cart);
36+
} catch (err) {
37+
console.error("Add item error:", err);
38+
res.status(500).json({ message: "Server error" });
39+
}
40+
};
41+
export const removeItem = async (req: Request, res: Response): Promise<void> => {
42+
const userId = req.user?.id;
43+
if (!userId) {
44+
res.status(401).json({ message: "Unauthorized" });
45+
return;
46+
}
47+
const { itemId } = req.params;
48+
try {
49+
const cart = await Cart.findOne({ userId });
50+
if (!cart) {
51+
res.status(404).json({ message: "Cart not found" });
52+
return;
53+
}
54+
const itemIndex = cart.items.findIndex(i => (i as any)._id?.toString() === itemId);
55+
if (itemIndex===-1) {
56+
res.status(404).json({ message: "Item not found" });
57+
return;
58+
}
59+
const item = cart.items[itemIndex];
60+
cart.totalPrice -= item.price * item.quantity;
61+
cart.items.splice(itemIndex, 1);
62+
await cart.save();
63+
64+
res.status(200).json(cart);
65+
} catch (err) {
66+
console.error("Remove item error:", err);
67+
res.status(500).json({ message: "Server error" });
68+
}
69+
};
70+
export const updateQuantity = async (req: Request, res: Response): Promise<void> => {
71+
const userId = req.user?.id;
72+
if (!userId) {
73+
res.status(401).json({ message: "Unauthorized" });
74+
return;
75+
}
76+
const { itemId } = req.params;
77+
const { quantity } = req.body as { quantity: number };
78+
try {
79+
const cart = await Cart.findOne({ userId });
80+
if (!cart) {
81+
res.status(404).json({ message: "Cart not found" });
82+
return;
83+
}
84+
const item = cart.items.find(i => (i as any)._id?.toString() === itemId);
85+
if (!item) {
86+
res.status(404).json({ message: "Item not found" });
87+
return;
88+
}
89+
item.quantity = quantity;
90+
cart.totalPrice = cart.items.reduce(
91+
(sum, i) => sum + i.price * i.quantity,
92+
0
93+
);
94+
95+
await cart.save();
96+
res.status(200).json(cart);
97+
} catch (err) {
98+
console.error("Update quantity error:", err);
99+
res.status(500).json({ message: "Server error" });
100+
}
101+
}
102+
export const getCart = async (req: Request, res: Response): Promise<void> => {
103+
const userId = req.user?.id;
104+
if (!userId) {
105+
res.status(401).json({ message: "Unauthorized" });
106+
return;
107+
}
108+
109+
try {
110+
const cart = await Cart.findOne({ userId }).populate("items.productId");
111+
res.status(200).json(cart || { items: [], totalPrice: 0 });
112+
} catch (err) {
113+
console.error("Get cart error:", err);
114+
res.status(500).json({ message: "Server error" });
115+
}
116+
};
117+
118+
export const checkout = async (req: Request, res: Response): Promise<void> => {
119+
const userId = req.user?.id;
120+
if (!userId) {
121+
res.status(401).json({ message: "Unauthorized" });
122+
return;
123+
}
124+
125+
try {
126+
const cart = await Cart.findOne({ userId });
127+
128+
if (!cart || cart.items.length === 0) {
129+
res.status(400).json({ message: "Cart is empty" });
130+
return;
131+
}
132+
133+
const orderData = {
134+
userId: new mongoose.Types.ObjectId(userId),
135+
items: cart.items,
136+
total: cart.totalPrice,
137+
date: new Date(),
138+
};
139+
140+
//Create order database and here push the orderData in it.
141+
142+
cart.items = [];
143+
cart.totalPrice = 0;
144+
await cart.save();
145+
146+
res.status(200).json({ message: "Checkout successful" });
147+
} catch (err) {
148+
console.error("Checkout error:", err);
149+
res.status(500).json({ message: "Server error" });
150+
}
151+
};

backend/src/models/cart.model.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import mongoose, { Schema, Types } from "mongoose";
2+
3+
export interface ICartItem {
4+
_id?: Types.ObjectId;
5+
productId: Types.ObjectId;
6+
name: string;
7+
price: number;
8+
quantity: number;
9+
}
10+
11+
export type CartItemInput = Omit<ICartItem, "_id">;
12+
13+
export interface ICart extends mongoose.Document {
14+
userId: Types.ObjectId;
15+
items: ICartItem[];
16+
totalPrice: number;
17+
}
18+
19+
const cartItemSchema = new Schema<ICartItem>(
20+
{
21+
productId: { type: Schema.Types.ObjectId, ref: "Product", required: true },
22+
name: { type: String, required: true },
23+
price: { type: Number, required: true },
24+
quantity: { type: Number, default: 1, min: 1 },
25+
},
26+
{ _id: true }
27+
);
28+
29+
const cartSchema = new Schema<ICart>(
30+
{
31+
userId: { type: Schema.Types.ObjectId, ref: "User", required: true },
32+
items: [cartItemSchema],
33+
totalPrice: { type: Number, default: 0 },
34+
},
35+
{ timestamps: true }
36+
);
37+
38+
export default mongoose.model<ICart>("Cart", cartSchema);

backend/src/routes/cartRoutes.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import express, { Router } from "express";
2+
import {
3+
addItem,
4+
updateQuantity,
5+
removeItem,
6+
getCart,
7+
checkout,
8+
} from "../controllers/cart.controller";
9+
10+
const router: Router = express.Router();
11+
12+
router.post("/", addItem);
13+
router.patch("/:itemId", updateQuantity);
14+
router.delete("/:itemId", removeItem);
15+
router.get("/", getCart);
16+
router.post("/checkout", checkout);
17+
18+
export default router;

backend/src/types.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
declare namespace Express {
22
export interface Request {
3-
user?: { sub: string; username: string; role: string };
3+
user?: { id: string; username: string; role: string };
44
}
5-
}
5+
}

0 commit comments

Comments
 (0)