Pārlūkot izejas kodu

feat module 6, 7 : add side bar for store availability, details available (location, stock, whatsapp)

master
nadia 1 dienu atpakaļ
vecāks
revīzija
0d046cfa5c
1 mainītis faili ar 316 papildinājumiem un 1 dzēšanām
  1. 316
    1
      src/components/ProductDetails/ProductDetails.jsx

+ 316
- 1
src/components/ProductDetails/ProductDetails.jsx Parādīt failu

@@ -6,13 +6,20 @@ import {
6 6
   TextField,
7 7
   CircularProgress,
8 8
   Dialog,
9
-  IconButton
9
+  IconButton,
10
+  Drawer,
11
+  InputBase,
12
+  Tooltip
10 13
 } from "@mui/material";
11 14
 import AddIcon from '@mui/icons-material/Add';
12 15
 import RemoveIcon from '@mui/icons-material/Remove';
13 16
 import StraightenIcon from '@mui/icons-material/Straighten';
14 17
 import StorefrontIcon from '@mui/icons-material/Storefront';
15 18
 import CloseIcon from '@mui/icons-material/Close';
19
+import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
20
+import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
21
+import WhatsAppIcon from '@mui/icons-material/WhatsApp';
22
+import LocationOnIcon from '@mui/icons-material/LocationOn';
16 23
 import AccountBalanceWalletOutlinedIcon from '@mui/icons-material/AccountBalanceWalletOutlined';
17 24
 import LayersOutlinedIcon from '@mui/icons-material/LayersOutlined';
18 25
 import ShoppingBagOutlinedIcon from '@mui/icons-material/ShoppingBagOutlined';
