@evenicanpm/storefront-core 2.1.0 → 2.3.0

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 (81) hide show
  1. package/package.json +9 -4
  2. package/src/api-manager/README.md +52 -27
  3. package/src/api-manager/datasources/d365/d365-cart.datasource.ts +166 -17
  4. package/src/api-manager/datasources/d365/d365-invoice.datasource.ts +100 -0
  5. package/src/api-manager/datasources/d365/d365-order.datasource.ts +75 -18
  6. package/src/api-manager/datasources/d365/d365-product.datasource.ts +19 -1
  7. package/src/api-manager/datasources/d365/d365-user.datasource.ts +96 -22
  8. package/src/api-manager/datasources/d365/d365.datasource.ts +3 -0
  9. package/src/api-manager/datasources/d365/utils/get-context-cookie.ts +14 -5
  10. package/src/api-manager/datasources/e4/e4.datasource.ts +9 -0
  11. package/src/api-manager/datasources/e4/middleware.ts +35 -0
  12. package/src/api-manager/datasources/e4/order/e4-order.datasource.ts +7 -4
  13. package/src/api-manager/datasources/e4/user/e4-user.datasource.ts +4 -0
  14. package/src/api-manager/index.ts +54 -3
  15. package/src/api-manager/lib/get-graphql-client.ts +13 -6
  16. package/src/api-manager/schemas/cart.schema.ts +10 -1
  17. package/src/api-manager/schemas/invoice.schema.ts +55 -0
  18. package/src/api-manager/schemas/order.schema.ts +13 -1
  19. package/src/api-manager/schemas/user.schema.ts +8 -0
  20. package/src/api-manager/services/create-query.ts +5 -56
  21. package/src/api-manager/services/invoice/queries/get-invoice-details.ts +18 -0
  22. package/src/api-manager/services/invoice/queries/get-invoices.ts +18 -0
  23. package/src/api-manager/services/order/queries/get-order-details.ts +1 -1
  24. package/src/api-manager/services/order/queries/get-orders.ts +6 -3
  25. package/src/api-manager/services/user/queries/get-customer-balance.ts +20 -0
  26. package/src/api-manager/types/Datasource.ts +21 -2
  27. package/src/auth/better-auth.ts +89 -0
  28. package/src/auth/signout.ts +5 -34
  29. package/src/cms/blocks/block-manager.tsx +18 -3
  30. package/src/cms/blocks/components/cta-banner.tsx +65 -0
  31. package/src/cms/blocks/components/hero-image.tsx +83 -0
  32. package/src/cms/blocks/index.tsx +2 -0
  33. package/src/cms/blocks/interfaces.ts +20 -0
  34. package/src/cms/draft-mode-badge.tsx +26 -0
  35. package/src/cms/queries.ts +81 -0
  36. package/src/components/flex-box/flex-box.tsx +2 -0
  37. package/src/components/header/__tests__/header.test.tsx +9 -2
  38. package/src/components/header/__tests__/user.test.tsx +34 -19
  39. package/src/components/header/components/user.tsx +70 -29
  40. package/src/components/mini-cart/mini-cart.tsx +3 -3
  41. package/src/components/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-dialog.tsx +3 -1
  42. package/src/components/wishlist-dialogs/add-to-wishlist/compound/wishlist-dialog-header.tsx +4 -1
  43. package/src/hooks/use-nextauth-session.ts +10 -14
  44. package/src/pages/account/account-navigation.tsx +88 -60
  45. package/src/pages/account/account-routes.ts +52 -0
  46. package/src/pages/account/orders/order-history-filter-types.tsx +26 -0
  47. package/src/pages/account/orders/order-history-filters-panel.tsx +375 -0
  48. package/src/pages/account/orders/order-history-header.tsx +54 -0
  49. package/src/pages/account/orders/order-history-pagination.tsx +55 -0
  50. package/src/pages/account/orders/order-history-root.tsx +125 -0
  51. package/src/pages/account/orders/order-history-row.tsx +110 -0
  52. package/src/pages/account/orders/order-history-search-and-filter.tsx +449 -0
  53. package/src/pages/account/orders/order-history-search-bar.tsx +84 -0
  54. package/src/pages/account/orders/order-history-search-dropdown.tsx +53 -0
  55. package/src/pages/account/orders/order-history-sort.tsx +90 -0
  56. package/src/pages/account/orders/order-history-table.tsx +54 -0
  57. package/src/pages/account/orders/order-row.tsx +2 -2
  58. package/src/pages/account/orders/orders-drop-down-handler.tsx +19 -0
  59. package/src/pages/account/table-header.tsx +20 -0
  60. package/src/pages/account/wishlist/wishlist-item.tsx +1 -1
  61. package/src/pages/blog/blog-card.tsx +9 -3
  62. package/src/pages/blog/blog-detail-view.tsx +150 -0
  63. package/src/pages/blog/blog-list-view.tsx +59 -0
  64. package/src/pages/cart/__tests__/checkout-form.test.tsx +4 -2
  65. package/src/pages/cart/estimate-shipping.tsx +6 -5
  66. package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +43 -21
  67. package/src/pages/checkout/checkout-alt-form/steps/payment/payment-details.tsx +144 -19
  68. package/src/pages/cms-page-view.tsx +53 -0
  69. package/src/pages/quickorder/order-upload.tsx +12 -6
  70. package/src/pages/quickorder/quick-order.tsx +25 -20
  71. package/src/api-manager/datasources/d365/utils/decode-jwt.ts +0 -10
  72. package/src/auth/auth-options.test.ts +0 -70
  73. package/src/auth/auth-options.ts +0 -101
  74. package/src/auth/next-auth-cookie-manager.ts +0 -87
  75. package/src/auth/providers/aadb2c-provider.ts +0 -32
  76. package/src/auth/providers/authentik-provider.ts +0 -24
  77. package/src/auth/providers/entra-external-id-provider.ts +0 -50
  78. package/src/auth/providers/index.ts +0 -16
  79. package/src/auth/providers/keycloak-provider.ts +0 -22
  80. package/src/auth/refresh-token.ts +0 -132
  81. package/src/auth/types/next-auth.d.ts +0 -38
