@evenicanpm/storefront-core 2.2.0 → 2.3.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 (80) hide show
  1. package/package.json +7 -3
  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 +125 -36
  8. package/src/api-manager/datasources/d365/d365.datasource.ts +3 -0
  9. package/src/api-manager/datasources/d365/utils/get-context-cookie.ts +10 -3
  10. package/src/api-manager/datasources/e4/e4.datasource.ts +9 -0
  11. package/src/api-manager/datasources/e4/graphqlRequestSdk.ts +7 -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 +11 -5
  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 +4 -10
  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 +1 -1
  28. package/src/cms/blocks/block-manager.tsx +18 -3
  29. package/src/cms/blocks/components/cta-banner.tsx +65 -0
  30. package/src/cms/blocks/components/footer/footer-content.tsx +61 -0
  31. package/src/cms/blocks/components/footer/index.tsx +9 -71
  32. package/src/cms/blocks/components/footer/interfaces.ts +4 -0
  33. package/src/cms/blocks/components/footer/sections/footer-contact.tsx +18 -9
  34. package/src/cms/blocks/components/footer/sections/footer-logo.tsx +17 -2
  35. package/src/cms/blocks/components/hero-image.tsx +83 -0
  36. package/src/cms/blocks/index.tsx +2 -0
  37. package/src/cms/blocks/interfaces.ts +20 -0
  38. package/src/cms/draft-mode-badge.tsx +26 -0
  39. package/src/cms/queries.ts +81 -0
  40. package/src/components/_tests_/site-logo.test.tsx +1 -1
  41. package/src/components/flex-box/flex-box.tsx +2 -0
  42. package/src/components/header/components/user.tsx +35 -11
  43. package/src/components/header/sticky-header.tsx +1 -0
  44. package/src/components/mini-cart/mini-cart.tsx +3 -3
  45. package/src/components/product-cards/product-card-plp/product-card.tsx +4 -12
  46. package/src/components/product-cards/product-card-plp/styles/index.ts +15 -0
  47. package/src/components/site-logo.tsx +1 -1
  48. package/src/components/wishlist-dialogs/add-to-wishlist/compound/wishlist-dialog-header.tsx +1 -0
  49. package/src/components/wishlist-dialogs/add-to-wishlist/compound/wishlist-dialog-item.tsx +3 -1
  50. package/src/pages/account/account-navigation.tsx +88 -60
  51. package/src/pages/account/account-routes.ts +86 -0
  52. package/src/pages/account/orders/order-history-filter-types.tsx +26 -0
  53. package/src/pages/account/orders/order-history-filters-panel.tsx +375 -0
  54. package/src/pages/account/orders/order-history-header.tsx +54 -0
  55. package/src/pages/account/orders/order-history-pagination.tsx +55 -0
  56. package/src/pages/account/orders/order-history-root.tsx +125 -0
  57. package/src/pages/account/orders/order-history-row.tsx +110 -0
  58. package/src/pages/account/orders/order-history-search-and-filter.tsx +449 -0
  59. package/src/pages/account/orders/order-history-search-bar.tsx +84 -0
  60. package/src/pages/account/orders/order-history-search-dropdown.tsx +53 -0
  61. package/src/pages/account/orders/order-history-sort.tsx +90 -0
  62. package/src/pages/account/orders/order-history-table.tsx +54 -0
  63. package/src/pages/account/orders/order-row.tsx +2 -2
  64. package/src/pages/account/orders/orders-drop-down-handler.tsx +19 -0
  65. package/src/pages/account/table-header.tsx +20 -0
  66. package/src/pages/account/table-row-skeleton.tsx +31 -0
  67. package/src/pages/account/wishlist/wishlist-item.tsx +4 -1
  68. package/src/pages/blog/blog-card.tsx +9 -3
  69. package/src/pages/blog/blog-detail-view.tsx +152 -0
  70. package/src/pages/blog/blog-list-view.tsx +59 -0
  71. package/src/pages/cart/estimate-shipping.tsx +6 -5
  72. package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +43 -21
  73. package/src/pages/checkout/checkout-alt-form/steps/payment/payment-details.tsx +144 -19
  74. package/src/pages/cms-page-view.tsx +53 -0
  75. package/src/pages/product-details/product-description.tsx +25 -13
  76. package/src/pages/product-details/product-intro/compound/product-intro-images.tsx +26 -8
  77. package/src/pages/product-details/product-intro/compound/thumbnail-with-skeleton.tsx +17 -21
  78. package/src/pages/product-details/product-tabs.tsx +20 -4
  79. package/src/pages/quickorder/order-upload.tsx +12 -6
  80. package/src/pages/quickorder/quick-order.tsx +25 -20
@@ -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;
@@ -0,0 +1,31 @@
1
+ import TableRow from "@evenicanpm/storefront-core/src/pages/account/table-row";
2
+ import Box from "@mui/material/Box";
3
+ import Skeleton from "@mui/material/Skeleton";
4
+ import type { SxProps, Theme } from "@mui/material/styles";
5
+
6
+ interface TableRowSkeletonProps {
7
+ sx?: SxProps<Theme>;
8
+ }
9
+
10
+ export default function TableRowSkeleton({
11
+ sx = { gridTemplateColumns: "2fr 1fr 1fr 1fr 1fr" },
12
+ }: TableRowSkeletonProps) {
13
+ return (
14
+ <TableRow sx={sx}>
15
+ <Skeleton variant="text" width="60%" />
16
+ <Box textAlign="center">
17
+ <Skeleton
18
+ variant="rounded"
19
+ width={70}
20
+ height={24}
21
+ sx={{ mx: "auto" }}
22
+ />
23
+ </Box>
24
+ <Skeleton variant="text" width="70%" sx={{ mx: { sm: "auto" } }} />
25
+ <Skeleton variant="text" width="50%" sx={{ mx: { sm: "auto" } }} />
26
+ <Box display={{ sm: "inline-flex", xs: "none" }} justifyContent="end">
27
+ <Skeleton variant="circular" width={32} height={32} />
28
+ </Box>
29
+ </TableRow>
30
+ );
31
+ }
@@ -16,6 +16,7 @@ import { format } from "date-fns";
16
16
  import { useTranslations } from "next-intl";
