Sfoglia il codice sorgente

sort by category

master
azri 4 settimane fa
parent
commit
a91415699d

+ 5
- 1
src/components/CollectionList/CollectionList.jsx Vedi File

@@ -54,7 +54,11 @@ const CollectionList = ({ collectionName }) => {
54 54
                 <Typography variant="h4" gutterBottom>
55 55
                   {name}
56 56
                 </Typography>
57
-                <Button onClick={()=>{ window.location.href = "/products" }} variant="contained" color="primary">
57
+                <Button onClick={()=>{ 
58
+                  sessionStorage.removeItem('amber-select-category')
59
+                  sessionStorage.setItem('amber-select-collection', JSON.stringify({id, name, src}))
60
+                  window.location.href = "/products" 
61
+                }} variant="contained" color="primary">
58 62
                   SHOP NOW
59 63
                 </Button>
60 64
               </Box>

+ 0
- 2
src/components/Filter/Filter.jsx Vedi File

@@ -44,8 +44,6 @@ const Filter = () => {
44 44
         show:1,
45 45
     })
46 46
 
47
-
48
-
49 47
     const handleChange = (event) => {
50 48
         setInput({...input, [event.target.name]:event.target.value });
51 49
     };

+ 8
- 5
src/components/Navbar/Navbar.jsx Vedi File

