Browse Source

fix product details

master
azri 2 weeks ago
parent
commit
eb88b3e7c5

+ 14
- 9
src/components/ImageView/ImageView.jsx View File

9
 } from "@mui/material";
9
 } from "@mui/material";
10
 import Grid from '@mui/material/Grid2';
10
 import Grid from '@mui/material/Grid2';
11
 
11
 
12
+// Utility function to check if an object is empty
13
+const isEmptyObject = (obj) => Object.keys(obj).length === 0 && obj.constructor === Object;
14
+
12
 const ImageView = () => {
15
 const ImageView = () => {
13
 
16
 
14
   const product = useSelector((state) => state.products.product.data)
17
   const product = useSelector((state) => state.products.product.data)
15
-  
18
+
16
   const [previewImage, setPreviewImage] = useState("");
19
   const [previewImage, setPreviewImage] = useState("");
17
   const [isZoomed, setIsZoomed] = useState(false);
20
   const [isZoomed, setIsZoomed] = useState(false);
18
 
21
 
19
-  useEffect(()=>{
22
+  useEffect(() => {
23
+
24
+    if (!isEmptyObject(product)) {
25
+      setPreviewImage(product?.images[0]?.url)
26
+    }
20
 
27
 
21
-    setPreviewImage(product?.images?.nodes[0]?.url)
22
-  
23
-  },[product])
28
+  }, [product])
24
 
29
 
25
   const handleThumbnailClick = (image) => {
30
   const handleThumbnailClick = (image) => {
26
     setPreviewImage(image);
31
     setPreviewImage(image);
49
                 width: "70%",
54
                 width: "70%",
50
                 height: "auto",
55
                 height: "auto",
51
                 display: "block",
56
                 display: "block",
52
-                margin:"auto auto"
57
+                margin: "auto auto"
53
               }}
58
               }}
54
             />
59
             />
55
           </Box>
60
           </Box>
61
             sx={{
66
             sx={{
62
               display: "flex",
67
               display: "flex",
63
               flexDirection: "row",
68
               flexDirection: "row",
64
-              justifyContent:"center",
69
+              justifyContent: "center",
65
               padding: 2,
70
               padding: 2,
66
-              gap:2
71
+              gap: 2
67
             }}
72
             }}
68
           >
73
           >
