@evenicanpm/storefront-core 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/package.json +3 -2
  2. package/src/api-manager/datasources/d365/d365-address.datasource.ts +1 -1
  3. package/src/api-manager/datasources/d365/d365-user.datasource.ts +0 -19
  4. package/src/api-manager/datasources/e4/e4-cart.datasource.ts +2 -2
  5. package/src/api-manager/datasources/e4/e4-product.datasource.ts +19 -4
  6. package/src/api-manager/datasources/e4/e4.datasource.ts +1 -2
  7. package/src/api-manager/datasources/e4/e4.remaps.ts +24 -0
  8. package/src/api-manager/datasources/e4/e4.translator.ts +17 -0
  9. package/src/api-manager/datasources/e4/{e4-user.datasource.ts → user/e4-user.datasource.ts} +5 -7
  10. package/src/api-manager/datasources/e4/user/e4-user.remaps.ts +4 -0
  11. package/src/api-manager/datasources/e4/user/e4-user.translator.ts +10 -0
  12. package/src/api-manager/index.ts +21 -6
  13. package/src/api-manager/schemas/product.schema.ts +1 -1
  14. package/src/api-manager/services/address/queries/get-states.ts +1 -1
  15. package/src/api-manager/services/create-query.ts +10 -3
  16. package/src/api-manager/types/Datasource.ts +9 -9
  17. package/src/components/Blocks/Components/ProductCarousel/index.tsx +6 -6
  18. package/src/components/_components/navbar/category-based-menu/components/categories.tsx +1 -1
  19. package/src/components/_components/navbar/mega-menu/mega-menu.tsx +1 -1
  20. package/src/components/_components/navbar/nav-list/nav-item-child.tsx +2 -2
  21. package/src/components/_components/product-dimensions/product-dimensions.tsx +114 -0
  22. package/src/components/_components/product-quantity-buttons/product-quantity-buttons.tsx +88 -0
  23. package/src/components/_components/section-header/section-header.tsx +1 -1
  24. package/src/components/_components/settings/setting.tsx +1 -1
  25. package/src/components/_components/wishlist-modal/TODO.md +10 -0
  26. package/src/components/_components/wishlist-modal/add-to-wishlist-button.tsx +68 -0
  27. package/src/components/_components/wishlist-modal/add-to-wishlist-dialog.tsx +161 -0
  28. package/src/components/_components/wishlist-modal/remove-from-wishlist-dialog.tsx +174 -0
  29. package/src/components/_components/wishlist-modal/wishlist-modal-item.tsx +65 -0
  30. package/src/components/countries-input.tsx +1 -0
  31. package/src/hooks/use-cart.tsx +82 -0
  32. package/src/hooks/use-variants.tsx +85 -0
  33. package/src/pages/account/addresses/address-form.tsx +289 -0
  34. package/src/pages/account/addresses/address-item.tsx +104 -0
  35. package/src/pages/account/dashboard-header.tsx +123 -0
  36. package/src/pages/account/navigation.tsx +84 -0
  37. package/src/pages/account/no-records.tsx +20 -0
  38. package/src/pages/account/orders/icons/delivery.tsx +14 -0
  39. package/src/pages/account/orders/icons/package-box.tsx +13 -0
  40. package/src/pages/account/orders/icons/truck-filled.tsx +14 -0
  41. package/src/pages/account/orders/order-progress.tsx +111 -0
  42. package/src/pages/account/orders/order-row.tsx +61 -0
  43. package/src/pages/account/orders/order-summary.tsx +83 -0
  44. package/src/pages/account/orders/ordered-products.tsx +114 -0
  45. package/src/pages/account/profile/profile-button.test.tsx +59 -0
  46. package/src/pages/account/profile/profile-button.tsx +50 -0
  47. package/src/pages/account/profile/user-info.tsx +59 -0
  48. package/src/pages/account/styles.ts +32 -0
  49. package/src/pages/account/table-row.tsx +19 -0
  50. package/src/pages/account/wishlist/create-new-list.tsx +167 -0
  51. package/src/pages/account/wishlist/create-wishlist-button.tsx +40 -0
  52. package/src/pages/account/wishlist/wishlist-item.tsx +82 -0
  53. package/src/pages/blog/pagination.tsx +38 -0
  54. package/src/pages/cart/cart-item.tsx +312 -0
  55. package/src/pages/cart/checkout-form.tsx +122 -0
  56. package/src/pages/cart/coupon-entry.tsx +90 -0
  57. package/src/pages/cart/estimate-shipping.tsx +183 -0
  58. package/src/pages/cart/wrapper.tsx +30 -0
  59. package/src/pages/checkout/checkout-alt-form/address-card.tsx +73 -0
  60. package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +133 -0
  61. package/src/pages/checkout/checkout-alt-form/checkout-step.tsx +120 -0
  62. package/src/pages/checkout/checkout-alt-form/customer-information.tsx +94 -0
  63. package/src/pages/checkout/checkout-alt-form/delivery-address.tsx +170 -0
  64. package/src/pages/checkout/checkout-alt-form/delivery-date.tsx +114 -0
  65. package/src/pages/checkout/checkout-alt-form/delivery-options.tsx +186 -0
  66. package/src/pages/checkout/checkout-alt-form/edit-address-form.tsx +130 -0
  67. package/src/pages/checkout/checkout-alt-form/heading.tsx +50 -0
  68. package/src/pages/checkout/checkout-alt-form/index.ts +1 -0
  69. package/src/pages/checkout/checkout-alt-form/new-address-form.tsx +294 -0
  70. package/src/pages/checkout/checkout-alt-form/node.tsx +9 -0
  71. package/src/pages/checkout/checkout-alt-form/payment-details.tsx +344 -0
  72. package/src/pages/checkout/checkout-alt-form/safe-card-preview-data.tsx +36 -0
  73. package/src/pages/checkout/checkout-alt-form/types.ts +20 -0
  74. package/src/pages/checkout/checkout-alt-summary/cart-item.tsx +39 -0
  75. package/src/pages/checkout/checkout-alt-summary/checkout-alt-summary.tsx +40 -0
  76. package/src/pages/checkout/checkout-alt-summary/index.ts +1 -0
  77. package/src/pages/checkout/checkout-alt-summary/list-item.tsx +31 -0
  78. package/src/pages/confirmation/address.tsx +22 -0
  79. package/src/pages/confirmation/confirmation-summary.tsx +52 -0
  80. package/src/pages/confirmation/ordered-products.tsx +108 -0
  81. package/src/pages/product-details/available-shops.tsx +48 -0
  82. package/src/pages/product-details/bopis/find-in-store-button.tsx +63 -0
  83. package/src/pages/product-details/bopis/find-in-store-modal.tsx +257 -0
  84. package/src/pages/product-details/bopis/pickup-option-select.tsx +127 -0
  85. package/src/pages/product-details/bopis/search-header.tsx +69 -0
  86. package/src/pages/product-details/checkbox-label.tsx +20 -0
  87. package/src/pages/product-details/currency.ts +64 -0
  88. package/src/pages/product-details/frequently-bought.tsx +90 -0
  89. package/src/pages/product-details/frequently-product-card.tsx +60 -0
  90. package/src/pages/product-details/product-comment.tsx +44 -0
  91. package/src/pages/product-details/product-description.tsx +22 -0
  92. package/src/pages/product-details/product-filter-card.tsx +257 -0
  93. package/src/pages/product-details/product-intro/product-intro-images.tsx +87 -0
  94. package/src/pages/product-details/product-intro/product-intro.tsx +250 -0
  95. package/src/pages/product-details/product-review.tsx +131 -0
  96. package/src/pages/product-details/product-tabs.tsx +51 -0
  97. package/src/pages/product-details/related-products.tsx +42 -0
  98. package/src/pages/product-details/types.ts +11 -0
  99. package/src/pages/product-list/breadcrumbs.tsx +39 -0
  100. package/src/pages/product-list/checkbox-label.tsx +20 -0
  101. package/src/pages/product-list/facet-group.tsx +125 -0
  102. package/src/pages/product-list/facet.tsx +36 -0
  103. package/src/pages/product-list/list-filter.tsx +40 -0
  104. package/src/pages/product-list/pagination.tsx +80 -0
  105. package/src/pages/product-list/product-list-context.tsx +302 -0
  106. package/src/pages/product-list/product-list-state.ts +187 -0
  107. package/src/pages/product-list/product-rating.tsx +16 -0
  108. package/src/pages/product-list/quick-view-dialog-content.tsx +205 -0
  109. package/src/pages/product-list/quick-view-dialog.tsx +54 -0
  110. package/src/pages/product-list/range-filter.tsx +125 -0
  111. package/src/pages/product-list/scrollbar.tsx +38 -0
  112. package/src/pages/product-list/search-bar.tsx +162 -0
  113. package/src/pages/product-list/selected-facets.tsx +80 -0
  114. package/src/pages/product-list/side-nav.tsx +49 -0
  115. package/src/pages/product-list/swatch.tsx +80 -0
  116. package/src/pages/product-list/types.ts +18 -0
  117. package/src/pages/product-list/use-product-list.ts +12 -0
  118. package/src/pages/product-list/useCategoryFilter.tsx +26 -0
  119. package/src/pages/product-list/utils/facet-helpers.ts +5 -0
  120. package/src/pages/product-list/utils/generate-breadcrumbs.ts +30 -0
  121. package/src/pages/product-list/utils/getCategoryFilterFromSlug.ts +153 -0
  122. package/src/pages/product-list/utils/product-list-helper.ts +111 -0
  123. package/src/pages/product-list/utils/product-list-types.ts +41 -0
  124. package/src/pages/product-list/utils/search-for-category.ts +76 -0
  125. package/src/pages/product-list/utils/sort-options.ts +44 -0
  126. package/src/pages/product-list/utils/use-previous-refiners.ts +14 -0
  127. package/src/pages/quickorder/order-upload.tsx +150 -0
  128. package/src/pages/quickorder/quick-order-form.tsx +343 -0
  129. package/src/pages/quickorder/quick-order-row.tsx +144 -0
  130. package/tsconfig.json +1 -0
  131. package/src/api-manager/datasources/e4/graphqlRequestSdk.ts +0 -10464
  132. /package/src/{components/_components/hooks/useOverflowDetect.ts → hooks/use-overflow-detect.ts} +0 -0
  133. /package/src/{components/_components/hooks/useSettings.ts → hooks/use-settings.ts} +0 -0
