Skip to content

Commit

Permalink
Cart function updates
Browse files Browse the repository at this point in the history
  • Loading branch information
david emioma committed Jun 8, 2024
1 parent 3802f26 commit 4291969
Show file tree
Hide file tree
Showing 8 changed files with 353 additions and 421 deletions.
282 changes: 282 additions & 0 deletions actions/cart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
"use server";

import prismadb from "@/lib/prisma";
import { apiRatelimit } from "@/lib/redis";
import { currentRole, currentUser } from "@/lib/auth";
import { CartItemSchema, CartItemValidator } from "@/lib/validators/cart-item";

export const addToCartHandler = async (values: CartItemValidator) => {
try {
//Check if there is a current user
const { user } = await currentUser();

if (!user || !user.id) {
throw new Error("Unauthorized, You need to be logged in.");
}

const { role } = await currentRole();

if (role !== "USER") {
throw new Error("Unauthorized, Only users can delete cartItem");
}

const { success } = await apiRatelimit.limit(user.id);

if (!success && process.env.VERCEL_ENV === "production") {
throw new Error("Too Many Requests! try again in 1 min");
}

const validatedBody = CartItemSchema.parse(values);

if (!validatedBody) {
throw new Error("Invalid Credentials");
}

const { productId, productItemId, availableItemId } = validatedBody;

//Check if product exists
const productExists = await prismadb.product.findUnique({
where: {
id: productId,
},
});

if (!productExists) {
throw new Error("Product with provided ID does not exist.");
}

//Check if product item exists
const productItemExists = await prismadb.productItem.findUnique({
where: {
id: productItemId,
},
});

if (!productItemExists) {
throw new Error("Product item with provided ID does not exist.");
}

//Check if available Item exists
const availableItemExists = await prismadb.available.findUnique({
where: {
id: availableItemId,
},
});

if (!availableItemExists) {
throw new Error("Available item with provided ID does not exist.");
}

const cart = await prismadb.cart.upsert({
where: {
userId: user.id,
},
update: {},
create: {
userId: user.id,
},
});

const cartItemExists = await prismadb.cartItem.findUnique({
where: {
cartId_productId_productItemId_availableItemId: {
cartId: cart.id,
productId,
productItemId,
availableItemId,
},
},
select: {
quantity: true,
availableItem: {
select: {
numInStocks: true,
},
},
},
});

//Check if item is available in stock
if (
cartItemExists &&
cartItemExists.quantity >= cartItemExists.availableItem.numInStocks
) {
throw new Error(
`Only ${cartItemExists.availableItem.numInStocks} of this item is in stocks!`
);
}

await prismadb.cartItem.upsert({
where: {
cartId_productId_productItemId_availableItemId: {
cartId: cart.id,
productId,
productItemId,
availableItemId,
},
},
update: {
quantity: {
increment: 1,
},
},
create: {
cartId: cart.id,
productId,
productItemId,
availableItemId,
quantity: 1,
},
});

return { message: "Item added to cart!" };
} catch (err) {
console.log("[CART_ITEM_CREATE]", err);

throw new Error("Internal server error");
}
};

export const updateCartItem = async ({
cartItemId,
task,
}: {
cartItemId: string;
task: "add" | "minus";
}) => {
try {
if (!cartItemId) return;

const { user } = await currentUser();

if (!user || !user.id) {
throw new Error("Unauthorized, You need to be logged in.");
}

const { role } = await currentRole();

if (role !== "USER") {
throw new Error("Unauthorized, Only users can delete cartItem");
}

const { success } = await apiRatelimit.limit(user.id);

if (!success && process.env.VERCEL_ENV === "production") {
throw new Error("Too Many Requests! try again in 1 min");
}

//Check if cart item exists
const cartItem = await prismadb.cartItem.findUnique({
where: {
id: cartItemId,
},
select: {
quantity: true,
availableItem: {
select: {
numInStocks: true,
},
},
},
});

if (!cartItem) {
throw new Error("Cart item not found!");
}

if (task === "add") {
//Check if there is an item in stocks.
if (cartItem.quantity >= cartItem.availableItem.numInStocks) {
throw new Error(
`Only ${cartItem.availableItem.numInStocks} of this item is in stocks!`
);
}

//If there is allow customer to increase quality
await prismadb.cartItem.update({
where: {
id: cartItemId,
},
data: {
quantity: {
increment: 1,
},
},
});
} else {
//If quality is more than 1 then decrease by 1
if (cartItem.quantity > 1) {
await prismadb.cartItem.update({
where: {
id: cartItemId,
},
data: {
quantity: {
decrement: 1,
},
},
});
} else {
//delete cart item.
await prismadb.cartItem.delete({
where: {
id: cartItemId,
},
});
}
}

return { message: "Cart item updated!" };
} catch (err) {
console.log("[CART_ITEM_PATCH]", err);

throw new Error("Internal server error");
}
};

