From 55cc97be925a9b51f660ae1645ee6e2f4e2e466b Mon Sep 17 00:00:00 2001 From: Thanachon Date: Sun, 8 Dec 2024 15:26:00 +0700 Subject: [PATCH 1/7] chore: Update product desc section content --- .../components/product/ProductDescription.js | 27 ++++++++++--------- client/components/product/SizeChart.js | 2 +- client/styles/styles.css | 11 ++++---- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/client/components/product/ProductDescription.js b/client/components/product/ProductDescription.js index be2c8843..d5651372 100644 --- a/client/components/product/ProductDescription.js +++ b/client/components/product/ProductDescription.js @@ -60,18 +60,16 @@ const ProductDescription = ({ product }) => {
Wash and Care:
-

- Dignissim enim sit amet venenatis urna. Sollicitudin aliquam - ultrices sagittis orci a scelerisque purus. Augue neque - gravida in fermentum et. Vel pretium lectus quam id leo.{' '} -

-

- Quis commodo odio aenean sed. Turpis massa sed elementum - tempus egestas. Semper eget duis at tellus. Suscipit tellus - mauris a diam maecenas sed enim. Nec nam aliquam sem et tortor - consequat id. In ornare quam viverra orci sagittis. -

-

Fames ac turpis egestas integer eget aliquet.

+

To keep your clothing looking fresh and vibrant, follow these general care instructions:

+
    +
  1. Machine Wash: Wash in cold water on a gentle cycle with like colors to prevent fading and bleeding.
  2. +
  3. Mild Detergent: Use a mild detergent to maintain the fabric's quality and colors.
  4. +
  5. Do Not Bleach: Avoid bleach to protect the integrity of the fabric.
  6. +
  7. Tumble Dry Low: If using a dryer, tumble dry on low heat. For optimal results, air drying is recommended.
  8. +
  9. Iron with Care: If needed, iron on low heat while the item is slightly damp to remove wrinkles. For added protection, place a cloth between the iron and fabric.
  10. +
  11. Store Properly: Hang or fold your clothes in a cool, dry place to maintain shape and quality.
  12. +
+

By following these care guidelines, you can ensure that your clothing remains a staple in your wardrobe for years to come!

@@ -80,7 +78,10 @@ const ProductDescription = ({ product }) => { eventKey="size-chart" title="Size Chart" > - +

Refer to our size chart to find your perfect fit. If you're between sizes, we recommend sizing up for added comfort.

+
+ +
diff --git a/client/components/product/SizeChart.js b/client/components/product/SizeChart.js index 6afa44c9..9cc43b2f 100644 --- a/client/components/product/SizeChart.js +++ b/client/components/product/SizeChart.js @@ -40,7 +40,7 @@ const SizeChart = () => { - + Date: Sun, 8 Dec 2024 15:29:38 +0700 Subject: [PATCH 2/7] refactor: Refactor onMobile flag in useWindowSize custom hook --- client/api/build-client.js | 8 ++-- client/components/header/Header.js | 2 +- client/hooks/useWindowSize.js | 15 ++++++- client/pages/404.js | 8 ++-- client/pages/index.js | 14 +----- client/pages/products/[productId].js | 14 ++---- client/pages/products/bestseller.js | 49 +++++++++------------ client/pages/products/bottoms/bestseller.js | 12 +---- client/pages/products/bottoms/index.js | 12 +---- client/pages/products/bottoms/new.js | 11 +---- client/pages/products/coats/bestseller.js | 11 +---- client/pages/products/coats/index.js | 12 +---- client/pages/products/coats/new.js | 12 +---- client/pages/products/dresses/bestseller.js | 12 +---- client/pages/products/dresses/index.js | 12 +---- client/pages/products/dresses/new.js | 11 +---- client/pages/products/sets/bestseller.js | 11 +---- client/pages/products/sets/index.js | 11 +---- client/pages/products/sets/new.js | 11 +---- client/pages/products/tops/bestseller.js | 11 +---- client/pages/products/tops/index.js | 11 +---- client/pages/products/tops/new.js | 11 +---- 22 files changed, 81 insertions(+), 200 deletions(-) diff --git a/client/api/build-client.js b/client/api/build-client.js index a91f5f16..edfcf7db 100644 --- a/client/api/build-client.js +++ b/client/api/build-client.js @@ -1,4 +1,4 @@ -import axios from 'axios' +import axios from 'axios'; export default ({ req }) => { if (typeof window === 'undefined') { @@ -8,11 +8,11 @@ export default ({ req }) => { baseURL: process.env.NODE_ENV === 'production' ? 'https://www.aurapan.com' : 'http://ingress-nginx-controller.ingress-nginx.svc.cluster.local', headers: req.headers, withCredentials: true - }) + }); } else { // We must be on the browser return axios.create({ baseUrl: '/' - }) + }); } -} +}; diff --git a/client/components/header/Header.js b/client/components/header/Header.js index 0e47661e..ec515133 100644 --- a/client/components/header/Header.js +++ b/client/components/header/Header.js @@ -9,7 +9,7 @@ const Header = ({ currentUser, products, bestseller }) => { const [showNotification, setShowNotification] = useState(false); const [onMobile, setOnMobile] = useState(true); - const { width } = useWindowSize(); + const { windowSize: { width } } = useWindowSize(); // All category on the website const productCategories = ['Top', 'Bottom', 'Dress', 'Set', 'Coat']; diff --git a/client/hooks/useWindowSize.js b/client/hooks/useWindowSize.js index 1d005a05..7eff46be 100644 --- a/client/hooks/useWindowSize.js +++ b/client/hooks/useWindowSize.js @@ -9,6 +9,8 @@ const useWindowSize = () => { height: isSSR ? 800 : window.innerHeight }); + const [onMobile, setOnMobile] = useState(false); + useEffect(() => { window.addEventListener('resize', () => { setWindowSize({ width: window.innerWidth, height: window.innerHeight }); @@ -21,7 +23,18 @@ const useWindowSize = () => { }; }, []); - return windowSize; + useEffect(() => { + if (windowSize.width <= 576) { + setOnMobile(true); + } else { + setOnMobile(false); + } + }, [windowSize.width]); + + return { + windowSize, + onMobile + }; }; export default useWindowSize; diff --git a/client/pages/404.js b/client/pages/404.js index 6fc1c596..21848c82 100644 --- a/client/pages/404.js +++ b/client/pages/404.js @@ -1,14 +1,14 @@ import React from 'react'; -function Custom404() { - return ( +function Custom404 () { + return (

404 Page not found :(

- ); + ); } -export default Custom404 +export default Custom404; diff --git a/client/pages/index.js b/client/pages/index.js index 34fde18c..8be00a6d 100644 --- a/client/pages/index.js +++ b/client/pages/index.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { Col, Row } from 'react-bootstrap'; import Product from '../components/home/Product'; @@ -12,17 +12,7 @@ import AdsBannerSrc3 from '../public/asset/ads-banner/ads_banner_3.png'; const adsBanners = [AdsBannerSrc1, AdsBannerSrc2, AdsBannerSrc3]; const Home = ({ products, currentUser }) => { - const [onMobile, setOnMobile] = useState(false); - - const { width } = useWindowSize(); - - useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - }, [width]); + const { onMobile } = useWindowSize(); return ( <> diff --git a/client/pages/products/[productId].js b/client/pages/products/[productId].js index 029347b9..eafd8005 100644 --- a/client/pages/products/[productId].js +++ b/client/pages/products/[productId].js @@ -33,26 +33,20 @@ const productDetail = ({ products, users, currentUser, myOrders }) => { const [imageEvent, setImageEvent] = useState(null); const [isPurchase, setIsPurchase] = useState(false); - const [onMobile, setOnMobile] = useState(false); - const [screenWidth, setScreenWidth] = useState(0); const product = products.find((product) => product.id === productId); - const categoryParams = `${product?.category.toLowerCase()}${product?.category === 'Dress' ? 'es' : 's' - }`; + const categoryParams = `${product?.category.toLowerCase()}${product?.category === 'Dress' ? 'es' : 's'}`; - const { width } = useWindowSize(); + const { windowSize: { width }, onMobile } = useWindowSize(); useEffect(() => { setScreenWidth(width); - if (width <= 576) { - setOnMobile(true); - } else { + if (onMobile) { setInitialImage(false); - setOnMobile(false); } - }, [width]); + }, [width, onMobile]); useEffect(async () => { // Check if orders is not an empty array diff --git a/client/pages/products/bestseller.js b/client/pages/products/bestseller.js index 7a7640fa..ef49ff55 100644 --- a/client/pages/products/bestseller.js +++ b/client/pages/products/bestseller.js @@ -1,43 +1,38 @@ -import { useEffect, useState } from "react"; -import { Breadcrumb, Col, Row } from "react-bootstrap"; -import Link from "next/link"; -import Head from "next/head"; +import React, { useEffect, useState } from 'react'; +import { Breadcrumb, Col, Row } from 'react-bootstrap'; +import Link from 'next/link'; +import Head from 'next/head'; -import Product from "../../components/home/Product"; -import Loader from "../../components/common/Loader"; -import useWindowSize from "../../hooks/useWindowSize"; +import Product from '../../components/home/Product'; +import Loader from '../../components/common/Loader'; +import useWindowSize from '../../hooks/useWindowSize'; const BestSeller = ({ bestseller, currentUser }) => { - const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); + const [loading, setLoading] = useState(true); - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); - useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } + useEffect(() => { + if (bestseller) { + setLoading(false); + } + }, [bestseller]); - if (bestseller) { - setLoading(false); - } - }, [width, bestseller]); - - return ( + return ( <> BestSeller | Aurapan - {loading ? ( + {loading + ? (
- ) : ( + ) + : ( <>

BestSeller

@@ -62,9 +57,9 @@ const BestSeller = ({ bestseller, currentUser }) => { ))}
- )} + )} - ); + ); }; export default BestSeller; diff --git a/client/pages/products/bottoms/bestseller.js b/client/pages/products/bottoms/bestseller.js index 141b3c41..86c26c1a 100644 --- a/client/pages/products/bottoms/bestseller.js +++ b/client/pages/products/bottoms/bestseller.js @@ -9,25 +9,17 @@ import useWindowSize from '../../../hooks/useWindowSize'; const BottomsBestseller = ({ bestseller, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const bottomsBestseller = bestseller?.filter( (bottom) => bottom.category === 'Bottom' ); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (bestseller && bottomsBestseller) { setLoading(false); } - }, [width, bestseller]); + }, [bestseller]); return ( <> diff --git a/client/pages/products/bottoms/index.js b/client/pages/products/bottoms/index.js index 90457814..d704a7f8 100644 --- a/client/pages/products/bottoms/index.js +++ b/client/pages/products/bottoms/index.js @@ -9,23 +9,15 @@ import useWindowSize from '../../../hooks/useWindowSize'; const Bottoms = ({ products, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const bottoms = products?.filter((product) => product.category === 'Bottom'); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (products) { setLoading(false); } - }, [width, products]); + }, [products]); return ( <> diff --git a/client/pages/products/bottoms/new.js b/client/pages/products/bottoms/new.js index 068b571a..302e14e0 100644 --- a/client/pages/products/bottoms/new.js +++ b/client/pages/products/bottoms/new.js @@ -9,25 +9,18 @@ import useWindowSize from '../../../hooks/useWindowSize'; const BottomsNewArrivals = ({ products, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const bottomsNewArrivals = products ?.filter((bottom) => bottom.category === 'Bottom') .reverse(); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (products && bottomsNewArrivals) { setLoading(false); } - }, [width, products]); + }, [products]); return ( <> diff --git a/client/pages/products/coats/bestseller.js b/client/pages/products/coats/bestseller.js index 987c5ccb..1b4d307f 100644 --- a/client/pages/products/coats/bestseller.js +++ b/client/pages/products/coats/bestseller.js @@ -9,25 +9,18 @@ import useWindowSize from '../../../hooks/useWindowSize'; const CoatsBestseller = ({ bestseller, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const coatsBestseller = bestseller?.filter( (coat) => coat.category === 'Coat' ); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (bestseller && coatsBestseller) { setLoading(false); } - }, [width, bestseller]); + }, [bestseller]); return ( <> diff --git a/client/pages/products/coats/index.js b/client/pages/products/coats/index.js index 2e3446a1..9e37c98e 100644 --- a/client/pages/products/coats/index.js +++ b/client/pages/products/coats/index.js @@ -9,23 +9,15 @@ import useWindowSize from '../../../hooks/useWindowSize'; const Coats = ({ products, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const coats = products?.filter((product) => product.category === 'Coat'); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (products) { setLoading(false); } - }, [width, products]); + }, [products]); return ( <> diff --git a/client/pages/products/coats/new.js b/client/pages/products/coats/new.js index 1523f35d..48353d6c 100644 --- a/client/pages/products/coats/new.js +++ b/client/pages/products/coats/new.js @@ -9,25 +9,17 @@ import useWindowSize from '../../../hooks/useWindowSize'; const CoatsNewArrivals = ({ products, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const coatsNewArrivals = products ?.filter((coat) => coat.category === 'Coat') .reverse(); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (products && coatsNewArrivals) { setLoading(false); } - }, [width, products]); + }, [products]); return ( <> diff --git a/client/pages/products/dresses/bestseller.js b/client/pages/products/dresses/bestseller.js index 5946052c..c465d639 100644 --- a/client/pages/products/dresses/bestseller.js +++ b/client/pages/products/dresses/bestseller.js @@ -9,25 +9,17 @@ import useWindowSize from '../../../hooks/useWindowSize'; const DressesBestseller = ({ bestseller, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const dressesBestseller = bestseller ?.filter((dress) => dress.category === 'Dress') .slice(0, 8); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (bestseller && dressesBestseller) { setLoading(false); } - }, [width, bestseller]); + }, [bestseller]); return ( <> diff --git a/client/pages/products/dresses/index.js b/client/pages/products/dresses/index.js index 5f78f03c..4af3539e 100644 --- a/client/pages/products/dresses/index.js +++ b/client/pages/products/dresses/index.js @@ -9,23 +9,15 @@ import useWindowSize from '../../../hooks/useWindowSize'; const Dresses = ({ products, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const dresses = products?.filter((product) => product.category === 'Dress'); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (products) { setLoading(false); } - }, [width, products]); + }, [products]); return ( <> diff --git a/client/pages/products/dresses/new.js b/client/pages/products/dresses/new.js index 62fcf533..163760e7 100644 --- a/client/pages/products/dresses/new.js +++ b/client/pages/products/dresses/new.js @@ -9,9 +9,8 @@ import useWindowSize from '../../../hooks/useWindowSize'; const DressesNewArrivals = ({ products, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const dressesNewArrivals = products ?.filter((dress) => dress.category === 'Dress') @@ -19,16 +18,10 @@ const DressesNewArrivals = ({ products, currentUser }) => { .slice(0, 4); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (products && dressesNewArrivals) { setLoading(false); } - }, [width, products]); + }, [products]); return ( <> diff --git a/client/pages/products/sets/bestseller.js b/client/pages/products/sets/bestseller.js index 2dbfbfb1..74c88812 100644 --- a/client/pages/products/sets/bestseller.js +++ b/client/pages/products/sets/bestseller.js @@ -9,23 +9,16 @@ import useWindowSize from '../../../hooks/useWindowSize'; const SetsBestseller = ({ bestseller, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const setsBestseller = bestseller?.filter((set) => set.category === 'Set'); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (bestseller && setsBestseller) { setLoading(false); } - }, [width, bestseller]); + }, [bestseller]); return ( <> diff --git a/client/pages/products/sets/index.js b/client/pages/products/sets/index.js index 1177084b..59561070 100644 --- a/client/pages/products/sets/index.js +++ b/client/pages/products/sets/index.js @@ -9,23 +9,16 @@ import useWindowSize from '../../../hooks/useWindowSize'; const Sets = ({ products, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const sets = products?.filter((product) => product.category === 'Set'); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (products) { setLoading(false); } - }, [width, products]); + }, [products]); return ( <> diff --git a/client/pages/products/sets/new.js b/client/pages/products/sets/new.js index c5acea88..0e5ae90d 100644 --- a/client/pages/products/sets/new.js +++ b/client/pages/products/sets/new.js @@ -9,25 +9,18 @@ import useWindowSize from '../../../hooks/useWindowSize'; const SetsNewArrivals = ({ products, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const setsNewArrivals = products ?.filter((set) => set.category === 'Set') .reverse(); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (products && setsNewArrivals) { setLoading(false); } - }, [width, products]); + }, [products]); return ( <> diff --git a/client/pages/products/tops/bestseller.js b/client/pages/products/tops/bestseller.js index 27942ed9..24a2c175 100644 --- a/client/pages/products/tops/bestseller.js +++ b/client/pages/products/tops/bestseller.js @@ -9,23 +9,16 @@ import useWindowSize from '../../../hooks/useWindowSize'; const TopsBestseller = ({ bestseller, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const topsBestseller = bestseller?.filter((top) => top.category === 'Top'); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (bestseller && topsBestseller) { setLoading(false); } - }, [width, bestseller]); + }, [bestseller]); return ( <> diff --git a/client/pages/products/tops/index.js b/client/pages/products/tops/index.js index 3be0ba56..4bd51700 100644 --- a/client/pages/products/tops/index.js +++ b/client/pages/products/tops/index.js @@ -9,23 +9,16 @@ import useWindowSize from '../../../hooks/useWindowSize'; const Tops = ({ products, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const tops = products?.filter((product) => product.category === 'Top'); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (products) { setLoading(false); } - }, [width, products]); + }, [products]); return ( <> diff --git a/client/pages/products/tops/new.js b/client/pages/products/tops/new.js index ac013148..ea86ded4 100644 --- a/client/pages/products/tops/new.js +++ b/client/pages/products/tops/new.js @@ -9,25 +9,18 @@ import useWindowSize from '../../../hooks/useWindowSize'; const TopsNewArrivals = ({ products, currentUser }) => { const [loading, setLoading] = useState(true); - const [onMobile, setOnMobile] = useState(false); - const { width } = useWindowSize(); + const { onMobile } = useWindowSize(); const topsNewArrivals = products ?.filter((top) => top.category === 'Top') .reverse(); useEffect(() => { - if (width <= 576) { - setOnMobile(true); - } else { - setOnMobile(false); - } - if (products && topsNewArrivals) { setLoading(false); } - }, [width, products]); + }, [products]); return ( <> From 86a8b70b12760d7cc7ee04f3e5083bff15dd92ef Mon Sep 17 00:00:00 2001 From: Thanachon Date: Sun, 8 Dec 2024 15:45:20 +0700 Subject: [PATCH 3/7] refactor: Implement caching technique with useMemo in price values --- client/pages/checkout.js | 51 +++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/client/pages/checkout.js b/client/pages/checkout.js index 0c0c7655..bafa4035 100644 --- a/client/pages/checkout.js +++ b/client/pages/checkout.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import PropTypes from 'prop-types'; import { Button, Col, ListGroup, Row, Card, Container } from 'react-bootstrap'; import Router from 'next/router'; @@ -15,16 +15,12 @@ const CheckoutPage = ({ currentUser }) => { const [shippingAddress, setShippingAddress] = useState(null); const [paymentMethod, setPaymentMethod] = useState(null); const [shippingDiscount] = useState(1); // TODO: apply overall discount feature + const [taxFactor] = useState(0.07); // Set to 7% by default const [onSuccess, setOnSuccess] = useState(false); const [storageReady, setStorageReady] = useState(false); - let itemsPrice; - let shippingPrice; - let taxPrice; - let totalPrice; - - const { doRequest, errors } = useRequest({ + const { doRequest: createOrder, errors } = useRequest({ url: '/api/orders', method: 'post', body: { @@ -76,27 +72,38 @@ const CheckoutPage = ({ currentUser }) => { } }, [onSuccess]); - if (cart && storageReady) { - itemsPrice = Number( - cart.reduce((acc, item) => acc + item.price * item.qty * item.discount, 0) - ).toFixed(2); + const itemsPrice = useMemo(() => { + if (!storageReady || !cart) { + return null; + } + return Number(cart.reduce((acc, item) => acc + item.price * item.qty * item.discount, 0)).toFixed(2); + }, [cart, storageReady]); - shippingPrice = ( - itemsPrice > 100.0 ? 0.0 : 10.0 * shippingDiscount - ).toFixed(2); + const shippingPrice = useMemo(() => { + if (!storageReady) { + return null; + } + return Number(itemsPrice > 100.0 ? 0.0 : 10.0 * shippingDiscount).toFixed(2); + }, [itemsPrice, shippingDiscount, storageReady]); - taxPrice = (0.07 * itemsPrice).toFixed(2); + const taxPrice = useMemo(() => { + if (!storageReady) { + return null; + } + return Number(taxFactor * itemsPrice).toFixed(2); + }, [itemsPrice, taxFactor, storageReady]); - totalPrice = ( - Number(itemsPrice) + - Number(shippingPrice) + - Number(taxPrice) - ).toFixed(2); - } + const totalPrice = useMemo(() => { + if (!storageReady) { + return null; + } + const sum = Number(itemsPrice) + Number(shippingPrice) + Number(taxPrice); + return sum.toFixed(2); + }, [itemsPrice, shippingPrice, taxPrice, storageReady]); const checkoutHandler = (e) => { e.preventDefault(); - doRequest(); + createOrder(); }; return ( From cf53bf051df711a3eeb3c4854d95d6c9f21e8886 Mon Sep 17 00:00:00 2001 From: Thanachon Date: Sun, 8 Dec 2024 16:39:01 +0700 Subject: [PATCH 4/7] task: Add payment option icons --- client/package-lock.json | 15 +++++++++ client/package.json | 1 + client/pages/payment.js | 68 +++++++++++++++++++++++++++++----------- client/styles/styles.css | 43 +++++++++++++------------ 4 files changed, 88 insertions(+), 39 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 29135af7..9f28d259 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -17,6 +17,7 @@ "react": "17.0.2", "react-bootstrap": "^2.1.2", "react-dom": "17.0.2", + "react-icons": "^5.4.0", "react-paypal-button-v2": "^2.6.3", "react-rating-stars-component": "^2.2.0", "react-share": "^4.4.0", @@ -4003,6 +4004,14 @@ "react": "17.0.2" } }, + "node_modules/react-icons": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.4.0.tgz", + "integrity": "sha512-7eltJxgVt7X64oHh6wSWNwwbKTCtMfK35hcjvJS0yxEAhPM8oUKdS3+kqaW1vicIltw+kR2unHaa12S9pPALoQ==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -7967,6 +7976,12 @@ "scheduler": "^0.20.2" } }, + "react-icons": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.4.0.tgz", + "integrity": "sha512-7eltJxgVt7X64oHh6wSWNwwbKTCtMfK35hcjvJS0yxEAhPM8oUKdS3+kqaW1vicIltw+kR2unHaa12S9pPALoQ==", + "requires": {} + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/client/package.json b/client/package.json index 60b77469..a56cf60a 100644 --- a/client/package.json +++ b/client/package.json @@ -19,6 +19,7 @@ "react": "17.0.2", "react-bootstrap": "^2.1.2", "react-dom": "17.0.2", + "react-icons": "^5.4.0", "react-paypal-button-v2": "^2.6.3", "react-rating-stars-component": "^2.2.0", "react-share": "^4.4.0", diff --git a/client/pages/payment.js b/client/pages/payment.js index 66e17507..ffc3afd5 100644 --- a/client/pages/payment.js +++ b/client/pages/payment.js @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'; import { Button, Col, Form } from 'react-bootstrap'; import Router from 'next/router'; import Head from 'next/head'; +import { BsStripe, BsPaypal, BsCash } from 'react-icons/bs'; import CheckoutSteps from '../components/cart/CheckoutSteps'; import FormContainer from '../components/common/FormContainer'; @@ -65,32 +66,61 @@ const PaymentPage = ({ currentUser }) => { />

Payment Method

- + Select Method setPaymentMethod(e.target.value)} - > + className="d-flex align-items-center gap-2 py-3" + > + setPaymentMethod(e.target.value)} + id="stripe" + /> + + +

