diff --git a/package.json b/package.json index 838e402..863c171 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", + "dev": "vite --host", "build": "tsc && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview", @@ -27,11 +27,13 @@ "react-dom": "^18.2.0", "react-hook-form": "^7.51.5", "react-icons": "^5.2.1", + "react-medium-image-zoom": "^5.2.5", "react-redux": "^9.1.2", "react-router-dom": "^6.23.1", "react-toastify": "^10.0.5", "sass": "^1.77.2", "tailwind-merge": "^2.3.0", + "tailwind-scrollbar": "^3.1.0", "tailwind-scrollbar-hide": "^1.1.7", "zod": "^3.23.8" }, diff --git a/src/App.tsx b/src/App.tsx index e0db5d3..efa2b0c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,7 +13,8 @@ import { useDispatch } from 'react-redux'; import { ProductResponse, Product } from './types/Types'; import { useEffect, useRef } from 'react'; import { useGetProductsQuery } from './services/productApi'; -import { setError, setIsLoading, setProductFetched, setProductsDataList } from './redux/slices/productsSlice'; +import { setError, setIsLoading, setProductFetched, setProductsDataList } from './redux/slices/productsSlice';import { ProductDetail } from './pages/product/ProductDetail'; + const App = () => { const { data, error, isLoading } = useGetProductsQuery(); const dispatch = useDispatch(); @@ -59,6 +60,10 @@ const App = () => { path: 'auth/success/:token', element: , }, + { + path: 'products/:id', + element: , + }, ], }, { diff --git a/src/components/Products/ColorComponent.tsx b/src/components/Products/ColorComponent.tsx new file mode 100644 index 0000000..bd87afc --- /dev/null +++ b/src/components/Products/ColorComponent.tsx @@ -0,0 +1,10 @@ +interface Props { + name: string; +} +export const ColorComponent = ({ name }: Props) => { + return ( +
+ {name} +
+ ); +}; diff --git a/src/components/Products/ImageCard.tsx b/src/components/Products/ImageCard.tsx new file mode 100644 index 0000000..182b7f2 --- /dev/null +++ b/src/components/Products/ImageCard.tsx @@ -0,0 +1,26 @@ +import Zoom from 'react-medium-image-zoom'; +import 'react-medium-image-zoom/dist/styles.css'; + +interface Props { + styles: string; + image: string; + alt?: string; + handleClick?: (image: string) => void; + enableZoom?: boolean; + isSpotted?: boolean; +} + +export const ImageCard = ({ image, styles, alt, handleClick, enableZoom = true, isSpotted = false }: Props) => { + const imgClass = isSpotted ? `border border-greenColor rounded-lg p-1 ${styles}` : styles; + const imgElement = ( + handleClick && handleClick(image)} className={imgClass} src={image} alt={alt} /> + ); + + return enableZoom ? ( + + {imgElement} + + ) : ( + imgElement + ); +}; diff --git a/src/components/Products/ImageToggle.tsx b/src/components/Products/ImageToggle.tsx new file mode 100644 index 0000000..b5f7c63 --- /dev/null +++ b/src/components/Products/ImageToggle.tsx @@ -0,0 +1,19 @@ +import { IconType } from 'react-icons'; + +interface Props { + icon: IconType; + positionClass: string; + size?: string; + handleClick?: (e: any) => void; +} + +export const ImageToggle = ({ icon: Icon, positionClass, size, handleClick }: Props) => { + return ( +
+ +
+ ); +}; diff --git a/src/components/Products/ProductCard.tsx b/src/components/Products/ProductCard.tsx index 2c9aa7c..d525706 100644 --- a/src/components/Products/ProductCard.tsx +++ b/src/components/Products/ProductCard.tsx @@ -1,3 +1,4 @@ +import { useNavigate } from 'react-router-dom'; import { Product } from '../../types/Types'; interface ProductCardProps { @@ -7,26 +8,29 @@ interface ProductCardProps { const ProductCard: React.FC = ({ product }) => { const imageUrl = product?.images?.[0] ?? 'default-image-url'; const price = product?.sizes?.[0]?.price ?? 'default-image-url'; + const navigate = useNavigate(); return ( -
-
- {product.name} +
{ + navigate(`/products/${product.id}`); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} + className='product-card min-w-48 bg-whiteColor hover:shadow-sm hover:cursor-pointer hover:transform hover:transition-all hover:scale-105' + > +
+ {product.name}
-
-
-
-

{product.name}

-

${price}

+
+
+
+

{product.name}

+

${price}

-

{product.manufacturer}

+

{product.manufacturer}

-
-
diff --git a/src/components/Products/ProductReviewCard.tsx b/src/components/Products/ProductReviewCard.tsx new file mode 100644 index 0000000..928c172 --- /dev/null +++ b/src/components/Products/ProductReviewCard.tsx @@ -0,0 +1,32 @@ +interface Props { + name: string; + profileImage: string; + reviewText: string; + dateReviewed: string; +} + +export const ProductReviewCard = ({ name, profileImage, reviewText, dateReviewed }: Props) => { + return ( +
+
+ Buyer image +
+
+
+

{name}

+
+ {/* + + + + + + */} + {dateReviewed} +
+

{reviewText}

+
+
+
+ ); +}; diff --git a/src/components/common/Button.tsx b/src/components/common/Button.tsx index 5760c38..472efaf 100644 --- a/src/components/common/Button.tsx +++ b/src/components/common/Button.tsx @@ -1,4 +1,4 @@ -import { cn } from "../../utils"; +import { cn } from '../../utils'; interface ButtonProps { text: string; @@ -6,12 +6,17 @@ interface ButtonProps { type?: 'submit' | 'reset' | 'button'; className?: string; onClick?: () => void; + disabled?: boolean; } -const Button = ({ text, type, className, onClick }: ButtonProps) => { +const Button = ({ text, type, className, disabled, onClick }: ButtonProps) => { return (
+
+ {/* PRODUCT stars */} +
+ Review + + + + + + + + (8) +
+ {/* PRODUCT DESCRIPTION */} +
+

+ {isDescriptionExpanded + ? productData.data.description + : `${productData.data.description.substring(0, 100)}...`} +

+

+ {isDescriptionExpanded ? 'Show less' : 'Show more'} +

+
+
+
+
+
+
+ {/* PRODUCT REVIEW AND COMMENTS */} +
+
+
+

Product Reviews

+
+ + + +
+
+
+ {/* RECOMMENDED PRODUCTS */} +
+

Similar Products

+
+ scrollRecommendedProducts('left')} + size='24px' + icon={IoIosArrowBack} + positionClass='absolute left-0 ml-2 z-50' + /> +
+ {products.map(product => ( + + ))} +
+ scrollRecommendedProducts('right')} + size='24px' + icon={IoIosArrowForward} + positionClass='absolute right-0 mr-2' + /> +
+
+
+