17
17
  import { createContext, type ReactNode, useContext } from "react";
18
18
  import { MdDelete as Delete } from "react-icons/md";
19
+ import { useCreateAccountRoutes } from "../account-routes";
19
20
 
20
21
  interface WishlistItemContextValue {
21
22
  wishlist: Wishlist;
@@ -76,10 +77,12 @@ WishlistItem.Name = () => {
76
77
  const ctx = useContext(WishlistItemContext);
77
78
  if (!ctx) return null;
78
79
  const { wishlist } = ctx;
80
+ const { wishlists: wishlistsRoute } = useCreateAccountRoutes();
81
+
79
82
  return (
80
83
  <Paragraph ellipsis>
81
84
  <NavLink3
82
- href={`/account/wish-lists/${wishlist?.Id}`}
85
+ href={`${wishlistsRoute.href}/${wishlist?.Id}`}
83
86
  text={wishlist?.Name ?? ""}
84
87
  />
85
88
  </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,152 @@
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, Paragraph, 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
+ const author = blogData?.author;
33
+
34
+ return (
35
+ <Container className="mt-2 mb-2" maxWidth="xl">
36
+ <BorderShadowCard style={{ padding: 0 }}>
37
+ {/* Hero header — full-width image with dark overlay and text */}
38
+ <Box
39
+ sx={{
40
+ position: "relative",
41
+ width: "100%",
42
+ minHeight: { xs: 280, md: 420 },
43
+ borderTopRightRadius: "inherit",
44
+ borderTopLeftRadius: "inherit",
45
+ overflow: "hidden",
46
+ }}
47
+ >
48
+ {imageUrl && (
49
+ <Box
50
+ component="img"
51
+ src={imageUrl}
52
+ alt={imageAlt}
53
+ sx={{
54
+ position: "absolute",
55
+ inset: 0,
56
+ width: "100%",
57
+ height: "100%",
58
+ objectFit: "cover",
59
+ }}
60
+ />
61
+ )}
62
+ {/* Dark gradient overlay */}
63
+ <Box
64
+ sx={{
65
+ position: "absolute",
66
+ inset: 0,
67
+ background:
68
+ "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%)",
69
+ }}
70
+ />
71
+ {/* Text content on top of overlay */}
72
+ <Box
73
+ sx={{
74
+ position: "relative",
75
+ zIndex: 1,
76
+ display: "flex",
77
+ flexDirection: "column",
78
+ justifyContent: "flex-end",
79
+ height: "100%",
80
+ minHeight: { xs: 280, md: 420 },
81
+ padding: { xs: "24px 20px", md: "40px 40px" },
82
+ color: "#fff",
83
+ }}
84
+ >
85
+ <Link
86
+ href="/blog"
87
+ style={{
88
+ color: "rgba(255,255,255,0.85)",
89
+ marginBottom: "16px",
90
+ display: "inline-flex",
91
+ alignItems: "center",
92
+ gap: 4,
93
+ }}
94
+ >
95
+ <ArrowBack fontSize="small" />
96
+ </Link>
97
+ <Display sx={{ color: "#fff", mb: 1 }}>{title}</Display>
98
+ <Typography
99
+ sx={{
100
+ color: "rgba(255,255,255,0.85)",
101
+ lineHeight: "1.7",
102
+ maxWidth: 640,
103
+ }}
104
+ >
105
+ {shortDescription}
106
+ </Typography>
107
+ </Box>
108
+ </Box>
109
+ <Box>
110
+ <Grid
111
+ container
112
+ sx={{
113
+ backgroundColor: "background.paper",
114
+ padding: "25px",
115
+ boxSizing: "border-box",
116
+ color: "grey.900",
117
+ }}
118
+ >
119
+ <Grid size={{ xs: 12, md: 9 }} order={{ xs: 2, md: 1 }}>
120
+ <Box sx={blogSx}>
121
+ <BlocksRenderer
122
+ content={
123
+ content as React.ComponentProps<
124
+ typeof BlocksRenderer
125
+ >["content"]
126
+ }
127
+ />
128
+ </Box>
129
+ </Grid>
130
+ <Grid size={{ xs: 12, md: 3 }} order={{ xs: 1, md: 2 }}>
131
+ <Box sx={{ paddingBottom: "15px" }}>
132
+ {author && <Paragraph sx={noSpaceSx}>{author}</Paragraph>}
133
+ <Subtitle sx={noSpaceSx}>Published on: {publishDate}</Subtitle>
134
+ </Box>
135
+ </Grid>
136
+ </Grid>
137
+ </Box>
138
+ </BorderShadowCard>
139
+ </Container>
140
+ );
141
+ };
142
+
143
+ const noSpaceSx = { margin: 0, padding: 0 };
144
+ const blogSx = {
145
+ maxWidth: "650px",
146
+ "& img": {
147
+ width: "100%",
148
+ },
149
+ "& p": {
150
+ lineHeight: "1.8",
151
+ },
152
+ };
@@ -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
+ };
@@ -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({