Stripe or Credit Card

+
+ setPaymentMethod(e.target.value)} - > + className="d-flex align-items-center gap-2 py-3" + > + setPaymentMethod(e.target.value)} + /> + + +

Paypal or Credit Card

+
+ + + + setPaymentMethod(e.target.value)} + /> + + +

Pay with cash

+
+
diff --git a/client/styles/styles.css b/client/styles/styles.css index 3b2fce7c..e03b86d3 100644 --- a/client/styles/styles.css +++ b/client/styles/styles.css @@ -143,15 +143,15 @@ a::before { } .zoom-effect { - overflow: hidden; + overflow: hidden; } .zoom-effect img { - transition: transform .3s ease; + transition: transform .3s ease; } .zoom-effect:hover img { - transform: scale(1.1); + transform: scale(1.1); } @@ -224,7 +224,7 @@ a::before { } /* Mobile Screen */ -@media only screen and (width <= 576px) { +@media only screen and (width <=576px) { .custom-swiper .swiper-button-next, .custom-swiper .swiper-button-prev { padding: 0 1.5rem; @@ -248,7 +248,9 @@ a::before { } .color-selector-outer:hover { - border-width: 1px !important; + border: 1px solid #80c6dd !important; + outline: 0; + box-shadow: 0 0 0 .25rem rgb(0 140 186 / 25%); } .color-selector-inner { @@ -739,14 +741,14 @@ header { } /* Tablet Screen */ -@media only screen and (width <= 576px) { +@media only screen and (width <=576px) { .breadcrumb-label { padding: 0 1rem; } } /* Mobile Screen */ -@media only screen and (width <= 425px) { +@media only screen and (width <=425px) { .add-to-cart-btn { padding: 0 15px; font-size: 0.6rem; @@ -876,7 +878,7 @@ header { } /* Mobile, Tablet Screen */ -@media only screen and (width <= 576px) { +@media only screen and (width <=576px) { #product-page .toggle-main-img { box-shadow: none; } @@ -936,6 +938,7 @@ header { #cart-items .cart-product-title { font-size: 1.2rem; text-transform: uppercase; + width: fit-content; } #cart-items .cart-price { @@ -961,21 +964,21 @@ header { } /* Tablet Screen */ -@media only screen and (width <= 768px) { +@media only screen and (width <=768px) { .order-id { font-size: 1.5rem; } } /* Tablet Screen */ -@media only screen and (width <= 576px) { +@media only screen and (width <=576px) { #cart-items .cart-price { width: 100%; } } /* Mobile Screen */ -@media only screen and (width <= 425px) { +@media only screen and (width <=425px) { .order-id { font-size: 1.2rem; } @@ -1041,7 +1044,7 @@ header { } /* Mobile, Tablet Screen */ -@media only screen and (width <= 768px) { +@media only screen and (width <=768px) { .register-box { max-width: none; width: auto; @@ -1131,14 +1134,14 @@ footer { } /* Mobile Screen */ -@media only screen and (width <= 486px) { +@media only screen and (width <=486px) { .footer-newsletter { flex-direction: column; } } /* Mobile Screen */ -@media only screen and (width >= 768px) and (width <= 1200px) { +@media only screen and (width >=768px) and (width <=1200px) { .footer-newsletter { flex-direction: column; } @@ -1156,7 +1159,7 @@ footer { * animation slide-in-bottom * ---------------------------------------- */ - @-webkit-keyframes slide-in-bottom { +@-webkit-keyframes slide-in-bottom { 0% { -webkit-transform: translateY(100px); transform: translateY(100px); @@ -1168,9 +1171,9 @@ footer { transform: translateY(0); opacity: 1; } - } +} - @keyframes slide-in-bottom { +@keyframes slide-in-bottom { 0% { -webkit-transform: translateY(100px); transform: translateY(100px); @@ -1189,7 +1192,7 @@ footer { * animation slide-out-bottom * ---------------------------------------- */ - @-webkit-keyframes slide-out-bottom { +@-webkit-keyframes slide-out-bottom { 0% { -webkit-transform: translateY(0); transform: translateY(0); @@ -1201,9 +1204,9 @@ footer { transform: translateY(100px); opacity: 0; } - } +} - @keyframes slide-out-bottom { +@keyframes slide-out-bottom { 0% { -webkit-transform: translateY(0); transform: translateY(0); From d6dca87f29abcba6c62a89ece3bacf769cd724aa Mon Sep 17 00:00:00 2001 From: Thanachon Date: Sun, 8 Dec 2024 17:12:25 +0700 Subject: [PATCH 5/7] chore: Run review number in order --- client/components/account/UserReviewList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/components/account/UserReviewList.js b/client/components/account/UserReviewList.js index 9c5f18d0..d4ff80ac 100644 --- a/client/components/account/UserReviewList.js +++ b/client/components/account/UserReviewList.js @@ -14,7 +14,7 @@ const UserReviewList = ({ myReviews, products }) => { - + @@ -28,7 +28,7 @@ const UserReviewList = ({ myReviews, products }) => { {myReviews.map((review, index) => ( @@ -53,7 +64,7 @@ const Support = ({ user }) => { Your Email setEmail(e.target.value)} @@ -74,7 +85,7 @@ const Support = ({ user }) => { Message setMessage(e.target.value)} @@ -82,7 +93,8 @@ const Support = ({ user }) => { - ); + ); }; export default Support; diff --git a/client/pages/company/contact.js b/client/pages/company/contact.js new file mode 100644 index 00000000..15d7e0ce --- /dev/null +++ b/client/pages/company/contact.js @@ -0,0 +1,25 @@ +import React from 'react'; +import Head from 'next/head'; +import { Col, Container, Row } from 'react-bootstrap'; + +import Support from '../../components/account/Support'; + +const ContactPage = () => { + return ( + <> + + Contact | Aurapan + + + + +

Account Setting

+ + + + + + ); +}; + +export default ContactPage; From 5c1c9ce502c5a5ba3513877260c3c8266738b2b2 Mon Sep 17 00:00:00 2001 From: Thanachon Date: Sun, 8 Dec 2024 17:16:30 +0700 Subject: [PATCH 7/7] fix: Fix copy --- client/pages/company/contact.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pages/company/contact.js b/client/pages/company/contact.js index 15d7e0ce..0a6367f2 100644 --- a/client/pages/company/contact.js +++ b/client/pages/company/contact.js @@ -13,7 +13,7 @@ const ContactPage = () => { -

Account Setting

+

Contact us

IDNo. PRODUCT MY RATING OVERALL RATING
- {' '} + {' '} Date: Sun, 8 Dec 2024 17:12:49 +0700 Subject: [PATCH 6/7] task: Add a contact page --- client/components/account/Support.js | 93 ++++++++++++++++------------ client/pages/company/contact.js | 25 ++++++++ 2 files changed, 78 insertions(+), 40 deletions(-) create mode 100644 client/pages/company/contact.js diff --git a/client/components/account/Support.js b/client/components/account/Support.js index 6d217bec..9c42ba3f 100644 --- a/client/components/account/Support.js +++ b/client/components/account/Support.js @@ -1,43 +1,54 @@ -import React, { useEffect, useState } from "react"; -import { Button, Col, Form, Row, Spinner } from "react-bootstrap"; +import React, { useEffect, useMemo, useState } from 'react'; +import { Button, Col, Form, Row, Spinner } from 'react-bootstrap'; -import Message from "../common/Message"; +import Message from '../common/Message'; const Support = ({ user }) => { - const [name, setName] = useState(""); - const [email, setEmail] = useState(""); - const [subject, setSubject] = useState(""); - const [message, setMessage] = useState(""); - - const [loading, setLoading] = useState(false); - const [text, setText] = useState(""); - const [showMessage, setShowMessage] = useState(false); - - useEffect(() => { - if (user) { - setName(user.name); - setEmail(user.email); - } - - // Dummy - if (loading) { - setTimeout(() => { - setLoading(false); - setText("Thank you for submit comment!"); - setShowMessage(true); - }, 1000); - } - }, [loading]); - - const submitHandler = async (e) => { - e.preventDefault(); - setLoading(true); - }; - - return ( + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + const [subject, setSubject] = useState(''); + const [message, setMessage] = useState(''); + + const [loading, setLoading] = useState(false); + const [successText, setSuccessText] = useState(''); + const [errorText, setErrorText] = useState(''); + + const showSuccessMessage = useMemo(() => !!successText, [successText]); + const showErrorMessage = useMemo(() => !!errorText, [errorText]); + + useEffect(() => { + if (user) { + setName(user.name); + setEmail(user.email); + } + + // Dummy + if (loading) { + setTimeout(() => { + setLoading(false); + setSuccessText('Thank you for submit comment!'); + }, 1000); + } + }, [loading]); + + const submitHandler = async (e) => { + e.preventDefault(); + + if (!name || !email || !subject || !message) { + setSuccessText(''); + setErrorText('Please fill out the form.'); + return; + } + + setErrorText(''); + setLoading(true); + }; + + return ( - {showMessage && {text}} + {showSuccessMessage && {successText}} + {showErrorMessage && {errorText}}