69
-            {product?.images?.nodes?.map(({src, url}, index) => (
74
+            {product?.images?.map(({ src, url }, index) => (
70
               <IconButton
75
               <IconButton
71
                 key={index}
76
                 key={index}
72
                 onClick={() => handleThumbnailClick(url)}
77
                 onClick={() => handleThumbnailClick(url)}

+ 12
- 3
src/components/Navbar/Navbar.jsx View File

18
 import BrushIcon from '@mui/icons-material/Brush';
18
 import BrushIcon from '@mui/icons-material/Brush';
19
 import LoyaltyIcon from '@mui/icons-material/Loyalty';
19
 import LoyaltyIcon from '@mui/icons-material/Loyalty';
20
 import Grid from '@mui/material/Grid2';
20
 import Grid from '@mui/material/Grid2';
21
-import { useSelector } from 'react-redux';
21
+import { useSelector, useDispatch } from 'react-redux';
22
+import { fetchProducts } from "../../redux/slices/productSlice";
22
 import ProductService from "../../services/ProductService";
23
 import ProductService from "../../services/ProductService";
23
 
24
 
24
 import { Swiper, SwiperSlide } from 'swiper/react';
25
 import { Swiper, SwiperSlide } from 'swiper/react';
109
   const [showHeader, setShowHeader] = useState(true);
110
   const [showHeader, setShowHeader] = useState(true);
110
   const [lastScrollPos, setLastScrollPos] = useState(0);
111
   const [lastScrollPos, setLastScrollPos] = useState(0);
111
   const [language, setLanguage] = useState('English');
112
   const [language, setLanguage] = useState('English');
113
+  const dispatch = useDispatch();
112
 
114
 
113
   const cart = useSelector((state) => state.cart.cart)
115
   const cart = useSelector((state) => state.cart.cart)
114
   const products = useSelector((state) => state.products.products.data)
116
   const products = useSelector((state) => state.products.products.data)
122
     list:[]
124
     list:[]
123
   })
125
   })
124
 
126
 
127
+  useEffect(() => {
128
+
129
+    dispatch(fetchProducts())
130
+
131
+  }, [])
132
+
125
   useEffect(() => {
133
   useEffect(() => {
126
 
134
 
127
     if (!cart?.lines?.nodes) return // don't need to do anything if we have no cart data
135
     if (!cart?.lines?.nodes) return // don't need to do anything if we have no cart data
327
               color="inherit"
335
               color="inherit"
328
               onClick={() => {
336
               onClick={() => {
329
                 sessionStorage.setItem('amber-select-product-type', productType)
337
                 sessionStorage.setItem('amber-select-product-type', productType)
338
+                sessionStorage.removeItem('amber-select-collection')
330
                 window.location.href = `/products`;
339
                 window.location.href = `/products`;
331
               }}
340
               }}
332
               onMouseEnter={() => {
341
               onMouseEnter={() => {
377
             </FormControl> */}
386
             </FormControl> */}
378
 
387
 
379
             <IconButton color="inherit">
388
             <IconButton color="inherit">
380
-              <SearchIcon sx={{ color: "white" }} />
389
+              <SearchIcon/>
381
             </IconButton>
390
             </IconButton>
382
 
391
 
383
             <Badge sx={{ cursor: "pointer" }} onClick={() => { window.location.href = "/cart" }} badgeContent={cartAmount} color="primary">
392
             <Badge sx={{ cursor: "pointer" }} onClick={() => { window.location.href = "/cart" }} badgeContent={cartAmount} color="primary">
384
-              <LocalMallIcon color="action" sx={{ color: "white" }} />
393
+              <LocalMallIcon color="action" />
385
             </Badge>
394
             </Badge>
386
 
395
 
387
             {/* <IconButton color="inherit">
396
             {/* <IconButton color="inherit">

+ 14
- 9
src/components/ProductDetails/ProductDetails.jsx View File

29
 
29
 
30
   const [quantity, setQuantity] = useState(1)
30
   const [quantity, setQuantity] = useState(1)
31
   const [variantSelection, setVariantSelection] = useState({
31
   const [variantSelection, setVariantSelection] = useState({
32
-    price: 0,
33
-    currencyCode: "",
34
-    quantityAvailable: 0
32
+    amount: "0",
33
+    currencyCode: ""
35
   })
34
   })
36
   const [variants, setVariants] = useState([])
35
   const [variants, setVariants] = useState([])
37
   const [showLoader, setShowLoader] = useState(false)
36
   const [showLoader, setShowLoader] = useState(false)
41
     if (!isEmptyObject(product)) {
40
     if (!isEmptyObject(product)) {
42
 
41
 
43
       console.log("Product: ", product)
42
       console.log("Product: ", product)
44
-      debugger
43
+      
45
 
44
 
46
-      let productVariants = product?.variants?.nodes
45
+      let productVariants = product?.variants
47
 
46
 
48
       // get all variant type
47
       // get all variant type
49
       if (!productVariants || productVariants?.length == 0) return
48
       if (!productVariants || productVariants?.length == 0) return
50
-
49
+      
51
       // we want to get the title for each variant
50
       // we want to get the title for each variant
52
       const uniqueOptions = {};
51
       const uniqueOptions = {};
53
       productVariants.forEach(variant => {
52
       productVariants.forEach(variant => {
53
+        
54
         variant.selectedOptions.forEach(option => {
54
         variant.selectedOptions.forEach(option => {
55
           if (!uniqueOptions[option.name]) {
55
           if (!uniqueOptions[option.name]) {
56
             uniqueOptions[option.name] = new Set();
56
             uniqueOptions[option.name] = new Set();
106
   }, [variantSelection])
106
   }, [variantSelection])
107
 
107
 
108
   const handleVariantClick = (name, value) => {
108
   const handleVariantClick = (name, value) => {
109
+
110
+    
111
+
109
     setVariantSelection({ ...variantSelection, [name]: value })
112
     setVariantSelection({ ...variantSelection, [name]: value })
110
 
113
 
111
     setVariantSelection((prev) => {
114
     setVariantSelection((prev) => {
112
 
115
 
113
       let newVariantSelection = { ...prev }
116
       let newVariantSelection = { ...prev }
114
       newVariantSelection = { ...newVariantSelection, [name]: value }
117
       newVariantSelection = { ...newVariantSelection, [name]: value }
115
-      let productVariants = product?.variants?.nodes
118
+      let productVariants = product?.variants
116
 
119
 
117
       // find variant price if it all match initial variant selection
120
       // find variant price if it all match initial variant selection
118
       for (const { selectedOptions, price, id, quantityAvailable } of productVariants) {
121
       for (const { selectedOptions, price, id, quantityAvailable } of productVariants) {
132
         }
135
         }
133
       }
136
       }
134
 
137
 
138
+      debugger
139
+
135
 
140
 
136
       return newVariantSelection
141
       return newVariantSelection
137
 
142
 
209
           <Typography variant="h5" sx={{ fontWeight: "bold", mb: 2 }}>
214
           <Typography variant="h5" sx={{ fontWeight: "bold", mb: 2 }}>
210
             {product?.title}
215
             {product?.title}
211
           </Typography>
216
           </Typography>
212
-          <Typography variant="body2" color="text.secondary">
217
+          {/* <Typography variant="body2" color="text.secondary">
213
             {product?.collections?.nodes[0]?.title}
218
             {product?.collections?.nodes[0]?.title}
214
-          </Typography>
219
+          </Typography> */}
215
 
220
 
216
 
221
 
217
           <Typography
222
           <Typography

+ 42
- 46
src/components/ProductList/ProductList.jsx View File

87
       if (!sessionStorage.getItem('amber-select-collection')) {
87
       if (!sessionStorage.getItem('amber-select-collection')) {
88
         const collectionList = getAllCollection(newFilteredProducts);
88
         const collectionList = getAllCollection(newFilteredProducts);
89
         setCollectionFilterOption(collectionList);
89
         setCollectionFilterOption(collectionList);
90
-      }else{
90
+      } else {
91
         setCollection(sessionStorage.getItem('amber-select-collection'))
91
         setCollection(sessionStorage.getItem('amber-select-collection'))
92
       }
92
       }
93
 
93
 
118
 
118
 
119
         }
119
         }
120
       );
120
       );
121
-      
121
+
122
       // Collection
122
       // Collection
123
       newFilteredProducts = newFilteredProducts.filter(
123
       newFilteredProducts = newFilteredProducts.filter(
124
         (product) => {
124
         (product) => {
131
 
131
 
132
         }
132
         }
133
       );
133
       );
134
-      
134
+
135
 
135
 
136
       if (sort === "title") {
136
       if (sort === "title") {
137
         newFilteredProducts = newFilteredProducts.sort((a, b) => a.title.localeCompare(b.title));
137
         newFilteredProducts = newFilteredProducts.sort((a, b) => a.title.localeCompare(b.title));
151
     //setInput({ ...input, [event.target.name]: event.target.value });
151
     //setInput({ ...input, [event.target.name]: event.target.value });
152
   };
152
   };
153
 
153
 
154
-  const renderProduct = (id, img_url, title, collection_name, minPrice, minPriceCurrency, maxPrice, maxPriceCurrency, extra_desc) => {
154
+  const renderProduct = (handle, img_url, title, collection_name, minPrice, minPriceCurrency, maxPrice, maxPriceCurrency, extra_desc) => {
155
 
155
 
156
     return (
156
     return (
157
-      <Grid item size={{ xs: 6, sm: 6, md: 3 }}>
158
-
159
-        <Box
160
-          onClick={() => { window.location.href = `/products/${id}` }}
161
-          sx={{
162
-            overflow: 'hidden',
163
-            position: 'relative',
164
-            cursor: 'pointer'
165
-          }}
166
-
167
-        >
168
-          <img
169
-            src={img_url}
170
-            alt={title}
171
-            style={{
172
-              width: '100%',
173
-              aspectRatio: '3 / 4',
174
-              objectFit: 'cover',
175
-              objectPosition: 'top center'
157
+      <Grid className="animate__animated animate__fadeIn" item size={{ xs: 6, sm: 6, md: 3 }}>
158
+
159
+        <a href={`/products/${handle}`} style={{textDecoration:"none",color:"#000"}}>
160
+          <Box
161
+            sx={{
162
+              overflow: 'hidden',
163
+              position: 'relative',
164
+              cursor: 'pointer'
176
             }}
165
             }}
177
-          />
178
 
166
 
179
-          {/* <Button sx={{ position: "absolute", top: 20, left: 20, boxShadow: 0 }} variant="contained">
167
+          >
168
+            <img
169
+              src={img_url}
170
+              alt={title}
171
+              style={{
172
+                width: '100%',
173
+                aspectRatio: '3 / 4',
174
+                objectFit: 'cover',
175
+                objectPosition: 'top center'
176
+              }}
177
+            />
178
+
179
+            {/* <Button sx={{ position: "absolute", top: 20, left: 20, boxShadow: 0 }} variant="contained">
180
             NEW
180
             NEW
181
           </Button> */}
181
           </Button> */}
182
 
182
 
183
-          <Box sx={{ pb: 5, pt: 3, width: "80%" }}>
184
-            <Typography variant="body1" sx={{ fontWeight: "400", mb: 1 }}>
185
-              {collection_name}
186
-            </Typography>
187
-            <Typography variant="h5" sx={{ fontWeight: "bolder", mb: 1 }}>
188
-              {title}
189
-            </Typography>
190
-            <Typography variant="body1" sx={{ fontWeight: "400" }}>
191
-              {`${minPriceCurrency} ${parseFloat(minPrice).toFixed(2)}`}
192
-            </Typography>
193
-            <Typography variant="body1" sx={{ mt: 2 }}>
194
-              {extra_desc}
195
-            </Typography>
183
+            <Box sx={{ pb: 5, pt: 3, width: "80%" }}>
184
+              <Typography variant="body1" sx={{ fontWeight: "400", mb: 1 }}>
185
+                {collection_name}
186
+              </Typography>
187
+              <Typography variant="h5" sx={{ fontWeight: "bolder", mb: 1 }}>
188
+                {title}
189
+              </Typography>
190
+              <Typography variant="body1" sx={{ fontWeight: "400" }}>
191
+                {`${minPriceCurrency} ${parseFloat(minPrice).toFixed(2)}`}
192
+              </Typography>
193
+              <Typography variant="body1" sx={{ mt: 2 }}>
194
+                {extra_desc}
195
+              </Typography>
196
+            </Box>
196
           </Box>
197
           </Box>
197
-        </Box>
198
+        </a>
198
       </Grid>
199
       </Grid>
199
     )
200
     )
200
 
201
 
305
         <Grid container spacing={0.5} columns={12}>
306
         <Grid container spacing={0.5} columns={12}>
306
           {filteredProducts.map((product, index) => {
307
           {filteredProducts.map((product, index) => {
307
 
308
 
308
-            let { id, title, images, collections, minVariantPrice, maxVariantPrice, productType, variants } = product
309
+            let { handle, title, images, collections, minVariantPrice, maxVariantPrice, productType, variants } = product
309
 
310
 
310
             let minPrice = minVariantPrice.amount
311
             let minPrice = minVariantPrice.amount
311
             let minPriceCurrency = minVariantPrice.currencyCode
312
             let minPriceCurrency = minVariantPrice.currencyCode
315
             let img_url = images[0].url
316
             let img_url = images[0].url
316
             let collection_name = collections[0].title
317
             let collection_name = collections[0].title
317
 
318
 
318
-            // ID
319
-            const parts = id.split('/');
320
-            let prodID = parts[parts.length - 1];
321
-
322
-
323
             if (index < size) {
319
             if (index < size) {
324
-              return renderProduct(prodID, img_url, title, collection_name, minPrice, minPriceCurrency, maxPrice, maxPriceCurrency, "")
320
+              return renderProduct(handle, img_url, title, collection_name, minPrice, minPriceCurrency, maxPrice, maxPriceCurrency, "")
325
             }
321
             }
326
 
322
 
327
           })}
323
           })}

+ 0
- 7
src/components/ProductSelected/ProductSelected.jsx View File

14
 
14
 
15
   const swiperRef = useRef(null); // Create a ref for the Swiper instance
15
   const swiperRef = useRef(null); // Create a ref for the Swiper instance
16
   const products = useSelector((state) => state.products.products.data)
16
   const products = useSelector((state) => state.products.products.data)
17
-  const dispatch = useDispatch();
18
 
17
 
19
   const [filterProducts, setFilterProducts] = useState([])
18
   const [filterProducts, setFilterProducts] = useState([])
20
 
19
 
21
-  useEffect(() => {
22
-
23
-    dispatch(fetchProducts())
24
-
25
-  }, [])
26
-
27
   useEffect(() => {
20
   useEffect(() => {
28
 
21
 
29
     if (products.length > 0) {
22
     if (products.length > 0) {

+ 4
- 4
src/pages/Products/Product.jsx View File

21
   useEffect(() => {
21
   useEffect(() => {
22
 
22
 
23
     dispatch(fetchProduct(pid))
23
     dispatch(fetchProduct(pid))
24
-
24
+    
25
     if(localStorage.getItem('amber-product-history')){
25
     if(localStorage.getItem('amber-product-history')){
26
 
26
 
27
       let productHistory = JSON.parse(localStorage.getItem('amber-product-history'))
27
       let productHistory = JSON.parse(localStorage.getItem('amber-product-history'))
71
         <Grid size={{ xs: 12, md: 6 }}>
71
         <Grid size={{ xs: 12, md: 6 }}>
72
           <Box sx={{ paddingRight: 1 }}>
72
           <Box sx={{ paddingRight: 1 }}>
73
             <Box>
73
             <Box>
74
-              {/* <ImageView /> */}
74
+              <ImageView />
75
             </Box>
75
             </Box>
76
           </Box>
76
           </Box>
77
         </Grid>
77
         </Grid>
95
           YOU MAY ALSO LIKE
95
           YOU MAY ALSO LIKE
96
         </Typography>
96
         </Typography>
97
 
97
 
98
-        <ProductSuggestion />
98
+        {/* <ProductSuggestion /> */}
99
 
99
 
100
       </Box>
100
       </Box>
101
 
101
 
109
           RECENTLY VIEWED
109
           RECENTLY VIEWED
110
         </Typography>
110
         </Typography>
111
 
111
 
112
-        <ProductHistoryList />
112
+        {/* <ProductHistoryList /> */}
113
 
113
 
114
       </Box>
114
       </Box>
115
 
115
 

+ 22
- 5
src/services/ProductService.js View File

8
   publicAccessToken: REACT_APP_ACCESS_TOKEN,
8
   publicAccessToken: REACT_APP_ACCESS_TOKEN,
9
 });
9
 });
10
 
10
 
11
-const getProducts = async (maxResults = 2) => {
11
+const getProducts = async () => {
12
 
12
 
13
   let hasNextCursor = true
13
   let hasNextCursor = true
14
   let products = []; 
14
   let products = []; 
20
       products(first: ${2}, ${cursor},  sortKey: CREATED_AT) {
20
       products(first: ${2}, ${cursor},  sortKey: CREATED_AT) {
21
         nodes {
21
         nodes {
22
           id
22
           id
23
+          handle
23
           title
24
           title
24
-          createdAt
25
+          createdAt 
25
           productType
26
           productType
26
           tags
27
           tags
27
           priceRange {
28
           priceRange {
94
 
95
 
95
 }
96
 }
96
 
97
 
97
-const getProduct = async (id = "") => {
98
+const getProduct = async (handle = "") => {
98
 
99
 
99
   const query = `{
100
   const query = `{
100
-    product(id: "gid://shopify/Product/${id}") {
101
+    product(handle: "${handle}") {
101
       id
102
       id
103
+      handle
102
       title
104
       title
105
+      createdAt 
103
       productType
106
       productType
107
+      tags
108
+      descriptionHtml
104
       priceRange {
109
       priceRange {
105
         minVariantPrice {
110
         minVariantPrice {
106
           amount
111
           amount
116
           url
121
           url
117
         }
122
         }
118
       }
123
       }
119
-      collections(first: 55) {
124
+      collections(first: 10) {
120
         nodes {
125
         nodes {
121
           title
126
           title
127
+          image {
128
+            url
129
+          }
122
         }
130
         }
123
       }
131
       }
124
       variants(first: 200) {
132
       variants(first: 200) {
129
             amount
137
             amount
130
             currencyCode
138
             currencyCode
131
           }
139
           }
140
+          selectedOptions {
141
+            name
142
+            value
143
+          }
144
+          quantityAvailable
132
         }
145
         }
133
       }
146
       }
147
+      metafield(key: "selected", namespace: "custom") {
148
+        key
149
+        value
150
+      }
134
     }
151
     }
135
   }`
152
   }`
136
 
153
 

+ 6
- 4
src/utils/helpers.js View File

7
 
7
 
8
     return {
8
     return {
9
         id: product?.id || null,
9
         id: product?.id || null,
10
+        handle: product?.handle || "",
10
         title: product?.title || "",
11
         title: product?.title || "",
11
         createdAt: product?.createdAt,
12
         createdAt: product?.createdAt,
12
-        collections: product?.collections?.nodes || [],
13
-        tags:product?.tags || [],
14
-        images: product?.images?.nodes || [],
13
+        collections: product?.collections?.nodes || null,
14
+        descriptionHtml: product?.descriptionHtml || "",
15
+        tags:product?.tags || null,
16
+        images: product?.images?.nodes || null,
15
         selected: (product?.metafield?.key == "selected") ? product?.metafield?.value == "true" : false, // cause I want to have a true false value, somehow BE return text value "true", thus == used to convert it to proper boolean value
17
         selected: (product?.metafield?.key == "selected") ? product?.metafield?.value == "true" : false, // cause I want to have a true false value, somehow BE return text value "true", thus == used to convert it to proper boolean value
16
         minVariantPrice: product?.priceRange?.minVariantPrice || {amount:0 , currencyCode:''},
18
         minVariantPrice: product?.priceRange?.minVariantPrice || {amount:0 , currencyCode:''},
17
         maxVariantPrice: product?.priceRange?.maxVariantPrice || {amount:0 , currencyCode:''},
19
         maxVariantPrice: product?.priceRange?.maxVariantPrice || {amount:0 , currencyCode:''},
18
         productType: product?.productType || null,
20
         productType: product?.productType || null,
19
-        variants: product?.variants?.nodes || []
21
+        variants: product?.variants?.nodes || null
20
     }
22
     }
21
 
23
 
22
 }
24
 }

Loading…
Cancel
Save