Bläddra i källkod

handle 0 cart

master
azri 2 veckor sedan
förälder
incheckning
9812f5b1be

+ 5
- 1
src/App.js Visa fil

@@ -12,7 +12,7 @@ import Footer from './components/Footer/Footer';
12 12
 import Header from './components/Header';
13 13
 import theme from './theme/theme';
14 14
 import { ThemeProvider } from '@mui/material';
15
-import { fetchCart } from './redux/slices/cartSlice';
15
+import { fetchCart, createCart } from './redux/slices/cartSlice';
16 16
 import { useSelector, useDispatch } from 'react-redux';
17 17
 
18 18
 const isEmptyObject = (obj) => Object.keys(obj).length === 0 && obj.constructor === Object;
@@ -32,6 +32,10 @@ function App() {
32 32
 
33 33
       dispatch(fetchCart(cartHistory.id));
34 34
 
35
+    }else{
36
+
37
+      dispatch(createCart())
38
+
35 39
     }
36 40
 
37 41
   }, [])

+ 10
- 9
src/components/Navbar/Navbar.jsx Visa fil

@@ -10,13 +10,14 @@ import LocalMallIcon from '@mui/icons-material/LocalMall';
10 10
 import AccountCircleIcon from "@mui/icons-material/AccountCircle";
11 11
 import logoSrc from "../../assets/svg/logo.svg";
12 12
 import { styled } from '@mui/material/styles';
13
-import { MenuItem, Select, FormControl, Badge } from '@mui/material';
13
+import { MenuItem, Select, FormControl, Badge, Typography } from '@mui/material';
14 14
 import MobileNav from "./components/MobileNav";
15 15
 import MenuIcon from "@mui/icons-material/Menu";
16 16
 import CategoryIcon from '@mui/icons-material/Category';
17 17
 import HomeIcon from '@mui/icons-material/Home';
18 18
 import BrushIcon from '@mui/icons-material/Brush';
19 19
 import LoyaltyIcon from '@mui/icons-material/Loyalty';
20
+import Grid from '@mui/material/Grid2';
20 21
 import { useSelector } from 'react-redux';
21 22
 
