@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.
- package/package.json +9 -4
- package/src/api-manager/README.md +52 -27
- package/src/api-manager/datasources/d365/d365-cart.datasource.ts +166 -17
- package/src/api-manager/datasources/d365/d365-invoice.datasource.ts +100 -0
- package/src/api-manager/datasources/d365/d365-order.datasource.ts +75 -18
- package/src/api-manager/datasources/d365/d365-product.datasource.ts +19 -1
- package/src/api-manager/datasources/d365/d365-user.datasource.ts +96 -22
- package/src/api-manager/datasources/d365/d365.datasource.ts +3 -0
- package/src/api-manager/datasources/d365/utils/get-context-cookie.ts +14 -5
- package/src/api-manager/datasources/e4/e4.datasource.ts +9 -0
- package/src/api-manager/datasources/e4/middleware.ts +35 -0
- package/src/api-manager/datasources/e4/order/e4-order.datasource.ts +7 -4
- package/src/api-manager/datasources/e4/user/e4-user.datasource.ts +4 -0
- package/src/api-manager/index.ts +54 -3
- package/src/api-manager/lib/get-graphql-client.ts +13 -6
- package/src/api-manager/schemas/cart.schema.ts +10 -1
- package/src/api-manager/schemas/invoice.schema.ts +55 -0
- package/src/api-manager/schemas/order.schema.ts +13 -1
- package/src/api-manager/schemas/user.schema.ts +8 -0
- package/src/api-manager/services/create-query.ts +5 -56
- package/src/api-manager/services/invoice/queries/get-invoice-details.ts +18 -0
- package/src/api-manager/services/invoice/queries/get-invoices.ts +18 -0
- package/src/api-manager/services/order/queries/get-order-details.ts +1 -1
- package/src/api-manager/services/order/queries/get-orders.ts +6 -3
- package/src/api-manager/services/user/queries/get-customer-balance.ts +20 -0
- package/src/api-manager/types/Datasource.ts +21 -2
- package/src/auth/better-auth.ts +89 -0
- package/src/auth/signout.ts +5 -34
- package/src/cms/blocks/block-manager.tsx +18 -3
- package/src/cms/blocks/components/cta-banner.tsx +65 -0
- package/src/cms/blocks/components/hero-image.tsx +83 -0
- package/src/cms/blocks/index.tsx +2 -0
- package/src/cms/blocks/interfaces.ts +20 -0
- package/src/cms/draft-mode-badge.tsx +26 -0
- package/src/cms/queries.ts +81 -0
- package/src/components/flex-box/flex-box.tsx +2 -0
- package/src/components/header/__tests__/header.test.tsx +9 -2
- package/src/components/header/__tests__/user.test.tsx +34 -19
- package/src/components/header/components/user.tsx +70 -29
- package/src/components/mini-cart/mini-cart.tsx +3 -3
- package/src/components/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-dialog.tsx +3 -1
- package/src/components/wishlist-dialogs/add-to-wishlist/compound/wishlist-dialog-header.tsx +4 -1
- package/src/hooks/use-nextauth-session.ts +10 -14
- package/src/pages/account/account-navigation.tsx +88 -60
- package/src/pages/account/account-routes.ts +52 -0
- package/src/pages/account/orders/order-history-filter-types.tsx +26 -0
- package/src/pages/account/orders/order-history-filters-panel.tsx +375 -0
- package/src/pages/account/orders/order-history-header.tsx +54 -0
- package/src/pages/account/orders/order-history-pagination.tsx +55 -0
- package/src/pages/account/orders/order-history-root.tsx +125 -0
- package/src/pages/account/orders/order-history-row.tsx +110 -0
- package/src/pages/account/orders/order-history-search-and-filter.tsx +449 -0
- package/src/pages/account/orders/order-history-search-bar.tsx +84 -0
- package/src/pages/account/orders/order-history-search-dropdown.tsx +53 -0
- package/src/pages/account/orders/order-history-sort.tsx +90 -0
- package/src/pages/account/orders/order-history-table.tsx +54 -0
- package/src/pages/account/orders/order-row.tsx +2 -2
- package/src/pages/account/orders/orders-drop-down-handler.tsx +19 -0
- package/src/pages/account/table-header.tsx +20 -0
- package/src/pages/account/wishlist/wishlist-item.tsx +1 -1
- package/src/pages/blog/blog-card.tsx +9 -3
- package/src/pages/blog/blog-detail-view.tsx +150 -0
- package/src/pages/blog/blog-list-view.tsx +59 -0
- package/src/pages/cart/__tests__/checkout-form.test.tsx +4 -2
- package/src/pages/cart/estimate-shipping.tsx +6 -5
- package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +43 -21
- package/src/pages/checkout/checkout-alt-form/steps/payment/payment-details.tsx +144 -19
- package/src/pages/cms-page-view.tsx +53 -0
- package/src/pages/quickorder/order-upload.tsx +12 -6
- package/src/pages/quickorder/quick-order.tsx +25 -20
- package/src/api-manager/datasources/d365/utils/decode-jwt.ts +0 -10
- package/src/auth/auth-options.test.ts +0 -70
- package/src/auth/auth-options.ts +0 -101
- package/src/auth/next-auth-cookie-manager.ts +0 -87
- package/src/auth/providers/aadb2c-provider.ts +0 -32
- package/src/auth/providers/authentik-provider.ts +0 -24
- package/src/auth/providers/entra-external-id-provider.ts +0 -50
- package/src/auth/providers/index.ts +0 -16
- package/src/auth/providers/keycloak-provider.ts +0 -22
- package/src/auth/refresh-token.ts +0 -132
- 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.
|
|
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.
|
|
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;
|
|
@@ -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
|
-
|
|
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("
|
|
14
|
-
|
|
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
|
-
{
|
|
166
|
-
|
|
167
|
-
{
|
|
168
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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({
|