@@ -0,0 +1,64 @@
1
+ import currencyJs from "currency.js";
2
+ import { formatDistanceStrict } from "date-fns";
3
+ /**
4
+ * NOTE: We can move this to a more appropriate location if needed.
5
+ */
6
+
7
+ /**
8
+ * GET THE DIFFERENCE DATE FORMAT
9
+ * @param DATE | NUMBER | STRING
10
+ * @returns FORMATTED DATE STRING
11
+ */
12
+
13
+ function getDateDifference(date: string | number | Date) {
14
+ const distance = formatDistanceStrict(new Date(), new Date(date));
15
+ return distance + " ago";
16
+ }
17
+
18
+ /**
19
+ * RENDER THE PRODUCT PAGINATION INFO
20
+ * @param page - CURRENT PAGE NUMBER
21
+ * @param perPageProduct - PER PAGE PRODUCT LIST
22
+ * @param totalProduct - TOTAL PRODUCT NUMBER
23
+ * @returns
24
+ */
25
+
26
+ function renderProductCount(
27
+ page: number,
28
+ perPageProduct: number,
29
+ totalProduct: number,
30
+ ) {
31
+ const startNumber = (page - 1) * perPageProduct;
32
+ let endNumber = page * perPageProduct;
33
+
34
+ if (endNumber > totalProduct) {
35
+ endNumber = totalProduct;
36
+ }
37
+ return `Showing ${startNumber - 1}-${endNumber} of ${totalProduct} products`;
38
+ }
39
+
40
+ /**
41
+ * CALCULATE PRICE WITH PRODUCT DISCOUNT THEN RETURN NEW PRODUCT PRICES
42
+ * @param price - PRODUCT PRICE
43
+ * @param discount - DISCOUNT PERCENT
44
+ * @returns - RETURN NEW PRICE
45
+ */
46
+
47
+ function calculateDiscount(price: number, discount: number) {
48
+ const afterDiscount = Number((price - price * (discount / 100)).toFixed(2));
49
+ return currency(afterDiscount);
50
+ }
51
+
52
+ /**
53
+ * CHANGE THE CURRENCY FORMAT
54
+ * @param price - PRODUCT PRICE
55
+ * @param fraction - HOW MANY FRACTION WANT TO SHOW
56
+ * @returns - RETURN PRICE WITH CURRENCY
57
+ */
58
+
59
+ function currency(price: number, fraction = 2) {
60
+ const formatCurrency = currencyJs(price).format({ precision: fraction });
61
+ return formatCurrency;
62
+ }
63
+
64
+ export { currency, getDateDifference, calculateDiscount, renderProductCount };
@@ -0,0 +1,90 @@
1
+ "use client";
2
+
3
+ import { Fragment } from "react";
4
+ import Box from "@mui/material/Box";
5
+ import Button from "@mui/material/Button";
6
+ import styled from "@mui/material/styles/styled";
7
+ // GLOBAL CUSTOM COMPONENTS
8
+ import { H2, H3, Span } from "@/components/_components/Typography";
9
+ import { FlexBox, FlexRowCenter } from "@/components/_components/flex-box";
10
+ // LOCAL CUSTOM COMPONENT
11
+ import FrequentlyProductCard from "./frequently-product-card";
12
+ // CUSTOM UTILS LIBRARY FUNCTION
13
+ import { currency } from "@/lib/lib";
14
+ // CUSTOM DATA MODEL
15
+ import { ProductSearchResult as Product } from "@msdyn365-commerce/retail-proxy";
16
+
17
+ // STYLED COMPONENTS
18
+ const WrapperBox = styled(Box)(({ theme }) => ({
19
+ [theme.breakpoints.down("sm")]: {
20
+ "& .card-holder": { flexDirection: "column" },
21
+ },
22
+ }));
23
+
24
+ // ============================================================
25
+ type Props = { products: Product[] };
26
+ // ============================================================
27
+
28
+ export default function FrequentlyBought({ products }: Props) {
29
+ return (
30
+ <WrapperBox mb={7.5}>
31
+ <H3 mb={3}>Frequently Bought Together</H3>
32
+
33
+ <FlexBox className="card-holder" flexWrap="wrap" m={-1}>
34
+ {products.map((item, ind) => (
35
+ <Fragment key={item.RecordId}>
36
+ <FrequentlyProductCard
37
+ id={item.RecordId}
38
+ key={item.RecordId}
39
+ slug={item.ItemId ?? ""}
40
+ price={item.Price}
41
+ title={item.Name ?? ""}
42
+ imgUrl={item.PrimaryImageUrl ?? ""}
43
+ />
44
+
45
+ {ind < products.length - 1 && (
46
+ <FlexRowCenter>
47
+ <H2 color="grey.600" mx={1}>
48
+ +
49
+ </H2>
50
+ </FlexRowCenter>
51
+ )}
52
+ </Fragment>
53
+ ))}
54
+
55
+ <FlexRowCenter>
56
+ <H2 color="grey.600" mx={3}>
57
+ =
58
+ </H2>
59
+ </FlexRowCenter>
60
+
61
+ <FlexRowCenter
62
+ m={1}
63
+ minWidth={300}
64
+ minHeight={200}
65
+ border="1px solid"
66
+ borderRadius="8px"
67
+ className="gray-box"
68
+ borderColor="grey.400"
69
+ flexDirection="column"
70
+ >
71
+ <H3 color="primary.main">{currency(2500)}</H3>
72
+
73
+ <Span mb={2} fontWeight="600" color="grey.600">
74
+ Save {currency(500)}
75
+ </Span>
76
+
77
+ <FlexBox gap={1.5}>
78
+ <Button variant="contained" color="primary">
79
+ Add to Cart
80
+ </Button>
81
+
82
+ <Button variant="outlined" color="primary">
83
+ Add to List
84
+ </Button>
85
+ </FlexBox>
86
+ </FlexRowCenter>
87
+ </FlexBox>
88
+ </WrapperBox>
89
+ );
90
+ }
@@ -0,0 +1,60 @@
1
+ import Link from "next/link";
2
+ import Box from "@mui/material/Box";
3
+ // GLOBAL CUSTOM COMPONENTS
4
+ import HoverBox from "@/components/_components/HoverBox";
5
+ import LazyImage from "@/components/_components/LazyImage";
6
+ import BazaarCard from "@/components/_components/BazaarCard";
7
+ import { H6, Span } from "@/components/_components/Typography";
8
+ import FlexBox from "@/components/_components/flex-box/flex-box";
9
+ // CUSTOM UTILS LIBRARY FUNCTIONS
10
+ import { calculateDiscount, currency } from "@/lib/lib";
11
+
12
+ // =======================================================
13
+ interface Props {
14
+ slug: string;
15
+ price: number;
16
+ title: string;
17
+ imgUrl: string;
18
+ id: string | number;
19
+ }
20
+ // =======================================================
21
+
22
+ export default function FrequentlyProductCard(props: Props) {
23
+ const {
24
+ imgUrl = "/assets/images/products/Rectangle 116.png",
25
+ price,
26
+ title,
27
+ slug,
28
+ } = props;
29
+
30
+ return (
31
+ <BazaarCard
32
+ sx={{
33
+ p: 2,
34
+ width: "100%",
35
+ flex: "1 1 0",
36
+ minWidth: "160px",
37
+ margin: { xs: 0, sm: 1 },
38
+ maxWidth: { xs: "100%", sm: "220px" },
39
+ }}
40
+ >
41
+ <Link href={`/products/${slug}`}>
42
+ <HoverBox mb={1.5} borderRadius="8px">
43
+ <LazyImage alt={title} width={500} height={500} src={imgUrl} />
44
+ </HoverBox>
45
+
46
+ <Span title={title} mb={0.5} color="inherit" ellipsis display="block">
47
+ {title}
48
+ </Span>
49
+
50
+ <FlexBox alignItems="center" gap={1}>
51
+ <H6 color="primary.main">{currency(price)}</H6>
52
+
53
+ <Box component="del" fontWeight={600} color="grey.600">
54
+ {calculateDiscount(price, 35)}
55
+ </Box>
56
+ </FlexBox>
57
+ </Link>
58
+ </BazaarCard>
59
+ );
60
+ }
@@ -0,0 +1,44 @@
1
+ "use client";
2
+
3
+ import Box from "@mui/material/Box";
4
+ import Avatar from "@mui/material/Avatar";
5
+ import Rating from "@mui/material/Rating";
6
+ // GLOBAL CUSTOM COMPONENTS
7
+ import { FlexBox } from "@/components/_components/flex-box";
8
+ import { H5, H6, Paragraph, Span } from "@/components/_components/Typography";
9
+ // CUSTOM UTILS LIBRARY FUNCTION
10
+ import { getDateDifference } from "@/lib/lib";
11
+
12
+ // ===========================================================
13
+ interface Props {
14
+ name: string;
15
+ date: string;
16
+ imgUrl: string;
17
+ rating: number;
18
+ comment: string;
19
+ }
20
+ // ===========================================================
21
+
22
+ export default function ProductComment(props: Props) {
23
+ const { name, imgUrl, rating, date, comment } = props || {};
24
+
25
+ return (
26
+ <Box mb={4} maxWidth={600}>
27
+ <FlexBox alignItems="center" mb={2} gap={2}>
28
+ <Avatar alt={name} src={imgUrl} sx={{ width: 48, height: 48 }} />
29
+
30
+ <div>
31
+ <H5 mb={1}>{name}</H5>
32
+
33
+ <FlexBox alignItems="center" gap={1.25}>
34
+ <Rating size="small" value={rating} color="warn" readOnly />
35
+ <H6>{rating}</H6>
36
+ <Span>{getDateDifference(date)}</Span>
37
+ </FlexBox>
38
+ </div>
39
+ </FlexBox>
40
+
41
+ <Paragraph color="grey.700">{comment}</Paragraph>
42
+ </Box>
43
+ );
44
+ }
@@ -0,0 +1,22 @@
1
+ "use client";
2
+
3
+ import { H3 } from "@/components/_components/Typography";
4
+ import { AttributeValue } from "@msdyn365-commerce/retail-proxy";
5
+
6
+ type Props = { attributes: AttributeValue[] };
7
+
8
+ export default function ProductDescription({ attributes }: Props) {
9
+ return (
10
+ <div>
11
+ <H3 mb={2}>Specifications:</H3>
12
+ {attributes?.map((attr, index) => {
13
+ if (attr?.TextValue && attr?.Name)
14
+ return (
15
+ <div key={index}>
16
+ {attr.Name}: {attr.TextValue}
17
+ </div>
18
+ );
19
+ })}
20
+ </div>
21
+ );
22
+ }
@@ -0,0 +1,257 @@
1
+ "use client";
2
+
3
+ import { Fragment, useState } from "react";
4
+ // MUI
5
+ import Box from "@mui/material/Box";
6
+ import Rating from "@mui/material/Rating";
7
+ import Divider from "@mui/material/Divider";
8
+ import Collapse from "@mui/material/Collapse";
9
+ import TextField from "@mui/material/TextField";
10
+ import FormGroup from "@mui/material/FormGroup";
11
+ // LOCAL CUSTOM COMPONENTS
12
+ import CheckboxLabel from "./checkbox-label";
13
+ // GLOBAL CUSTOM COMPONENTS
14
+ import { FlexBetween, FlexBox } from "@/components/_components/flex-box";
15
+ import { H5, H6, Paragraph, Span } from "@/components/_components/Typography";
16
+ import AccordionHeader from "@/components/_components/accordion/accordion-header";
17
+ // TYPE
18
+ import {
19
+ ProductFilterKeys,
20
+ ProductFilterValues,
21
+ ProductFilters,
22
+ } from "./types";
23
+ import { Slider } from "@mui/material";
24
+
25
+ // FILTER OPTIONS
26
+ const categoryList = [
27
+ {
28
+ title: "Bath Preparations",
29
+ subCategories: ["Bubble Bath", "Bath Capsules", "Others"],
30
+ },
31
+ { title: "Eye Makeup Preparations" },
32
+ { title: "Fragrance" },
33
+ { title: "Hair Preparations" },
34
+ ];
35
+
36
+ const BRANDS = [
37
+ { label: "Mac", value: "mac" },
38
+ { label: "Karts", value: "karts" },
39
+ { label: "Baals", value: "baals" },
40
+ { label: "Bukks", value: "bukks" },
41
+ { label: "Luasis", value: "luasis" },
42
+ ];
43
+
44
+ const OTHERS = [
45
+ { label: "On Sale", value: "sale" },
46
+ { label: "In Stock", value: "stock" },
47
+ { label: "Featured", value: "featured" },
48
+ ];
49
+
50
+ const colorList = [
51
+ "#1C1C1C",
52
+ "#FF7A7A",
53
+ "#FFC672",
54
+ "#84FFB5",
55
+ "#70F6FF",
56
+ "#6B7AFF",
57
+ ];
58
+
59
+ // ============================================================================
60
+ interface Props {
61
+ filters: ProductFilters;
62
+ changeFilters: (key: ProductFilterKeys, values: ProductFilterValues) => void;
63
+ }
64
+ // ============================================================================
65
+
66
+ export default function ProductFilterCard({ filters, changeFilters }: Props) {
67
+ const [collapsed, setCollapsed] = useState(true);
68
+
69
+ const handleChangePrice = (values: number[]) => {
70
+ changeFilters("price", values);
71
+ };
72
+
73
+ const handleChangeColor = (value: string) => {
74
+ const values = filters.color.includes(value)
75
+ ? filters.color.filter((item) => item !== value)
76
+ : [...filters.color, value];
77
+
78
+ changeFilters("color", values);
79
+ };
80
+
81
+ const handleChangeBrand = (value: string) => {
82
+ const values = filters.brand.includes(value)
83
+ ? filters.brand.filter((item) => item !== value)
84
+ : [...filters.brand, value];
85
+
86
+ changeFilters("brand", values);
87
+ };
88
+
89
+ const handleChangeSales = (value: string) => {
90
+ const values = filters.sales.includes(value)
91
+ ? filters.sales.filter((item) => item !== value)
92
+ : [...filters.sales, value];
93
+
94
+ changeFilters("sales", values);
95
+ };
96
+
97
+ const handleChangeRating = (value: number) => {
98
+ changeFilters("rating", value);
99
+ };
100
+
101
+ return (
102
+ <div>
103
+ {/* CATEGORY VARIANT FILTER */}
104
+ <H6 mb={1.25}>Categories</H6>
105
+ {categoryList.map((item) =>
106
+ item.subCategories ? (
107
+ <Fragment key={item.title}>
108
+ <AccordionHeader
109
+ open={collapsed}
110
+ onClick={() => setCollapsed((state) => !state)}
111
+ sx={{ padding: ".5rem 0", cursor: "pointer", color: "grey.600" }}
112
+ >
113
+ <Span>{item.title}</Span>
114
+ </AccordionHeader>
115
+
116
+ <Collapse in={collapsed}>
117
+ {item.subCategories.map((name) => (
118
+ <Paragraph
119
+ pl="22px"
120
+ py={0.75}
121
+ key={name}
122
+ fontSize="14px"
123
+ color="grey.600"
124
+ sx={{ cursor: "pointer" }}
125
+ >
126
+ {name}
127
+ </Paragraph>
128
+ ))}
129
+ </Collapse>
130
+ </Fragment>
131
+ ) : (
132
+ <Paragraph
133
+ key={item.title}
134
+ sx={{
135
+ py: 0.75,
136
+ fontSize: 14,
137
+ cursor: "pointer",
138
+ color: "grey.600",
139
+ }}
140
+ >
141
+ {item.title}
142
+ </Paragraph>
143
+ ),
144
+ )}
145
+
146
+ <Box component={Divider} my={3} />
147
+
148
+ {/* PRICE VARIANT FILTER */}
149
+ <H6 mb={2}>Price Range</H6>
150
+
151
+ <Slider
152
+ min={0}
153
+ max={300}
154
+ size="small"
155
+ value={filters.price}
156
+ valueLabelDisplay="auto"
157
+ valueLabelFormat={(v) => `$${v}`}
158
+ onChange={(_, v) => handleChangePrice(v as number[])}
159
+ />
160
+ <FlexBetween>
161
+ <TextField
162
+ fullWidth
163
+ size="small"
164
+ type="number"
165
+ placeholder="0"
166
+ value={filters.price[0]}
167
+ onChange={(e) =>
168
+ handleChangePrice([+e.target.value, filters.price[1]])
169
+ }
170
+ />
171
+
172
+ <H5 color="grey.600" px={1}>
173
+ -
174
+ </H5>
175
+
176
+ <TextField
177
+ fullWidth
178
+ size="small"
179
+ type="number"
180
+ placeholder="250"
181
+ value={filters.price[1]}
182
+ onChange={(e) =>
183
+ handleChangePrice([filters.price[0], +e.target.value])
184
+ }
185
+ />
186
+ </FlexBetween>
187
+
188
+ <Box component={Divider} my={3} />
189
+
190
+ {/* BRAND VARIANT FILTER */}
191
+ <H6 mb={2}>Brands</H6>
192
+ <FormGroup>
193
+ {BRANDS.map(({ label, value }) => (
194
+ <CheckboxLabel
195
+ key={value}
196
+ label={label}
197
+ checked={filters.brand.includes(value)}
198
+ onChange={() => handleChangeBrand(value)}
199
+ />
200
+ ))}
201
+ </FormGroup>
202
+
203
+ <Box component={Divider} my={3} />
204
+
205
+ {/* SALES OPTIONS */}
206
+ <FormGroup>
207
+ {OTHERS.map(({ label, value }) => (
208
+ <CheckboxLabel
209
+ key={value}
210
+ label={label}
211
+ checked={filters.sales.includes(value)}
212
+ onChange={() => handleChangeSales(value)}
213
+ />
214
+ ))}
215
+ </FormGroup>
216
+
217
+ <Box component={Divider} my={3} />
218
+
219
+ {/* RATINGS FILTER */}
220
+ <H6 mb={2}>Ratings</H6>
221
+ <FormGroup>
222
+ {[5, 4, 3, 2, 1].map((item) => (
223
+ <CheckboxLabel
224
+ key={item}
225
+ checked={filters.rating === item}
226
+ onChange={() => handleChangeRating(item)}
227
+ label={<Rating size="small" value={item} color="warn" readOnly />}
228
+ />
229
+ ))}
230
+ </FormGroup>
231
+
232
+ <Box component={Divider} my={3} />
233
+
234
+ {/* COLORS VARIANT FILTER */}
235
+ <H6 mb={2}>Colors</H6>
236
+ <FlexBox mb={2} flexWrap="wrap" gap={1.5}>
237
+ {colorList.map((item) => (
238
+ <Box
239
+ key={item}
240
+ width={25}
241
+ height={25}
242
+ flexShrink={0}
243
+ bgcolor={item}
244
+ borderRadius="50%"
245
+ onClick={() => handleChangeColor(item)}
246
+ sx={{
247
+ outlineOffset: 1,
248
+ cursor: "pointer",
249
+ outline: filters.color.includes(item) ? 1 : 0,
250
+ outlineColor: item,
251
+ }}
252
+ />
253
+ ))}
254
+ </FlexBox>
255
+ </div>
256
+ );
257
+ }
@@ -0,0 +1,87 @@
1
+ "use client";
2
+ import Grid from "@mui/material/Grid2";
3
+ import Avatar from "@mui/material/Avatar";
4
+
5
+ // GLOBAL CUSTOM COMPONENTS
6
+ import { FlexBox, FlexRowCenter } from "@/components/_components/flex-box";
7
+ import ImageWithFallback from "@/components/ImageWithFallback";
8
+
9
+ /**
10
+ * Master product data comes in as prop, selected variant details are called
11
+ * every render, if there is a valid variant id selected.
12
+ */
13
+
14
+ // ================================================================
15
+ type Props = {
16
+ selectedImageIndex?: number;
17
+ primaryImageUrl: string;
18
+ handleImageClick: (ind: number) => () => void;
19
+ images: string[];
20
+ name: string | undefined;
21
+ };
22
+
23
+ // ================================================================
24
+
25
+ export default function ProductImages({
26
+ name,
27
+ selectedImageIndex,
28
+ primaryImageUrl,
29
+ images,
30
+ handleImageClick,
31
+ }: Props) {
32
+ return (
33
+ <>
34
+ <Grid size={{ md: 6, xs: 12 }} alignItems="center">
35
+ {/*MAIN IMAGE*/}
36
+ <FlexBox
37
+ borderRadius={3}
38
+ overflow="hidden"
39
+ justifyContent="center"
40
+ mb={6}
41
+ >
42
+ <ImageWithFallback
43
+ alt={name ?? "product"}
44
+ width={300}
45
+ height={300}
46
+ loading="eager"
47
+ src={
48
+ Array.isArray(images) &&
49
+ selectedImageIndex !== undefined &&
50
+ images[selectedImageIndex]
51
+ ? images[selectedImageIndex]
52
+ : primaryImageUrl
53
+ }
54
+ style={{ objectFit: "contain" }}
55
+ />
56
+ </FlexBox>
57
+
58
+ {/*THUMBNAIL IMAGES*/}
59
+ <FlexBox overflow="auto" gap={1} justifyContent="center">
60
+ {images?.map((url, ind) => (
61
+ <FlexRowCenter
62
+ key={ind}
63
+ width={64}
64
+ height={64}
65
+ minWidth={64}
66
+ bgcolor="white"
67
+ border="1px solid"
68
+ borderRadius="10px"
69
+ style={{ cursor: "pointer" }}
70
+ onClick={handleImageClick(ind)}
71
+ borderColor={
72
+ selectedImageIndex === ind ? "primary.main" : "grey.400"
73
+ }
74
+ >
75
+ <Avatar
76
+ alt="product"
77
+ src={url}
78
+ variant="square"
79
+ sx={{ height: 40 }}
80
+ />
81
+ </FlexRowCenter>
82
+ ))}
83
+ </FlexBox>
84
+ </Grid>
85
+ </>
86
+ );
87
+ }