-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b9d0043
commit 2b27ebb
Showing
19 changed files
with
303 additions
and
221 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,4 @@ MONGO_DATABASE=dbname | |
TOTAL_PRODUCTS=2000 | ||
PORT=3000 | ||
NEXT_PUBLIC_URL=http://localhost:3000 | ||
JWT_SECRET=secret |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import Image from "next/image"; | ||
import { formatCurrency } from "@/lib/utils/number"; | ||
import { CartItem as CartItemType } from '@/lib/db/cart_collection'; | ||
import { CartItemAction } from "./CartItemAction"; | ||
|
||
type CartItemProps = { | ||
item: CartItemType | ||
} | ||
|
||
export function CartItem({ item }: CartItemProps) { | ||
return ( | ||
<div className="flex items-center py-6 border-b border-gray-200 last:border-b-0 bg-white rounded-lg mb-4 overflow-hidden transition-all duration-300"> | ||
<div className="flex-shrink-0 w-24 h-24 bg-gray-100 rounded-md overflow-hidden relative ml-4"> | ||
<Image | ||
src={item.thumbnail} | ||
alt={item.name} | ||
layout="fill" | ||
objectFit="cover" | ||
className="transition-transform duration-300 hover:scale-105" | ||
/> | ||
</div> | ||
<div className="ml-6 flex-1"> | ||
<h3 className="text-lg font-semibold text-gray-800">{item.name}</h3> | ||
<div className="mt-2 flex items-center space-x-4"> | ||
<p className="text-sm text-gray-600">Unit Price: <span className="text-gray-800 font-medium">{formatCurrency(item.price)}</span></p> | ||
<p className="text-sm text-gray-600">Quantity: <span className="text-gray-800 font-medium">{item.quantity}</span></p> | ||
</div> | ||
<p className="mt-2 text-md text-gray-700">Total: <span className="text-gray-900 font-bold">{formatCurrency(item.price * item.quantity)}</span></p> | ||
</div> | ||
<div className="mr-4"> | ||
<CartItemAction item={item} /> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
'use client' | ||
|
||
import { FiMinus, FiPlus, FiTrash2 } from "react-icons/fi"; | ||
import type { CartItem } from "@/lib/db/cart_collection"; | ||
import { removeFromCart, updateCartItemQuantity } from "@/lib/actions/cart"; | ||
|
||
type CartItemActionProps = { | ||
item: CartItem | ||
} | ||
|
||
export function CartItemAction({ item }: CartItemActionProps) { | ||
return ( | ||
<div className="flex items-center"> | ||
<button | ||
type="submit" | ||
className="text-gray-500 hover:text-gray-600" | ||
onClick={() => { | ||
if (item.quantity <= 1) { | ||
removeFromCart(item.productId.toString()) | ||
} else { | ||
updateCartItemQuantity(item.productId.toString(), item.quantity - 1) | ||
} | ||
}} | ||
> | ||
<FiMinus /> | ||
</button> | ||
<span className="mx-2 text-gray-700">{item.quantity}</span> | ||
<button | ||
type="submit" | ||
className="text-gray-500 hover:text-gray-600" | ||
onClick={() => updateCartItemQuantity(item.productId.toString(), item.quantity + 1)} | ||
> | ||
<FiPlus /> | ||
</button> | ||
<button | ||
type="submit" | ||
className="ml-4 text-red-500 hover:text-red-600" | ||
onClick={() => removeFromCart(item.productId.toString())} | ||
> | ||
<FiTrash2 /> | ||
</button> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { CartItemSchema, cartsCollection } from "@/lib/db/cart_collection"; | ||
import { productsCollection } from "@/lib/db/product_collection"; | ||
import { NextRequest, NextResponse } from "next/server"; | ||
import { ObjectId } from "mongodb"; | ||
import { z } from "zod"; | ||
|
||
const CartItemDto = z.object({ | ||
quantity: z.number() | ||
}) | ||
const CartParamsDto = z.object({ | ||
productId: z.string() | ||
}) | ||
|
||
type NextRouteParams = { | ||
params: { | ||
productId: string | ||
} | ||
} | ||
|
||
export async function POST(req: NextRequest, { params }: NextRouteParams) { | ||
try { | ||
const { productId } = CartParamsDto.parse(params) | ||
const userId = String(req.headers.get("x-user-id")) | ||
const rawBody = await req.json(); | ||
const { quantity } = CartItemDto.parse(rawBody) | ||
|
||
const product = await productsCollection.findOne({ _id: new ObjectId(productId) }); | ||
if (!product) { | ||
return NextResponse.json({ errors: ["Product not found"] }, { status: 404 }); | ||
} | ||
|
||
const cartItem = CartItemSchema.parse({ | ||
productId: new ObjectId(product._id), | ||
name: product.name, | ||
price: product.price, | ||
thumbnail: product.thumbnail, | ||
quantity: quantity, | ||
}); | ||
|
||
await cartsCollection.updateOne( | ||
{ userId: new ObjectId(userId) }, | ||
{ $push: { items: cartItem } }, | ||
{ upsert: true } | ||
); | ||
|
||
return NextResponse.json({ message: "Product added to cart successfully" }); | ||
} catch (error) { | ||
return NextResponse.json({ errors: ["An unexpected error occurred"] }, { status: 500 }); | ||
} | ||
} | ||
|
||
export async function PUT(req: NextRequest, { params }: NextRouteParams) { | ||
try { | ||
const { productId } = CartParamsDto.parse(params) | ||
const userId = String(req.headers.get("x-user-id")); | ||
const rawBody = await req.json(); | ||
const { quantity } = CartItemDto.parse(rawBody) | ||
|
||
await cartsCollection.updateOne( | ||
{ | ||
userId: new ObjectId(userId), | ||
"items.productId": new ObjectId(productId) | ||
}, | ||
{ | ||
$set: { | ||
"items.$.quantity": quantity | ||
} | ||
} | ||
); | ||
|
||
return NextResponse.json({ message: "Product qty updated in cart successfully" }); | ||
} catch (error) { | ||
return NextResponse.json({ errors: ["An unexpected error occurred"] }, { status: 500 }); | ||
} | ||
} | ||
|
||
export async function DELETE(req: NextRequest, { params }: NextRouteParams) { | ||
try { | ||
const userId = String(req.headers.get("x-user-id")); | ||
|
||
const { productId } = CartParamsDto.parse(params) | ||
|
||
await cartsCollection.updateOne( | ||
{ userId: new ObjectId(userId) }, | ||
{ $pull: { items: { productId: new ObjectId(productId) } } } | ||
); | ||
|
||
return NextResponse.json({ message: "Product removed from cart successfully" }); | ||
} catch (error) { | ||
return NextResponse.json({ errors: ["An unexpected error occurred"] }, { status: 500 }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { cartsCollection } from "@/lib/db/cart_collection"; | ||
import { NextRequest, NextResponse } from "next/server"; | ||
import { ObjectId } from "mongodb"; | ||
|
||
export async function GET(req: NextRequest) { | ||
try { | ||
const userId = String(req.headers.get("x-user-id")) | ||
const cart = await cartsCollection.findOne({ userId: new ObjectId(userId) }); | ||
if (!cart) { | ||
return NextResponse.json({ errors: ["Cart not found"] }, { status: 404 }); | ||
} | ||
return NextResponse.json(cart); | ||
} catch (error) { | ||
return NextResponse.json({ errors: ["An unexpected error occurred"] }, { status: 500 }); | ||
} | ||
} |
Oops, something went wrong.