Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shop page #27

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft
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
10 changes: 10 additions & 0 deletions app/(site)/shop/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { getProducts } from "components/Shop/SizePicker/payloadAction"
import React from "react";
import ShopPageContent from "./pageContent";

export default async function ShopPage() {

const products = (await getProducts()).docs

return <ShopPageContent products={products} />;
}
60 changes: 60 additions & 0 deletions app/(site)/shop/pageContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"use client";

import SectionHeader from "@/components/Common/SectionHeader";
import ShopCart from "@/components/Shop/Cart";
import CartItem from "@/components/Shop/CartItem";
import ProductPreview from "@/components/Shop/ProductPreview";
import ShopCard from "@/components/Shop/ShopCard";
import { Product } from "@/payload-types";
import { cartProduct } from "@/types/cartProduct";
import React from "react";

export default function ShopPageContent({ products }) {
const [previewProduct, setPreviewProduct] = React.useState(products[0]);
const [openCart, setOpenCart] = React.useState(false);
const [cartProducts, setCardProducts] = React.useState<cartProduct[]>([]);

const addToCart = (item: cartProduct) => {
setCardProducts((prevCardProducts) => {
if (prevCardProducts.some((p) => p.product.id === item.product.id && p.size === item.size)) {
return prevCardProducts;
}
return [...prevCardProducts, item];
});
}

const removeFromCart = (itemCart: cartProduct) => {
setCardProducts((prevCardProducts) => prevCardProducts.filter((item) => item.product.id !== itemCart.product.id || (item.product.id === itemCart.product.id && item.size !== itemCart.size)));
}

return (
<>
<div className="flex flex-row my-5 mx-20 mt-40">
<section className="flex flex-col max-w-[300px]">
<ProductPreview product={previewProduct} setCartState={setOpenCart} addToCart={addToCart}></ProductPreview>
</section>
<ShopCart isOpen={openCart} onOpenChange={setOpenCart} products={cartProducts} removeFromCart={removeFromCart}></ShopCart>
<section className="flex flex-col ml-30">
<h1 className="text-7xl font-serif tracking-tight text-black">
Student Essentials
</h1>
<h1 className="text-2xl font-serif text-black mt-5 tracking-tight max-w-96">
Os <span className="text-engenharia italic">merch-essentials</span>{" "}
deste ano para viveres a tua vida académica ao máximo, com estilo.
</h1>
<div>
<div className="mt-10 flex gap-2">
{products.map((product: Product) => (
<ShopCard
product={product}
key={product.id}
customClick={() => setPreviewProduct(product)}
></ShopCard>
))}
</div>
</div>
</section>
</div>
</>
);
}
3 changes: 3 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
body {
@apply relative z-1 font-inter text-regular font-normal text-waterloo dark:text-manatee;
}
:root {
--radius: 0.5rem;
}
}

@layer components {
Expand Down
66 changes: 66 additions & 0 deletions collections/Product.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { CollectionConfig } from "payload";

export const Product: CollectionConfig = {
slug: "product",
fields: [
{
name: "name",
type: "text",
required: true,
},
{
name: "price",
type: "number",
required: true,
},
{
name: "description",
type: "text",
required: true,
},
{
name: "color",
type: "text",
required: true,
},
{
name: "instances",
type: "array",
fields: [
{
name: "Size",
type: "select",
hasMany: false,
options: [
{
label: "XS",
value: "XS"
},
{
label: "S",
value: "S"
},
{
label: "M",
value: "M"
},
{
label: "L",
value: "L"
},
{
label: "XL",
value: "XL"
},
],
required: true,
},
{
name: "quantity",
type: "number",
required: true,
}
]
}
],
};
20 changes: 20 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "app/globals.css",
"baseColor": "slate",
"cssVariables": false,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}
59 changes: 59 additions & 0 deletions components/Shop/Cart/apiCall.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import axios from 'axios';

export async function requestMBWAY(phoneNumber: string, amount: number) {
console.log("Function requestMBWAY called with:", phoneNumber, amount);

const options = {
method: 'POST',
url: 'https://api.ifthenpay.com/spg/payment/mbway',
headers: { 'Content-Type': 'application/json' },
data: {
mbWayKey: 'BYX-186558',
orderId: 'teste',
amount: amount.toString(),
mobileNumber: '351#' + phoneNumber,
}
};

console.log("Options prepared:", options);

try {
const { data } = await axios.request(options);
console.log("Response data:", data);
pollPaymentStatus(data.RequestId);
return;
} catch (error) {
console.error("Error occurred:", error);
}
}

