@evenicanpm/storefront-core 1.0.0-alpha → 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 (196) hide show
  1. package/package.json +3 -2
  2. package/src/api-manager/api-manager.config.json +1 -1
  3. package/src/api-manager/datasources/d365/d365-address.datasource.ts +4 -4
  4. package/src/api-manager/datasources/d365/d365-cart.datasource.ts +100 -7
  5. package/src/api-manager/datasources/d365/d365-product.datasource.ts +39 -17
  6. package/src/api-manager/datasources/d365/d365-user.datasource.ts +220 -68
  7. package/src/api-manager/datasources/d365/utils/decode-jwt.ts +2 -2
  8. package/src/api-manager/datasources/e4/e4-cart.datasource.ts +6 -0
  9. package/src/api-manager/datasources/e4/e4-product.datasource.ts +22 -2
  10. package/src/api-manager/datasources/e4/e4.datasource.ts +1 -1
  11. package/src/api-manager/datasources/e4/e4.remaps.ts +24 -0
  12. package/src/api-manager/datasources/e4/e4.translator.ts +19 -1
  13. package/src/api-manager/datasources/e4/{e4-user.datasource.ts → user/e4-user.datasource.ts} +41 -19
  14. package/src/api-manager/datasources/e4/user/e4-user.remaps.ts +4 -0
  15. package/src/api-manager/datasources/e4/user/e4-user.translator.ts +10 -0
  16. package/src/api-manager/index.ts +77 -15
  17. package/src/api-manager/lib/get-graphql-client.ts +7 -7
  18. package/src/api-manager/schemas/address.schema.ts +1 -19
  19. package/src/api-manager/schemas/cart.schema.ts +18 -0
  20. package/src/api-manager/schemas/product.schema.ts +8 -1
  21. package/src/api-manager/schemas/user.schema.ts +52 -1
  22. package/src/api-manager/services/address/queries/get-states.ts +1 -1
  23. package/src/api-manager/services/cart/mutations/checkout.ts +16 -1
  24. package/src/api-manager/services/cart/queries/get-cart-lines-inventory.ts +15 -0
  25. package/src/api-manager/services/create-extension-query.ts +26 -0
  26. package/src/api-manager/services/create-mutation.ts +5 -1
  27. package/src/api-manager/services/create-query.ts +10 -4
  28. package/src/api-manager/services/organization/queries/get-channel-configuration.ts +1 -1
  29. package/src/api-manager/services/product/queries/get-product-prices.ts +16 -0
  30. package/src/api-manager/services/product/queries/search-products.ts +1 -0
  31. package/src/api-manager/services/user/mutations/add-to-wishlist.ts +24 -0
  32. package/src/api-manager/services/user/mutations/copy-cart-to-wishlist.ts +23 -0
  33. package/src/api-manager/services/user/mutations/create-wishlist.ts +4 -4
  34. package/src/api-manager/services/user/mutations/delete-wishlist.ts +4 -3
  35. package/src/api-manager/services/user/mutations/remove-from-wishlist.ts +21 -0
  36. package/src/api-manager/services/user/mutations/update-wishlist.ts +23 -0
  37. package/src/api-manager/services/user/queries/get-wishlist-details.ts +11 -4
  38. package/src/api-manager/services/user/queries/get-wishlists.ts +4 -3
  39. package/src/api-manager/types/Datasource.ts +42 -14
  40. package/src/auth/auth-options.ts +2 -2
  41. package/src/auth/msal.ts +9 -9
  42. package/src/auth/next-auth-cookie-manager.ts +5 -3
  43. package/src/auth/providers/aadb2c-provider.ts +3 -4
  44. package/src/cms/endpoints.ts +2 -3
  45. package/src/components/Blocks/Components/ProductCarousel/index.tsx +6 -6
  46. package/src/components/_components/LazyImage.tsx +5 -2
  47. package/src/components/_components/header/components/user.tsx +1 -2
  48. package/src/components/_components/navbar/category-based-menu/components/categories.tsx +1 -1
  49. package/src/components/_components/navbar/mega-menu/mega-menu.tsx +1 -1
  50. package/src/components/_components/navbar/nav-list/nav-item-child.tsx +2 -2
  51. package/src/components/_components/product-cards/product-card-1/components/quantity-buttons.tsx +17 -8
  52. package/src/components/_components/product-cards/product-card-1/product-card-1-wrapper.tsx +304 -0
  53. package/src/components/_components/product-cards/product-card-1/product-card.tsx +16 -55
  54. package/src/components/_components/product-cards/product-card-8/product-card.tsx +80 -60
  55. package/src/components/_components/product-cards/product-card-8/styles/index.ts +10 -0
  56. package/src/components/_components/product-cards/product-card-wishlist/components/hover-actions.tsx +37 -0
  57. package/src/components/_components/product-cards/product-card-wishlist/index.ts +1 -0
  58. package/src/components/_components/product-cards/product-card-wishlist/product-card.tsx +118 -0
  59. package/src/components/_components/product-cards/product-card-wishlist/styles/index.ts +51 -0
  60. package/src/components/_components/product-dimensions/product-dimensions.tsx +114 -0
  61. package/src/components/_components/product-quantity-buttons/product-quantity-buttons.tsx +88 -0
  62. package/src/components/_components/products-view/product-view-dialog.tsx +48 -10
  63. package/src/components/_components/products-view/products-grid-view.tsx +31 -17
  64. package/src/components/_components/products-view/products-list-view.tsx +30 -16
  65. package/src/components/_components/search-box/__test__/search-input-category.test.tsx +110 -0
  66. package/src/components/_components/search-box/__test__/search-input.test.tsx +137 -0
  67. package/src/components/_components/search-box/__test__/search-results.json +165 -0
  68. package/src/components/_components/search-box/components/search-result.tsx +66 -12
  69. package/src/components/_components/search-box/search-input-category.tsx +135 -32
  70. package/src/components/_components/search-box/styles/index.ts +2 -0
  71. package/src/components/_components/section-header/section-creator.tsx +1 -0
  72. package/src/components/_components/section-header/section-header.tsx +1 -1
  73. package/src/components/_components/settings/setting.tsx +1 -1
  74. package/src/components/_components/wishlist/add-to-wishlist-dialog.tsx +161 -0
  75. package/src/components/_components/wishlist/components/create-new-list.tsx +167 -0
  76. package/src/components/_components/wishlist/components/create-wishlist-button.tsx +40 -0
  77. package/src/components/_components/wishlist/components/dashboard-header.tsx +123 -0
  78. package/src/components/_components/wishlist/components/navigation.tsx +87 -0
  79. package/src/components/_components/wishlist/components/no-records.tsx +20 -0
  80. package/src/components/_components/wishlist/components/wishlist-modal-item.tsx +66 -0
  81. package/src/components/_components/wishlist/remove-from-wishlist-dialog.tsx +174 -0
  82. package/src/components/_components/wishlist/styles.ts +61 -0
  83. package/src/components/_components/wishlist-modal/TODO.md +10 -0
  84. package/src/components/_components/wishlist-modal/add-to-wishlist-button.tsx +68 -0
  85. package/src/components/_components/wishlist-modal/add-to-wishlist-dialog.tsx +161 -0
  86. package/src/components/_components/wishlist-modal/remove-from-wishlist-dialog.tsx +174 -0
  87. package/src/components/_components/wishlist-modal/wishlist-modal-item.tsx +65 -0
  88. package/src/components/countries-input.tsx +7 -12
  89. package/src/hooks/use-cart.tsx +82 -0
  90. package/src/hooks/use-variants.tsx +85 -0
  91. package/src/lib/cart-cookie-handler-constants.ts +8 -0
  92. package/src/lib/cart-cookie-handler.ts +27 -5
  93. package/src/lib/create-graphql-client.ts +1 -2
  94. package/src/lib/product-list-types.ts +41 -0
  95. package/src/pages/account/addresses/address-form.tsx +289 -0
  96. package/src/pages/account/addresses/address-item.tsx +104 -0
  97. package/src/pages/account/dashboard-header.tsx +123 -0
  98. package/src/pages/account/navigation.tsx +84 -0
  99. package/src/pages/account/no-records.tsx +20 -0
  100. package/src/pages/account/orders/icons/delivery.tsx +14 -0
  101. package/src/pages/account/orders/icons/package-box.tsx +13 -0
  102. package/src/pages/account/orders/icons/truck-filled.tsx +14 -0
  103. package/src/pages/account/orders/order-progress.tsx +111 -0
  104. package/src/pages/account/orders/order-row.tsx +61 -0
  105. package/src/pages/account/orders/order-summary.tsx +83 -0
  106. package/src/pages/account/orders/ordered-products.tsx +114 -0
  107. package/src/pages/account/profile/profile-button.test.tsx +59 -0
  108. package/src/pages/account/profile/profile-button.tsx +50 -0
  109. package/src/pages/account/profile/user-info.tsx +59 -0
  110. package/src/pages/account/styles.ts +32 -0
  111. package/src/pages/account/table-row.tsx +19 -0
  112. package/src/pages/account/wishlist/create-new-list.tsx +167 -0
  113. package/src/pages/account/wishlist/create-wishlist-button.tsx +40 -0
  114. package/src/pages/account/wishlist/wishlist-item.tsx +82 -0
  115. package/src/pages/blog/pagination.tsx +38 -0
  116. package/src/pages/cart/cart-item.tsx +312 -0
  117. package/src/pages/cart/checkout-form.tsx +122 -0
  118. package/src/pages/cart/coupon-entry.tsx +90 -0
  119. package/src/pages/cart/estimate-shipping.tsx +183 -0
  120. package/src/pages/cart/wrapper.tsx +30 -0
  121. package/src/pages/checkout/checkout-alt-form/address-card.tsx +73 -0
  122. package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +133 -0
  123. package/src/pages/checkout/checkout-alt-form/checkout-step.tsx +120 -0
  124. package/src/pages/checkout/checkout-alt-form/customer-information.tsx +94 -0
  125. package/src/pages/checkout/checkout-alt-form/delivery-address.tsx +170 -0
  126. package/src/pages/checkout/checkout-alt-form/delivery-date.tsx +114 -0
  127. package/src/pages/checkout/checkout-alt-form/delivery-options.tsx +186 -0
  128. package/src/pages/checkout/checkout-alt-form/edit-address-form.tsx +130 -0
  129. package/src/pages/checkout/checkout-alt-form/heading.tsx +50 -0
  130. package/src/pages/checkout/checkout-alt-form/index.ts +1 -0
  131. package/src/pages/checkout/checkout-alt-form/new-address-form.tsx +294 -0
  132. package/src/pages/checkout/checkout-alt-form/node.tsx +9 -0
  133. package/src/pages/checkout/checkout-alt-form/payment-details.tsx +344 -0
  134. package/src/pages/checkout/checkout-alt-form/safe-card-preview-data.tsx +36 -0
  135. package/src/pages/checkout/checkout-alt-form/types.ts +20 -0
  136. package/src/pages/checkout/checkout-alt-summary/cart-item.tsx +39 -0
  137. package/src/pages/checkout/checkout-alt-summary/checkout-alt-summary.tsx +40 -0
  138. package/src/pages/checkout/checkout-alt-summary/index.ts +1 -0
  139. package/src/pages/checkout/checkout-alt-summary/list-item.tsx +31 -0
  140. package/src/pages/confirmation/address.tsx +22 -0
  141. package/src/pages/confirmation/confirmation-summary.tsx +52 -0
  142. package/src/pages/confirmation/ordered-products.tsx +108 -0
  143. package/src/pages/product-details/available-shops.tsx +48 -0
  144. package/src/pages/product-details/bopis/find-in-store-button.tsx +63 -0
  145. package/src/pages/product-details/bopis/find-in-store-modal.tsx +257 -0
  146. package/src/pages/product-details/bopis/pickup-option-select.tsx +127 -0
  147. package/src/pages/product-details/bopis/search-header.tsx +69 -0
  148. package/src/pages/product-details/checkbox-label.tsx +20 -0
  149. package/src/pages/product-details/currency.ts +64 -0
  150. package/src/pages/product-details/frequently-bought.tsx +90 -0
  151. package/src/pages/product-details/frequently-product-card.tsx +60 -0
  152. package/src/pages/product-details/product-comment.tsx +44 -0
  153. package/src/pages/product-details/product-description.tsx +22 -0
  154. package/src/pages/product-details/product-filter-card.tsx +257 -0
  155. package/src/pages/product-details/product-intro/product-intro-images.tsx +87 -0
  156. package/src/pages/product-details/product-intro/product-intro.tsx +250 -0
  157. package/src/pages/product-details/product-review.tsx +131 -0
  158. package/src/pages/product-details/product-tabs.tsx +51 -0
  159. package/src/pages/product-details/related-products.tsx +42 -0
  160. package/src/pages/product-details/types.ts +11 -0
  161. package/src/pages/product-list/breadcrumbs.tsx +39 -0
  162. package/src/pages/product-list/checkbox-label.tsx +20 -0
  163. package/src/pages/product-list/facet-group.tsx +125 -0
  164. package/src/pages/product-list/facet.tsx +36 -0
  165. package/src/pages/product-list/list-filter.tsx +40 -0
  166. package/src/pages/product-list/pagination.tsx +80 -0
  167. package/src/pages/product-list/product-list-context.tsx +302 -0
  168. package/src/pages/product-list/product-list-state.ts +187 -0
  169. package/src/pages/product-list/product-rating.tsx +16 -0
  170. package/src/pages/product-list/quick-view-dialog-content.tsx +205 -0
  171. package/src/pages/product-list/quick-view-dialog.tsx +54 -0
  172. package/src/pages/product-list/range-filter.tsx +125 -0
  173. package/src/pages/product-list/scrollbar.tsx +38 -0
  174. package/src/pages/product-list/search-bar.tsx +162 -0
  175. package/src/pages/product-list/selected-facets.tsx +80 -0
  176. package/src/pages/product-list/side-nav.tsx +49 -0
  177. package/src/pages/product-list/swatch.tsx +80 -0
  178. package/src/pages/product-list/types.ts +18 -0
  179. package/src/pages/product-list/use-product-list.ts +12 -0
  180. package/src/pages/product-list/useCategoryFilter.tsx +26 -0
  181. package/src/pages/product-list/utils/facet-helpers.ts +5 -0
  182. package/src/pages/product-list/utils/generate-breadcrumbs.ts +30 -0
  183. package/src/pages/product-list/utils/getCategoryFilterFromSlug.ts +153 -0
  184. package/src/pages/product-list/utils/product-list-helper.ts +111 -0
  185. package/src/pages/product-list/utils/product-list-types.ts +41 -0
  186. package/src/pages/product-list/utils/search-for-category.ts +76 -0
  187. package/src/pages/product-list/utils/sort-options.ts +44 -0
  188. package/src/pages/product-list/utils/use-previous-refiners.ts +14 -0
  189. package/src/pages/quickorder/order-upload.tsx +150 -0
  190. package/src/pages/quickorder/quick-order-form.tsx +343 -0
  191. package/src/pages/quickorder/quick-order-row.tsx +144 -0
  192. package/tsconfig.json +1 -0
  193. package/src/api-manager/datasources/e4/graphqlRequestSdk.ts +0 -43515
  194. package/src/config.ts +0 -47
  195. /package/src/{components/_components/hooks/useOverflowDetect.ts → hooks/use-overflow-detect.ts} +0 -0
  196. /package/src/{components/_components/hooks/useSettings.ts → hooks/use-settings.ts} +0 -0
