diff --git a/package.json b/package.json index fe5f54a..c9ad426 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "@mui/styled-engine-sc": "^5.12.0", "@mui/x-data-grid": "^6.9.2", "@reduxjs/toolkit": "^1.9.5", + "@tanstack/react-query": "^4.29.19", + "axios": "^1.4.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.45.1", diff --git a/src/App.tsx b/src/App.tsx index dd001f7..326934e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,6 +14,7 @@ import Error from './scenes/Error'; import Checkout from './scenes/Checkout'; import Cart from './scenes/Cart'; import ProductDetail from './scenes/Shop/fragments/ProductDetail'; +import { QueryClientProvider, useQueryClient } from '@tanstack/react-query'; const App = () => { const mode = useAppSelector((state) => state.global.mode) as PaletteMode; @@ -29,7 +30,7 @@ const App = () => { } /> } /> } /> - } /> + } /> } /> } /> } /> diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..b176e11 --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1 @@ +export * from './products'; diff --git a/src/api/products.ts b/src/api/products.ts new file mode 100644 index 0000000..4608a88 --- /dev/null +++ b/src/api/products.ts @@ -0,0 +1,24 @@ +import axios from '../utils/axios'; + +export type Product = { + id: number; + title: string; + price: number; + description: string; + category: string; + image: string; + rating: Rating; +}; + +export type Rating = { + rate: number; + count: number; +}; + +export const getProducts = (): Promise => { + return axios.get('/products', { method: 'get' }).then((resp) => resp.data); +}; + +export const getProduct = (id: string): Promise => { + return axios.get(`/products/${id}`, { method: 'get' }).then((resp) => resp.data); +}; diff --git a/src/components/Skeleton/CardLoader.tsx b/src/components/Skeleton/CardLoader.tsx new file mode 100644 index 0000000..517c84c --- /dev/null +++ b/src/components/Skeleton/CardLoader.tsx @@ -0,0 +1,15 @@ +import { Box, Skeleton } from '@mui/material'; +import React from 'react'; +import useStyles from '../../scenes/Dashboard/useStyles'; + +const SkeletonLoader = React.memo(() => { + const classes = useStyles(); + return ( + + + + + ); +}); + +export default SkeletonLoader; diff --git a/src/scenes/Dashboard/fragments/FeaturedProducts.tsx b/src/scenes/Dashboard/fragments/FeaturedProducts.tsx index ff23758..98cd69c 100644 --- a/src/scenes/Dashboard/fragments/FeaturedProducts.tsx +++ b/src/scenes/Dashboard/fragments/FeaturedProducts.tsx @@ -1,9 +1,15 @@ +import React, { useState } from 'react'; import { Box, Card, CardMedia, CardContent, Typography, Button, Fade } from '@mui/material'; -import { dataSource } from '../../../data'; -import { useState } from 'react'; import useStyles from '../useStyles'; +import { UseQueryResult } from '@tanstack/react-query'; +import { Product } from '../../../api'; +import SkeletonLoader from '../../../components/Skeleton/CardLoader'; -const FeaturedProducts = () => { +type FeaturedProductsProps = { + query: UseQueryResult; +}; + +const FeaturedProducts = ({ query: { error, isLoading, data } }: FeaturedProductsProps) => { const classes = useStyles(); const [hoveredCard, setHoveredCard] = useState(null); const [isHovered, setIsHovered] = useState(false); @@ -18,37 +24,53 @@ const FeaturedProducts = () => { setIsHovered(false); }; + if (isLoading) { + return ( + + {new Array(8).fill(null).map((_, index) => ( + + ))} + + ); + } + return ( Our Products - {dataSource.map((item) => ( + {data?.slice(0, 8)?.map((item) => ( handleCardHover(item.id)} + onMouseEnter={() => handleCardHover(item.id.toString())} onMouseLeave={handleCardLeave} > - {item?.productMeta?.title} + {item?.title} - {item?.productMeta?.desc} + {item?.description} - + - - - - - SKU : ss0011 + + + {/* RIGHT */} + + + {data?.title} - - Category : Sofa + + Rs {data?.price} - - Tags : Single Seater, Luxury + + {data?.description} - + + + + Colors + + + + + + + + + + + + + + + SKU : {data?.id} + + + Category : {data?.category} + + + Tags : {data?.category} + + + - - - + + {/* - - - - ); + */} + + + ); }; export default ProductDetail; diff --git a/src/scenes/Shop/fragments/ProductList.tsx b/src/scenes/Shop/fragments/ProductList.tsx index ff46041..8625301 100644 --- a/src/scenes/Shop/fragments/ProductList.tsx +++ b/src/scenes/Shop/fragments/ProductList.tsx @@ -19,15 +19,23 @@ import { PaginationItem, } from '@mui/material'; import useStyles from '../../Dashboard/useStyles'; -import { dataSource } from '../../../data'; +import { UseQueryResult } from '@tanstack/react-query'; import FlexBetween from '../../../components/FlexBetween'; /* ASSETS */ import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; +import SkeletonLoader from '../../../components/Skeleton/CardLoader'; +import { Product } from '../../../api'; +import { useNavigate } from 'react-router-dom'; -const ProductList = () => { +type ProductListProps = { + query: UseQueryResult; +}; + +const ProductList = ({ query: { error, isLoading, data } }: ProductListProps) => { const classes = useStyles(); + const navigate = useNavigate(); const theme = useTheme(); const [hoveredCard, setHoveredCard] = useState(null); const [isHovered, setIsHovered] = useState(false); @@ -57,6 +65,22 @@ const ProductList = () => { setPage(value); }; + if (isLoading) { + return ( + + {new Array(8).fill(null).map((_, index) => ( + + ))} + + ); + } + return ( { width='90%' px='4rem' > - {dataSource.map((item) => ( + {data?.map((item) => ( handleCardHover(item.id)} + onMouseEnter={() => handleCardHover(item.id.toString())} onMouseLeave={handleCardLeave} + onClick={() => navigate(`/products/${item.id}`)} + sx={{ '&:hover': { cursor: 'pointer' } }} > - {item?.productMeta?.title} + {item?.title} - {item?.productMeta?.desc} + {item?.description} - + {/* - + */} ))} diff --git a/src/scenes/Shop/index.tsx b/src/scenes/Shop/index.tsx index 781fc74..5e7c123 100644 --- a/src/scenes/Shop/index.tsx +++ b/src/scenes/Shop/index.tsx @@ -2,12 +2,16 @@ import { Box } from '@mui/material'; import Header from '../../components/Header'; import ProductList from './fragments/ProductList'; import Banner from '../../components/Banner'; +import { useQuery } from '@tanstack/react-query'; +import { getProducts } from '../../api'; const Shop = () => { + const query = useQuery({ queryKey: ['products'], queryFn: getProducts }); + return ( <>
- + {/* */} diff --git a/src/utils/axios.ts b/src/utils/axios.ts new file mode 100644 index 0000000..804cffa --- /dev/null +++ b/src/utils/axios.ts @@ -0,0 +1,9 @@ +import axios from 'axios'; + +const instance = axios.create({ + baseURL: import.meta.env.VITE_APP_BASE_URL, + timeout: 1000, + headers: { 'X-Custom-Header': 'foobar' }, +}); + +export default instance; diff --git a/yarn.lock b/yarn.lock index af6343f..6119638 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1704,6 +1704,19 @@ "@svgr/hast-util-to-babel-ast" "^7.0.0" svg-parser "^2.0.4" +"@tanstack/query-core@4.29.19": + version "4.29.19" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.29.19.tgz#49ccbd0606633d1e55baf3b91ab7cc7aef411b1d" + integrity sha512-uPe1DukeIpIHpQi6UzIgBcXsjjsDaLnc7hF+zLBKnaUlh7jFE/A+P8t4cU4VzKPMFB/C970n/9SxtpO5hmIRgw== + +"@tanstack/react-query@^4.29.19": + version "4.29.19" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.29.19.tgz#6ba187f2d0ea36ae83ff1f67068f53c88ce7b228" + integrity sha512-XiTIOHHQ5Cw1WUlHaD4fmVUMhoWjuNJlAeJGq7eM4BraI5z7y8WkZO+NR8PSuRnQGblpuVdjClQbDFtwxTtTUw== + dependencies: + "@tanstack/query-core" "4.29.19" + use-sync-external-store "^1.2.0" + "@types/estree@^1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" @@ -1933,6 +1946,20 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + babel-plugin-macros@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" @@ -2082,6 +2109,13 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" @@ -2165,6 +2199,11 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -2439,6 +2478,20 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fs-readdir-recursive@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" @@ -2739,6 +2792,18 @@ micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -2899,6 +2964,11 @@ prop-types@^15.6.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + punycode@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" @@ -3309,7 +3379,7 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -use-sync-external-store@^1.0.0: +use-sync-external-store@^1.0.0, use-sync-external-store@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==