export async function checkPayment(requestID: string) {
const options = {
method: 'GET',
url: 'https://api.ifthenpay.com/spg/payment/mbway/status',
params: { mbWayKey: 'BYX-186558', requestId: requestID }
};

try {
const { data } = await axios.request(options);
console.log(data);
return data;
} catch (error) {
console.error(error);
}
}

const pollPaymentStatus = (requestID: string) => {
const paymentStatus = setInterval(async () => {
const status = await checkPayment(requestID)

if (status.Message == "Success") {
alert("euerka");
clearInterval(paymentStatus)
}
if (status.Message == "Declined by user") {
alert("declined by user");
clearInterval(paymentStatus)
}
}, 5000)
}
54 changes: 54 additions & 0 deletions components/Shop/Cart/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
Sheet,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
} from "@/components/ui/sheet"
import React from "react";
import CartItem from "../CartItem";
import { cartProduct } from "@/types/cartProduct";
import startPaymentProcess from "./payment";
import PaymentForm from "../PaymentForm";
import PuffLoader from "react-spinners/PuffLoader";
import { Separator } from "@/components/ui/separator"

type ShopCartProps = {
isOpen: boolean,
onOpenChange: (bool: boolean) => void,
products: cartProduct[],
removeFromCart: (item: cartProduct) => void,
}

const ShopCart = ({ isOpen, onOpenChange, products, removeFromCart }: ShopCartProps) => {
const [number, setNumber] = React.useState("")
const [processingPayment, setProcessingPayment] = React.useState(false)

return <Sheet open={isOpen} onOpenChange={onOpenChange}>
<SheetContent className="z-100000 flex flex-col justify-between">
<SheetHeader>
<SheetTitle className="mb-5">Cart</SheetTitle>
<SheetDescription className="flex flex-col gap-5">
{products.map((p) => {
return <>
<CartItem item={p} removeFromCart={removeFromCart}></CartItem>
<Separator />
</>
})}
</SheetDescription>
</SheetHeader>
<SheetFooter className="sm:justify-center">

{!processingPayment ? <PaymentForm setNumber={setNumber} products={products} setProcessingPayment={setProcessingPayment}></PaymentForm> :
<div className="flex gap-2 items-center">
<h2>Waiting for confirmation</h2>
<PuffLoader size={25} color="#90ee90" />
</div>
}
</SheetFooter >
</SheetContent >
</Sheet >
}

export default ShopCart;
11 changes: 11 additions & 0 deletions components/Shop/Cart/payment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { cartProduct } from "@/types/cartProduct";
import { requestMBWAY } from "./apiCall";

export default async function startPaymentProcess(products: cartProduct[]) {
await requestMBWAY("915612870", calculcateCost(products));
}

function calculcateCost(products: cartProduct[]) {
const totalCost = 0
return products.reduce((prev, curr) => prev + curr.product.price, totalCost)
}
53 changes: 53 additions & 0 deletions components/Shop/CartItem/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { cartProduct } from "@/types/cartProduct";
import Image from 'next/image';
import React from "react";

type CartItemProps = {
item: cartProduct,
removeFromCart: (item: cartProduct) => void,
}

const CartItem = ({ item, removeFromCart }: CartItemProps) => {
const [quantity, setQuantity] = React.useState(item.quantity);

const increaseQuantity = () => {
setQuantity(quantity + 1)
item.quantity = quantity + 1
}

const decreaseQuantity = () => {
if (quantity - 1 > 0) {
setQuantity(quantity - 1)
item.quantity = quantity - 1
}
}

return <div className="flex gap-5">
<Image
src="/images/cactus.jpg"
alt="hero"
width={100}
height={90}
/>
<div className="flex flex-col text-black justify-between flex-grow">
<div>
<h1 className="text-base">{item.product.name}</h1>
<h1 className="font-bold">{item.product.price} €</h1>
<div className="mt-1 text-xs text-gray-500 flex">
<div className="pr-5 border-r">
<button onClick={() => decreaseQuantity()}>-</button>
<span className="px-4">{item.quantity} un</span>
<button onClick={() => increaseQuantity()}>+</button>
</div>
<span className="px-5 border-r">{item.size}</span>
<span className="ml-5">{item.product.color}</span>
</div>
</div>
<div className="flex gap-1 self-end">
<button className="rounded-full w-8 h-8 bg-slate-400 text-white" onClick={() => removeFromCart(item)}>R</button>
</div>
</div>
</div >
}

export default CartItem;
Loading