@@ -45,6 +52,9 @@ const ProductDetails = () => {
45 52
   const [variants, setVariants] = useState([])
46 53
   const [showLoader, setShowLoader] = useState(false)
47 54
   const [atomeOpen, setAtomeOpen] = useState(false)
55
+  const [storeAvailabilityOpen, setStoreAvailabilityOpen] = useState(false)
56
+  const [storeSearch, setStoreSearch] = useState("")
57
+  const [expandedStoreRows, setExpandedStoreRows] = useState({})
48 58
   const [expandedInfoRows, setExpandedInfoRows] = useState({})
49 59
   const [alert, setAlert] = useState({ open: false, severity: '', message: '' });
50 60
 
@@ -231,6 +241,46 @@ const ProductDetails = () => {
231 241
   const installmentPrice = `${(amount / 3).toFixed(2)} ${currencyCode}`
232 242
   // const shortDescriptionHtml = getFirstDescriptionBlock(product?.descriptionHtml)
233 243
 
244
+  const storeLocations = [
245
+    {
246
+      name: "AMBER IOI CITY MALL",
247
+      address: "G-67 IOI City Mall, Putrajaya",
248
+      whatsapp: "https://wa.me/60172282072",
249
+      maps: "https://www.google.com/maps/search/?api=1&query=AMBER%20IOI%20City%20Mall%20Putrajaya"
250
+    },
251
+    {
252
+      name: "AMBER WANGSA WALK MALL",
253
+      address: "Lot G-77, Wangsa Walk Mall, Kuala Lumpur",
254
+      whatsapp: "https://wa.me/60172282072",
255
+      maps: "https://www.google.com/maps/search/?api=1&query=AMBER%20Wangsa%20Walk%20Mall%20Kuala%20Lumpur"
256
+    }
257
+  ]
258
+
259
+  const filteredStoreLocations = storeLocations.filter(({ name, address }) => {
260
+    const searchValue = storeSearch.trim().toLowerCase()
261
+
262
+    if (!searchValue) return true
263
+
264
+    return `${name} ${address}`.toLowerCase().includes(searchValue)
265
+  })
266
+
267
+  const selectedVariantLabel = Object.entries(variantSelection)
268
+    .filter(([key]) => !["amount", "currencyCode", "id", "quantityAvailable"].includes(key))
269
+    .map(([, value]) => value)
270
+    .join(" / ")
271
+
272
+  const storeStockText = variantSelection?.quantityAvailable > 0
273
+    ? `${variantSelection.quantityAvailable} stock left`
274
+    : "Out of stock"
275
+
276
+  const getStoreWhatsappUrl = (store) => {
277
+    const productTitle = product?.title || "this item"
278
+    const variantText = selectedVariantLabel ? ` - ${selectedVariantLabel}` : ""
279
+    const message = `Halo ${store.name}, apakah ${productTitle}${variantText} masih tersedia? Terima kasih`
280
+
281
+    return `${store.whatsapp}?text=${encodeURIComponent(message)}`
282
+  }
283
+
234 284
   const toggleInfoRow = (row) => {
235 285
     setExpandedInfoRows((prevRows) => ({
236 286
       ...prevRows,
@@ -238,6 +288,13 @@ const ProductDetails = () => {
238 288
     }))
239 289
   }
240 290
 
291
+  const toggleStoreRow = (row) => {
292
+    setExpandedStoreRows((prevRows) => ({
293
+      ...prevRows,
294
+      [row]: !prevRows[row]
295
+    }))
296
+  }
297
+
241 298
   const infoRowSx = (isExpanded) => ({
242 299
     display: "flex",
243 300
     alignItems: "flex-start",
@@ -544,12 +601,29 @@ const ProductDetails = () => {
544 601
         {/* Product information rows: store availability, details, material, care, shipping, and returns */}
545 602
         <Box sx={{ mt: 4, borderTop: "1px solid #DDD" }}>
546 603
           {/* Store Availability */}
604
+          {/*
547 605
           <Box sx={{ display: "flex", alignItems: "center", py: 2, borderBottom: "1px solid #DDD" }}>
548 606
             <StorefrontIcon sx={{ mr: 1, fontSize: 20 }} />
549 607
             <Typography variant="body2" sx={{ textTransform: "uppercase", fontWeight: "400", fontSize: { xs: "0.875rem", md: "1.1rem" } }}>
550 608
               Store Availability
551 609
             </Typography>
552 610
           </Box>
611
+          */}
612
+          <Box
613
+            onClick={() => setStoreAvailabilityOpen(true)}
614
+            sx={{
615
+              display: "flex",
616
+              alignItems: "center",
617
+              py: 2,
618
+              borderBottom: "1px solid #DDD",
619
+              cursor: "pointer"
620
+            }}
621
+          >
622
+            <StorefrontIcon sx={{ mr: 1, fontSize: 20 }} />
623
+            <Typography variant="body2" sx={{ textTransform: "uppercase", fontWeight: "400", fontSize: { xs: "0.875rem", md: "1.1rem" } }}>
624
+              Store Availability
625
+            </Typography>
626
+          </Box>
553 627
 
554 628
           {/* Product Details */}
555 629
           <Box
@@ -782,8 +856,249 @@ const ProductDetails = () => {
782 856
           </Box>
783 857
         </Box>
784 858
       </Dialog>
859
+
860
+      {/* Store Availability Sidebar */}
861
+      <Drawer
862
+        anchor="right"
863
+        open={storeAvailabilityOpen}
864
+        onClose={() => setStoreAvailabilityOpen(false)}
865
+        PaperProps={{
866
+          sx: {
867
+            width: { xs: "100vw", sm: 430, md: 500 },
868
+            maxWidth: "100%",
869
+            boxSizing: "border-box",
870
+            overflowX: "hidden",
871
+            px: { xs: 2, md: 4 },
872
+            py: { xs: 3, md: 4 },
873
+            borderRadius: 0,
874
+            fontFamily: "inherit"
875
+          }
876
+        }}
877
+      >
878
+        <Box sx={{ position: "relative" }}>
879
+          <IconButton
880
+            aria-label="Close store availability"
881
+            onClick={() => setStoreAvailabilityOpen(false)}
882
+            sx={{
883
+              position: "absolute",
884
+              right: -12,
885
+              top: -12,
886
+              color: "#000"
887
+            }}
888
+          >
889
+            <CloseIcon sx={{ fontSize: 30 }} />
890
+          </IconButton>
891
+
892
+          <Typography
893
+            variant="body2"
894
+            sx={{
895
+              textTransform: "uppercase",
896
+              fontWeight: "400",
897
+              fontSize: { xs: "1.05rem", sm: "1.2rem", md: "1.5rem" },
898
+              pr: 4,
899
+              mb: 0.5
900
+            }}
901
+          >
902
+            Store Availability
903
+          </Typography>
904
+
905
+          <Typography variant="body2" sx={{ fontWeight: "400", fontSize: { xs: "0.9rem", md: "1.05rem" }, mb: 1.5 }}>
906
+            Select variant
907
+          </Typography>
908
+
909
+          <Box sx={{ borderBottom: "1px solid #999", pb: 2.5, mb: 3 }}>
910
+            {variants.map(({ name, options }, index) => (
911
+              <Box key={name || index} sx={{ display: (name === "Title") ? "none" : "flex", flexWrap: "wrap", gap: { xs: 0.75, md: 1 } }}>
912
+                {options?.map((value) => (
913
+                  <Button
914
+                    key={value}
915
+                    variant="outlined"
916
+                    sx={{
917
+                      minWidth: { xs: 46, md: 50 },
918
+                      height: { xs: 42, md: 44 },
919
+                      px: 1.5,
920
+                      borderRadius: 0,
921
+                      borderColor: variantSelection[name] === value ? "primary.main" : "#999",
922
+                      backgroundColor: variantSelection[name] === value ? "primary.main" : "#FFF",
923
+                      color: variantSelection[name] === value ? "#FFF" : "#000",
924
+                      fontSize: "0.875rem",
925
+                      "&:hover": {
926
+                        borderColor: "primary.main",
927
+                        backgroundColor: variantSelection[name] === value ? "primary.main" : "#FFF",
928
+                      }
929
+                    }}
930
+                    onClick={() => handleVariantClick(name, value)}
931
+                  >
932
+                    {value}
933
+                  </Button>
934
+                ))}
935
+              </Box>
936
+            ))}
937
+          </Box>
938
+
939
+          <Box
940
+            sx={{
941
+              backgroundColor: "#F4F4F4",
942
+              height: { xs: 48, md: 52 },
943
+              display: "flex",
944
+              alignItems: "center",
945
+              px: 2,
946
+              mb: 2
947
+            }}
948
+          >
949
+            <InputBase
950
+              value={storeSearch}
951
+              onChange={(event) => setStoreSearch(event.target.value)}
952
+              placeholder="Search store"
953
+              fullWidth
954
+              sx={{
955
+                fontSize: "0.95rem",
956
+                color: "#000"
957
+              }}
958
+            />
959
+          </Box>
960
+
961
+          {selectedVariantLabel && (
962
+            <Typography sx={{ color: "text.secondary", fontSize: "0.8rem", mb: 1.5 }}>
963
+              Showing availability for {selectedVariantLabel}
964
+            </Typography>
965
+          )}
966
+
967
+          <Box>
968
+            {filteredStoreLocations.map((store) => {
969
+              const isExpanded = expandedStoreRows[store.name]
970
+
971
+              return (
972
+                <Box key={store.name} sx={{ borderBottom: "1px solid #999", py: 2 }}>
973
+                  <Box sx={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 1.5 }}>
974
+                    <Box sx={{ minWidth: 0 }}>
975
+                      <Typography
976
+                        sx={{
977
+                          textTransform: "uppercase",
978
+                          fontWeight: "400",
979
+                          fontSize: { xs: "0.95rem", md: "1.1rem" },
980
+                          mb: 0.5,
981
+                          lineHeight: 1.35,
982
+                          wordBreak: "break-word"
983
+                        }}
984
+                      >
985
+                        {store.name}
986
+                      </Typography>
987
+                      <Typography
988
+                        sx={{
989
+                          display: "flex",
990
+                          alignItems: "center",
991
+                          gap: 0.5,
992
+                          color: variantSelection?.quantityAvailable > 0 ? "#808000" : "error.main",
993
+                          fontSize: "0.9rem"
994
+                        }}
995
+                      >
996
+                        <Box
997
+                          component="span"
998
+                          sx={{
999
+                            width: 8,
1000
+                            height: 8,
1001
+                            borderRadius: "50%",
1002
+                            backgroundColor: variantSelection?.quantityAvailable > 0 ? "#808000" : "error.main",
1003
+                            display: "inline-block",
1004
+                            flexShrink: 0
1005
+                          }}
1006
+                        />
1007
+                        {storeStockText}
1008
+                      </Typography>
1009
+                    </Box>
1010
+
1011
+                    <Box sx={{ display: "flex", alignItems: "center", gap: 0.5, flexShrink: 0 }}>
1012
+                      <IconButton
1013
+                        component="a"
1014
+                        href={getStoreWhatsappUrl(store)}
1015
+                        target="_blank"
1016
+                        rel="noopener noreferrer"
1017
+                        aria-label={`Whatsapp ${store.name}`}
1018
+                        sx={{ color: "#000", p: 0.5 }}
1019
+                      >
1020
+                        <WhatsAppIcon sx={{ fontSize: 20 }} />
1021
+                      </IconButton>
1022
+                      <IconButton
1023
+                        aria-label={`Toggle ${store.name}`}
1024
+                        onClick={() => toggleStoreRow(store.name)}
1025
+                        sx={{ color: "#000", p: 0.5 }}
1026
+                      >
1027
+                        {isExpanded ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
1028
+                      </IconButton>
1029
+                    </Box>
1030
+                  </Box>
1031
+
1032
+                  <Box
1033
+                    sx={{
1034
+                      maxHeight: isExpanded ? 110 : 0,
1035
+                      overflow: "hidden",
1036
+                      transition: "max-height 0.25s ease-in-out"
1037
+                    }}
1038
+                  >
1039
+                    <Box sx={{ display: "flex", alignItems: "center", gap: 0.75, pt: 1, minWidth: 0 }}>
1040
+                      <Typography sx={{ color: "text.secondary", fontSize: "0.875rem", minWidth: 0, wordBreak: "break-word" }}>
1041
+                        {store.address}
1042
+                      </Typography>
1043
+                      <Tooltip title="Open in Maps" arrow>
1044
+                        <IconButton
1045
+                          component="a"
1046
+                          href={store.maps}
1047
+                          target="_blank"
1048
+                          rel="noopener noreferrer"
1049
+                          aria-label={`Open ${store.name} in maps`}
1050
+                          sx={{
1051
+                            color: "#000",
1052
+                            p: 0.25
1053
+                          }}
1054
+                        >
1055
+                          <LocationOnIcon sx={{ fontSize: 20 }} />
1056
+                        </IconButton>
1057
+                      </Tooltip>
1058
+                    </Box>
1059
+                    {/*
1060
+                    <Button
1061
+                      component="a"
1062
+                      href={store.maps}
1063
+                      target="_blank"
1064
+                      rel="noopener noreferrer"
1065
+                      variant="outlined"
1066
+                      sx={{
1067
+                        mt: 1.25,
1068
+                        minWidth: 74,
1069
+                        height: 36,
1070
+                        borderRadius: 0,
1071
+                        borderColor: "#000",
1072
+                        color: "#000",
1073
+                        textTransform: "none",
1074
+                        fontSize: "0.875rem",
1075
+                        fontWeight: 400,
1076
+                        px: 2,
1077
+                        "&:hover": {
1078
+                          borderColor: "primary.main",
1079
+                          backgroundColor: "transparent"
1080
+                        }
1081
+                      }}
1082
+                    >
1083
+                      Maps
1084
+                    </Button>
1085
+                    */}
1086
+                  </Box>
1087
+                </Box>
1088
+              )
1089
+            })}
1090
+
1091
+            {filteredStoreLocations.length === 0 && (
1092
+              <Typography sx={{ color: "text.secondary", fontSize: "0.95rem", py: 3 }}>
1093
+                No store found.
1094
+              </Typography>
1095
+            )}
1096
+          </Box>
1097
+        </Box>
1098
+      </Drawer>
785 1099
     </Box>
786 1100
   );
787 1101
 };
788 1102
 
789 1103
 export default ProductDetails;
1104
+

Notiek ielāde…
Atcelt
Saglabāt