export const deleteCartItem = async (cartItemId: string) => {
try {
if (!cartItemId) {
return;
}

const { user } = await currentUser();

if (!user || !user.id) {
throw new Error("Unauthorized, You need to be logged in.");
}

const { role } = await currentRole();

if (role !== "USER") {
throw new Error("Unauthorized, Only users can delete cartItem");
}

//Check if cart item exists
const cartItem = await prismadb.cartItem.findUnique({
where: {
id: cartItemId,
},
select: {
id: true,
},
});

if (!cartItem) {
throw new Error("Cart item not found!");
}

//Delete cart item
await prismadb.cartItem.delete({
where: {
id: cartItemId,
},
});

return { message: "Cart item deleted!" };
} catch (err) {
console.log("[CART_ITEM_DELETE]", err);

throw new Error("Internal server error");
}
};
7 changes: 3 additions & 4 deletions app/(marketing)/checkout/_components/CheckoutContent.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
"use client";

import React from "react";
import axios from "axios";
import { CartType } from "@/types";
import Empty from "@/components/Empty";
import OrderSummary from "./OrderSummary";
import { getCartItems } from "@/data/cart";
import { useQuery } from "@tanstack/react-query";
import CartItem from "@/components/cart/CartItem";
import CheckoutSkeleton from "@/components/CheckoutSkeleton";
Expand All @@ -17,9 +16,9 @@ const CheckoutContent = () => {
} = useQuery({
queryKey: ["get-cart-item"],
queryFn: async () => {
const res = await axios.get("/api/cart");
const data = await getCartItems();

return res.data as CartType;
return data;
},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
"use client";

import React, { useState } from "react";
import axios from "axios";
import Link from "next/link";
import Image from "next/image";
import { toast } from "sonner";
import axios, { AxiosError } from "axios";
import ProductSlider from "./ProductSlider";
import { cn, formatPrice } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { addToCartHandler } from "@/actions/cart";
import useCurrentUser from "@/hooks/use-current-user";
import AverageRating from "@/components/AverageRating";
import { Size, Color, UserRole } from "@prisma/client";
import TooltipContainer from "@/components/TooltipContainer";
import { Avatar, AvatarImage } from "@/components/ui/avatar";
import { CartItemValidator } from "@/lib/validators/cart-item";
import { ProductItemType, ProductDetailType } from "../../../../../types";
import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query";

Expand Down Expand Up @@ -64,9 +64,7 @@ const ProductContent = ({ product }: Props) => {

const { mutate: addToCart, isPending } = useMutation({
mutationKey: ["add-to-cart"],
mutationFn: async (values: CartItemValidator) => {
await axios.post("/api/cart", values);
},
mutationFn: addToCartHandler,
onSuccess: () => {
toast.success("Item added to cart!");

Expand All @@ -75,11 +73,7 @@ const ProductContent = ({ product }: Props) => {
});
},
onError: (err) => {
if (err instanceof AxiosError) {
toast.error(err.response?.data);
} else {
toast.error("Something went wrong");
}
toast.error(err.message || "Something went wrong");
},
});

Expand Down
Loading

0 comments on commit 4291969

Please sign in to comment.