22 23
 function calculateTotalQuantity(cartItems) {
@@ -83,20 +84,20 @@ const Navbar = () => {
83 84
   const [showHeader, setShowHeader] = useState(true);
84 85
   const [lastScrollPos, setLastScrollPos] = useState(0);
85 86
   const [language, setLanguage] = useState('English');
86
-  
87
+
87 88
   const cart = useSelector((state) => state.cart.cart)
88 89
   const [cartAmount, setCartAmount] = useState(0);
89 90
 
90 91
   const [open, setOpen] = React.useState(false);
91
-  
92
+
92 93
   useEffect(() => {
93
-    
94
-    if(!cart?.lines?.nodes) return // don't need to do anything if we have no cart data
94
+
95
+    if (!cart?.lines?.nodes) return // don't need to do anything if we have no cart data
95 96
 
96 97
     setCartAmount(calculateTotalQuantity(cart?.lines?.nodes))
97 98
 
98 99
   }, [cart])
99
-  
100
+
100 101
 
101 102
 
102 103
   const handleChange = (event) => {
@@ -126,7 +127,7 @@ const Navbar = () => {
126 127
         sx={{
127 128
           backgroundColor: {
128 129
             xs: showHeader ? "rgba(0,0,0,0)" : "background.black",// somehow the logic for scrolling header is the same so I just gonna use showHeader state
129
-            md: "rgba(0,0,0,0.9)", 
130
+            md: "rgba(0,0,0,0.9)",
130 131
             lg: "background.black"
131 132
           },
132 133
           boxShadow: {
@@ -246,7 +247,7 @@ const Navbar = () => {
246 247
               <SearchIcon sx={{ color: "white" }} />
247 248
             </IconButton>
248 249
 
249
-            <Badge sx={{cursor:"pointer"}} onClick={()=>{ window.location.href = "/cart" }} badgeContent={cartAmount} color="primary">
250
+            <Badge sx={{ cursor: "pointer" }} onClick={() => { window.location.href = "/cart" }} badgeContent={cartAmount} color="primary">
250 251
               <LocalMallIcon color="action" sx={{ color: "white" }} />
251 252
             </Badge>
252 253
 
@@ -258,7 +259,7 @@ const Navbar = () => {
258 259
         </Toolbar>
259 260
       </AppBar>
260 261
 
261
-      <MobileNav open={open} menu={navItem} onClose={()=>{setOpen(false)}} />
262
+      <MobileNav open={open} menu={navItem} onClose={() => { setOpen(false) }} />
262 263
     </>
263 264
   );
264 265
 };

+ 18
- 0
src/components/ProductDetails/ProductDetails.jsx Visa fil

@@ -320,6 +320,24 @@ const ProductDetails = () => {
320 320
         >
321 321
           ADD TO CART {showLoader && <CircularProgress sx={{ml:1}} color="white" size={20} />} 
322 322
         </Button>
323
+
324
+        { cart?.lines?.nodes?.length > 0 && <Button
325
+          onClick={() => { window.location.href = '/cart' }}
326
+          variant="contained"
327
+          color="common.black"
328
+          fullWidth
329
+          disabled={showLoader}
330
+          sx={{
331
+            backgroundColor: (theme) => theme.palette.primary.main,
332
+            color: "white",
333
+            textTransform: "none",
334
+            "&:hover": {
335
+              backgroundColor: (theme) => theme.palette.grey[900],
336
+            },
337
+          }}
338
+        >
339
+          PAY NOW
340
+        </Button>}
323 341
       </Box>
324 342
       {alert.open && (
325 343
         <Alert

+ 42
- 10
src/pages/Cart.jsx Visa fil

@@ -12,8 +12,13 @@ import TableHead from '@mui/material/TableHead';
12 12
 import TableRow from '@mui/material/TableRow';
13 13
 import AddIcon from '@mui/icons-material/Add';
14 14
 import RemoveIcon from '@mui/icons-material/Remove';
15
+import { addItemToCart, updateItemQuantity } from '../redux/slices/cartSlice';
15 16
 import { useSelector, useDispatch } from 'react-redux';
16 17
 
18
+
19
+// Utility function to check if an object is empty
20
+const isEmptyObject = (obj) => Object.keys(obj).length === 0 && obj.constructor === Object;
21
+
17 22
 const Cart = () => {
18 23
 
19 24
   const dispatch = useDispatch()
@@ -27,9 +32,26 @@ const Cart = () => {
27 32
   }, [])
28 33
 
29 34
   useEffect(() => {
35
+
36
+    if(cart?.lines?.nodes?.length == 0) window.location.href = "/"
37
+
30 38
     setCartProducts(cart?.lines?.nodes || [])
39
+
31 40
   }, [cart])
32 41
 
42
+  const handleUpdateCart = ({ quantity,  lineId}) => {
43
+
44
+    let cartHistory = localStorage.getItem('amber-cart');
45
+    cartHistory = cartHistory ? JSON.parse(cartHistory) : {};
46
+
47
+    dispatch(updateItemQuantity({
48
+      cartId:cartHistory.id,
49
+      lineId,
50
+      quantity,
51
+    }))
52
+
53
+  }
54
+
33 55
 
34 56
   return (
35 57
     <>
@@ -83,10 +105,10 @@ const Cart = () => {
83 105
               </TableRow>
84 106
             </TableHead>
85 107
             <TableBody>
86
-              {cartProducts?.map(({ cost, merchandise, quantity }) => {
87
-                
88
-                let {amount, currencyCode} = cost.totalAmount
89
-                let {id, image, product, title } = merchandise // the title here is actually the variant title name
108
+              {cartProducts?.map(({ id, cost, merchandise, quantity }) => {
109
+
110
+                let { amount, currencyCode } = cost.totalAmount
111
+                let { image, product, title } = merchandise
90 112
 
91 113
                 return (
92 114
                   <TableRow
@@ -105,14 +127,14 @@ const Cart = () => {
105 127
                           height: 100,
106 128
                           aspectRatio: '4 / 4',
107 129
                           objectFit: 'cover',
108
-                          objectPosition:"top center"
130
+                          objectPosition: "top center"
109 131
                         }}
110 132
                       />
111 133
                     </TableCell>
112 134
                     <TableCell>
113 135
                       <Typography variant='body2' sx={{ fontWeight: "bold", mb: 1 }}>{product.title}</Typography>
114
-                      
115
-                      {product?.collections?.nodes?.map(({title}) => (<Typography variant='body2'>{title}</Typography>) )}
136
+
137
+                      {product?.collections?.nodes?.map(({ title }) => (<Typography variant='body2'>{title}</Typography>))}
116 138
 
117 139
                     </TableCell>
118 140
                     <TableCell align='center'>
@@ -120,13 +142,23 @@ const Cart = () => {
120 142
                     </TableCell>
121 143
                     <TableCell align='center'>
122 144
                       <Box sx={{ display: "flex", justifyContent: "center" }}>
123
-                        <IconButton>
145
+
146
+                        <IconButton onClick={() => {
147
+
148
+                          handleUpdateCart({ quantity: quantity - 1, lineId:id })
149
+                        }}>
124 150
                           <RemoveIcon sx={{ fontSize: 16, margin: "0 15px" }} />
125 151
                         </IconButton>
152
+
126 153
                         <p style={{ fontSize: 20, fontWeight: "bold" }}>{quantity}</p>
127
-                        <IconButton >
154
+
155
+                        <IconButton onClick={() => {
156
+
157
+                          handleUpdateCart({ quantity: quantity + 1, lineId:id })
158
+                        }}>
128 159
                           <AddIcon sx={{ fontSize: 16, margin: "0 15px" }} />
129 160
                         </IconButton>
161
+
130 162
                       </Box>
131 163
                     </TableCell>
132 164
                     <TableCell align='center'>
@@ -197,7 +229,7 @@ const Cart = () => {
197 229
             <Button
198 230
               variant="contained"
199 231
               color="common.black"
200
-              onClick={() => { window.location.href = "/checkout" }}
232
+              onClick={() => { window.location.href = cart.checkoutUrl }}
201 233
               sx={{
202 234
                 backgroundColor: (theme) => theme.palette.common.black,
203 235
                 color: "white",

+ 4
- 1
src/redux/slices/cartSlice.js Visa fil

@@ -108,7 +108,9 @@ 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
+        if(action.payload) localStorage.setItem('amber-cart', JSON.stringify(action.payload))
112
+        else localStorage.removeItem('amber-cart')
113
+        
112 114
         state.cart = action.payload;
113 115
       })
114 116
       .addCase(fetchCart.rejected, (state, action) => {
@@ -123,6 +125,7 @@ export const cartSlice = createSlice({
123 125
       })
124 126
       .addCase(updateItemQuantity.fulfilled, (state, action) => {
125 127
         state.status = 'succeeded';
128
+        localStorage.setItem('amber-cart', JSON.stringify(action.payload));
126 129
         state.cart = action.payload;
127 130
       })
128 131
       .addCase(updateItemQuantity.rejected, (state, action) => {

+ 56
- 14
src/services/CartService.js Visa fil

@@ -32,6 +32,7 @@ const addItemToCart = async (cartId, lines) => {
32 32
       cartLinesAdd(cartId: $cartId, lines: $lines) {
33 33
         cart {
34 34
           id
35
+          checkoutUrl
35 36
           createdAt
36 37
           updatedAt
37 38
           cost {
@@ -48,7 +49,7 @@ const addItemToCart = async (cartId, lines) => {
48 49
               currencyCode
49 50
             }
50 51
           }
51
-          lines(first: 10) {
52
+          lines(first: 99) {
52 53
             nodes {
53 54
               id
54 55
               merchandise {
@@ -104,6 +105,7 @@ const getCart = async (cartId) => {
104 105
     {
105 106
       cart(id: "${cartId}") {
106 107
         id
108
+        checkoutUrl
107 109
         createdAt
108 110
         updatedAt
109 111
         cost {
@@ -165,29 +167,64 @@ const getCart = async (cartId) => {
165 167
 
166 168
 // Update a line item's quantity
167 169
 const updateItemQuantity = async (cartId, lineId, quantity) => {
170
+
171
+  
168 172
   const query = `
169 173
     mutation UpdateItemQuantity($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
170 174
       cartLinesUpdate(cartId: $cartId, lines: $lines) {
171 175
         cart {
172 176
           id
173
-          lines(first: 10) {
174
-            edges {
175
-              node {
176
-                id
177
-                quantity
178
-                merchandise {
179
-                  ... on ProductVariant {
180
-                    id
177
+          checkoutUrl
178
+          createdAt
179
+          updatedAt
180
+          cost {
181
+            totalAmount {
182
+              amount
183
+              currencyCode
184
+            }
185
+            subtotalAmount {
186
+              amount
187
+              currencyCode
188
+            }
189
+            totalTaxAmount {
190
+              amount
191
+              currencyCode
192
+            }
193
+          }
194
+          lines(first: 99) {
195
+            nodes {
196
+              id
197
+              merchandise {
198
+                ... on ProductVariant {
199
+                  id
200
+                  title
201
+                  selectedOptions {
202
+                    name
203
+                    value
204
+                  }
205
+                  image {
206
+                    src
207
+                  }
208
+                  product {
209
+                    title
210
+                    collections(first: 5) {
211
+                      nodes {
212
+                        title
213
+                      }
214
+                    }
181 215
                   }
182 216
                 }
183 217
               }
218
+              quantity
219
+              cost {
220
+                totalAmount {
221
+                  amount
222
+                  currencyCode
223
+                }
224
+              }
184 225
             }
185 226
           }
186 227
         }
187
-        userErrors {
188
-          field
189
-          message
190
-        }
191 228
       }
192 229
     }
193 230
   `;
@@ -195,7 +232,12 @@ const updateItemQuantity = async (cartId, lineId, quantity) => {
195 232
     cartId,
196 233
     lines: [{ id: lineId, quantity }],
197 234
   };
198
-  const { data } = await client.request(query, variables);
235
+  const {data, errors, extensions} = await client.request(query,{
236
+    variables: {
237
+      cartId,
238
+      lines: [{ id: lineId, quantity }]
239
+    }
240
+  });
199 241
   return data.cartLinesUpdate.cart;
200 242
 };
201 243
 

Laddar…
Avbryt
Spara