azri 11 години тому
джерело
коміт
b1d0b2aae8

+ 4
- 1
src/App.js Переглянути файл

@@ -30,7 +30,10 @@ function App() {
30 30
     // if we got cart, then just fetch from BE based on ID
31 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 Переглянути файл

@@ -1,4 +1,4 @@
1
-import { useState } from "react";
1
+import { useState, useEffect } from "react";
2 2
 import {
3 3
   Box,
4 4
   Typography,
@@ -13,6 +13,11 @@ import { createCart } from "../../redux/slices/cartSlice";
13 13
 // Utility function to check if an object is empty
14 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 21
 const ProductDetails = () => {
17 22
 
18 23
   const dispatch = useDispatch()
@@ -20,7 +25,113 @@ const ProductDetails = () => {
20 25
   const cart = useSelector((state) => state.cart.cart)
21 26
 
22 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 136
   const handleCart = () => {
26 137
 
@@ -31,7 +142,7 @@ const ProductDetails = () => {
31 142
     if (isEmptyObject(cart) || isEmptyObject(cartHistory)) {
32 143
 
33 144
       dispatch(createCart());
34
-      
145
+
35 146
     } else {
36 147
 
37 148
       // // Update cart content
@@ -62,9 +173,6 @@ const ProductDetails = () => {
62 173
     setQuantity((prevQuantity) => (prevQuantity > 1 ? prevQuantity - 1 : 1));
63 174
   };
64 175
 
65
-  const handleSizeClick = (size) => {
66
-    setSelectedSize(size);
67
-  };
68 176
 
69 177
   return (
70 178
     <Box sx={{ position: "relative" }}>
@@ -82,42 +190,58 @@ const ProductDetails = () => {
82 190
             variant="body1"
83 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 199
           </Typography>
88 200
         </Box>
89 201
 
90
-        {/* Section 2: Size */}
202
+        {/* Section 2: Variants */}
91 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 231
         </Box>
109 232
 
110 233
 
111 234
         {/* Section 3: Quantity */}
112 235
         <Box sx={{ mb: 5 }}>
113 236
           <Typography variant="body1" sx={{ fontWeight: "bold", color: "#000", mb: 2 }}>
114
-            Qunatity
237
+            Quantity
115 238
           </Typography>
116 239
           <Box display="flex" alignItems="center" gap={2}>
117 240
             <Button
118 241
               variant="contained"
119 242
               color="primary"
120 243
               sx={{ width: "35px" }}
244
+              disabled={variantSelection?.quantityAvailable == 0}
121 245
               onClick={handleDecrement}
122 246
             >
123 247
               <RemoveIcon />
@@ -135,6 +259,7 @@ const ProductDetails = () => {
135 259
               variant="contained"
136 260
               color="primary"
137 261
               sx={{ width: "35px" }}
262
+              disabled={variantSelection?.quantityAvailable == 0}
138 263
               onClick={handleIncrement}
139 264
             >
140 265
               <AddIcon />

+ 4
- 0
src/components/ProductList/ProductList.jsx Переглянути файл

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

+ 1
- 0
src/redux/slices/cartSlice.js Переглянути файл

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

+ 16
- 21
src/services/CartService.js Переглянути файл

@@ -64,13 +64,24 @@ const addItemToCart = async (cartId, lines) => {
64 64
 
65 65
 // Get cart details
66 66
 const getCart = async (cartId) => {
67
+
67 68
   const query = `
68
-    query GetCart($cartId: ID!) {
69
-      cart(id: $cartId) {
69
+    {
70
+      cart(id: "${cartId}") {
70 71
         id
71 72
         createdAt
72 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 85
           edges {
75 86
             node {
76 87
               id
@@ -84,31 +95,15 @@ const getCart = async (cartId) => {
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 103
   return data.cart;
110 104
 };
111 105
 
106
+
112 107
 // Update a line item's quantity
113 108
 const updateItemQuantity = async (cartId, lineId, quantity) => {
114 109
   const query = `

+ 10
- 5
src/services/ProductService.js Переглянути файл

@@ -30,18 +30,18 @@ const getProducts = async (maxResults = defaultResult, sortBy = defaultSortBy, c
30 30
             currencyCode
31 31
           }
32 32
         }
33
-        images(first: 1) {
33
+        images(first: 4) {
34 34
           nodes {
35 35
             src
36 36
             url
37 37
           }
38 38
         }
39
-        collections(first: 1) {
39
+        collections(first: 55) {
40 40
           nodes {
41 41
             title
42 42
           }
43 43
         }
44
-        variants(first: 5) {
44
+        variants(first: 200) {
45 45
           nodes {
46 46
             id
47 47
             title
@@ -93,7 +93,7 @@ const getProduct = async (id = "") => {
93 93
           url
94 94
         }
95 95
       }
96
-      variants(first: 5) {
96
+      variants(first: 200) {
97 97
         nodes {
98 98
           id
99 99
           title
@@ -101,9 +101,14 @@ const getProduct = async (id = "") => {
101 101
             amount
102 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 112
         nodes {
108 113
           title
109 114
         }

Завантаження…
Відмінити
Зберегти