@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,344 @@
1
+ import { useTranslations } from "next-intl";
2
+ import { CheckoutStepComponentProps } from "./checkout-step";
3
+ import React from "react";
4
+ import { H6 } from "@/components/_components/Typography";
5
+ import { Alert, Box, Card } from "@mui/material";
6
+ import { Paragraph, Small } from "@/components/_components/Typography";
7
+ import { RetrieveCardPaymentAcceptResultInput } from "@/api-manager/schemas/cart.schema";
8
+ import { CardPaymentAcceptResult } from "@msdyn365-commerce/retail-proxy";
9
+ import usePaymentAcceptResult from "@/api-manager/services/cart/mutations/get-payment-accept-result";
10
+ import { FlexBox } from "@/components/_components/flex-box";
11
+ import getPaymentAcceptPoint from "@/api-manager/services/cart/queries/get-payment-accept-point";
12
+
13
+ enum PaymentConnectorPostMessageType {
14
+ CardPrefix = "msax-cc-cardprefix",
15
+ Error = "msax-cc-error",
16
+ Height = "msax-cc-height",
17
+ Result = "msax-cc-result",
18
+ Submit = "msax-cc-submit",
19
+ ExtraContext = "msax-cc-extracontext",
20
+ Redirect = "msax-cc-redirect",
21
+ Showoverlay = "msax-cc-showoverlay",
22
+ Hideoverlay = "msax-cc-hideoverlay",
23
+ ShippingAddress = "msax-shipping-address",
24
+ Amount = "msax-cc-amount",
25
+ }
26
+
27
+ type PaymentConnectorPostMessage = {
28
+ message: string;
29
+ targetOrigin: string;
30
+ };
31
+
32
+ type PaymentError = {
33
+ Code: number;
34
+ Message: string;
35
+ };
36
+
37
+ export default function PaymentDetails(props: CheckoutStepComponentProps) {
38
+ const {
39
+ cart,
40
+ setStepSubmissionState,
41
+ stepSubmissionState,
42
+ handleStepCompletion,
43
+ stepViewState,
44
+ } = props;
45
+ const [paymentAcceptResult, setPaymentAcceptResult] =
46
+ React.useState<CardPaymentAcceptResult | null>(null);
47
+ const t = useTranslations("Checkout.Payment");
48
+ const { data: paymentAcceptPointData } = getPaymentAcceptPoint.useData({
49
+ cartId: cart.Id,
50
+ totalAmount: cart.TotalAmount ?? 0,
51
+ origin:
52
+ (typeof window !== "undefined" && window.location.origin) || undefined,
53
+ isClient: typeof window !== "undefined",
54
+ });
55
+
56
+ const { mutateAsync: retrievePaymentAcceptResult } = usePaymentAcceptResult();
57
+ const iframeReference: React.RefObject<HTMLIFrameElement> =
58
+ React.useRef(null);
59
+ const [iframeHeight, setIFrameHeight] = React.useState<number>(1);
60
+ const [errorState, setErrorState] = React.useState<PaymentError[]>([]);
61
+ // const [showOverlay, setShowOverlay] = React.useState(false);
62
+
63
+ const handleResultsAccessCode = React.useCallback(
64
+ async (accessCode: string): Promise<void> => {
65
+ const input: RetrieveCardPaymentAcceptResultInput = {
66
+ cartId: cart.Id,
67
+ resultAccessCode: accessCode,
68
+ settings: {
69
+ PaymentConnectorId: paymentAcceptPointData?.PaymentConnectorId,
70
+ ReturnUrl: window.location.href,
71
+ },
72
+ };
73
+ const paymentAcceptResultData = await retrievePaymentAcceptResult(input);
74
+ setPaymentAcceptResult(paymentAcceptResultData);
75
+ },
76
+ [
77
+ cart?.Id,
78
+ paymentAcceptPointData?.PaymentConnectorId,
79
+ retrievePaymentAcceptResult,
80
+ ],
81
+ );
82
+
83
+ const postMessageToIframe = React.useCallback(
84
+ (messageParameters: PaymentConnectorPostMessage) => {
85
+ iframeReference.current?.contentWindow?.postMessage(
86
+ messageParameters.message,
87
+ messageParameters.targetOrigin,
88
+ );
89
+ },
90
+ [],
91
+ );
92
+
93
+ const onIFrameMessage = React.useCallback(
94
+ async (event: MessageEvent): Promise<void> => {
95
+ let result;
96
+ try {
97
+ if (typeof event.data !== "string") {
98
+ return;
99
+ }
100
+ result = JSON.parse(event.data);
101
+ } catch (error) {
102
+ console.error(error);
103
+ return;
104
+ }
105
+
106
+ if (!result || !result.type) {
107
+ return;
108
+ }
109
+ const { type, value } = result;
110
+ if (!cart.TotalAmount) {
111
+ return;
112
+ }
113
+
114
+ switch (type) {
115
+ case PaymentConnectorPostMessageType.Height: {
116
+ // The payment connector will post height after it completes initialization
117
+ setIFrameHeight(value as number);
118
+ postMessageToIframe(
119
+ paymentConnectorMessage(
120
+ cart.TotalAmount.toString(),
121
+ PaymentConnectorPostMessageType.Amount,
122
+ ),
123
+ );
124
+ return;
125
+ }
126
+ case PaymentConnectorPostMessageType.Result: {
127
+ try {
128
+ await handleResultsAccessCode(value);
129
+ setStepSubmissionState("idle");
130
+ handleStepCompletion();
131
+ setErrorState([]);
132
+ } catch (error) {
133
+ console.error(error);
134
+ setErrorState([{ Code: 500, Message: t("cardErrorMessage") }]);
135
+ setStepSubmissionState("error");
136
+ }
137
+ // executeGetPaymentAcceptResults(value);
138
+ // Handle Payment Result
139
+ return;
140
+ }
141
+ case PaymentConnectorPostMessageType.CardPrefix: {
142
+ // Handle Card Prefix
143
+ return;
144
+ }
145
+ case PaymentConnectorPostMessageType.Error: {
146
+ if (
147
+ Array.isArray(value) &&
148
+ value.length > 0 &&
149
+ "Code" in value[0] &&
150
+ "Message" in value[0]
151
+ ) {
152
+ setErrorState(value as PaymentError[]);
153
+ setStepSubmissionState("error");
154
+ }
155
+ return;
156
+ }
157
+ case PaymentConnectorPostMessageType.Redirect: {
158
+ // Handle Redirect (Not used in this implementation)
159
+ return;
160
+ }
161
+ case PaymentConnectorPostMessageType.Showoverlay: {
162
+ // Handle Show overlay (used for PP)
163
+ // setShowOverlay(true);
164
+ return;
165
+ }
166
+ case PaymentConnectorPostMessageType.Hideoverlay: {
167
+ // Hide Overlay
168
+ // setShowOverlay(false);
169
+ return;
170
+ }
171
+ default: {
172
+ return;
173
+ }
174
+ // Do nothing
175
+ }
176
+ },
177
+ [
178
+ cart.TotalAmount,
179
+ handleResultsAccessCode,
180
+ handleStepCompletion,
181
+ postMessageToIframe,
182
+ setStepSubmissionState,
183
+ t,
184
+ ],
185
+ );
186
+
187
+ const onEvent = React.useCallback(
188
+ (event: MessageEvent) => {
189
+ // const requestHost = window.location.origin;
190
+ // const messageHost = getHostName(paymentAcceptPointData?.MessageOrigin);
191
+ // Important: security check
192
+ // check actual origin matches with expected origin
193
+ //Needs to be put in
194
+ onIFrameMessage(event);
195
+ },
196
+ [onIFrameMessage],
197
+ );
198
+
199
+ React.useEffect(() => {
200
+ window.addEventListener("message", onEvent);
201
+ return () => {
202
+ window.removeEventListener("message", onEvent);
203
+ };
204
+ }, [onEvent]);
205
+
206
+ const postSubmitMessage = React.useCallback(() => {
207
+ postMessageToIframe(
208
+ paymentConnectorMessage("true", PaymentConnectorPostMessageType.Submit),
209
+ );
210
+ }, [postMessageToIframe]);
211
+
212
+ React.useEffect(() => {
213
+ if (stepSubmissionState === "submitting") {
214
+ // * INFO: This will be confusing. This listens for the stepSubmissionState to change to 'submitting' and then calls the postSubmitMessage function.
215
+ // * INFO: The postSubmitMessage function will then call the postMessageToIframe function with the paymentConnectorMessage function as an argument.
216
+ // * INFO: The paymentConnectorMessage function will return an object with a message and targetOrigin property.
217
+ // * INFO: We have to listen for an event of that message which will tell us what to do with the payment. It's a chain of listeners with no function returns.
218
+ postSubmitMessage();
219
+ }
220
+ }, [postSubmitMessage, stepSubmissionState]);
221
+
222
+ React.useEffect(() => {
223
+ if (!paymentAcceptPointData || stepViewState !== "active") {
224
+ return;
225
+ }
226
+ if (
227
+ !paymentAcceptPointData.AcceptPageUrl?.length &&
228
+ paymentAcceptPointData.AcceptPageContent?.length &&
229
+ iframeReference.current
230
+ ) {
231
+ const innerDocument = iframeReference.current.contentDocument;
232
+ if (innerDocument) {
233
+ innerDocument.open();
234
+ innerDocument.write(paymentAcceptPointData.AcceptPageContent);
235
+ innerDocument.close();
236
+ }
237
+ }
238
+ }, [paymentAcceptPointData, stepViewState]);
239
+
240
+ const paymentConnectorMessage = (
241
+ data: string,
242
+ messageType: PaymentConnectorPostMessageType,
243
+ ): PaymentConnectorPostMessage => {
244
+ return {
245
+ message: JSON.stringify({
246
+ type: messageType,
247
+ value: data,
248
+ }),
249
+ targetOrigin: "*",
250
+ };
251
+ };
252
+
253
+ // const getHostName = (url: string = ""): string => {
254
+ // return (
255
+ // url.includes("//") ? url.split("/")[2] : url.split("/")[0]
256
+ // ).toLowerCase();
257
+ // };
258
+
259
+ const renderPaymentWindow = () => (
260
+ <Box sx={{ m: 2 }}>
261
+ <H6 sx={{ mb: 2 }}>{t("cardFormHeader")}</H6>
262
+ {paymentAcceptPointData && (
263
+ <iframe
264
+ title={"Payment frame"}
265
+ src={paymentAcceptPointData.AcceptPageUrl || undefined}
266
+ ref={iframeReference}
267
+ sandbox="allow-scripts allow-forms allow-same-origin allow-popups"
268
+ height={iframeHeight}
269
+ width={"100%"}
270
+ style={{ border: "none" }}
271
+ />
272
+ )}
273
+ {renderErrorMessages()}
274
+ {/* <Button variant={"contained"} onClick={postSubmitMessage}>
275
+ {t("cardFormSaveButtonText")}
276
+ </Button> */}
277
+ </Box>
278
+ );
279
+
280
+ const renderErrorMessages = () =>
281
+ errorState.length > 0 ? (
282
+ <Alert severity="error">
283
+ <Paragraph color={"error"} fontWeight={"bold"}>
284
+ {t("errorMessage")}
285
+ </Paragraph>
286
+ {errorState.map((message, index) => (
287
+ <Small color={"error"} key={`${message.Code}-${index}`}>
288
+ {message.Message}
289
+ </Small>
290
+ ))}
291
+ </Alert>
292
+ ) : undefined;
293
+
294
+ // const handleChangeButtonClick = () => {
295
+ // getNewPaymentAcceptPoint();
296
+ // };
297
+
298
+ const renderSafeCardData = () => {
299
+ if (!paymentAcceptResult) {
300
+ return null;
301
+ }
302
+ return (
303
+ <FlexBox flexDirection={"column"} sx={{ m: 2 }}>
304
+ <H6 sx={{ mb: 2 }}>{t("cardPaymentDetails")}</H6>
305
+ <Card
306
+ sx={{
307
+ padding: 2,
308
+ boxShadow: "none",
309
+ cursor: "pointer",
310
+ border: "1px solid",
311
+ position: "relative",
312
+ backgroundColor: "grey.100",
313
+ borderColor: "secondary.main",
314
+ }}
315
+ >
316
+ <Paragraph>
317
+ {paymentAcceptResult.TokenizedPaymentCard?.NameOnCard}
318
+ </Paragraph>
319
+ <Paragraph>
320
+ {paymentAcceptResult.TokenizedPaymentCard?.CardTypeId}
321
+ </Paragraph>
322
+ <Paragraph>
323
+ {
324
+ paymentAcceptResult.TokenizedPaymentCard?.CardTokenInfo
325
+ ?.MaskedCardNumber
326
+ }
327
+ </Paragraph>
328
+ <Paragraph>
329
+ {`${paymentAcceptResult.TokenizedPaymentCard?.ExpirationMonth}/${paymentAcceptResult.TokenizedPaymentCard?.ExpirationYear}`}
330
+ </Paragraph>
331
+ </Card>
332
+ </FlexBox>
333
+ );
334
+ };
335
+
336
+ if (stepViewState === "pending") {
337
+ return null;
338
+ }
339
+ if (stepViewState === "preview") {
340
+ return renderSafeCardData();
341
+ }
342
+ <pre>{stepViewState}</pre>;
343
+ return renderPaymentWindow();
344
+ }
@@ -0,0 +1,36 @@
1
+ import { CardPaymentAcceptResult } from "@msdyn365-commerce/retail-proxy";
2
+ import { Box, Typography } from "@mui/material";
3
+ import React from "react";
4
+
5
+ interface SafeCardDataPreviewProps {
6
+ paymentAcceptResult: CardPaymentAcceptResult;
7
+ heading: string;
8
+ }
9
+
10
+ export const SafeCardDataPreview: React.FC<SafeCardDataPreviewProps> = ({
11
+ paymentAcceptResult,
12
+ heading,
13
+ }) => {
14
+ return (
15
+ <Box>
16
+ <Typography variant={"h6"} sx={{ mb: 2 }}>
17
+ {heading}
18
+ </Typography>
19
+ <Typography variant={"body2"}>
20
+ {paymentAcceptResult.TokenizedPaymentCard?.NameOnCard}
21
+ </Typography>
22
+ <Typography variant={"body2"}>
23
+ {paymentAcceptResult.TokenizedPaymentCard?.CardTypeId}
24
+ </Typography>
25
+ <Typography variant={"body2"}>
26
+ {
27
+ paymentAcceptResult.TokenizedPaymentCard?.CardTokenInfo
28
+ ?.MaskedCardNumber
29
+ }
30
+ </Typography>
31
+ <Typography
32
+ variant={"body2"}
33
+ >{`${paymentAcceptResult.TokenizedPaymentCard?.ExpirationMonth}/${paymentAcceptResult.TokenizedPaymentCard?.ExpirationYear}`}</Typography>
34
+ </Box>
35
+ );
36
+ };
@@ -0,0 +1,20 @@
1
+ export interface Address {
2
+ id: number;
3
+ name: string;
4
+ phone: string;
5
+ street1: string;
6
+ street2: string;
7
+ }
8
+
9
+ export interface InitialValues {
10
+ card: string;
11
+ date: string;
12
+ time: string;
13
+ address: string;
14
+ voucher: string;
15
+ cardCVC: string;
16
+ cardYear: string;
17
+ cardMonth: string;
18
+ cardNumber: string;
19
+ cardHolderName: string;
20
+ }
@@ -0,0 +1,39 @@
1
+ // GLOBAL CUSTOM COMPONENTS
2
+ import { FlexBetween } from "@/components/_components/flex-box/index";
3
+ import { Paragraph, Span } from "@/components/_components/Typography";
4
+ import useCurrencyFormatter from "@/lib/currency-formatter";
5
+ import { CartLine } from "@/api-manager/schemas/cart.schema";
6
+ // CUSTOM UTILS LIBRARY FUNCTION
7
+
8
+ // ==============================================================
9
+ interface Props {
10
+ cartLine: CartLine;
11
+ }
12
+ // ==============================================================
13
+
14
+ export default function CartItem({ cartLine }: Props) {
15
+ const { formatCurrency } = useCurrencyFormatter();
16
+ const hasDiscount = cartLine.DiscountAmount && cartLine.DiscountAmount > 0;
17
+ return (
18
+ <FlexBetween mb={1.5}>
19
+ <Paragraph>
20
+ <Span fontWeight="700">{cartLine.Quantity}</Span> x{" "}
21
+ {cartLine.Product?.Name}
22
+ </Paragraph>
23
+
24
+ <Paragraph>
25
+ {hasDiscount ? (
26
+ <s>{formatCurrency(cartLine.NetPrice || 0)}</s>
27
+ ) : (
28
+ formatCurrency(cartLine.TotalAmount || 0)
29
+ )}
30
+ {hasDiscount ? (
31
+ <Span color="error.500">
32
+ {" "}
33
+ {formatCurrency(cartLine.TotalAmount || 0)}
34
+ </Span>
35
+ ) : null}
36
+ </Paragraph>
37
+ </FlexBetween>
38
+ );
39
+ }
@@ -0,0 +1,40 @@
1
+ "use client";
2
+
3
+ import Box from "@mui/material/Box";
4
+ import Divider from "@mui/material/Divider";
5
+ // GLOBAL CUSTOM COMPONENTS
6
+ import { Paragraph } from "@/components/_components/Typography";
7
+ // GLOBAL CUSTOM HOOK
8
+ // LOCAL CUSTOM COMPONENTS
9
+ import CartItem from "./cart-item";
10
+ import ListItem from "./list-item";
11
+ import { Cart } from "@/api-manager/schemas/cart.schema";
12
+ import { useTranslations } from "next-intl";
13
+
14
+ interface Props {
15
+ cart: Cart;
16
+ }
17
+
18
+ export default function CheckoutAltSummary({ cart }: Props) {
19
+ const t = useTranslations("Checkout.Summary");
20
+ return (
21
+ <div>
22
+ <Paragraph color="secondary.900" fontWeight={700} mb={2}>
23
+ {t("summaryTitle")}
24
+ </Paragraph>
25
+
26
+ {cart.CartLines?.map((cl) => <CartItem cartLine={cl} key={cl.LineId} />)}
27
+
28
+ <Box component={Divider} borderColor="grey.300" my={3} />
29
+
30
+ <ListItem title={t("subtotal")} value={cart.SubtotalAmount} />
31
+ <ListItem title={t("shipping")} value={cart.ShippingChargeAmount} />
32
+ <ListItem title={t("tax")} value={cart.TaxAmount} />
33
+ <ListItem title={t("discount")} value={cart.DiscountAmount} mb={3} />
34
+
35
+ <Box component={Divider} borderColor="grey.300" mb={1} />
36
+
37
+ <ListItem title={t("total")} value={cart.TotalAmount} color="inherit" />
38
+ </div>
39
+ );
40
+ }
@@ -0,0 +1 @@
1
+ export { default as CheckoutAltSummary } from "./checkout-alt-summary";
@@ -0,0 +1,31 @@
1
+ // GLOBAL CUSTOM COMPONENTS
2
+ import { Paragraph } from "@/components/_components/Typography";
3
+ import { FlexBetween } from "@/components/_components/flex-box/index";
4
+ import useCurrencyFormatter from "@/lib/currency-formatter";
5
+ // CUSTOM UTILS LIBRARY FUNCTION
6
+
7
+ // ==============================================================
8
+ interface Props {
9
+ title: string;
10
+ value?: number;
11
+ mb?: number;
12
+ color?: string;
13
+ }
14
+ // ==============================================================
15
+
16
+ export default function ListItem({
17
+ title,
18
+ value,
19
+ mb = 0.5,
20
+ color = "grey.600",
21
+ }: Props) {
22
+ const { formatCurrency } = useCurrencyFormatter();
23
+ return (
24
+ <FlexBetween mb={mb}>
25
+ <Paragraph color={color}>{title}:</Paragraph>
26
+ <Paragraph fontWeight="700">
27
+ {value ? formatCurrency(value) : "-"}
28
+ </Paragraph>
29
+ </FlexBetween>
30
+ );
31
+ }
@@ -0,0 +1,22 @@
1
+ import { Card } from "@mui/material";
2
+ import { Address } from "@/api-manager/schemas/address.schema";
3
+ import { H5, Paragraph } from "@/components/_components/Typography";
4
+ import { useTranslations } from "next-intl";
5
+
6
+ type Props = { address: Address };
7
+
8
+ export default function OrderAddress({ address }: Props) {
9
+ const t = useTranslations("Address");
10
+ return (
11
+ <Card sx={{ p: 3 }}>
12
+ <H5 mt={0} mb={2}>
13
+ {t("shippingTo")}
14
+ </H5>
15
+ <Paragraph>{address.Name}</Paragraph>
16
+ <Paragraph>{address.City}</Paragraph>
17
+ <Paragraph>{address.State}</Paragraph>
18
+ <Paragraph>{address.ZipCode}</Paragraph>
19
+ <Paragraph>{address.ThreeLetterISORegionName}</Paragraph>
20
+ </Card>
21
+ );
22
+ }
@@ -0,0 +1,52 @@
1
+ import { FlexBetween, FlexBox } from "@/components/_components/flex-box";
2
+ import { H6 } from "@/components/_components/Typography";
3
+ import useCurrencyFormatter from "@/lib/currency-formatter";
4
+ import { Card, Divider } from "@mui/material";
5
+ import { Paragraph } from "@/components/_components/Typography";
6
+ import { useTranslations } from "next-intl";
7
+
8
+ type Props = {
9
+ subtotal: number;
10
+ shipping: number;
11
+ tax: number;
12
+ discount: number;
13
+ total: number;
14
+ };
15
+
16
+ export default function OrderSummary({
17
+ discount,
18
+ shipping,
19
+ subtotal,
20
+ tax,
21
+ total,
22
+ }: Props) {
23
+ const t = useTranslations("Checkout.Summary");
24
+ const t2 = useTranslations("Confirmation");
25
+ const { formatCurrency } = useCurrencyFormatter();
26
+
27
+ const renderListItem = (title: string, value: number) => (
28
+ <FlexBetween mb={1}>
29
+ <Paragraph color="grey.600">{title}</Paragraph>
30
+ <H6>{formatCurrency(value)}</H6>
31
+ </FlexBetween>
32
+ );
33
+ return (
34
+ <Card sx={{ px: 3, mb: "30px" }}>
35
+ <FlexBox
36
+ justifyContent={"center"}
37
+ px={3}
38
+ py={2}
39
+ borderBottom={"1px solid"}
40
+ borderColor={"divider"}
41
+ >
42
+ {t2("orderSummary")}
43
+ </FlexBox>
44
+ {renderListItem(t("subtotal"), subtotal)}
45
+ {renderListItem(t("shipping"), shipping)}
46
+ {renderListItem(t("tax"), tax)}
47
+ {renderListItem(t("discount"), discount)}
48
+ <Divider sx={{ mb: 1 }} />
49
+ {renderListItem(t("total"), total)}
50
+ </Card>
51
+ );
52
+ }