@lookiero/checkout 3.1.2 → 3.2.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/dist/infrastructure/ui/routing/CheckoutMiddleware.js +4 -0
- package/dist/infrastructure/ui/routing/Routing.js +10 -1
- package/dist/infrastructure/ui/routing/routes.d.ts +1 -0
- package/dist/infrastructure/ui/routing/routes.js +1 -0
- package/dist/infrastructure/ui/views/summary/Summary.d.ts +2 -1
- package/dist/infrastructure/ui/views/summary/Summary.js +21 -31
- package/dist/infrastructure/ui/views/summaryTabs/SummaryTabs.d.ts +8 -0
- package/dist/infrastructure/ui/views/summaryTabs/SummaryTabs.js +52 -0
- package/dist/infrastructure/ui/views/{summary → summaryTabs}/components/checkoutItemsTabs/CheckoutItemsTabs.d.ts +1 -0
- package/dist/infrastructure/ui/views/{summary → summaryTabs}/components/checkoutItemsTabs/CheckoutItemsTabs.js +2 -2
- package/package.json +1 -1
- /package/dist/infrastructure/ui/views/{summary → summaryTabs}/components/checkoutItemsTabs/CheckoutItemsTabs.style.d.ts +0 -0
- /package/dist/infrastructure/ui/views/{summary → summaryTabs}/components/checkoutItemsTabs/CheckoutItemsTabs.style.js +0 -0
|
@@ -24,6 +24,9 @@ const CheckoutMiddleware = ({ customerId, onNotAccessible, loader = React.create
|
|
|
24
24
|
const summaryRouteMatch = useMatch(`${basePath}/${Routes.SUMMARY}`);
|
|
25
25
|
const summaryRouteMatchRef = useRef(summaryRouteMatch);
|
|
26
26
|
summaryRouteMatchRef.current = summaryRouteMatch;
|
|
27
|
+
const summaryTabsRouteMatch = useMatch(`${basePath}/${Routes.SUMMARY}/${Routes.SUMMARY_TABS}`);
|
|
28
|
+
const summaryTabsRouteMatchRef = useRef(summaryTabsRouteMatch);
|
|
29
|
+
summaryTabsRouteMatchRef.current = summaryTabsRouteMatch;
|
|
27
30
|
const checkoutRouteMatch = useMatch(`${basePath}/${Routes.CHECKOUT}`);
|
|
28
31
|
const checkoutRouteMatchRef = useRef(checkoutRouteMatch);
|
|
29
32
|
checkoutRouteMatchRef.current = checkoutRouteMatch;
|
|
@@ -61,6 +64,7 @@ const CheckoutMiddleware = ({ customerId, onNotAccessible, loader = React.create
|
|
|
61
64
|
/* Navigate to the summary if required */
|
|
62
65
|
if (itemWithoutCustomerDecision === undefined &&
|
|
63
66
|
!(summaryRouteMatchRef.current ||
|
|
67
|
+
summaryTabsRouteMatchRef.current ||
|
|
64
68
|
itemDetailRouteMatchRef.current ||
|
|
65
69
|
checkoutRouteMatchRef.current ||
|
|
66
70
|
checkoutPaymentRouteMatchRef.current)) {
|
|
@@ -9,6 +9,7 @@ import { Routes } from "./routes";
|
|
|
9
9
|
import { BasePathProvider } from "./useBasePath";
|
|
10
10
|
const Item = lazy(() => import("../views/item/Item").then((module) => ({ default: module.Item })));
|
|
11
11
|
const Summary = lazy(() => import("../views/summary/Summary").then((module) => ({ default: module.Summary })));
|
|
12
|
+
const SummaryTabs = lazy(() => import("../views/summaryTabs/SummaryTabs").then((module) => ({ default: module.SummaryTabs })));
|
|
12
13
|
const Checkout = lazy(() => import("../views/checkout/Checkout").then((module) => ({ default: module.Checkout })));
|
|
13
14
|
const Feedback = lazy(() => import("../views/feedback/Feedback").then((module) => ({ default: module.Feedback })));
|
|
14
15
|
const Routing = ({ basePath = "", customer, locale, I18n, layout, getAuthToken, onI18nError, onNotAccessible, onCheckoutSubmitted, useRedirect, useRoutes = reactRouterUseRoutes, }) => {
|
|
@@ -35,7 +36,15 @@ const Routing = ({ basePath = "", customer, locale, I18n, layout, getAuthToken,
|
|
|
35
36
|
{
|
|
36
37
|
path: Routes.SUMMARY,
|
|
37
38
|
element: (React.createElement(Suspense, { fallback: React.createElement(Spinner, null) },
|
|
38
|
-
React.createElement(Summary, { country: customer?.country, customerId: customer?.customerId, layout: layout }
|
|
39
|
+
React.createElement(Summary, { country: customer?.country, customerId: customer?.customerId, layout: layout },
|
|
40
|
+
React.createElement(Outlet, null)))),
|
|
41
|
+
children: [
|
|
42
|
+
{
|
|
43
|
+
path: Routes.SUMMARY_TABS,
|
|
44
|
+
element: (React.createElement(Suspense, { fallback: React.createElement(Spinner, null) },
|
|
45
|
+
React.createElement(SummaryTabs, { country: customer?.country, customerId: customer?.customerId }))),
|
|
46
|
+
},
|
|
47
|
+
],
|
|
39
48
|
},
|
|
40
49
|
{
|
|
41
50
|
path: Routes.CHECKOUT,
|
|
@@ -4,6 +4,7 @@ export var Routes;
|
|
|
4
4
|
Routes["ITEM_DETAIL"] = "item/:id/detail";
|
|
5
5
|
Routes["ITEM"] = "item/:id";
|
|
6
6
|
Routes["SUMMARY"] = "summary";
|
|
7
|
+
Routes["SUMMARY_TABS"] = "tabs/:tab";
|
|
7
8
|
Routes["CHECKOUT"] = "checkout";
|
|
8
9
|
Routes["CHECKOUT_PAYMENT"] = "payment";
|
|
9
10
|
Routes["FEEDBACK"] = "feedback";
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { FC } from "react";
|
|
1
|
+
import { FC, ReactNode } from "react";
|
|
2
2
|
import { Country } from "../../../../projection/shared/country";
|
|
3
3
|
import { Layout } from "../../components/layouts/layout/Layout";
|
|
4
4
|
interface SummaryProps {
|
|
5
5
|
readonly customerId: string;
|
|
6
6
|
readonly country: Country;
|
|
7
7
|
readonly layout: Layout;
|
|
8
|
+
readonly children: ReactNode;
|
|
8
9
|
}
|
|
9
10
|
declare const Summary: FC<SummaryProps>;
|
|
10
11
|
export { Summary };
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { Text } from "@lookiero/aurora";
|
|
2
2
|
import { useI18nMessage } from "@lookiero/i18n-react";
|
|
3
3
|
import { QueryStatus } from "@lookiero/messaging-react";
|
|
4
|
-
import React, { useCallback, useMemo, useState } from "react";
|
|
4
|
+
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|
5
5
|
import { Platform, ScrollView, View } from "react-native";
|
|
6
|
-
import { generatePath, useNavigate } from "react-router-native";
|
|
6
|
+
import { generatePath, useMatch, useNavigate } from "react-router-native";
|
|
7
7
|
import { CheckoutItemStatus } from "../../../../domain/checkoutItem/model/checkoutItem";
|
|
8
8
|
import { useTrackPageView } from "../../../../shared/tracking/infrastructure/useTrackPageView";
|
|
9
9
|
import { useTrackPressContinue } from "../../../../shared/tracking/infrastructure/useTrackPressContinue";
|
|
10
|
-
import { useTrackPressItem } from "../../../../shared/tracking/infrastructure/useTrackPressItem";
|
|
11
10
|
import { useTrackPressPricing } from "../../../../shared/tracking/infrastructure/useTrackPressPricing";
|
|
12
|
-
import { useTrackTabView } from "../../../../shared/tracking/infrastructure/useTrackTabView";
|
|
13
11
|
import { Spinner } from "../../../../shared/ui/components/atoms/spinner/Spinner";
|
|
14
12
|
import { useViewFirstAvailableCheckoutByCustomerId } from "../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId";
|
|
15
13
|
import { useViewFiveItemsDiscountByCustomerId } from "../../../projection/checkout/react/useViewFiveItemsDiscountByCustomerId";
|
|
@@ -21,25 +19,35 @@ import { Routes } from "../../routing/routes";
|
|
|
21
19
|
import { useBasePath } from "../../routing/useBasePath";
|
|
22
20
|
import { theme } from "../../theme/theme";
|
|
23
21
|
import { style } from "./Summary.style";
|
|
24
|
-
import { CheckoutItemsTabs } from "./components/checkoutItemsTabs/CheckoutItemsTabs";
|
|
25
22
|
import { FiveItemsDiscountBanner } from "./components/fiveItemsDiscountBanner/FiveItemsDiscountBanner";
|
|
26
23
|
import { StickyPricing } from "./components/stickyPricing/StickyPricing";
|
|
27
24
|
const { spaceXXL } = theme();
|
|
28
|
-
const Summary = ({ customerId, country, layout: Layout }) => {
|
|
25
|
+
const Summary = ({ customerId, country, layout: Layout, children }) => {
|
|
29
26
|
const titleText = useI18nMessage({ id: I18nMessages.SUMMARY_TITLE });
|
|
30
27
|
const descriptionText = useI18nMessage({ id: I18nMessages.SUMMARY_DESCRIPTION });
|
|
31
28
|
const [pricingHeight, setPricingHeight] = useState(0);
|
|
32
29
|
const handleOnPricingLayout = useCallback(({ height }) => setPricingHeight(height), []);
|
|
33
|
-
const [checkout
|
|
30
|
+
const [checkout] = useViewFirstAvailableCheckoutByCustomerId({ customerId });
|
|
34
31
|
const [pricing, pricingStatus] = useViewPricingByCheckoutId({ checkoutId: checkout?.id });
|
|
35
32
|
const [fiveItemsDiscount = 0, fiveItemsDiscountStatus] = useViewFiveItemsDiscountByCustomerId({ customerId });
|
|
33
|
+
const navigate = useNavigate();
|
|
34
|
+
const basePath = useBasePath();
|
|
35
|
+
const match = useMatch(`${basePath}/${Routes.SUMMARY}/${Routes.SUMMARY_TABS}`);
|
|
36
|
+
const tab = match?.params.tab;
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (!tab) {
|
|
39
|
+
navigate(generatePath(Routes.SUMMARY_TABS, {
|
|
40
|
+
tab: "keep",
|
|
41
|
+
}), {
|
|
42
|
+
replace: true,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}, [navigate, tab]);
|
|
36
46
|
useTrackPageView({
|
|
37
47
|
page: TrackingPage.SUMMARY,
|
|
38
48
|
country,
|
|
39
49
|
checkoutId: checkout?.id,
|
|
40
50
|
});
|
|
41
|
-
const navigate = useNavigate();
|
|
42
|
-
const basePath = useBasePath();
|
|
43
51
|
const trackPressPricing = useTrackPressPricing({
|
|
44
52
|
page: TrackingPage.SUMMARY,
|
|
45
53
|
country,
|
|
@@ -60,29 +68,11 @@ const Summary = ({ customerId, country, layout: Layout }) => {
|
|
|
60
68
|
trackPressContinue();
|
|
61
69
|
navigate(`${basePath}/${Routes.CHECKOUT}`);
|
|
62
70
|
}, [basePath, navigate, trackPressContinue]);
|
|
63
|
-
const
|
|
64
|
-
page: TrackingPage.SUMMARY,
|
|
65
|
-
country,
|
|
66
|
-
checkoutId: checkout?.id,
|
|
67
|
-
});
|
|
68
|
-
const handleOnPressItem = useCallback((checkoutItemId) => {
|
|
69
|
-
trackPressItem({ checkoutItemId });
|
|
70
|
-
navigate(`${basePath}/${generatePath(Routes.ITEM_DETAIL, { id: checkoutItemId })}`);
|
|
71
|
-
}, [basePath, navigate, trackPressItem]);
|
|
72
|
-
const trackTabView = useTrackTabView({
|
|
73
|
-
page: TrackingPage.SUMMARY,
|
|
74
|
-
country,
|
|
75
|
-
checkoutId: checkout?.id,
|
|
76
|
-
});
|
|
77
|
-
const handleOnChangedTab = useCallback((active) => trackTabView({ tab: active === 0 ? "keep" : "return" }), [trackTabView]);
|
|
78
|
-
const checkoutItemsKept = useMemo(() => checkout?.items.filter((checkoutItem) => checkoutItem.status === CheckoutItemStatus.KEPT || checkoutItem.status === CheckoutItemStatus.REPLACED), [checkout?.items]);
|
|
79
|
-
const checkoutItemsReturned = useMemo(() => checkout?.items.filter((checkoutItem) => checkoutItem.status === CheckoutItemStatus.RETURNED || checkoutItem.status === CheckoutItemStatus.REPLACED), [checkout?.items]);
|
|
71
|
+
const totalCheckoutItemsKept = useMemo(() => checkout?.items.filter((checkoutItem) => checkoutItem.status === CheckoutItemStatus.KEPT || checkoutItem.status === CheckoutItemStatus.REPLACED).length || 0, [checkout?.items]);
|
|
80
72
|
const [height, setHeight] = useState(0);
|
|
81
73
|
const handleOnLayout = useCallback((event) => setHeight(event.nativeEvent.layout.height), []);
|
|
82
74
|
const dependenciesLoadedStatuses = [QueryStatus.ERROR, QueryStatus.SUCCESS];
|
|
83
|
-
const dependenciesLoaded = dependenciesLoadedStatuses.includes(
|
|
84
|
-
dependenciesLoadedStatuses.includes(pricingStatus) &&
|
|
85
|
-
dependenciesLoadedStatuses.includes(fiveItemsDiscountStatus);
|
|
75
|
+
const dependenciesLoaded = dependenciesLoadedStatuses.includes(pricingStatus) && dependenciesLoadedStatuses.includes(fiveItemsDiscountStatus);
|
|
86
76
|
if (!dependenciesLoaded)
|
|
87
77
|
return React.createElement(Spinner, null);
|
|
88
78
|
return (React.createElement(Layout, { scrollEnabled: false, style: { scrollView: { height } }, onLayout: handleOnLayout },
|
|
@@ -92,7 +82,7 @@ const Summary = ({ customerId, country, layout: Layout }) => {
|
|
|
92
82
|
React.createElement(View, { style: style.content },
|
|
93
83
|
React.createElement(Text, { level: 3, style: style.title, heading: true }, titleText),
|
|
94
84
|
React.createElement(Text, { level: 3, style: style.description }, descriptionText)),
|
|
95
|
-
|
|
96
|
-
pricing && (React.createElement(StickyPricing, { collapsed: pricingCollapsed, pricing: pricing, totalCheckoutItemsKept:
|
|
85
|
+
children)),
|
|
86
|
+
pricing && (React.createElement(StickyPricing, { collapsed: pricingCollapsed, pricing: pricing, totalCheckoutItemsKept: totalCheckoutItemsKept, onLayout: Platform.OS !== "web" ? handleOnPricingLayout : undefined, onPress: handleOnPressPricing, onSubmit: handleOnSubmit }))));
|
|
97
87
|
};
|
|
98
88
|
export { Summary };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
import { Country } from "../../../../projection/shared/country";
|
|
3
|
+
interface SummaryTabsProps {
|
|
4
|
+
readonly customerId: string;
|
|
5
|
+
readonly country: Country;
|
|
6
|
+
}
|
|
7
|
+
declare const SummaryTabs: FC<SummaryTabsProps>;
|
|
8
|
+
export { SummaryTabs };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { QueryStatus } from "@lookiero/messaging-react";
|
|
2
|
+
import React, { useCallback, useMemo } from "react";
|
|
3
|
+
import { generatePath, useNavigate, useParams } from "react-router-native";
|
|
4
|
+
import { CheckoutItemStatus } from "../../../../domain/checkoutItem/model/checkoutItem";
|
|
5
|
+
import { useTrackPageView } from "../../../../shared/tracking/infrastructure/useTrackPageView";
|
|
6
|
+
import { useTrackPressItem } from "../../../../shared/tracking/infrastructure/useTrackPressItem";
|
|
7
|
+
import { useTrackTabView } from "../../../../shared/tracking/infrastructure/useTrackTabView";
|
|
8
|
+
import { Spinner } from "../../../../shared/ui/components/atoms/spinner/Spinner";
|
|
9
|
+
import { useViewFirstAvailableCheckoutByCustomerId } from "../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId";
|
|
10
|
+
import { TrackingPage } from "../../../tracking/tracking";
|
|
11
|
+
import { Routes } from "../../routing/routes";
|
|
12
|
+
import { useBasePath } from "../../routing/useBasePath";
|
|
13
|
+
import { CheckoutItemsTabs } from "./components/checkoutItemsTabs/CheckoutItemsTabs";
|
|
14
|
+
const TAB_INDEX = {
|
|
15
|
+
keep: 0,
|
|
16
|
+
return: 1,
|
|
17
|
+
};
|
|
18
|
+
const SummaryTabs = ({ country, customerId }) => {
|
|
19
|
+
const { tab } = useParams();
|
|
20
|
+
const tabIndex = useMemo(() => TAB_INDEX[tab] || 0, [tab]);
|
|
21
|
+
const [checkout, checkoutStatus] = useViewFirstAvailableCheckoutByCustomerId({ customerId });
|
|
22
|
+
const navigate = useNavigate();
|
|
23
|
+
const basePath = useBasePath();
|
|
24
|
+
useTrackPageView({
|
|
25
|
+
page: TrackingPage.SUMMARY,
|
|
26
|
+
country,
|
|
27
|
+
checkoutId: checkout?.id,
|
|
28
|
+
});
|
|
29
|
+
const trackPressItem = useTrackPressItem({
|
|
30
|
+
page: TrackingPage.SUMMARY,
|
|
31
|
+
country,
|
|
32
|
+
checkoutId: checkout?.id,
|
|
33
|
+
});
|
|
34
|
+
const handleOnPressItem = useCallback((checkoutItemId) => {
|
|
35
|
+
trackPressItem({ checkoutItemId });
|
|
36
|
+
navigate(`${basePath}/${generatePath(Routes.ITEM_DETAIL, { id: checkoutItemId })}`);
|
|
37
|
+
}, [basePath, navigate, trackPressItem]);
|
|
38
|
+
const trackTabView = useTrackTabView({
|
|
39
|
+
page: TrackingPage.SUMMARY,
|
|
40
|
+
country,
|
|
41
|
+
checkoutId: checkout?.id,
|
|
42
|
+
});
|
|
43
|
+
const handleOnChangedTab = useCallback((active) => trackTabView({ tab: active === 0 ? "keep" : "return" }), [trackTabView]);
|
|
44
|
+
const checkoutItemsKept = useMemo(() => checkout?.items.filter((checkoutItem) => checkoutItem.status === CheckoutItemStatus.KEPT || checkoutItem.status === CheckoutItemStatus.REPLACED), [checkout?.items]);
|
|
45
|
+
const checkoutItemsReturned = useMemo(() => checkout?.items.filter((checkoutItem) => checkoutItem.status === CheckoutItemStatus.RETURNED || checkoutItem.status === CheckoutItemStatus.REPLACED), [checkout?.items]);
|
|
46
|
+
const dependenciesLoadedStatuses = [QueryStatus.ERROR, QueryStatus.SUCCESS];
|
|
47
|
+
const dependenciesLoaded = dependenciesLoadedStatuses.includes(checkoutStatus);
|
|
48
|
+
if (!dependenciesLoaded)
|
|
49
|
+
return React.createElement(Spinner, null);
|
|
50
|
+
return (React.createElement(CheckoutItemsTabs, { checkoutItemsKept: checkoutItemsKept || [], checkoutItemsReturned: checkoutItemsReturned || [], country: country, tabIndex: tabIndex, onChanged: handleOnChangedTab, onPressItem: handleOnPressItem }));
|
|
51
|
+
};
|
|
52
|
+
export { SummaryTabs };
|
|
@@ -2,6 +2,7 @@ import { FC } from "react";
|
|
|
2
2
|
import { CheckoutItemProjection } from "../../../../../../projection/checkoutItem/checkoutItem";
|
|
3
3
|
import { Country } from "../../../../../../projection/shared/country";
|
|
4
4
|
interface CheckoutItemsTabsProps {
|
|
5
|
+
readonly tabIndex: number;
|
|
5
6
|
readonly checkoutItemsKept: CheckoutItemProjection[];
|
|
6
7
|
readonly checkoutItemsReturned: CheckoutItemProjection[];
|
|
7
8
|
readonly country: Country;
|
|
@@ -9,13 +9,13 @@ import { ProductVariant } from "../../../shared/components/productVariant/Produc
|
|
|
9
9
|
import { style } from "./CheckoutItemsTabs.style";
|
|
10
10
|
const CheckoutItem = ({ checkoutItemStatus, checkoutItemProductVariant, checkoutItemPrice, country, discarded = false, testID, style: customStyle, onPress, }) => (React.createElement(View, { style: style.checkoutItem, testID: testID },
|
|
11
11
|
React.createElement(ProductVariant, { brand: checkoutItemProductVariant.brand, color: checkoutItemProductVariant.color, country: country, discarded: discarded, media: checkoutItemProductVariant.media, name: checkoutItemProductVariant.name, price: checkoutItemPrice, size: checkoutItemProductVariant.size, status: checkoutItemStatus, style: customStyle, onPress: onPress })));
|
|
12
|
-
const CheckoutItemsTabs = ({ checkoutItemsKept, checkoutItemsReturned, country, onPressItem, onChanged, }) => {
|
|
12
|
+
const CheckoutItemsTabs = ({ tabIndex, checkoutItemsKept, checkoutItemsReturned, country, onPressItem, onChanged, }) => {
|
|
13
13
|
const keepTabText = useI18nMessage({ id: I18nMessages.SUMMARY_KEEP_TAB });
|
|
14
14
|
const returnTabText = useI18nMessage({ id: I18nMessages.SUMMARY_RETURN_TAB });
|
|
15
15
|
const keepEmptyText = useI18nMessage({ id: I18nMessages.SUMMARY_KEEP_EMPTY });
|
|
16
16
|
const returnEmptyText = useI18nMessage({ id: I18nMessages.SUMMARY_RETURN_EMPTY });
|
|
17
17
|
const handleOnPressItem = useCallback((checkoutItemId) => onPressItem(checkoutItemId), [onPressItem]);
|
|
18
|
-
return (React.createElement(Tabs, { style: { sliderContainer: style.sliderContainer }, tabLabels: [`${keepTabText} (${checkoutItemsKept.length})`, `${returnTabText} (${checkoutItemsReturned.length})`], onChanged: onChanged },
|
|
18
|
+
return (React.createElement(Tabs, { defaultIndex: tabIndex, style: { sliderContainer: style.sliderContainer }, tabLabels: [`${keepTabText} (${checkoutItemsKept.length})`, `${returnTabText} (${checkoutItemsReturned.length})`], onChanged: onChanged },
|
|
19
19
|
React.createElement(React.Fragment, null, checkoutItemsKept.length === 0 ? (React.createElement(Text, { style: style.emptyText }, keepEmptyText)) : (checkoutItemsKept.map((checkoutItem) => (React.createElement(CheckoutItem, { key: checkoutItem.id, checkoutItemPrice: checkoutItem.price, checkoutItemStatus: checkoutItem.status, country: country, testID: "keep-checkout-item", checkoutItemProductVariant: checkoutItem.status === CheckoutItemStatus.REPLACED && checkoutItem.replacedFor
|
|
20
20
|
? checkoutItem.replacedFor
|
|
21
21
|
: checkoutItem.productVariant, onPress: () => handleOnPressItem(checkoutItem.id) }))))),
|
package/package.json
CHANGED
|
File without changes
|