@@ -0,0 +1,90 @@
1
+ import type { SortColumn } from "@msdyn365-commerce/retail-proxy";
2
+ import { FormControl, InputLabel, MenuItem, Select } from "@mui/material";
3
+ import { useTranslations } from "next-intl";
4
+ import { useState } from "react";
5
+
6
+ interface SortOption {
7
+ id: number;
8
+ label: string;
9
+ columnName: string;
10
+ isDescending: boolean;
11
+ }
12
+
13
+ const sortOptions: SortOption[] = [
14
+ {
15
+ id: 0,
16
+ label: "Newest order placed",
17
+ columnName: "CREATEDDATETIME",
18
+ isDescending: true,
19
+ },
20
+ {
21
+ id: 1,
22
+ label: "Oldest order placed",
23
+ columnName: "CREATEDDATETIME",
24
+ isDescending: false,
25
+ },
26
+ {
27
+ id: 2,
28
+ label: "Newest confirmed ship date",
29
+ columnName: "CONFIRMEDSHIPDATE",
30
+ isDescending: true,
31
+ },
32
+ {
33
+ id: 3,
34
+ label: "Oldest confirmed ship date",
35
+ columnName: "CONFIRMEDSHIPDATE",
36
+ isDescending: false,
37
+ },
38
+ ];
39
+
40
+ interface OrderSortProps {
41
+ setSortColumn: (selectedSortColumn: SortColumn) => void;
42
+ }
43
+
44
+ export default function OrdersSort({ setSortColumn }: OrderSortProps) {
45
+ const [selectedSort, setSelectedSort] = useState<number>(0);
46
+ const t = useTranslations("Account.Orders");
47
+
48
+ const handleSelectSort = (sortId: number) => {
49
+ const sortColumn = getSortCriteria(sortId);
50
+ setSortColumn(sortColumn);
51
+ setSelectedSort(sortId);
52
+ };
53
+
54
+ const getSortCriteria = (sortId: number): SortColumn => {
55
+ const sortOption = sortOptions.find((e) => e.id === sortId);
56
+ if (sortOption) {
57
+ return {
58
+ ColumnName: sortOption.columnName,
59
+ IsDescending: sortOption.isDescending,
60
+ };
61
+ } else {
62
+ return {
63
+ ColumnName: "CREATEDDATETIME",
64
+ IsDescending: false,
65
+ };
66
+ }
67
+ };
68
+
69
+ return (
70
+ <FormControl
71
+ fullWidth
72
+ size="small"
73
+ sx={{ maxWidth: 240, backgroundColor: "#fff" }}
74
+ >
75
+ <InputLabel id="orders-sort-label">{t("sortByLabel")}</InputLabel>
76
+ <Select
77
+ labelId="orders-sort-label"
78
+ value={selectedSort}
79
+ label={t("sortByLabel")}
80
+ onChange={(e) => handleSelectSort(Number(e.target.value))}
81
+ >
82
+ {sortOptions.map((option) => (
83
+ <MenuItem key={option.id} value={option.id}>
84
+ {option.label}
85
+ </MenuItem>
86
+ ))}
87
+ </Select>
88
+ </FormControl>
89
+ );
90
+ }
@@ -0,0 +1,54 @@
1
+ import NoRecords from "@evenicanpm/storefront-core/src/components/no-records";
2
+ import { Box, CircularProgress } from "@mui/material";
3
+ import { cloneElement, Fragment } from "react";
4
+ import OrderHistoryHeader from "./order-history-header";
5
+ import OrdersPagination from "./order-history-pagination";
6
+ import { useOrderHistory } from "./order-history-root";
7
+ import OrderHistoryRow, {
8
+ OrderHistoryTableRowContext,
9
+ } from "./order-history-row";
10
+
11
+ const OrderHistoryTable = ({ children }: { children: React.ReactNode }) => {
12
+ return <>{children}</>;
13
+ };
14
+
15
+ OrderHistoryTable.List = ({ children }: { children: React.ReactElement }) => {
16
+ const { orders, isLoading } = useOrderHistory();
17
+
18
+ if (isLoading) {
19
+ return (
20
+ <Box display="flex" justifyContent="center" my={4}>
21
+ <CircularProgress />
22
+ </Box>
23
+ );
24
+ }
25
+
26
+ if (!orders || orders.length === 0) {
27
+ return <NoRecords message="Account.Orders.noOrders" />;
28
+ }
29
+
30
+ return (
31
+ <Fragment>
32
+ {orders.map((order, index) => (
33
+ <OrderHistoryTableRowContext.Provider
34
+ key={order.RecordId ?? index}
35
+ value={{ order }}
36
+ >
37
+ {cloneElement(children)}
38
+ </OrderHistoryTableRowContext.Provider>
39
+ ))}
40
+ <Box mt={4}>
41
+ <OrderHistoryTable.Pagination>
42
+ <OrderHistoryTable.Pagination.ResultNumber />
43
+ <OrderHistoryTable.Pagination.PaginationControl />
44
+ </OrderHistoryTable.Pagination>
45
+ </Box>
46
+ </Fragment>
47
+ );
48
+ };
49
+
50
+ OrderHistoryTable.Header = OrderHistoryHeader;
51
+ OrderHistoryTable.Row = OrderHistoryRow;
52
+ OrderHistoryTable.Pagination = OrdersPagination;
53
+
54
+ export default OrderHistoryTable;
@@ -39,7 +39,7 @@ const OrderRow = ({ order, children }: React.PropsWithChildren<Props>) => {
39
39
 
40
40
  return (
41
41
  <OrderRowContext.Provider value={{ order, formatCurrency }}>
42
- <Link href={`/account/orders/${order.Id}`}>
42
+ <Link href={`/account/orders/${order.SalesId}`}>
43
43
  <TableRow sx={{ gridTemplateColumns: "2fr 1fr 1fr 1fr 1fr" }}>
44
44
  {children}
45
45
  </TableRow>
@@ -73,7 +73,7 @@ OrderRow.Date = () => {
73
73
  const { order } = context;
74
74
  return (
75
75
  <Paragraph textAlign={{ sm: "center", xs: "left" }}>
76
- {format(new Date(order.TransactionDate ?? ""), "MMM dd, yyyy")}
76
+ {format(new Date(order.CreatedDateTime ?? ""), "MMM dd, yyyy")}
77
77
  </Paragraph>
78
78
  );
79
79
  };
@@ -0,0 +1,19 @@
1
+ "use client";
2
+
3
+ import Box from "@mui/material/Box";
4
+ import styled from "@mui/material/styles/styled";
5
+
6
+ export const OrdersDropDownHandler = styled(Box)(({ theme }) => ({
7
+ width: 150,
8
+ minWidth: 150,
9
+ height: "100%",
10
+ display: "flex",
11
+ whiteSpace: "pre",
12
+ alignItems: "center",
13
+ gap: theme.spacing(0.5),
14
+ justifyContent: "flex-start",
15
+ color: theme.palette.grey[700],
16
+ paddingInline: theme.spacing(2),
17
+ borderRight: `1px solid ${theme.palette.grey[400]}`,
18
+ [theme.breakpoints.down("xs")]: { display: "none" },
19
+ }));
@@ -0,0 +1,20 @@
1
+ import Card from "@mui/material/Card";
2
+ import styled from "@mui/material/styles/styled";
3
+
4
+ const TableHeader = styled(Card)(({ theme }) => ({
5
+ gap: 16,
6
+ marginBlock: 16,
7
+ display: "grid",
8
+ borderRadius: 10,
9
+ alignItems: "center",
10
+ padding: ".6rem 1.2rem",
11
+ gridTemplateColumns: "1.5fr 2fr 1.5fr auto",
12
+ [theme.breakpoints.down("sm")]: {
13
+ gap: 8,
14
+ gridTemplateColumns: "repeat(2, 1fr)",
15
+ },
16
+ backgroundColor: theme.palette.primary.main,
17
+ color: theme.palette.primary.contrastText,
18
+ }));
19
+
20
+ export default TableHeader;
@@ -79,7 +79,7 @@ WishlistItem.Name = () => {
79
79
  return (
80
80
  <Paragraph ellipsis>
81
81
  <NavLink3
82
- href={`/account/wish-lists/${wishlist?.Id}`}
82
+ href={`/account/order-templates/${wishlist?.Id}`}
83
83
  text={wishlist?.Name ?? ""}
84
84
  />
85
85
  </Paragraph>
@@ -14,7 +14,7 @@ import { format } from "date-fns";
14
14
 
15
15
  interface Props {
16
16
  title: string;
17
- imageUrl: string;
17
+ imageUrl: string | undefined;
18
18
  description: string;
19
19
  createdAt: Date;
20
20
  id: string;
@@ -31,8 +31,14 @@ export default function BlogCard({
31
31
  }: Readonly<Props>) {
32
32
  return (
33
33
  <Box overflow="hidden">
34
- <ZoomLazyImage width={588} height={272} alt="blog-image" src={imageUrl} />
35
-
34
+ {imageUrl && (
35
+ <ZoomLazyImage
36
+ width={588}
37
+ height={272}
38
+ alt="blog-image"
39
+ src={imageUrl}
40
+ />
41
+ )}
36
42
  <Box py={3}>
37
43
  <H3 lineHeight={1.3} color="secondary.900">
38
44
  {title}
@@ -0,0 +1,150 @@
1
+ "use client";
2
+ import ArrowBack from "@mui/icons-material/ArrowBack";
3
+ import { Box, Container, Typography } from "@mui/material";
4
+ import Grid from "@mui/material/Grid2";
5
+ import { BlocksRenderer } from "@strapi/blocks-react-renderer";
6
+ import { useSuspenseQuery } from "@tanstack/react-query";
7
+ import Link from "next/link";
8
+ import { cmsQueryOptions } from "../../cms/queries";
9
+ import { BorderShadowCard } from "../../components/BoxShadowCard";
10
+ import { Display, Subtitle } from "../../components/Typography";
11
+
12
+ interface Props {
13
+ id: string;
14
+ locale: string;
15
+ cmsLocale?: string;
16
+ }
17
+
18
+ export const BlogDetailView = ({ id, locale, cmsLocale }: Props) => {
19
+ const { data: blog } = useSuspenseQuery(cmsQueryOptions.blog(id, cmsLocale));
20
+
21
+ if (!blog) return null;
22
+
23
+ const blogData = blog?.data;
24
+ const imageUrl = blogData?.image?.formats?.large?.url;
25
+ const imageAlt = blogData?.image?.alternativeText || "Image";
26
+ const publishDate = new Date(blogData?.publishedAt).toLocaleDateString(
27
+ locale,
28
+ );
29
+ const title = blogData?.title;
30
+ const shortDescription = blogData?.shortDescription;
31
+ const content = blogData?.content;
32
+
33
+ return (
34
+ <Container className="mt-2 mb-2" maxWidth="xl">
35
+ <BorderShadowCard style={{ padding: 0 }}>
36
+ {/* Hero header — full-width image with dark overlay and text */}
37
+ <Box
38
+ sx={{
39
+ position: "relative",
40
+ width: "100%",
41
+ minHeight: { xs: 280, md: 420 },
42
+ borderTopRightRadius: "inherit",
43
+ borderTopLeftRadius: "inherit",
44
+ overflow: "hidden",
45
+ }}
46
+ >
47
+ {imageUrl && (
48
+ <Box
49
+ component="img"
50
+ src={imageUrl}
51
+ alt={imageAlt}
52
+ sx={{
53
+ position: "absolute",
54
+ inset: 0,
55
+ width: "100%",
56
+ height: "100%",
57
+ objectFit: "cover",
58
+ }}
59
+ />
60
+ )}
61
+ {/* Dark gradient overlay */}
62
+ <Box
63
+ sx={{
64
+ position: "absolute",
65
+ inset: 0,
66
+ background:
67
+ "linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0.35) 60%, rgba(0,0,0,0.15) 100%)",
68
+ }}
69
+ />
70
+ {/* Text content on top of overlay */}
71
+ <Box
72
+ sx={{
73
+ position: "relative",
74
+ zIndex: 1,
75
+ display: "flex",
76
+ flexDirection: "column",
77
+ justifyContent: "flex-end",
78
+ height: "100%",
79
+ minHeight: { xs: 280, md: 420 },
80
+ padding: { xs: "24px 20px", md: "40px 40px" },
81
+ color: "#fff",
82
+ }}
83
+ >
84
+ <Link
85
+ href="/blog"
86
+ style={{
87
+ color: "rgba(255,255,255,0.85)",
88
+ marginBottom: "16px",
89
+ display: "inline-flex",
90
+ alignItems: "center",
91
+ gap: 4,
92
+ }}
93
+ >
94
+ <ArrowBack fontSize="small" />
95
+ </Link>
96
+ <Display sx={{ color: "#fff", mb: 1 }}>{title}</Display>
97
+ <Typography
98
+ sx={{
99
+ color: "rgba(255,255,255,0.85)",
100
+ lineHeight: "1.7",
101
+ maxWidth: 640,
102
+ }}
103
+ >
104
+ {shortDescription}
105
+ </Typography>
106
+ </Box>
107
+ </Box>
108
+ <Box>
109
+ <Grid
110
+ container
111
+ sx={{
112
+ backgroundColor: "background.paper",
113
+ padding: "25px",
114
+ boxSizing: "border-box",
115
+ color: "grey.900",
116
+ }}
117
+ >
118
+ <Grid size={{ xs: 12, md: 9 }} order={{ xs: 2, md: 1 }}>
119
+ <Box sx={blogSx}>
120
+ <BlocksRenderer
121
+ content={
122
+ content as React.ComponentProps<
123
+ typeof BlocksRenderer
124
+ >["content"]
125
+ }
126
+ />
127
+ </Box>
128
+ </Grid>
129
+ <Grid size={{ xs: 12, md: 3 }} order={{ xs: 1, md: 2 }}>
130
+ <Box sx={{ paddingBottom: "15px" }}>
131
+ <Subtitle sx={noSpaceSx}>Published on: {publishDate}</Subtitle>
132
+ </Box>
133
+ </Grid>
134
+ </Grid>
135
+ </Box>
136
+ </BorderShadowCard>
137
+ </Container>
138
+ );
139
+ };
140
+
141
+ const noSpaceSx = { margin: 0, padding: 0 };
142
+ const blogSx = {
143
+ maxWidth: "650px",
144
+ "& img": {
145
+ width: "100%",
146
+ },
147
+ "& p": {
148
+ lineHeight: "1.8",
149
+ },
150
+ };
@@ -0,0 +1,59 @@
1
+ "use client";
2
+ import { Box, Container, Grid } from "@mui/material";
3
+ import { useSuspenseQuery } from "@tanstack/react-query";
4
+ import { cmsQueryOptions } from "../../cms/queries";
5
+ import { BorderShadowCard } from "../../components/BoxShadowCard";
6
+ import { FlexBox } from "../../components/flex-box";
7
+ import { H1, H3 } from "../../components/Typography";
8
+ import BlogCard from "./blog-card";
9
+ import Pagination from "./pagination";
10
+
11
+ interface Props {
12
+ page: number;
13
+ pageSize: number;
14
+ cmsLocale?: string;
15
+ }
16
+
17
+ export const BlogListView = ({ page, pageSize, cmsLocale }: Props) => {
18
+ const { data: blogs } = useSuspenseQuery(
19
+ cmsQueryOptions.blogs(page, pageSize, cmsLocale),
20
+ );
21
+
22
+ const pageCount = blogs?.meta.pagination?.pageCount ?? 0;
23
+
24
+ if (blogs.data.length === 0) {
25
+ return (
26
+ <BorderShadowCard>
27
+ <FlexBox justifyContent="center">
28
+ <H3 color={"grey.700"}>No posts yet, visit again soon!</H3>
29
+ </FlexBox>
30
+ </BorderShadowCard>
31
+ );
32
+ }
33
+
34
+ return (
35
+ <Container className="mt-2 mb-2" maxWidth="xl">
36
+ <H1>Posts</H1>
37
+ <Grid container spacing={3}>
38
+ {blogs.data.map((blog) => {
39
+ const imageUrl = blog?.image?.formats?.small?.url;
40
+ return (
41
+ <Grid item md={6} xs={12} key={blog.id}>
42
+ <BlogCard
43
+ key={blog?.id}
44
+ title={blog?.title}
45
+ imageUrl={imageUrl || undefined}
46
+ description={blog?.shortDescription}
47
+ createdAt={blog?.createdAt}
48
+ id={blog.documentId}
49
+ />
50
+ </Grid>
51
+ );
52
+ })}
53
+ </Grid>
54
+ <Box display="flex" justifyContent="center" sx={{ my: 2 }}>
55
+ <Pagination count={pageCount} page={page} pageSize={pageSize} />
56
+ </Box>
57
+ </Container>
58
+ );
59
+ };
@@ -10,8 +10,10 @@ import { NextIntlClientProvider } from "next-intl";
10
10
  import type React from "react";
11
11
  import { vi } from "vitest";
12
12
 
13
- vi.mock("next-auth/react", () => ({
14
- // Empty mock for next-auth to stop throwing errors
13
+ vi.mock("better-auth/react", () => ({
14
+ createAuthClient: () => ({
15
+ useSession: () => ({ data: null, isPending: false }),
16
+ }),
15
17
  }));
16
18
 
17
19
  // global.fetch = vi.fn(() => Promise.resolve({ json: () => ({}) }));
@@ -162,11 +162,12 @@ EstimateShipping.State = () => {
162
162
  onChange={(e) => setSelectedState(e.target.value)}
163
163
  >
164
164
  <MenuItem value="">Select a state</MenuItem>
165
- {states.map(({ StateName, StateId }) => (
166
- <MenuItem value={StateId} key={StateId}>
167
- {StateName}
168
- </MenuItem>
169
- ))}
165
+ {Array.isArray(states) &&
166
+ states.map(({ StateName, StateId }) => (
167
+ <MenuItem value={StateId} key={StateId}>
168
+ {StateName}
169
+ </MenuItem>
170
+ ))}
170
171
  </TextField>
171
172
  );
172
173
  };
@@ -70,29 +70,51 @@ const CheckoutForm = ({ cart, children }: CheckoutFormProps) => {
70
70
  const placeOrder = async () => {
71
71
  setIsSubmitting(true);
72
72
  const cache = queryClient.getQueryCache();
73
- const acceptResultCache = cache.get(
74
- JSON.stringify(["paymentAcceptResult", cart.Id]),
75
- );
76
- const paymentAcceptResult = acceptResultCache?.state
77
- .data as CardPaymentAcceptResult;
78
-
79
- if (!paymentAcceptResult?.TokenizedPaymentCard) {
80
- return;
81
- }
73
+
74
+ const selectedPaymentMethod =
75
+ (queryClient.getQueryData<string>(["selectedPaymentMethod", cart.Id]) as
76
+ | "card"
77
+ | "on-account") ?? "card";
82
78
 
83
79
  try {
84
- const data = await placeOrderMutation({
85
- id: cart.Id,
86
- receiptEmail: cart.ReceiptEmail ?? "",
87
- tokenizedPaymentCard: paymentAcceptResult.TokenizedPaymentCard,
88
- currency: channelConfigurationData?.Currency ?? "",
89
- amount: cart.TotalAmount || 0,
90
- });
91
- await cleanupCart(data.Id);
92
- setNotification({
93
- message: t("placeOrderSuccessMessage"),
94
- severity: "success",
95
- });
80
+ if (selectedPaymentMethod === "on-account") {
81
+ const data = await placeOrderMutation({
82
+ id: cart.Id,
83
+ receiptEmail: cart.ReceiptEmail ?? "",
84
+ currency: channelConfigurationData?.Currency ?? "",
85
+ amount: cart.TotalAmount || 0,
86
+ paymentMethod: "on-account",
87
+ });
88
+ await cleanupCart(data.Id);
89
+ setNotification({
90
+ message: t("placeOrderSuccessMessage"),
91
+ severity: "success",
92
+ });
93
+ } else {
94
+ const acceptResultCache = cache.get(
95
+ JSON.stringify(["paymentAcceptResult", cart.Id]),
96
+ );
97
+ const paymentAcceptResult = acceptResultCache?.state
98
+ .data as CardPaymentAcceptResult;
99
+
100
+ if (!paymentAcceptResult?.TokenizedPaymentCard) {
101
+ return;
102
+ }
103
+
104
+ const data = await placeOrderMutation({
105
+ id: cart.Id,
106
+ receiptEmail: cart.ReceiptEmail ?? "",
107
+ tokenizedPaymentCard: paymentAcceptResult.TokenizedPaymentCard,
108
+ currency: channelConfigurationData?.Currency ?? "",
109
+ amount: cart.TotalAmount || 0,
110
+ paymentMethod: "card",
111
+ });
112
+ await cleanupCart(data.Id);
113
+ setNotification({
114
+ message: t("placeOrderSuccessMessage"),
115
+ severity: "success",
116
+ });
117
+ }
96
118
  } catch (error) {
97
119
  console.error(error);
98
120
  setNotification({