Browse Source

set quantity

master
azri 10 hours ago
parent
commit
b1d0b2aae8

+ 4
- 1
src/App.js View File

30
     // if we got cart, then just fetch from BE based on ID
30
     // if we got cart, then just fetch from BE based on ID
31
     if (!isEmptyObject(cartHistory)) {
31
     if (!isEmptyObject(cartHistory)) {
32
 
32
 
33
-      dispatch(fetchCart(cartHistory.id));
33
+      let cartId = cartHistory.id.split('/')
34
+      cartId = cartId[cartId.length - 1]
35
+
36
+      dispatch(fetchCart("gid://shopify/Cart/Z2NwLWFzaWEtc291dGhlYXN0MTowMUpGTkVDODRQNFY2RUtNV1JZTlJDVFBQNA?key=6d58758401066b048e3795c9cf0fa716"));
34
 
37
 
35
     }
38
     }
36
 
39
 

+ 151
- 26
src/components/ProductDetails/ProductDetails.jsx View File

1
-import { useState } from "react";
1
+import { useState, useEffect } from "react";
2
 import {
2
 import {
3
   Box,
3
   Box,
4
   Typography,
4
   Typography,
13
 // Utility function to check if an object is empty
13
 // Utility function to check if an object is empty
14
 const isEmptyObject = (obj) => Object.keys(obj).length === 0 && obj.constructor === Object;
14
 const isEmptyObject = (obj) => Object.keys(obj).length === 0 && obj.constructor === Object;
15
 
15
 
16
+// Check if every key in the target object matches the source object
17
+const hasMatchingProperties = (source, target) => {
18
+  return Object.entries(target).every(([key, value]) => source[key] === value);
19
+};
20
+
16
 const ProductDetails = () => {
21
 const ProductDetails = () => {
17
 
22
 
18
   const dispatch = useDispatch()
23
   const dispatch = useDispatch()
20
   const cart = useSelector((state) => state.cart.cart)
25
   const cart = useSelector((state) => state.cart.cart)
21
 
26
 
22
   const [quantity, setQuantity] = useState(1)
27
   const [quantity, setQuantity] = useState(1)
23
-  const [selectedSize, setSelectedSize] = useState(null)
28
+  const [variantSelection, setVariantSelection] = useState({
29
+    price: 0,
30
+    currencyCode: "",
31
+    quantityAvailable: 0
32
+  })
33
+  const [variants, setVariants] = useState([])
34
+
35
+  useEffect(() => {
36
+    if (product) {
37
+
38
+      console.log("Product: ", product)
39
+
40
+      let productVariants = product?.variants?.nodes
41
+
42
+      // get all variant type
43
+      if (!productVariants || productVariants?.length == 0) return
44
+
45
+      // we want to get the title for each variant
46
+      const uniqueOptions = {};
47
+      productVariants.forEach(variant => {
48
+        variant.selectedOptions.forEach(option => {
49
+          if (!uniqueOptions[option.name]) {
50
+            uniqueOptions[option.name] = new Set();
51
+          }
52
+          uniqueOptions[option.name].add(option.value);
53
+        });
54
+      });
55
+
56
+      const VariantsArr = Object.entries(uniqueOptions).map(([key, valueSet]) => ({
57
+        name: key,
58
+        options: Array.from(valueSet),
59
+      }));
60
+
61
+      // get variants value
62
+      setVariants(VariantsArr)
63
+
64
+      // setting Initial value for variants selection
65
+      setVariantSelection((prev) => {
66
+
67
+        let newVariantSelection = { ...prev }
68
+
69
+        // setting inital selection
70
+        VariantsArr.forEach(({ name, options }) => {
71
+          newVariantSelection = { ...newVariantSelection, [name]: options[0] }
72
+        })
73
+
74
+        // find variant price if it all match initial variant selection
75
+        for (const { selectedOptions, price, id, quantityAvailable } of productVariants) {
76
+          let { amount, currencyCode } = price;
77
+        
78
+          // Convert array to object
79
+          const optionsObject = selectedOptions.reduce(
80
+            (a, { name, value }) => ({ ...a, [name]: value }),
81
+            {}
82
+          );
83
+        
84
+          if (hasMatchingProperties(newVariantSelection, optionsObject)) {
85
+            newVariantSelection = { ...newVariantSelection, amount, currencyCode, id, quantityAvailable };
86
+            break; // Exit the loop when condition is met
87
+          }
88
+        }
89
+
90
+        return newVariantSelection
91
+
92
+      })
93
+
94
+    }
95
+
96
+  }, [product])
97
+
98
+  useEffect(() => {
99
+    console.log("variantSelection: ", variantSelection)
100
+  }, [variantSelection])
101
+
102
+  const handleVariantClick = (name, value) => {
103
+    setVariantSelection({ ...variantSelection, [name]: value })
104
+
105
+    setVariantSelection((prev) => {
106
+
107
+      let newVariantSelection = { ...prev }
108
+      newVariantSelection = { ...newVariantSelection, [name]: value }
109
+      let productVariants = product?.variants?.nodes
110
+
111
+      // find variant price if it all match initial variant selection
112
+      for (const { selectedOptions, price, id, quantityAvailable } of productVariants) {
113
+        let { amount, currencyCode } = price;
114
+      
115
+        // Convert array to object
116
+        const optionsObject = selectedOptions.reduce(
117
+          (a, { name, value }) => ({ ...a, [name]: value }),
118
+          {}
119
+        );
120
+      
121
+        if (hasMatchingProperties(newVariantSelection, optionsObject)) {
122
+          newVariantSelection = { ...newVariantSelection, amount, currencyCode, id, quantityAvailable };
123
+          if(quantityAvailable == 0) setQuantity(0)
124
+          else setQuantity(1)
125
+          break; // Exit the loop when condition is met
126
+        }
127
+      }
128
+
129
+
130
+      return newVariantSelection
131
+
132
+    })
133
+
134
+  }
24
 
135
 
25
   const handleCart = () => {
136
   const handleCart = () => {
26
 
137
 
31
     if (isEmptyObject(cart) || isEmptyObject(cartHistory)) {
142
     if (isEmptyObject(cart) || isEmptyObject(cartHistory)) {
32
 
143
 
33
       dispatch(createCart());
144
       dispatch(createCart());
34
-      
145
+
35
     } else {
146
     } else {
36
 
147
 
37
       // // Update cart content
148
       // // Update cart content
62
     setQuantity((prevQuantity) => (prevQuantity > 1 ? prevQuantity - 1 : 1));
173
     setQuantity((prevQuantity) => (prevQuantity > 1 ? prevQuantity - 1 : 1));
63
   };
174
   };
64
 
175
 
65
-  const handleSizeClick = (size) => {
66
-    setSelectedSize(size);
67
-  };
68
 
176
 
69
   return (
177
   return (
70
     <Box sx={{ position: "relative" }}>
178
     <Box sx={{ position: "relative" }}>
82
             variant="body1"
190
             variant="body1"
83
             sx={{ fontWeight: "bold" }}
191
             sx={{ fontWeight: "bold" }}
84
           >
192
           >
85
-            {`${product?.compareAtPriceRange?.minVariantPrice?.currencyCode} 
86
-            ${parseFloat(product?.compareAtPriceRange?.minVariantPrice?.amount).toFixed(2)}`}
193
+            {`${variantSelection.currencyCode} ${parseFloat(variantSelection.amount).toFixed(2)}`}
194
+          </Typography>
195
+          <Typography
196
+            variant="body1"
197
+          >
198
+            IN STOCK: {`${variantSelection?.quantityAvailable}`}
87
           </Typography>
199
           </Typography>
88
         </Box>
200
         </Box>
89
 
201
 
90
-        {/* Section 2: Size */}
202
+        {/* Section 2: Variants */}
91
         <Box>
203
         <Box>
92
-          <Typography variant="body1" sx={{ fontWeight: "bold", color: "#000", mb: 2 }}>
93
-            Size
94
-          </Typography>
95
-          <Box display="flex" gap={2}>
96
-            {["S", "M", "L", "XL"].map((size) => (
97
-              <Button
98
-                key={size}
99
-                variant={selectedSize === size ? "contained" : "outlined"}
100
-                color={selectedSize === size ? "primary" : "primary"}
101
-                sx={{ color: selectedSize === size ? "#FFF" : "#000" }}
102
-                onClick={() => handleSizeClick(size)}
103
-              >
104
-                {size}
105
-              </Button>
106
-            ))}
107
-          </Box>
204
+
205
+          {variants.map(({ name, options }, index) => {
206
+
207
+            return (
208
+              <>
209
+                <Typography variant="body1" sx={{ fontWeight: "bold", color: "#000", mb:1 }}>
210
+                  {name}
211
+                </Typography>
212
+
213
+                <Box display="flex" gap={2} sx={{ mb: 2 }}>
214
+                  {options?.map((value) => (
215
+                    <Button
216
+                      key={value}
217
+                      variant={variantSelection[name] === value ? "contained" : "outlined"}
218
+                      color={variantSelection[name] === value ? "primary" : "primary"}
219
+                      sx={{ color: variantSelection[name] === value ? "#FFF" : "#000" }}
220
+                      onClick={() => handleVariantClick(name, value)}
221
+                    >
222
+                      {value}
223
+                    </Button>
224
+                  ))}
225
+                </Box>
226
+              </>
227
+            )
228
+
229
+          })}
230
+
108
         </Box>
231
         </Box>
109
 
232
 
110
 
233
 
111
         {/* Section 3: Quantity */}
234
         {/* Section 3: Quantity */}
112
         <Box sx={{ mb: 5 }}>
235
         <Box sx={{ mb: 5 }}>
113
           <Typography variant="body1" sx={{ fontWeight: "bold", color: "#000", mb: 2 }}>
236
           <Typography variant="body1" sx={{ fontWeight: "bold", color: "#000", mb: 2 }}>
114
-            Qunatity
237
+            Quantity
115
           </Typography>
238
           </Typography>
116
           <Box display="flex" alignItems="center" gap={2}>
239
           <Box display="flex" alignItems="center" gap={2}>
117
             <Button
240
             <Button
118
               variant="contained"
241
               variant="contained"
119
               color="primary"
242
               color="primary"
120
               sx={{ width: "35px" }}
243
               sx={{ width: "35px" }}
244
+              disabled={variantSelection?.quantityAvailable == 0}
121
               onClick={handleDecrement}
245
               onClick={handleDecrement}
122
             >
246
             >
123
               <RemoveIcon />
247
               <RemoveIcon />
135
               variant="contained"
259
               variant="contained"
136
               color="primary"
260
               color="primary"
137
               sx={{ width: "35px" }}
261
               sx={{ width: "35px" }}
262
+              disabled={variantSelection?.quantityAvailable == 0}
138
               onClick={handleIncrement}
263
               onClick={handleIncrement}
139
             >
264
             >
140
               <AddIcon />
265
               <AddIcon />

+ 4
- 0
src/components/ProductList/ProductList.jsx View File

17
 
17
 
18
   }, [])
18
   }, [])
19
 
19
 
20
+  useEffect(() => {
21
+    if(products) console.log(products)
22
+  }, [products])
23
+
20
   const renderProduct = (id, img_url, title, collection, price, currency, extra_desc) => {
24
   const renderProduct = (id, img_url, title, collection, price, currency, extra_desc) => {
21
 
25
 
22
     return (
26
     return (

+ 1
- 0
src/redux/slices/cartSlice.js View File

108
       })
108
       })
109
       .addCase(fetchCart.fulfilled, (state, action) => {
109
       .addCase(fetchCart.fulfilled, (state, action) => {
110
         state.status = 'succeeded';
110
         state.status = 'succeeded';
111
+        localStorage.setItem('amber-cart', JSON.stringify(action.payload));
111
         state.cart = action.payload;
112
         state.cart = action.payload;
112
       })
113
       })
113
       .addCase(fetchCart.rejected, (state, action) => {
114
       .addCase(fetchCart.rejected, (state, action) => {

+ 16
- 21
src/services/CartService.js View File

64
 
64
 
65
 // Get cart details
65
 // Get cart details
66
 const getCart = async (cartId) => {
66
 const getCart = async (cartId) => {
67
+
67
   const query = `
68
   const query = `
68
-    query GetCart($cartId: ID!) {
69
-      cart(id: $cartId) {
69
+    {
70
+      cart(id: "${cartId}") {
70
         id
71
         id
71
         createdAt
72
         createdAt
72
         updatedAt
73
         updatedAt
73
-        lines(first: 10) {
74
+        cost {
75
+          totalAmount {
76
+            amount
77
+            currencyCode
78
+          }
79
+          subtotalAmount {
80
+            amount
81
+            currencyCode
82
+          }
83
+        }
84
+        lines(first: 99) {
74
           edges {
85
           edges {
75
             node {
86
             node {
76
               id
87
               id
84
             }
95
             }
85
           }
96
           }
86
         }
97
         }
87
-        cost {
88
-          subtotalAmount {
89
-            amount
90
-            currencyCode
91
-          }
92
-          totalAmount {
93
-            amount
94
-            currencyCode
95
-          }
96
-          totalTaxAmount {
97
-            amount
98
-            currencyCode
99
-          }
100
-        }
101
       }
98
       }
102
     }
99
     }
103
   `;
100
   `;
104
 
101
 
105
-   
106
-  const variables = { cartId };
107
-  const { data } = await client.request(query, variables);
108
-  debugger
102
+  const { data } = await client.request(query);
109
   return data.cart;
103
   return data.cart;
110
 };
104
 };
111
 
105
 
106
+
112
 // Update a line item's quantity
107
 // Update a line item's quantity
113
 const updateItemQuantity = async (cartId, lineId, quantity) => {
108
 const updateItemQuantity = async (cartId, lineId, quantity) => {
114
   const query = `
109
   const query = `

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

30
             currencyCode
30
             currencyCode
31
           }
31
           }
32
         }
32
         }
33
-        images(first: 1) {
33
+        images(first: 4) {
34
           nodes {
34
           nodes {
35
             src
35
             src
36
             url
36
             url
37
           }
37
           }
38
         }
38
         }
39
-        collections(first: 1) {
39
+        collections(first: 55) {
40
           nodes {
40
           nodes {
41
             title
41
             title
42
           }
42
           }
43
         }
43
         }
44
-        variants(first: 5) {
44
+        variants(first: 200) {
45
           nodes {
45
           nodes {
46
             id
46
             id
47
             title
47
             title
93
           url
93
           url
94
         }
94
         }
95
       }
95
       }
96
-      variants(first: 5) {
96
+      variants(first: 200) {
97
         nodes {
97
         nodes {
98
           id
98
           id
99
           title
99
           title
101
             amount
101
             amount
102
             currencyCode
102
             currencyCode
103
           }
103
           }
104
+          selectedOptions {
105
+            name
106
+            value
107
+          }
108
+          quantityAvailable
104
         }
109
         }
105
       }
110
       }
106
-      collections(first: 1) {
111
+      collections(first: 20) {
107
         nodes {
112
         nodes {
108
           title
113
           title
109
         }
114
         }

Loading…
Cancel
Save