@@ -137,7 +137,7 @@ const Navbar = () => {
137 137
       >
138 138
 
139 139
         {/* Conditionally render the Header */}
140
-        {showHeader && <Header />}
140
+        {/* {showHeader && <Header />} */}
141 141
 
142 142
         <Toolbar sx={{
143 143
           px: {
@@ -205,7 +205,10 @@ const Navbar = () => {
205 205
             {navItem.map(({ title, link }) => (<Button
206 206
               color="inherit"
207 207
               onClick={() => {
208
+
209
+                sessionStorage.setItem('amber-select-product-type',title.toLocaleLowerCase())
208 210
                 window.location.href = link;
211
+              
209 212
               }}
210 213
             >
211 214
               {title}
@@ -216,7 +219,7 @@ const Navbar = () => {
216 219
           {/* Right Section: Search and Profile */}
217 220
           <Box sx={{ display: "flex", alignItems: "center", gap: 1.2, ml: "auto" }}>
218 221
 
219
-            <FormControl
222
+            {/* <FormControl
220 223
               sx={{
221 224
                 display: {
222 225
                   xs: "none",
@@ -241,7 +244,7 @@ const Navbar = () => {
241 244
                 <LanguageSelectItem value="Malay">Malay</LanguageSelectItem>
242 245
                 <LanguageSelectItem value="Chinese">Chinese</LanguageSelectItem>
243 246
               </LanguageSelect>
244
-            </FormControl>
247
+            </FormControl> */}
245 248
 
246 249
             <IconButton color="inherit">
247 250
               <SearchIcon sx={{ color: "white" }} />
@@ -251,9 +254,9 @@ const Navbar = () => {
251 254
               <LocalMallIcon color="action" sx={{ color: "white" }} />
252 255
             </Badge>
253 256
 
254
-            <IconButton color="inherit">
257
+            {/* <IconButton color="inherit">
255 258
               <AccountCircleIcon sx={{ color: "white" }} />
256
-            </IconButton>
259
+            </IconButton> */}
257 260
           </Box>
258 261
 
259 262
         </Toolbar>

+ 179
- 29
src/components/ProductList/ProductList.jsx Vedi File

@@ -1,26 +1,121 @@
1
-import React, { useEffect } from 'react';
2
-import { Box, Typography, Button } from '@mui/material';
1
+import { useEffect, useState } from 'react';
2
+import { Box, Typography, Button, FormControl, Select, MenuItem, InputBase } from '@mui/material';
3 3
 import Grid from '@mui/material/Grid2';
4
-
4
+import { styled } from "@mui/material";
5 5
 //REDUX
6 6
 import { useSelector, useDispatch } from 'react-redux';
7
-import { fetchProducts } from '../../redux/slices/productSlice';
7
+import { fetchProducts, fetchProductsByCollection } from '../../redux/slices/productSlice';
8
+
9
+//UTIL FUNCTION
10
+function getAllTags(data) {
11
+  const products = data || [];
12
+  const allTags = products.flatMap(product => product.tags);
13
+  const uniqueTags = [...new Set(allTags)];
14
+  return uniqueTags;
15
+}
16
+
17
+const BootstrapInput = styled(InputBase)(({ theme }) => ({
18
+  'label + &': {
19
+    marginTop: theme.spacing(3),
20
+  },
21
+  '& .MuiInputBase-input': {
22
+    position: 'relative',
23
+    backgroundColor: "#2E2E2E",
24
+    border: '1px solid #ced4da',
25
+    color: "#FFF",
26
+    fontSize: 13,
27
+    padding: '5px 0',
28
+    paddingRight: '50px !important',
29
+    paddingLeft: "10px",
30
+    transition: theme.transitions.create(['border-color', 'box-shadow']),
31
+    '&:focus': {
32
+      borderRadius: 4,
33
+      borderColor: '#80bdff',
34
+      boxShadow: '0 0 0 0.2rem rgba(0,123,255,.25)',
35
+    },
36
+  },
37
+  '& .MuiSvgIcon-root': {
38
+    color: "#FFF !important"
39
+  },
40
+}));
8 41
 
9 42
 const ProductList = ({ size = 99999 }) => {
10 43
 
11
-  const products = useSelector((state) => state.products.products.data)
44
+  const products = useSelector((state) => state.products.products.data) // only used as referenced
45
+  const [filteredProducts, setFilteredProducts] = useState([]) // this one is the actual data to be rendered
46
+  const [categoryFilterOption, setCategoryFilterOption] = useState([])
12 47
   const dispatch = useDispatch();
13 48
 
49
+  //filter
50
+  const [category, setCategory] = useState('all');
51
+  const [sort, setSort] = useState('all')
52
+
14 53
   useEffect(() => {
15 54
 
16
-    dispatch(fetchProducts())
55
+    // if user come from select collection
56
+    if (sessionStorage.getItem('amber-select-collection')) {
57
+
58
+      let { id } = JSON.parse(sessionStorage.getItem('amber-select-collection'))
59
+      dispatch(fetchProductsByCollection({ collectionId: id }))
60
+
61
+    } else if (sessionStorage.getItem('amber-select-category')) {
62
+
63
+    }
17 64
 
18 65
   }, [])
19 66
 
20 67
   useEffect(() => {
21
-    if(products) console.log(products)
68
+
69
+    let productType = sessionStorage.getItem('amber-select-product-type')
70
+    let newFilteredProducts = products.filter(
71
+      (product) => product.productType === productType
72
+    );
73
+
74
+    const categoryList = getAllTags(newFilteredProducts) // yes we use tags as category, while I was coding this, this is the only way to implement category
75
+
76
+    setFilteredProducts(newFilteredProducts)
77
+    setCategoryFilterOption(categoryList)
78
+
22 79
   }, [products])
23 80
 
81
+
82
+  useEffect(() => {
83
+
84
+    if (products?.length > 0) {
85
+
86
+      let productType = sessionStorage.getItem('amber-select-product-type')
87
+
88
+      let newFilteredProducts = products.filter(
89
+        (product) => {
90
+
91
+          if(category == 'all'){
92
+            return product.productType === productType
93
+          } else {
94
+            return product.productType === productType && product.tags.includes(category)
95
+          }
96
+
97
+        }
98
+      );
99
+
100
+      setFilteredProducts(newFilteredProducts)
101
+
102
+    }
103
+
104
+  }, [category])
105
+
106
+
107
+
108
+  useEffect(() => {
109
+    if (products) console.log(products)
110
+  }, [products])
111
+
112
+
113
+
114
+
115
+  const handleChange = (event) => {
116
+    //setInput({ ...input, [event.target.name]: event.target.value });
117
+  };
118
+
24 119
   const renderProduct = (id, img_url, title, collection, price, currency, extra_desc) => {
25 120
 
26 121
     return (
@@ -41,7 +136,7 @@ const ProductList = ({ size = 99999 }) => {
41 136
               width: '100%',
42 137
               aspectRatio: '3 / 4',
43 138
               objectFit: 'cover',
44
-              objectPosition:'top center'
139
+              objectPosition: 'top center'
45 140
             }}
46 141
           />
47 142
 
@@ -70,28 +165,83 @@ const ProductList = ({ size = 99999 }) => {
70 165
   }
71 166
 
72 167
   return (
73
-    <Box sx={{ mb: 5 }}>
74
-      <Grid container spacing={1} columns={12}>
75
-        {products.map((product, index) => {
76
-          
77
-          let {id, title, compareAtPriceRange, images, collections} = product
78
-          let price = compareAtPriceRange.maxVariantPrice.amount
79
-          let currency = compareAtPriceRange.maxVariantPrice.currencyCode
80
-          let img_url = images.nodes[0].url
81
-          let collection_name = collections.nodes[0]?.title
82
-          
83
-          // ID
84
-          const parts = id.split('/');
85
-          let prodID = parts[parts.length - 1];
86
-
87
-
88
-          if (index < size) {
89
-            return renderProduct(prodID, img_url, title, collection_name, price, currency, "" )
90
-          }
168
+    <>
169
+      {/* FILTER */}
170
+      <Box
171
+        sx={{
172
+          display: "flex",
173
+          justifyContent: "space-between",
174
+          alignItems: "center",
175
+          backgroundColor: "background.black",
176
+          color: "white",
177
+          px: 2, // Add padding around the box
178
+          my: 4
179
+        }}
180
+      >
181
+        {/* Left Side: Page Title */}
182
+        <Typography variant="body2">
183
+          {`${filteredProducts.length} Item`}
184
+        </Typography>
185
+
186
+        {/* Right Side: Option Inputs */}
187
+        <Box sx={{ display: "flex", gap: 2 }}>
188
+
189
+          <FormControl sx={{ m: 1, display: "flex", flexDirection: "row" }} variant="standard">
190
+            <Typography variant="body2" sx={{ mr: 1, my: "auto" }}>Filter By Category</Typography>
191
+            <Select
192
+              value={category}
193
+              onChange={(event) => {
194
+                setCategory(event.target.value);
195
+              }}
196
+              input={<BootstrapInput />}
197
+              name="type"
198
+            >
199
+              <MenuItem value={'all'}>All</MenuItem>
200
+              {categoryFilterOption?.map((data) => (<MenuItem value={data}>{data}</MenuItem>))}
201
+            </Select>
202
+          </FormControl>
203
+
204
+          <FormControl sx={{ m: 1, display: { xs: "none", sm: "none", md: "flex" }, flexDirection: "row" }} variant="standard">
205
+            <Typography variant="body2" sx={{ mr: 1, my: "auto" }}>Sort By</Typography>
206
+            <Select
207
+              value={sort}
208
+              onChange={handleChange}
209
+              input={<BootstrapInput />}
210
+              name="sort"
211
+            >
212
+              <MenuItem defaultValue value={''}>All</MenuItem>
213
+              <MenuItem defaultValue value={'all'}>Newest</MenuItem>
214
+
215
+            </Select>
216
+          </FormControl>
91 217
 
92
-        })}
93
-      </Grid>
94
-    </Box>
218
+        </Box>
219
+      </Box>
220
+
221
+      {/* LIST */}
222
+      <Box sx={{ mb: 5 }}>
223
+        <Grid container spacing={1} columns={12}>
224
+          {filteredProducts.map((product, index) => {
225
+
226
+            let { id, title, compareAtPriceRange, images, collections } = product
227
+            let price = compareAtPriceRange.maxVariantPrice.amount
228
+            let currency = compareAtPriceRange.maxVariantPrice.currencyCode
229
+            let img_url = images.nodes[0].url
230
+            let collection_name = collections.nodes[0]?.title
231
+
232
+            // ID
233
+            const parts = id.split('/');
234
+            let prodID = parts[parts.length - 1];
235
+
236
+
237
+            if (index < size) {
238
+              return renderProduct(prodID, img_url, title, collection_name, price, currency, "")
239
+            }
240
+
241
+          })}
242
+        </Grid>
243
+      </Box>
244
+    </>
95 245
   );
96 246
 };
97 247
 

+ 0
- 7
src/pages/Collection.jsx Vedi File

@@ -14,13 +14,6 @@ import CollectionList from '../components/CollectionList/CollectionList'
14 14
 const Collection = () => {
15 15
 
16 16
   let { pid } = useParams();
17
-  const dispatch = useDispatch();
18
-
19
-  useEffect(()=>{
20
-
21
-   //dispatch(fetchProducts(250, 'created_at',  `product_type:${pid}`))
22
-
23
-  }, [])
24 17
 
25 18
   return (
26 19
     <>

+ 25
- 3
src/pages/Products/index.jsx Vedi File

@@ -1,4 +1,4 @@
1
-import React from 'react'
1
+import {useEffect, useState} from 'react'
2 2
 import PageTitle from '../../components/PageTitle'
3 3
 import Filter from '../../components/Filter'
4 4
 import ProductList from '../../components/ProductList'
@@ -7,9 +7,32 @@ import SocialMedia from '../../components/SocialMedia'
7 7
 import Feature from '../../components/Feature'
8 8
 
9 9
 const Products = () => {
10
+
11
+  const [title, setTitle] = useState('')
12
+  const [image, setImage] = useState('')
13
+
14
+  useEffect(()=>{
15
+    
16
+    // if user come from select collection
17
+    if(sessionStorage.getItem('amber-select-collection')){
18
+
19
+      let { id, name, src } = JSON.parse(sessionStorage.getItem('amber-select-collection'))
20
+      setTitle(name)
21
+      setImage(src)
22
+
23
+    }else if(sessionStorage.getItem('amber-select-category')){
24
+
25
+      let categoryData = JSON.parse(sessionStorage.getItem('amber-select-category'))
26
+
27
+    }else{
28
+      window.location.href = '/'
29
+    }
30
+
31
+  },[])
32
+
10 33
   return (
11 34
     <>
12
-      <PageTitle />
35
+      <PageTitle title={title} image={image} />
13 36
       <Box sx={{
14 37
         px: {
15 38
           xs: 2,
@@ -22,7 +45,6 @@ const Products = () => {
22 45
           lg: 10
23 46
         }
24 47
       }}>
25
-        <Filter />
26 48
         <ProductList />
27 49
         <SocialMedia />
28 50
         <Feature />

+ 23
- 0
src/redux/slices/productSlice.js Vedi File

@@ -41,6 +41,16 @@ export const fetchProducts = createAsyncThunk(
41 41
   }
42 42
 )
43 43
 
44
+export const fetchProductsByCollection = createAsyncThunk(
45
+  'product/fetchProductsByCollection',
46
+  async ({collectionId}) => {
47
+    
48
+    const response = await ProductService.getProductsByCollection(collectionId)
49
+    return response
50
+
51
+  }
52
+)
53
+
44 54
 export const fetchProductsHistory = createAsyncThunk(
45 55
   'product/fetchProductsHistory',
46 56
   async (productIDList) => {
@@ -83,6 +93,19 @@ export const productSlice = createSlice({
83 93
         state.product.data = {};
84 94
         state.product.error = action.error.message;
85 95
       })
96
+      .addCase(fetchProductsByCollection.pending, (state) => {
97
+        state.products.status = 'loading';
98
+        state.products.error = null;
99
+      })
100
+      .addCase(fetchProductsByCollection.fulfilled, (state, action) => {
101
+        state.products.status = 'succeeded';
102
+        state.products.data = action.payload
103
+      })
104
+      .addCase(fetchProductsByCollection.rejected, (state, action) => {
105
+        state.products.status = 'failed';
106
+        state.products.data = [];
107
+        state.products.error = action.error.message;
108
+      })
86 109
       .addCase(fetchProductsHistory.pending, (state) => {
87 110
         state.productsHistory.status = 'loading';
88 111
         state.productsHistory.error = null;

+ 46
- 2
src/services/ProductService.js Vedi File

@@ -65,8 +65,6 @@ const getProducts = async (maxResults = defaultResult, sortBy = defaultSortBy, c
65 65
     },
66 66
   });
67 67
 
68
-  debugger
69
-
70 68
   return data.products.nodes
71 69
 
72 70
 }
@@ -128,6 +126,49 @@ const getProduct = async (id = "") => {
128 126
 
129 127
 }
130 128
 
129
+const getProductsByCollection = async (collectionId) => {
130
+
131
+  const query = `{
132
+    collection(id: "${collectionId}") {
133
+      products(first: 250) {
134
+        nodes {
135
+          id
136
+          title
137
+          productType
138
+          tags
139
+          compareAtPriceRange {
140
+            maxVariantPrice {
141
+              amount
142
+              currencyCode
143
+            }
144
+            minVariantPrice {
145
+              amount
146
+              currencyCode
147
+            }
148
+          }
149
+          images(first: 4) {
150
+            nodes {
151
+              src
152
+              url
153
+            }
154
+          }
155
+          collections(first: 55) {
156
+            nodes {
157
+              title
158
+            }
159
+          }
160
+        }
161
+      }
162
+    }
163
+  }`
164
+
165
+  const { data, errors, extensions } = await client.request(query, {
166
+    variables: {},
167
+  });
168
+
169
+  return data.collection.products.nodes
170
+}
171
+
131 172
 const getProductTypes = async () => {
132 173
   const query = `{
133 174
     productTypes(first: 250) {
@@ -191,9 +232,12 @@ const getCollections = async (name) => {
191 232
 
192 233
 }
193 234
 
235
+
236
+
194 237
 const ProductService = {
195 238
   getProducts,
196 239
   getProduct,
240
+  getProductsByCollection,
197 241
   getProductTypes,
198 242
   getCollections
199 243
 }

Loading…
Annulla
Salva