@@ -0,0 +1,13 @@
1
+ import createSvgIcon from "@mui/material/utils/createSvgIcon";
2
+
3
+ const PackageBox = createSvgIcon(
4
+ <svg fill="currentColor" viewBox="0 0 32 32">
5
+ <path d="M7.40692 11.3465L20.5779 4.304L16.0004 2L2.41992 8.836L7.40692 11.3465Z" />
6
+ <path d="M24.5773 6.3175L11.4062 13.36L15.9998 15.6725L29.5803 8.83601L24.5773 6.3175Z" />
7
+ <path d="M15.625 16.3275L11 13.9995V19.1145L9 17.101H7V11.986L2 9.46948V23.1415L15.625 30V16.3275Z" />
8
+ <path d="M16.375 16.3275V30L30 23.1415V9.46948L16.375 16.3275Z" />
9
+ </svg>,
10
+ "PackageBox",
11
+ );
12
+
13
+ export default PackageBox;
@@ -0,0 +1,14 @@
1
+ import createSvgIcon from "@mui/material/utils/createSvgIcon";
2
+
3
+ const TruckFilled = createSvgIcon(
4
+ <svg fill="currentColor" viewBox="0 0 36 36">
5
+ <path
6
+ fillRule="evenodd"
7
+ clipRule="evenodd"
8
+ d="M5.1302 7.5H22.4118C22.8992 7.5 23.2941 7.89504 23.2941 8.38236V10.1471H28.322C28.8735 10.1471 29.3583 10.5127 29.5098 11.043L31.0223 16.3368C31.1997 16.368 31.3647 16.4529 31.4937 16.5819L32.7416 17.8298C32.907 17.9952 33 18.2198 33 18.4538V24.2648C33 24.752 32.6049 25.1471 32.1177 25.1471H29.4707C29.4707 27.0963 27.8904 28.6764 25.9412 28.6764C23.9919 28.6764 22.4118 27.0963 22.4118 25.1471H13.5882C13.5882 27.0963 12.0081 28.6764 10.0588 28.6764C8.10959 28.6764 6.52941 27.0963 6.52941 25.1471H3.88236C3.39504 25.1471 3 24.752 3 24.2648V9.63019C3 9.39618 3.09296 9.17175 3.25844 9.00627L4.50627 7.75843C4.67175 7.59295 4.89618 7.5 5.1302 7.5ZM29.1833 16.3236L27.9227 11.9118H23.2941V15.4412C23.2941 15.9285 23.6892 16.3236 24.1764 16.3236H29.1833ZM25.9412 23.3823C26.9159 23.3823 27.7059 24.1725 27.7059 25.1471C27.7059 26.1218 26.9159 26.9118 25.9412 26.9118C24.9666 26.9118 24.1764 26.1218 24.1764 25.1471C24.1764 24.1725 24.9666 23.3823 25.9412 23.3823ZM8.29412 25.1471C8.29412 26.1218 9.08421 26.9118 10.0588 26.9118C11.0334 26.9118 11.8235 26.1218 11.8235 25.1471C11.8235 24.1725 11.0334 23.3823 10.0588 23.3823C9.08421 23.3823 8.29412 24.1725 8.29412 25.1471Z"
9
+ />
10
+ </svg>,
11
+ "TruckFilled",
12
+ );
13
+
14
+ export default TruckFilled;
@@ -0,0 +1,111 @@
1
+ import { Fragment } from "react";
2
+ import { useTranslations } from "next-intl";
3
+ // MUI
4
+ import Box from "@mui/material/Box";
5
+ import Card from "@mui/material/Card";
6
+ import Avatar from "@mui/material/Avatar";
7
+ import styled from "@mui/material/styles/styled";
8
+ import { MdDone as Done } from "react-icons/md";
9
+ // CUSTOM ICON COMPONENTS
10
+ import Delivery from "./icons/delivery";
11
+ import PackageBox from "./icons/package-box";
12
+ import TruckFilled from "./icons/truck-filled";
13
+ // GLOBAL CUSTOM COMPONENTS
14
+ import { Paragraph } from "@/components/_components/Typography";
15
+ import { FlexBetween, FlexBox } from "@/components/_components/flex-box/index";
16
+
17
+ // STYLED COMPONENTS
18
+ const StyledFlexbox = styled(FlexBetween)(({ theme }) => ({
19
+ flexWrap: "wrap",
20
+ marginTop: "2rem",
21
+ marginBottom: "2rem",
22
+ [theme.breakpoints.down("sm")]: { flexDirection: "column" },
23
+ "& .line": {
24
+ height: 4,
25
+ minWidth: 50,
26
+ flex: "1 1 0",
27
+ [theme.breakpoints.down("sm")]: { flex: "unset", height: 50, minWidth: 4 },
28
+ },
29
+ }));
30
+
31
+ const StyledAvatar = styled(Avatar)(({ theme }) => ({
32
+ top: -5,
33
+ right: -5,
34
+ width: 22,
35
+ height: 22,
36
+ position: "absolute",
37
+ bgcolor: theme.palette.grey[200],
38
+ color: theme.palette.success.main,
39
+ }));
40
+
41
+ type Props = {
42
+ status: number;
43
+ };
44
+ export default function OrderProgress({ status }: Props) {
45
+ const t = useTranslations("Account.OrderDetails");
46
+
47
+ let ORDER_STATUS = "";
48
+
49
+ // Map API statuses to ORDER_STATUS_LIST values
50
+ if ([1, 2, 4, 5].includes(status)) ORDER_STATUS = "Packaging";
51
+ if ([3].includes(status)) ORDER_STATUS = "Complete";
52
+
53
+ const hideDeliveryDate = !![4, 5].includes(status);
54
+
55
+ const STEP_ICONS = [PackageBox, TruckFilled, Delivery];
56
+ const ORDER_STATUS_LIST = ["Packaging", "Shipping", "Delivering", "Complete"];
57
+
58
+ const statusIndex = ORDER_STATUS_LIST.indexOf(ORDER_STATUS);
59
+
60
+ return (
61
+ <Card sx={{ p: "2rem 1.5rem", mb: 4 }}>
62
+ <StyledFlexbox>
63
+ {STEP_ICONS.map((Icon, index) => (
64
+ <Fragment key={index}>
65
+ <Box position="relative">
66
+ <Avatar
67
+ alt="shipping"
68
+ sx={{
69
+ width: 64,
70
+ height: 64,
71
+ color: index <= statusIndex ? "white" : "primary.main",
72
+ bgcolor: index <= statusIndex ? "primary.main" : "grey.300",
73
+ }}
74
+ >
75
+ <Icon color="inherit" fontSize="large" />
76
+ </Avatar>
77
+
78
+ {index < statusIndex ? (
79
+ <StyledAvatar alt="done">
80
+ <Done color="inherit" size={16} />
81
+ </StyledAvatar>
82
+ ) : null}
83
+ </Box>
84
+
85
+ {index < STEP_ICONS.length - 1 ? (
86
+ <Box
87
+ className="line"
88
+ bgcolor={index < statusIndex ? "primary.main" : "grey.300"}
89
+ />
90
+ ) : null}
91
+ </Fragment>
92
+ ))}
93
+ </StyledFlexbox>
94
+
95
+ {!hideDeliveryDate && (
96
+ <FlexBox justifyContent={{ xs: "center", sm: "flex-end" }}>
97
+ <Paragraph
98
+ p="0.5rem 1rem"
99
+ textAlign="center"
100
+ borderRadius="300px"
101
+ color="primary.main"
102
+ bgcolor="primary.light"
103
+ >
104
+ {t(statusIndex === 3 ? "deliveredDate" : "estimatedDeliveryDate")}
105
+ <b> 4th October</b>
106
+ </Paragraph>
107
+ </FlexBox>
108
+ )}
109
+ </Card>
110
+ );
111
+ }
@@ -0,0 +1,61 @@
1
+ import Link from "next/link";
2
+ import Box from "@mui/material/Box";
3
+ import Chip from "@mui/material/Chip";
4
+ import IconButton from "@mui/material/IconButton";
5
+ import { MdEast as East } from "react-icons/md";
6
+ import { ChipProps } from "@mui/material";
7
+ import { format } from "date-fns";
8
+ // GLOBAL CUSTOM COMPONENT
9
+ import { H6, Paragraph } from "@/components/_components/Typography";
10
+ // Local CUSTOM COMPONENT
11
+ import TableRow from "../table-row";
12
+ // CUSTOM UTILS LIBRARY FUNCTION
13
+ import useCurrencyFormatter from "@/lib/currency-formatter";
14
+ import { SalesOrder } from "@/api-manager/schemas/order.schema";
15
+
16
+ // =================================================
17
+ type Props = { order: SalesOrder };
18
+ // =================================================
19
+ const STATUS_MAP: Record<number, { label: string; color: ChipProps["color"] }> =
20
+ {
21
+ 1: { label: "Open", color: "secondary" },
22
+ 2: { label: "Processing", color: "secondary" },
23
+ 3: { label: "Complete", color: "success" },
24
+ 4: { label: "On Hold", color: "primary" },
25
+ 5: { label: "Cancelled", color: "primary" },
26
+ };
27
+
28
+ const getStatusInfo = (status: number) => {
29
+ return STATUS_MAP[status] ?? { label: "Unknown", color: "default" };
30
+ };
31
+ export default function OrderRow({ order }: Props) {
32
+ const { formatCurrency } = useCurrencyFormatter();
33
+
34
+ const { label, color } = getStatusInfo(order.StatusValue ?? 0);
35
+
36
+ return (
37
+ <Link href={`/account/orders/${order.Id}`}>
38
+ <TableRow sx={{ gridTemplateColumns: "2fr 1fr 1fr 1fr 1fr" }}>
39
+ <H6 ellipsis>{order?.ChannelReferenceId}</H6>
40
+
41
+ <Box textAlign="center">
42
+ <Chip size="small" label={label} color={color} />
43
+ </Box>
44
+
45
+ <Paragraph textAlign={{ sm: "center", xs: "left" }}>
46
+ {format(new Date(order.TransactionDate ?? ""), "MMM dd, yyyy")}
47
+ </Paragraph>
48
+
49
+ <Paragraph textAlign="center">
50
+ {formatCurrency(order.TotalAmount ?? 0)}
51
+ </Paragraph>
52
+
53
+ <Box display={{ sm: "inline-flex", xs: "none" }} justifyContent="end">
54
+ <IconButton sx={{ color: "grey.500" }}>
55
+ <East fontSize="large" />
56
+ </IconButton>
57
+ </Box>
58
+ </TableRow>
59
+ </Link>
60
+ );
61
+ }
@@ -0,0 +1,83 @@
1
+ import Grid from "@mui/material/Grid2";
2
+ import Card from "@mui/material/Card";
3
+ import Divider from "@mui/material/Divider";
4
+ import { useTranslations } from "next-intl";
5
+ // GLOBAL CUSTOM COMPONENTS
6
+ import { FlexBetween } from "@/components/_components/flex-box/index";
7
+ import { H5, H6, Paragraph } from "@/components/_components/Typography";
8
+ // CUSTOM UTILS LIBRARY FUNCTION
9
+ import useCurrencyFormatter from "@/lib/currency-formatter";
10
+ import { SalesOrder } from "@/api-manager/schemas/order.schema";
11
+
12
+ // ==============================================================
13
+ type Props = { order: SalesOrder };
14
+ // ==============================================================
15
+
16
+ function ListItem({ title, value }: { title: string; value: string }) {
17
+ const t = useTranslations("Account.OrderDetails");
18
+ return (
19
+ <FlexBetween mb={1}>
20
+ <Paragraph color="grey.600">{t(title)}</Paragraph>
21
+ <H6>{value}</H6>
22
+ </FlexBetween>
23
+ );
24
+ }
25
+
26
+ export default function OrderSummary({ order }: Props) {
27
+ const t = useTranslations("Account.OrderDetails");
28
+ const { formatCurrency } = useCurrencyFormatter();
29
+ const shippingAddress = order.ShippingAddress;
30
+ return (
31
+ <Grid container spacing={3}>
32
+ {/* SHIPMENT ADDRESS SECTION */}
33
+ <Grid size={{ lg: 6, md: 6, xs: 12 }}>
34
+ <Card sx={{ p: 3 }}>
35
+ <H5 mt={0} mb={2}>
36
+ {t("addressHeading")}
37
+ </H5>
38
+
39
+ {shippingAddress && (
40
+ <Paragraph fontSize={14} my={0}>
41
+ {`${shippingAddress.Name}, ${shippingAddress.Street}, ${shippingAddress.City} ${shippingAddress.State} ${shippingAddress.ThreeLetterISORegionName} ${shippingAddress.ZipCode}`}
42
+ </Paragraph>
43
+ )}
44
+ </Card>
45
+ </Grid>
46
+
47
+ {/* TOTAL SUMMERY SECTION */}
48
+ <Grid size={{ lg: 6, md: 6, xs: 12 }}>
49
+ <Card sx={{ p: 3 }}>
50
+ <H5 mt={0} mb={2}>
51
+ {t("headingSummary")}
52
+ </H5>
53
+
54
+ <ListItem
55
+ title="subTotal"
56
+ value={formatCurrency(order.SubtotalAmount ?? 0)}
57
+ />
58
+ <ListItem
59
+ title="shippingTotal"
60
+ value={formatCurrency(order.ShippingChargeAmount ?? 0)}
61
+ />
62
+ <ListItem
63
+ title="taxTotal"
64
+ value={formatCurrency(order.TaxAmount ?? 0)}
65
+ />
66
+ <ListItem
67
+ title="discountTotal"
68
+ value={formatCurrency(order.DiscountAmount ?? 0)}
69
+ />
70
+
71
+ <Divider sx={{ mb: 1 }} />
72
+
73
+ <FlexBetween mb={2}>
74
+ <H6>{t("total")}</H6>
75
+ <H6>{formatCurrency(order.TotalAmount ?? 0)}</H6>
76
+ </FlexBetween>
77
+
78
+ <Paragraph>{t("paidBy")}</Paragraph>
79
+ </Card>
80
+ </Grid>
81
+ </Grid>
82
+ );
83
+ }
@@ -0,0 +1,114 @@
1
+ import Card from "@mui/material/Card";
2
+ import Button from "@mui/material/Button";
3
+ import Avatar from "@mui/material/Avatar";
4
+ import Box from "@mui/material/Box";
5
+ import Grid from "@mui/material/Grid2";
6
+ import { format } from "date-fns";
7
+ import Link from "next/link";
8
+ import { useTranslations } from "next-intl";
9
+ // GLOBAL CUSTOM COMPONENTS
10
+ import { H6, Paragraph, Span } from "@/components/_components/Typography";
11
+ import { FlexBetween, FlexBox } from "@/components/_components/flex-box/index";
12
+ // CUSTOM UTILS LIBRARY FUNCTION
13
+ import useCurrencyFormatter from "@/lib/currency-formatter";
14
+ import { SalesLine, SalesOrder } from "@/api-manager/schemas/order.schema";
15
+ import { ProductDimensionType } from "@msdyn365-commerce/retail-proxy";
16
+ import buildDetailUrl from "@/lib/build-detail-url";
17
+
18
+ // ==============================================================
19
+ type Props = { order: SalesOrder };
20
+ // ==============================================================
21
+
22
+ export default function OrderedProducts({ order }: Props) {
23
+ const t = useTranslations("Account.OrderDetails");
24
+ const { formatCurrency } = useCurrencyFormatter();
25
+
26
+ const deliveredDate = "";
27
+
28
+ return (
29
+ <Card sx={{ p: 0, mb: "30px" }}>
30
+ <FlexBetween px={3} py={2} flexWrap="wrap" bgcolor="grey.200">
31
+ <Item title="orderId" value={order?.ChannelReferenceId ?? ""} />
32
+ <Item
33
+ title="orderDate"
34
+ value={format(new Date(order?.TransactionDate ?? ""), "dd MMM, yyyy")}
35
+ />
36
+ <Item
37
+ title="deliveredOn"
38
+ value={
39
+ deliveredDate
40
+ ? format(new Date(deliveredDate), "dd MMM, yyyy")
41
+ : t("deliveredOnValue")
42
+ }
43
+ />
44
+ </FlexBetween>
45
+
46
+ <Grid
47
+ container
48
+ spacing={2}
49
+ padding={"20px 15px 10px"}
50
+ alignItems={"center"}
51
+ >
52
+ {order?.SalesLines?.map((item: SalesLine, index: number) => (
53
+ <Link
54
+ href={buildDetailUrl(
55
+ item?.Product?.Name ?? "",
56
+ item?.Product?.RecordId ?? 0,
57
+ )}
58
+ key={index}
59
+ style={{ display: "contents" }}
60
+ >
61
+ <Grid size={{ xs: 12, sm: 5 }}>
62
+ <FlexBox gap={2.5} alignItems="center" padding={"12px 15px"}>
63
+ <Avatar
64
+ alt={item?.Product?.Name}
65
+ src={item?.Product?.PrimaryImageUrl}
66
+ sx={{ height: 64, width: 64 }}
67
+ />
68
+ <div>
69
+ <H6>{item?.Product?.Name}</H6>
70
+ <Paragraph color="grey.600">
71
+ {formatCurrency(item?.Price ?? 0)} x {item?.Quantity || 1}
72
+ </Paragraph>
73
+ </div>
74
+ </FlexBox>
75
+ </Grid>
76
+
77
+ <Grid size={{ xs: 8, sm: 4 }}>
78
+ {item.Product?.Dimensions?.length ? (
79
+ item.Product.Dimensions.map((dim, dimInd) => (
80
+ <Paragraph key={dimInd} paddingLeft={"15px"}>
81
+ <Span>
82
+ {ProductDimensionType[dim.DimensionTypeValue || 0]}:
83
+ </Span>
84
+ <Span sx={{ ml: 1 }}>{dim.DimensionValue?.Value}</Span>
85
+ </Paragraph>
86
+ ))
87
+ ) : (
88
+ <Box />
89
+ )}
90
+ </Grid>
91
+
92
+ <Grid size={{ xs: 4, sm: 3 }}>
93
+ <Box justifySelf={"end"}>
94
+ <Button variant="text" color="primary">
95
+ {t("writeReview")}
96
+ </Button>
97
+ </Box>
98
+ </Grid>
99
+ </Link>
100
+ ))}
101
+ </Grid>
102
+ </Card>
103
+ );
104
+ }
105
+
106
+ function Item({ title, value }: { title: string; value: string }) {
107
+ const t = useTranslations("Account.OrderDetails");
108
+ return (
109
+ <FlexBox gap={1} alignItems="center">
110
+ <Paragraph color="grey.600">{t(title)}</Paragraph>
111
+ <Paragraph>{value}</Paragraph>
112
+ </FlexBox>
113
+ );
114
+ }
@@ -0,0 +1,59 @@
1
+ import { render, screen, fireEvent, waitFor } from "@testing-library/react";
2
+ import { vi } from "vitest";
3
+ import ProfileButton from "./profile-button";
4
+ import { getMsalInstance, initializeMsal, getRequestConfig } from "@/auth/msal";
5
+
6
+ vi.mock("@/auth/msal", () => ({
7
+ initializeMsal: vi.fn(),
8
+ getMsalInstance: vi.fn(),
9
+ getRequestConfig: vi.fn(),
10
+ }));
11
+
12
+ vi.mock("@/components/T/index", () => ({
13
+ default: ({ path }: { path: string }) => <span>{path}</span>,
14
+ }));
15
+
16
+ describe("ProfileButton Component", () => {
17
+ afterEach(() => {
18
+ vi.restoreAllMocks();
19
+ });
20
+
21
+ it("renders disabled button initially (MSAL not ready)", () => {
22
+ render(<ProfileButton type="edit" path="Account.Profile.btnEditProfile" />);
23
+ const button = screen.getByRole("button");
24
+ expect(button).toBeDisabled();
25
+ });
26
+
27
+ it("initializes MSAL on mount", async () => {
28
+ render(<ProfileButton type="edit" path="Account.Profile.btnEditProfile" />);
29
+ await waitFor(() => {
30
+ expect(initializeMsal).toHaveBeenCalled();
31
+ });
32
+ });
33
+
34
+ it("calls MSAL loginRedirect when clicked", async () => {
35
+ (initializeMsal as ReturnType<typeof vi.fn>).mockImplementation(() =>
36
+ Promise.resolve(),
37
+ );
38
+ (getRequestConfig as ReturnType<typeof vi.fn>).mockReturnValue({
39
+ scopes: ["user.read"],
40
+ });
41
+ (getMsalInstance as ReturnType<typeof vi.fn>).mockReturnValue({
42
+ loginRedirect: vi.fn(),
43
+ });
44
+ render(<ProfileButton type="edit" path="Account.Profile.btnEditProfile" />);
45
+
46
+ await waitFor(() => {
47
+ expect(initializeMsal).toHaveBeenCalled();
48
+ });
49
+
50
+ const button = screen.getByRole("button");
51
+ fireEvent.click(button);
52
+
53
+ await waitFor(() => {
54
+ expect(getMsalInstance().loginRedirect).toHaveBeenCalledWith({
55
+ scopes: ["user.read"],
56
+ });
57
+ });
58
+ });
59
+ });
@@ -0,0 +1,50 @@
1
+ "use client";
2
+
3
+ import { getMsalInstance, initializeMsal, getRequestConfig } from "@/auth/msal";
4
+ import { useEffect, useState } from "react";
5
+ import { Button } from "@mui/material";
6
+ import T from "@/components/T/index";
7
+ import { RedirectRequest } from "@azure/msal-browser";
8
+ interface Props {
9
+ type: string;
10
+ path: string;
11
+ }
12
+
13
+ const ProfileButton = ({ type, path }: Props) => {
14
+ const [isMsalReady, setIsMsalReady] = useState(false);
15
+
16
+ useEffect(() => {
17
+ const initMsal = async () => {
18
+ await initializeMsal();
19
+ setIsMsalReady(true);
20
+ };
21
+ initMsal();
22
+ }, []);
23
+
24
+ const handleProfile = async () => {
25
+ if (!isMsalReady) {
26
+ console.error("MSAL is not initialized yet.");
27
+ return;
28
+ }
29
+
30
+ try {
31
+ const req = getRequestConfig(type);
32
+ await getMsalInstance().loginRedirect(req as RedirectRequest); // "handleRedirectPromise" is in user component.
33
+ } catch (error) {
34
+ console.error("Profile update through MSAL failed", error);
35
+ }
36
+ };
37
+
38
+ return (
39
+ <Button
40
+ onClick={() => handleProfile()}
41
+ disabled={!isMsalReady}
42
+ color="primary"
43
+ sx={{ bgcolor: "primary.light", px: 4 }}
44
+ >
45
+ <T path={path} />
46
+ </Button>
47
+ );
48
+ };
49
+
50
+ export default ProfileButton;
@@ -0,0 +1,59 @@
1
+ import _ from "lodash";
2
+ import useMediaQuery from "@mui/material/useMediaQuery";
3
+ import { Theme } from "@mui/material/styles";
4
+ import Card from "@mui/material/Card";
5
+ import { useTranslations } from "next-intl";
6
+ // GLOBAL CUSTOM COMPONENTS
7
+ import FlexBox from "@/components/_components/flex-box/flex-box";
8
+ import { Small, Span } from "@/components/_components/Typography";
9
+ // API
10
+ import { User } from "@/api-manager/schemas/user.schema";
11
+ type Props = {
12
+ user: User;
13
+ };
14
+ export default function UserInfo({ user }: Props) {
15
+ const downMd = useMediaQuery((theme: Theme) => theme.breakpoints.down("sm"));
16
+
17
+ return (
18
+ <Card
19
+ sx={{
20
+ mt: 3,
21
+ display: "flex",
22
+ flexWrap: "wrap",
23
+ gap: "5rem",
24
+ p: "0.75rem 1.5rem",
25
+ alignItems: "center",
26
+ justifyContent: "flex-start",
27
+ ...(downMd && {
28
+ gap: "1rem",
29
+ alignItems: "start",
30
+ flexDirection: "column",
31
+ justifyContent: "flex-start",
32
+ }),
33
+ }}
34
+ >
35
+ <TableRowItem
36
+ title="labelFirstName"
37
+ value={_.capitalize(user?.FirstName)}
38
+ />
39
+ <TableRowItem
40
+ title="labelLastName"
41
+ value={_.capitalize(user?.LastName)}
42
+ />
43
+ <TableRowItem title="labelEmail" value={user?.Email ?? ""} />
44
+ </Card>
45
+ );
46
+ }
47
+
48
+ function TableRowItem({ title, value = "" }: { title: string; value: string }) {
49
+ const t = useTranslations("Account.Profile");
50
+ return (
51
+ <FlexBox flexDirection="column" p={1}>
52
+ <Small color="grey.600" mb={0.5}>
53
+ {t(title)}
54
+ </Small>
55
+
56
+ <Span>{value}</Span>
57
+ </FlexBox>
58
+ );
59
+ }
@@ -0,0 +1,32 @@
1
+ import Card from "@mui/material/Card";
2
+ import styled from "@mui/material/styles/styled";
3
+ import NavLink from "@/components/_components/nav-link/nav-link";
4
+
5
+ export const MainContainer = styled(Card)(({ theme }) => ({
6
+ paddingBottom: "1.5rem",
7
+ [theme.breakpoints.down("md")]: {
8
+ boxShadow: "none",
9
+ overflowY: "auto",
10
+ height: "calc(100vh - 64px)",
11
+ },
12
+ }));
13
+
14
+ export const StyledNavLink = styled(NavLink, {
15
+ shouldForwardProp: (prop) => prop !== "isCurrentPath",
16
+ })<{ isCurrentPath: boolean }>(({ theme, isCurrentPath }) => ({
17
+ display: "flex",
18
+ alignItems: "center",
19
+ borderLeft: "4px solid",
20
+ paddingLeft: "1.5rem",
21
+ paddingRight: "1.5rem",
22
+ marginBottom: "1.25rem",
23
+ justifyContent: "space-between",
24
+ borderColor: isCurrentPath ? theme.palette.primary.main : "transparent",
25
+ "& .nav-icon": {
26
+ color: isCurrentPath ? theme.palette.primary.main : theme.palette.grey[600],
27
+ },
28
+ "&:hover": {
29
+ borderColor: theme.palette.primary.main,
30
+ "& .nav-icon": { color: theme.palette.primary.main },
31
+ },
32
+ }));
@@ -0,0 +1,19 @@
1
+ import Card from "@mui/material/Card";
2
+ import styled from "@mui/material/styles/styled";
3
+
4
+ const TableRow = styled(Card)(({ theme }) => ({
5
+ gap: 16,
6
+ marginBlock: 16,
7
+ display: "grid",
8
+ borderRadius: 10,
9
+ cursor: "pointer",
10
+ alignItems: "center",
11
+ padding: ".6rem 1.2rem",
12
+ gridTemplateColumns: "1.5fr 2fr 1.5fr auto",
13
+ [theme.breakpoints.down("sm")]: {
14
+ gap: 8,
15
+ gridTemplateColumns: "repeat(2, 1fr)",
16
+ },
17
+ }));
18
+
19
+ export default TableRow;