Amber Shopify Project created using ReactJS+React-Redux with GraphQL API integration. Storefront Shopify API: https://github.com/Shopify/shopify-app-js/tree/main/packages/api-clients/storefront-api-client#readme
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ProductSelected.jsx 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. import { useEffect, useState, useRef } from 'react';
  2. import { Box, Typography, Button, useMediaQuery } from '@mui/material';
  3. import Grid from '@mui/material/Grid2';
  4. import { useSelector, useDispatch } from 'react-redux';
  5. import { fetchProducts } from '../../redux/slices/productSlice';
  6. import { Swiper, SwiperSlide } from 'swiper/react';
  7. import { Scrollbar, A11y } from 'swiper/modules';
  8. import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
  9. import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
  10. import defaultImage from "../../assets/images/default.png"
  11. import atomeLogo from "../../assets/images/atome.webp"
  12. import { isProductInVisibleMenu } from "../../config/menuCollections";
  13. const ProductSelected = () => {
  14. const swiperRef = useRef(null); // Create a ref for the Swiper instance
  15. const products = useSelector((state) => state.products.products.data)
  16. const [filterProducts, setFilterProducts] = useState([])
  17. useEffect(() => {
  18. if (products.length > 0) {
  19. let selectedProducts = products.filter((product) => (
  20. product.selected && isProductInVisibleMenu(product, products)
  21. )) || []
  22. //sort
  23. selectedProducts = selectedProducts.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
  24. setFilterProducts(selectedProducts)
  25. }
  26. }, [products])
  27. const swipeToLeft = () => {
  28. if (swiperRef.current) {
  29. swiperRef.current.slidePrev(); // Move to the previous slide
  30. }
  31. };
  32. const swipeToRight = () => {
  33. if (swiperRef.current) {
  34. swiperRef.current.slideNext(); // Move to the previous slide
  35. }
  36. };
  37. const renderProduct = (
  38. handle,
  39. img_url,
  40. title,
  41. collection_name,
  42. minPrice,
  43. minPriceCurrency,
  44. maxPrice,
  45. maxPriceCurrency,
  46. minDiscountPrice,
  47. minDiscountPriceCurrency,
  48. maxDiscountPrice,
  49. maxDiscountPriceCurrency,
  50. extra_desc,
  51. selected = false
  52. ) => {
  53. return (
  54. <Grid item size={{ xs: 6, sm: 6, md: 3 }}>
  55. <a
  56. href={`/products/${handle}`}
  57. style={{
  58. color: 'black',
  59. textDecoration: 'none'
  60. }}>
  61. <Box
  62. sx={{
  63. overflow: 'hidden',
  64. position: 'relative',
  65. cursor: 'pointer',
  66. }}
  67. >
  68. <img
  69. src={img_url}
  70. alt={title}
  71. style={{
  72. width: '100%',
  73. aspectRatio: '3 / 4',
  74. objectFit: 'cover',
  75. objectPosition: 'top center'
  76. }}
  77. />
  78. <Button
  79. sx={{
  80. position: "absolute",
  81. top: {
  82. xs: 0,
  83. sm: 0,
  84. md: 10,
  85. lg: 20
  86. },
  87. left: {
  88. xs: 0,
  89. sm: 0,
  90. md: 10,
  91. lg: 20
  92. },
  93. boxShadow: 0,
  94. fontSize: 10
  95. }}
  96. variant="contained"
  97. >
  98. NEW
  99. </Button>
  100. <Box sx={{ pb: 1, pt: 1, width: "90%" }}>
  101. <Typography variant="body2" sx={{
  102. fontWeight: "100", fontSize: {
  103. xs: "0.65rem",
  104. sm: "0.65rem",
  105. md: "0.75rem",
  106. },
  107. }}>
  108. {collection_name}
  109. </Typography>
  110. <Typography variant="body2" sx={{
  111. fontWeight: "400", fontSize: {
  112. xs: "0.73rem",
  113. sm: "0.73rem",
  114. md: "0.875rem",
  115. },
  116. }}>
  117. {title}
  118. </Typography>
  119. <Typography
  120. variant="body2"
  121. sx={{
  122. fontWeight: "100",
  123. fontSize: {
  124. xs: "0.73rem",
  125. sm: "0.73rem",
  126. md: "0.875rem",
  127. },
  128. textDecoration: (minDiscountPrice > 0) ? "line-through" : "none"
  129. }}
  130. >
  131. {`${minPriceCurrency} ${parseFloat(minPrice).toFixed(2)}`}
  132. </Typography>
  133. {(minDiscountPrice > 0) && <Typography
  134. variant="body2"
  135. sx={{
  136. fontWeight: "100",
  137. fontSize: {
  138. xs: "0.73rem",
  139. sm: "0.73rem",
  140. md: "0.875rem",
  141. },
  142. }}
  143. >
  144. {`${minDiscountPriceCurrency} ${parseFloat(minDiscountPrice).toFixed(2)}`}
  145. </Typography>}
  146. <Typography
  147. variant="body2"
  148. sx={{
  149. fontWeight: "100",
  150. fontSize: {
  151. xs: "0.73rem",
  152. sm: "0.73rem",
  153. md: "0.875rem",
  154. },
  155. }}
  156. >
  157. or 3 payments of <span style={{ fontWeight: "900" }}> {`${(minDiscountPrice > 0) ? parseFloat((minDiscountPrice / 3)).toFixed(2) : parseFloat((minPrice / 3)).toFixed(2)} ${minPriceCurrency}`}</span> with <span style={{ fontWeight: "900" }}>Atome <img style={{ marginLeft: 5, verticalAlign: "middle" }} width="15" src={atomeLogo} /> </span>
  158. </Typography>
  159. </Box>
  160. </Box>
  161. </a>
  162. </Grid>
  163. )
  164. }
  165. return (
  166. <Box sx={{
  167. my: {
  168. xs: 0,
  169. sm: 2,
  170. md: 5
  171. }
  172. }}>
  173. {/* OFFSET PURPOSE */}
  174. <Grid
  175. size={{ xs: 12, md: 4 }}
  176. sx={{
  177. mx: "auto",
  178. display: "flex",
  179. alignItems: "center",
  180. justifyContent: "center",
  181. my: 4,
  182. mb: 5
  183. }}>
  184. <ChevronLeftIcon
  185. style={{
  186. cursor: "pointer",
  187. color: "#B7B7B7"
  188. }}
  189. onClick={swipeToLeft}
  190. />
  191. <Typography variant="h4" sx={{ mx: 2, fontWeight: "50" }}>
  192. NEW IN
  193. </Typography>
  194. <KeyboardArrowRightIcon
  195. style={{
  196. cursor: "pointer",
  197. color: "#B7B7B7"
  198. }}
  199. onClick={swipeToRight}
  200. />
  201. </Grid>
  202. <Box sx={{ mt: 2 }}>
  203. <Swiper
  204. modules={[Scrollbar, A11y]}
  205. spaceBetween={10}
  206. breakpoints={{
  207. 0: { slidesPerView: 2 }, // For very small screens
  208. 640: { slidesPerView: 2 }, // For screens >= 640px
  209. 1024: { slidesPerView: 4 }, // For screens >= 1024px
  210. }}
  211. pagination={{ clickable: true }}
  212. scrollbar={{ draggable: true }}
  213. onSwiper={(swiper) => (swiperRef.current = swiper)}
  214. >
  215. {filterProducts.map((product, index) => {
  216. let {
  217. handle,
  218. title,
  219. images,
  220. collections,
  221. minVariantPrice,
  222. maxVariantPrice,
  223. compareAtPriceRangeMinVariantPrice,
  224. compareAtPriceRangeMaxVariantPrice,
  225. productType,
  226. variants,
  227. selected,
  228. } = product;
  229. let minPrice = minVariantPrice.amount
  230. let minPriceCurrency = minVariantPrice.currencyCode
  231. let maxPrice = maxVariantPrice.amount
  232. let maxPriceCurrency = maxVariantPrice.currencyCode
  233. // SHOULD BE THE DISCOUNTED PRICE
  234. let minDiscountPrice = compareAtPriceRangeMinVariantPrice.amount;
  235. let minDiscountPriceCurrency = compareAtPriceRangeMinVariantPrice.currencyCode;
  236. let maxDiscountPrice = compareAtPriceRangeMaxVariantPrice.amount;
  237. let maxDiscountPriceCurrency = compareAtPriceRangeMaxVariantPrice.currencyCode;
  238. let img_url = images[0]?.url || defaultImage
  239. let collection_name = collections[0]?.title
  240. return (
  241. <SwiperSlide>
  242. {renderProduct(
  243. handle,
  244. img_url,
  245. title,
  246. collection_name,
  247. minPrice,
  248. minPriceCurrency,
  249. maxPrice,
  250. maxPriceCurrency,
  251. minDiscountPrice,
  252. minDiscountPriceCurrency,
  253. maxDiscountPrice,
  254. maxDiscountPriceCurrency,
  255. "",
  256. selected
  257. )}
  258. </SwiperSlide>
  259. )
  260. })}
  261. </Swiper>
  262. </Box>
  263. </Box>
  264. );
  265. };
  266. export default ProductSelected;