|
|
@@ -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
|
+
|