@lookiero/checkout 1.3.0 → 2.0.0-beta.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.
- package/dist/domain/checkoutBooking/event/createNotificationWhenCheckoutBookingExpired.d.ts +6 -0
- package/dist/domain/checkoutBooking/event/createNotificationWhenCheckoutBookingExpired.js +13 -0
- package/dist/infrastructure/domain/uiSetting/react/useIncrementIntroShownCount.d.ts +10 -0
- package/dist/infrastructure/domain/uiSetting/react/useIncrementIntroShownCount.js +11 -0
- package/dist/infrastructure/projection/uiSetting/react/useShouldIntroBeShown.d.ts +6 -0
- package/dist/infrastructure/projection/uiSetting/react/useShouldIntroBeShown.js +7 -0
- package/dist/infrastructure/projection/uiSetting/react/useViewIntroShownCount.d.ts +6 -0
- package/dist/infrastructure/projection/uiSetting/react/useViewIntroShownCount.js +9 -0
- package/dist/infrastructure/ui/Root.d.ts +1 -3
- package/dist/infrastructure/ui/Root.js +2 -2
- package/dist/infrastructure/ui/hooks/usePaymentInstrumentUpdateEvents.d.ts +2 -0
- package/dist/infrastructure/ui/hooks/usePaymentInstrumentUpdateEvents.js +28 -0
- package/dist/infrastructure/ui/routing/Routing.d.ts +1 -3
- package/dist/infrastructure/ui/routing/Routing.js +2 -2
- package/dist/infrastructure/ui/views/App.d.ts +2 -4
- package/dist/infrastructure/ui/views/App.js +2 -2
- package/dist/infrastructure/ui/views/checkout/components/checkoutSuccessModal/CheckoutSuccessModal.d.ts +7 -0
- package/dist/infrastructure/ui/views/checkout/components/checkoutSuccessModal/CheckoutSuccessModal.js +25 -0
- package/dist/infrastructure/ui/views/checkout/components/checkoutSuccessModal/CheckoutSuccessModal.style.d.ts +16 -0
- package/dist/infrastructure/ui/views/checkout/components/checkoutSuccessModal/CheckoutSuccessModal.style.js +19 -0
- package/dist/infrastructure/ui/views/intro/Intro.d.ts +6 -0
- package/dist/infrastructure/ui/views/intro/Intro.js +52 -0
- package/dist/infrastructure/ui/views/intro/Intro.style.d.ts +52 -0
- package/dist/infrastructure/ui/views/intro/Intro.style.js +55 -0
- package/dist/infrastructure/ui/views/item/Item.style.d.ts +15 -0
- package/dist/infrastructure/ui/views/item/Item.style.js +18 -0
- package/dist/infrastructure/ui/views/item/components/productVariant/ProductVariant.d.ts +26 -0
- package/dist/infrastructure/ui/views/item/components/productVariant/ProductVariant.js +44 -0
- package/dist/infrastructure/ui/views/item/components/productVariant/ProductVariant.style.d.ts +45 -0
- package/dist/infrastructure/ui/views/item/components/productVariant/ProductVariant.style.js +53 -0
- package/dist/infrastructure/ui/views/item/components/sizeChangeModal/SizeChangeModal.d.ts +7 -0
- package/dist/infrastructure/ui/views/item/components/sizeChangeModal/SizeChangeModal.js +25 -0
- package/dist/infrastructure/ui/views/item/components/sizeChangeModal/SizeChangeModal.style.d.ts +16 -0
- package/dist/infrastructure/ui/views/item/components/sizeChangeModal/SizeChangeModal.style.js +19 -0
- package/dist/infrastructure/ui/views/navigation/Navigation.d.ts +2 -3
- package/dist/infrastructure/ui/views/navigation/Navigation.js +6 -20
- package/dist/infrastructure/ui/views/navigation/components/header/Header.d.ts +0 -12
- package/dist/infrastructure/ui/views/navigation/components/header/Header.js +2 -5
- package/dist/shared/notifications/domain/notification/command/createNotification.d.ts +17 -0
- package/dist/shared/notifications/domain/notification/command/createNotification.js +7 -0
- package/dist/shared/notifications/infrastructure/domain/notification/react/useCreateNotification.d.ts +21 -0
- package/dist/shared/notifications/infrastructure/domain/notification/react/useCreateNotification.js +22 -0
- package/dist/shared/notifications/infrastructure/ui/components/NotificationItem.d.ts +12 -0
- package/dist/shared/notifications/infrastructure/ui/components/NotificationItem.js +24 -0
- package/dist/shared/notifications/infrastructure/ui/components/Notifications.d.ts +9 -0
- package/dist/shared/notifications/infrastructure/ui/components/Notifications.js +20 -0
- package/dist/shared/notifications/infrastructure/ui/components/Notifications.style.d.ts +36 -0
- package/dist/shared/notifications/infrastructure/ui/components/Notifications.style.js +31 -0
- package/dist/shared/tracking/infrastructure/usePageView.d.ts +13 -0
- package/dist/shared/tracking/infrastructure/usePageView.js +27 -0
- package/dist/shared/ui/components/atoms/error/Error.style.d.ts +6 -0
- package/dist/shared/ui/components/atoms/error/Error.style.js +9 -0
- package/package.json +1 -1
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ProcessManagerFunction } from "@lookiero/messaging";
|
|
2
|
+
import { CheckoutBookingExpired } from "../model/checkoutBookingExpired";
|
|
3
|
+
interface CreateNotificationWhenCheckoutBookingExpiredFunction extends ProcessManagerFunction<CheckoutBookingExpired> {
|
|
4
|
+
}
|
|
5
|
+
declare const createNotificationWhenCheckoutBookingExpired: CreateNotificationWhenCheckoutBookingExpiredFunction;
|
|
6
|
+
export { createNotificationWhenCheckoutBookingExpired };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { v4 as uuid } from "uuid";
|
|
2
|
+
import { I18nMessages } from "../../../infrastructure/ui/i18n/i18n";
|
|
3
|
+
import { createNotification } from "../../../shared/notifications/domain/notification/command/createNotification";
|
|
4
|
+
import { NotificationLevel } from "../../../shared/notifications/domain/notification/model/notification";
|
|
5
|
+
const createNotificationWhenCheckoutBookingExpired = ({ commandBus }) => async () => {
|
|
6
|
+
await commandBus(createNotification({
|
|
7
|
+
aggregateId: uuid(),
|
|
8
|
+
level: NotificationLevel.ERROR,
|
|
9
|
+
titleI18nKey: undefined,
|
|
10
|
+
bodyI18nKey: I18nMessages.CHECKOUT_TOAST_BOOKING_EXPIRED,
|
|
11
|
+
}));
|
|
12
|
+
};
|
|
13
|
+
export { createNotificationWhenCheckoutBookingExpired };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CommandStatus } from "@lookiero/messaging-react";
|
|
2
|
+
interface IncrementIntroShownCountFunction {
|
|
3
|
+
(): Promise<void>;
|
|
4
|
+
}
|
|
5
|
+
type UseIncrementIntroShownCount = [incrementIntroShownCount: IncrementIntroShownCountFunction, status: CommandStatus];
|
|
6
|
+
interface UseIncrementIntroShownCountFunction {
|
|
7
|
+
(): UseIncrementIntroShownCount;
|
|
8
|
+
}
|
|
9
|
+
declare const useIncrementIntroShownCount: UseIncrementIntroShownCountFunction;
|
|
10
|
+
export { useIncrementIntroShownCount };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useViewIntroShownCount } from "../../../projection/uiSetting/react/useViewIntroShownCount";
|
|
3
|
+
import { UISettings } from "../../../ui/settings/UISettings";
|
|
4
|
+
import { useUpdateUiSetting } from "./useUpdateUiSetting";
|
|
5
|
+
const useIncrementIntroShownCount = () => {
|
|
6
|
+
const [update, status] = useUpdateUiSetting();
|
|
7
|
+
const [introShownCount] = useViewIntroShownCount();
|
|
8
|
+
const incrementIntroShownCount = useCallback(() => update({ key: UISettings.INTRO_SHOWN_COUNT, value: introShownCount + 1 }), [introShownCount, update]);
|
|
9
|
+
return [incrementIntroShownCount, status];
|
|
10
|
+
};
|
|
11
|
+
export { useIncrementIntroShownCount };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { useViewIntroShownCount } from "./useViewIntroShownCount";
|
|
2
|
+
const MAX_INTRO_SHOWN_COUNT = 2;
|
|
3
|
+
const useShouldIntroBeShown = () => {
|
|
4
|
+
const [introShownCount, introShownCountStatus] = useViewIntroShownCount();
|
|
5
|
+
return [introShownCount < MAX_INTRO_SHOWN_COUNT, introShownCountStatus];
|
|
6
|
+
};
|
|
7
|
+
export { useShouldIntroBeShown };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { UISettings } from "../../../ui/settings/UISettings";
|
|
2
|
+
import { useViewUiSettingByKey } from "./useViewUiSettingByKey";
|
|
3
|
+
const useViewIntroShownCount = () => {
|
|
4
|
+
const [introShownCount, introShownCountStatus] = useViewUiSettingByKey({
|
|
5
|
+
key: UISettings.INTRO_SHOWN_COUNT,
|
|
6
|
+
});
|
|
7
|
+
return [introShownCount?.value || 0, introShownCountStatus];
|
|
8
|
+
};
|
|
9
|
+
export { useViewIntroShownCount };
|
|
@@ -4,7 +4,6 @@ import { ComponentType } from "react";
|
|
|
4
4
|
import { useRoutes as reactRouterUseRoutes } from "react-router-native";
|
|
5
5
|
import { Customer } from "../../projection/shared/customer";
|
|
6
6
|
import { SentryEnvironment } from "../../shared/logging/SentryLogger";
|
|
7
|
-
import { Menu, TabBar } from "./views/navigation/Navigation";
|
|
8
7
|
interface RootFunctionArgs {
|
|
9
8
|
readonly Messaging: MessagingRoot;
|
|
10
9
|
readonly I18n: I18n;
|
|
@@ -19,9 +18,8 @@ interface RootProps {
|
|
|
19
18
|
readonly basePath: string;
|
|
20
19
|
readonly locale?: string;
|
|
21
20
|
readonly customer: Customer | undefined;
|
|
22
|
-
readonly menu: Menu;
|
|
23
|
-
readonly tabBar: TabBar;
|
|
24
21
|
readonly onNotAccessible: () => void;
|
|
22
|
+
readonly onOpenMenu: () => void;
|
|
25
23
|
readonly useRedirect: () => Record<string, string>;
|
|
26
24
|
readonly useRoutes?: typeof reactRouterUseRoutes;
|
|
27
25
|
}
|
|
@@ -6,10 +6,10 @@ import { sentryLoggerHOC } from "../../shared/logging/SentryLoggerHOC";
|
|
|
6
6
|
import { Routing } from "./routing/Routing";
|
|
7
7
|
const root = ({ Messaging, I18n, getAuthToken, development, sentry }) => {
|
|
8
8
|
// eslint-disable-next-line react/display-name, react/prop-types
|
|
9
|
-
const Root = ({ basePath, locale = "en", customer,
|
|
9
|
+
const Root = ({ basePath, locale = "en", customer, onNotAccessible, onOpenMenu, useRedirect, useRoutes = reactRouterUseRoutes, }) => {
|
|
10
10
|
const handleOnI18nError = useCallback(() => void 0, []);
|
|
11
11
|
return (React.createElement(Messaging, { includeReactQueryDevTools: Platform.OS === "web" },
|
|
12
|
-
React.createElement(Routing, { I18n: I18n, basePath: basePath, customer: customer, getAuthToken: getAuthToken, locale: locale,
|
|
12
|
+
React.createElement(Routing, { I18n: I18n, basePath: basePath, customer: customer, getAuthToken: getAuthToken, locale: locale, useRedirect: useRedirect, useRoutes: useRoutes, onI18nError: development ? undefined : handleOnI18nError, onNotAccessible: onNotAccessible, onOpenMenu: onOpenMenu })));
|
|
13
13
|
};
|
|
14
14
|
return sentryLoggerHOC({ logger: sentryLogger(sentry) })(Root);
|
|
15
15
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useEvent } from "@lookiero/event";
|
|
2
|
+
import { useCallback, useEffect } from "react";
|
|
3
|
+
import { useCreateNotification } from "../../../shared/notifications";
|
|
4
|
+
import { MESSAGING_CONTEXT_ID } from "../../delivery/baseBootstrap";
|
|
5
|
+
import { I18nMessages } from "../i18n/i18n";
|
|
6
|
+
import { useToast } from "./useToast";
|
|
7
|
+
const PAYMENT_ERROR = "ERROR";
|
|
8
|
+
const PAYMENT_SUCCESS = "PAYMENT_INSTRUMENT_UPDATED";
|
|
9
|
+
const usePaymentInstrumentUpdateEvents = () => {
|
|
10
|
+
const { subscribe, unsubscribe } = useEvent();
|
|
11
|
+
const { showError, showSuccess } = useToast();
|
|
12
|
+
const [createNotification] = useCreateNotification({ contextId: MESSAGING_CONTEXT_ID });
|
|
13
|
+
const onSuccess = useCallback(({ message }) => showSuccess({ message: message.id }), [showSuccess]);
|
|
14
|
+
const onError = useCallback(({ error }) => {
|
|
15
|
+
if (error.toaster) {
|
|
16
|
+
showError({ message: error.toaster.id || I18nMessages.CHECKOUT_TOAST_PAYMENT_ERROR });
|
|
17
|
+
}
|
|
18
|
+
}, [showError]);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
subscribe({ event: PAYMENT_ERROR }, onError);
|
|
21
|
+
subscribe({ event: PAYMENT_SUCCESS }, onSuccess);
|
|
22
|
+
return () => {
|
|
23
|
+
unsubscribe({ event: PAYMENT_ERROR }, onError);
|
|
24
|
+
unsubscribe({ event: PAYMENT_SUCCESS }, onSuccess);
|
|
25
|
+
};
|
|
26
|
+
}, [subscribe, unsubscribe, createNotification, onError, onSuccess]);
|
|
27
|
+
};
|
|
28
|
+
export { usePaymentInstrumentUpdateEvents };
|
|
@@ -2,16 +2,14 @@ import { I18n } from "@lookiero/i18n-react";
|
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { useRoutes as reactRouterUseRoutes } from "react-router-native";
|
|
4
4
|
import { Customer } from "../../../projection/shared/customer";
|
|
5
|
-
import { Menu, TabBar } from "../views/navigation/Navigation";
|
|
6
5
|
interface RoutingProps {
|
|
7
6
|
readonly basePath?: string;
|
|
8
7
|
readonly customer: Customer | undefined;
|
|
9
8
|
readonly locale: string;
|
|
10
9
|
readonly I18n: I18n;
|
|
11
|
-
readonly menu: Menu;
|
|
12
|
-
readonly tabBar: TabBar;
|
|
13
10
|
readonly getAuthToken: () => Promise<string>;
|
|
14
11
|
readonly onNotAccessible: () => void;
|
|
12
|
+
readonly onOpenMenu: () => void;
|
|
15
13
|
readonly onI18nError?: (err: Error) => void;
|
|
16
14
|
readonly useRedirect: () => Record<string, string>;
|
|
17
15
|
readonly useRoutes: typeof reactRouterUseRoutes;
|
|
@@ -11,7 +11,7 @@ const Item = lazy(() => import("../views/item/Item").then((module) => ({ default
|
|
|
11
11
|
const Summary = lazy(() => import("../views/summary/Summary").then((module) => ({ default: module.Summary })));
|
|
12
12
|
const Checkout = lazy(() => import("../views/checkout/Checkout").then((module) => ({ default: module.Checkout })));
|
|
13
13
|
const Feedback = lazy(() => import("../views/feedback/Feedback").then((module) => ({ default: module.Feedback })));
|
|
14
|
-
const Routing = ({ basePath = "", customer, locale, I18n,
|
|
14
|
+
const Routing = ({ basePath = "", customer, locale, I18n, getAuthToken, onI18nError, onNotAccessible, onOpenMenu, useRedirect, useRoutes = reactRouterUseRoutes, }) => {
|
|
15
15
|
return useRoutes([
|
|
16
16
|
{
|
|
17
17
|
path: "",
|
|
@@ -19,7 +19,7 @@ const Routing = ({ basePath = "", customer, locale, I18n, menu, tabBar, getAuthT
|
|
|
19
19
|
React.createElement(CheckoutAccessibilityMiddleware, { customerId: customer?.customerId, onNotAccessible: onNotAccessible },
|
|
20
20
|
React.createElement(I18n, { loader: React.createElement(Spinner, null), locale: locale, onError: onI18nError },
|
|
21
21
|
React.createElement(CheckoutMiddleware, { customerId: customer?.customerId },
|
|
22
|
-
React.createElement(App, { country: customer?.country, customerId: customer?.customerId,
|
|
22
|
+
React.createElement(App, { country: customer?.country, customerId: customer?.customerId, onOpenMenu: onOpenMenu },
|
|
23
23
|
React.createElement(Outlet, null))))))),
|
|
24
24
|
children: [
|
|
25
25
|
{
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { FC } from "react";
|
|
2
2
|
import { Country } from "../../../projection/shared/country";
|
|
3
|
-
import { Menu, TabBar } from "./navigation/Navigation";
|
|
4
3
|
interface AppProps {
|
|
4
|
+
readonly children: JSX.Element;
|
|
5
5
|
readonly customerId: string;
|
|
6
6
|
readonly country: Country;
|
|
7
|
-
readonly
|
|
8
|
-
readonly tabBar: TabBar;
|
|
9
|
-
readonly children: JSX.Element;
|
|
7
|
+
readonly onOpenMenu: () => void;
|
|
10
8
|
}
|
|
11
9
|
declare const App: FC<AppProps>;
|
|
12
10
|
export { App };
|
|
@@ -7,9 +7,9 @@ import { MESSAGING_CONTEXT_ID } from "../../delivery/baseBootstrap";
|
|
|
7
7
|
import { theme } from "../theme/theme";
|
|
8
8
|
import { Navigation } from "./navigation/Navigation";
|
|
9
9
|
const { colorBase } = theme();
|
|
10
|
-
const App = ({ customerId, country,
|
|
10
|
+
const App = ({ children, customerId, country, onOpenMenu }) => (React.createElement(SafeAreaProvider, null,
|
|
11
11
|
React.createElement(PortalProvider, null,
|
|
12
12
|
React.createElement(StatusBar, { backgroundColor: colorBase, barStyle: "dark-content", translucent: true }),
|
|
13
13
|
React.createElement(Notifications, { contextId: MESSAGING_CONTEXT_ID }),
|
|
14
|
-
React.createElement(Navigation, { country: country, customerId: customerId,
|
|
14
|
+
React.createElement(Navigation, { country: country, customerId: customerId, onOpenMenu: onOpenMenu }, children))));
|
|
15
15
|
export { App };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Button } from "@lookiero/aurora-next/build/components/atoms/Button/Button";
|
|
2
|
+
import { Text } from "@lookiero/aurora-next/build/components/primitives/Text/Text";
|
|
3
|
+
import { useI18nMessage } from "@lookiero/i18n-react";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { View } from "react-native";
|
|
6
|
+
import { Modal } from "../../../../../../shared/ui/components/layouts/modal/Modal";
|
|
7
|
+
import { I18nMessages } from "../../../../i18n/i18n";
|
|
8
|
+
import { style } from "./CheckoutSuccessModal.style";
|
|
9
|
+
const CheckoutSuccessModal = ({ visible, onDismiss }) => {
|
|
10
|
+
const titleText = useI18nMessage({ id: I18nMessages.CHECKOUT_SUCCESS_MODAL_TITLE });
|
|
11
|
+
const descriptionText = useI18nMessage({ id: I18nMessages.CHECKOUT_SUCCESS_MODAL_DESCRIPTION });
|
|
12
|
+
const buttonText = useI18nMessage({ id: I18nMessages.CHECKOUT_SUCCESS_MODAL_BUTTON });
|
|
13
|
+
return (React.createElement(Modal, { visible: visible, onClose: onDismiss },
|
|
14
|
+
React.createElement(View, { style: style.modal },
|
|
15
|
+
React.createElement(Text, { level: 1, style: style.modalTitle, body: true }, titleText),
|
|
16
|
+
React.createElement(Text, { level: 3, style: style.modalDescription, body: true }, descriptionText),
|
|
17
|
+
React.createElement(Button
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
, {
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
22
|
+
// @ts-ignore
|
|
23
|
+
style: style.button, onPress: onDismiss }, buttonText))));
|
|
24
|
+
};
|
|
25
|
+
export { CheckoutSuccessModal };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { StyleSheet } from "react-native";
|
|
2
|
+
import { theme } from "../../../../theme/theme";
|
|
3
|
+
const { spaceXL } = theme();
|
|
4
|
+
const style = StyleSheet.create({
|
|
5
|
+
button: {
|
|
6
|
+
flex: 0,
|
|
7
|
+
},
|
|
8
|
+
modal: {
|
|
9
|
+
padding: spaceXL,
|
|
10
|
+
},
|
|
11
|
+
modalDescription: {
|
|
12
|
+
marginVertical: spaceXL,
|
|
13
|
+
textAlign: "center",
|
|
14
|
+
},
|
|
15
|
+
modalTitle: {
|
|
16
|
+
textAlign: "center",
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
export { style };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Button } from "@lookiero/aurora-next/build/components/atoms/Button/Button";
|
|
2
|
+
import { Icon } from "@lookiero/aurora-next/build/components/primitives/Icon/Icon";
|
|
3
|
+
import { Text } from "@lookiero/aurora-next/build/components/primitives/Text/Text";
|
|
4
|
+
import { useI18nMessage } from "@lookiero/i18n-react";
|
|
5
|
+
import { CommandStatus, QueryStatus } from "@lookiero/messaging-react";
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { Platform, View } from "react-native";
|
|
8
|
+
import { Spinner } from "../../../../shared/ui/components/atoms/spinner/Spinner";
|
|
9
|
+
import { useIncrementIntroShownCount } from "../../../domain/uiSetting/react/useIncrementIntroShownCount";
|
|
10
|
+
import { useViewFiveItemsDiscountByCustomerId } from "../../../projection/checkout/react/useViewFiveItemsDiscountByCustomerId";
|
|
11
|
+
import { Body } from "../../components/layouts/body/Body";
|
|
12
|
+
import { SafeAreaScrollView } from "../../components/templates/SafeAreaScrollView";
|
|
13
|
+
import { I18nMessages } from "../../i18n/i18n";
|
|
14
|
+
import { HEADER_HEIGHT } from "../navigation/components/header/Header.style";
|
|
15
|
+
import { style } from "./Intro.style";
|
|
16
|
+
const Bullet = ({ text }) => (React.createElement(View, { style: style.bullet },
|
|
17
|
+
React.createElement(Icon, { name: "rounded-tick", style: style.tickIcon }),
|
|
18
|
+
React.createElement(Text, { level: 2, detail: true }, text)));
|
|
19
|
+
const Intro = ({ customerId }) => {
|
|
20
|
+
const titleText = useI18nMessage({ id: I18nMessages.INTRO_TITLE });
|
|
21
|
+
const subTitleText = useI18nMessage({ id: I18nMessages.INTRO_SUBTITLE });
|
|
22
|
+
const discountText = useI18nMessage({ id: I18nMessages.INTRO_DISCOUNT });
|
|
23
|
+
const bulletOneText = useI18nMessage({ id: I18nMessages.INTRO_BULLET_ONE });
|
|
24
|
+
const bulletTwoText = useI18nMessage({ id: I18nMessages.INTRO_BULLET_TWO });
|
|
25
|
+
const bulletThreeText = useI18nMessage({ id: I18nMessages.INTRO_BULLET_THREE });
|
|
26
|
+
const buttonText = useI18nMessage({ id: I18nMessages.INTRO_BUTTON });
|
|
27
|
+
const [fiveItemsDiscount, fiveItemsDiscountStatus] = useViewFiveItemsDiscountByCustomerId({
|
|
28
|
+
customerId,
|
|
29
|
+
});
|
|
30
|
+
const [incrementIntroShownCount, incrementIntroShownCountStatus] = useIncrementIntroShownCount();
|
|
31
|
+
if (fiveItemsDiscountStatus === QueryStatus.LOADING)
|
|
32
|
+
return React.createElement(Spinner, null);
|
|
33
|
+
return (React.createElement(SafeAreaScrollView, { scrollerPaddingTop: Platform.OS === "web" || Platform.OS === "android" ? HEADER_HEIGHT : 0 },
|
|
34
|
+
React.createElement(Body, { style: { column: style.bodyColumn } },
|
|
35
|
+
React.createElement(View, { style: style.intro },
|
|
36
|
+
React.createElement(View, { style: style.content },
|
|
37
|
+
React.createElement(Text, { level: 3, style: style.title, heading: true }, titleText),
|
|
38
|
+
React.createElement(Text, { level: 3, style: style.subtitle, body: true }, subTitleText),
|
|
39
|
+
fiveItemsDiscount !== 0 && (React.createElement(View, { style: style.discountContainer },
|
|
40
|
+
React.createElement(View, { style: style.discount },
|
|
41
|
+
React.createElement(Text, { level: 2, heading: true },
|
|
42
|
+
"- ",
|
|
43
|
+
fiveItemsDiscount),
|
|
44
|
+
React.createElement(Text, { level: 3, style: style.percentage, heading: true }, "%")),
|
|
45
|
+
React.createElement(Text, { level: 3, body: true }, discountText))),
|
|
46
|
+
React.createElement(View, { style: style.description },
|
|
47
|
+
React.createElement(Bullet, { text: bulletOneText }),
|
|
48
|
+
React.createElement(Bullet, { text: bulletTwoText }),
|
|
49
|
+
React.createElement(Bullet, { text: bulletThreeText }))),
|
|
50
|
+
React.createElement(Button, { disabled: incrementIntroShownCountStatus === CommandStatus.LOADING, onPress: incrementIntroShownCount }, buttonText)))));
|
|
51
|
+
};
|
|
52
|
+
export { Intro };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
declare const style: {
|
|
2
|
+
bodyColumn: {
|
|
3
|
+
paddingHorizontal: number;
|
|
4
|
+
};
|
|
5
|
+
bullet: {
|
|
6
|
+
alignItems: "center";
|
|
7
|
+
flexDirection: "row";
|
|
8
|
+
marginVertical: number;
|
|
9
|
+
};
|
|
10
|
+
content: {
|
|
11
|
+
alignItems: "center";
|
|
12
|
+
};
|
|
13
|
+
description: {
|
|
14
|
+
borderWidth: number;
|
|
15
|
+
marginBottom: number;
|
|
16
|
+
padding: number;
|
|
17
|
+
width: string;
|
|
18
|
+
};
|
|
19
|
+
discount: {
|
|
20
|
+
alignItems: "flex-end";
|
|
21
|
+
flexDirection: "row";
|
|
22
|
+
justifyContent: "center";
|
|
23
|
+
marginBottom: number;
|
|
24
|
+
};
|
|
25
|
+
discountContainer: {
|
|
26
|
+
alignItems: "center";
|
|
27
|
+
backgroundColor: string;
|
|
28
|
+
marginBottom: number;
|
|
29
|
+
padding: number;
|
|
30
|
+
textAlign: "center";
|
|
31
|
+
width: string;
|
|
32
|
+
};
|
|
33
|
+
intro: {
|
|
34
|
+
flex: number;
|
|
35
|
+
justifyContent: "space-between";
|
|
36
|
+
};
|
|
37
|
+
percentage: {
|
|
38
|
+
marginLeft: number;
|
|
39
|
+
};
|
|
40
|
+
subtitle: {
|
|
41
|
+
marginBottom: number;
|
|
42
|
+
textAlign: "center";
|
|
43
|
+
};
|
|
44
|
+
tickIcon: {
|
|
45
|
+
marginRight: number;
|
|
46
|
+
};
|
|
47
|
+
title: {
|
|
48
|
+
marginBottom: number;
|
|
49
|
+
textAlign: "center";
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
export { style };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { StyleSheet } from "react-native";
|
|
2
|
+
import { theme } from "../../theme/theme";
|
|
3
|
+
const { borderSize, colorAccent, spaceXS, spaceS, spaceM, spaceL, spaceXL } = theme();
|
|
4
|
+
const style = StyleSheet.create({
|
|
5
|
+
bodyColumn: {
|
|
6
|
+
paddingHorizontal: spaceXL,
|
|
7
|
+
},
|
|
8
|
+
bullet: {
|
|
9
|
+
alignItems: "center",
|
|
10
|
+
flexDirection: "row",
|
|
11
|
+
marginVertical: spaceS,
|
|
12
|
+
},
|
|
13
|
+
content: {
|
|
14
|
+
alignItems: "center",
|
|
15
|
+
},
|
|
16
|
+
description: {
|
|
17
|
+
borderWidth: borderSize,
|
|
18
|
+
marginBottom: spaceXL,
|
|
19
|
+
padding: spaceM,
|
|
20
|
+
width: "100%",
|
|
21
|
+
},
|
|
22
|
+
discount: {
|
|
23
|
+
alignItems: "flex-end",
|
|
24
|
+
flexDirection: "row",
|
|
25
|
+
justifyContent: "center",
|
|
26
|
+
marginBottom: spaceS,
|
|
27
|
+
},
|
|
28
|
+
discountContainer: {
|
|
29
|
+
alignItems: "center",
|
|
30
|
+
backgroundColor: colorAccent,
|
|
31
|
+
marginBottom: spaceL,
|
|
32
|
+
padding: spaceM,
|
|
33
|
+
textAlign: "center",
|
|
34
|
+
width: "100%",
|
|
35
|
+
},
|
|
36
|
+
intro: {
|
|
37
|
+
flex: 1,
|
|
38
|
+
justifyContent: "space-between",
|
|
39
|
+
},
|
|
40
|
+
percentage: {
|
|
41
|
+
marginLeft: spaceXS,
|
|
42
|
+
},
|
|
43
|
+
subtitle: {
|
|
44
|
+
marginBottom: spaceXL,
|
|
45
|
+
textAlign: "center",
|
|
46
|
+
},
|
|
47
|
+
tickIcon: {
|
|
48
|
+
marginRight: spaceS,
|
|
49
|
+
},
|
|
50
|
+
title: {
|
|
51
|
+
marginBottom: spaceM,
|
|
52
|
+
textAlign: "center",
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
export { style };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare const style: {
|
|
2
|
+
content: {
|
|
3
|
+
paddingHorizontal: number;
|
|
4
|
+
};
|
|
5
|
+
feedbackContainer: {
|
|
6
|
+
borderTopColor: string;
|
|
7
|
+
borderTopWidth: number;
|
|
8
|
+
marginVertical: number;
|
|
9
|
+
};
|
|
10
|
+
sliderContainer: {
|
|
11
|
+
marginBottom: number;
|
|
12
|
+
minHeight: number;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export { style };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { StyleSheet } from "react-native";
|
|
2
|
+
import { theme } from "../../theme/theme";
|
|
3
|
+
const { borderSize, colorGrayscaleM, spaceXL } = theme();
|
|
4
|
+
const style = StyleSheet.create({
|
|
5
|
+
content: {
|
|
6
|
+
paddingHorizontal: spaceXL,
|
|
7
|
+
},
|
|
8
|
+
feedbackContainer: {
|
|
9
|
+
borderTopColor: colorGrayscaleM,
|
|
10
|
+
borderTopWidth: borderSize,
|
|
11
|
+
marginVertical: spaceXL,
|
|
12
|
+
},
|
|
13
|
+
sliderContainer: {
|
|
14
|
+
marginBottom: spaceXL,
|
|
15
|
+
minHeight: 500,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
export { style };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
import { ImageStyle, StyleProp } from "react-native";
|
|
3
|
+
import { CheckoutItemStatus } from "../../../../../../domain/checkoutItem/model/checkoutItem";
|
|
4
|
+
import { ColorProjection, MediaProjection } from "../../../../../../projection/checkoutItem/checkoutItem";
|
|
5
|
+
import { Country } from "../../../../../../projection/shared/country";
|
|
6
|
+
import { PriceProjection } from "../../../../../../projection/shared/price";
|
|
7
|
+
import { SizeProjection } from "../../../../../../projection/shared/size";
|
|
8
|
+
interface ProductVariantStyle {
|
|
9
|
+
readonly image: StyleProp<ImageStyle>;
|
|
10
|
+
}
|
|
11
|
+
interface ProductVariantProps {
|
|
12
|
+
readonly media: MediaProjection[];
|
|
13
|
+
readonly brand: string;
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly price: PriceProjection;
|
|
16
|
+
readonly size: SizeProjection;
|
|
17
|
+
readonly color: ColorProjection;
|
|
18
|
+
readonly status?: CheckoutItemStatus;
|
|
19
|
+
readonly discarded?: boolean;
|
|
20
|
+
readonly country: Country;
|
|
21
|
+
readonly style?: Partial<ProductVariantStyle>;
|
|
22
|
+
readonly onPress?: () => void;
|
|
23
|
+
}
|
|
24
|
+
declare const ProductVariant: FC<ProductVariantProps>;
|
|
25
|
+
export type { ProductVariantStyle };
|
|
26
|
+
export { ProductVariant };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Text } from "@lookiero/aurora-next/build/components/primitives/Text/Text";
|
|
2
|
+
import { useI18nMessage } from "@lookiero/i18n-react";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Image, Pressable, View } from "react-native";
|
|
5
|
+
import Svg, { Line } from "react-native-svg";
|
|
6
|
+
import { CheckoutItemStatus } from "../../../../../../domain/checkoutItem/model/checkoutItem";
|
|
7
|
+
import { size } from "../../../../../../projection/shared/size";
|
|
8
|
+
import { Icon } from "../../../../../../shared/ui/components/atoms/icon/Icon";
|
|
9
|
+
import { Price } from "../../../../components/atoms/price/Price";
|
|
10
|
+
import { useMediaImage } from "../../../../hooks/useMediaImage";
|
|
11
|
+
import { COLOR_I18N_PREFIX, I18nMessages } from "../../../../i18n/i18n";
|
|
12
|
+
import { theme } from "../../../../theme/theme";
|
|
13
|
+
import { IMAGE_WIDTH, style } from "./ProductVariant.style";
|
|
14
|
+
const { colorContent } = theme();
|
|
15
|
+
const ProductVariant = ({ media, brand, name, price, size: sizeProjection, color, status, discarded, country, style: customStyle, onPress, }) => {
|
|
16
|
+
const uniqueText = useI18nMessage({ id: I18nMessages.ITEM_UNIQUE });
|
|
17
|
+
const colorLabel = useI18nMessage({ id: color.label, prefix: COLOR_I18N_PREFIX });
|
|
18
|
+
const sizeChangeText = useI18nMessage({ id: I18nMessages.PRODUCT_VARIANT_SIZE_CHANGE });
|
|
19
|
+
const cdnImageUrl = useMediaImage();
|
|
20
|
+
return (React.createElement(Pressable, { pointerEvents: onPress ? "auto" : "none", style: style.container, testID: "product-variant", onPress: onPress },
|
|
21
|
+
React.createElement(View, { style: style.row },
|
|
22
|
+
React.createElement(View, null,
|
|
23
|
+
React.createElement(Image, { resizeMode: "contain", style: [style.media, customStyle?.image], testID: "product-variant-media", source: {
|
|
24
|
+
uri: cdnImageUrl({
|
|
25
|
+
url: media[0]?.url,
|
|
26
|
+
width: IMAGE_WIDTH,
|
|
27
|
+
}),
|
|
28
|
+
} }),
|
|
29
|
+
discarded && (React.createElement(Svg, { preserveAspectRatio: "none", stroke: colorContent, style: style.discarded, testID: "discarded", viewBox: "0 0 100 100" },
|
|
30
|
+
React.createElement(Line, { vectorEffect: "non-scaling-stroke", x1: "100", x2: "0", y1: "0", y2: "100" })))),
|
|
31
|
+
React.createElement(View, { style: style.descriptionContainer },
|
|
32
|
+
React.createElement(View, null,
|
|
33
|
+
React.createElement(Text, { level: 2, style: style.brand, detail: true }, brand),
|
|
34
|
+
React.createElement(Text, { level: 2, detail: true }, name),
|
|
35
|
+
React.createElement(Text, { level: 2, style: style.priceAndColor, detail: true },
|
|
36
|
+
sizeProjection.unique ? uniqueText : size({ size: sizeProjection, country }),
|
|
37
|
+
" / ",
|
|
38
|
+
colorLabel),
|
|
39
|
+
status === CheckoutItemStatus.REPLACED && (React.createElement(View, { style: style.sizeChange },
|
|
40
|
+
React.createElement(Text, { level: 3, style: style.sizeChangeText, detail: true }, sizeChangeText)))),
|
|
41
|
+
React.createElement(Price, { price: price, variant: "detail" }))),
|
|
42
|
+
onPress && React.createElement(Icon, { name: "arrow-right" })));
|
|
43
|
+
};
|
|
44
|
+
export { ProductVariant };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
declare const IMAGE_WIDTH = 96;
|
|
2
|
+
declare const style: {
|
|
3
|
+
brand: {
|
|
4
|
+
color: string;
|
|
5
|
+
};
|
|
6
|
+
container: {
|
|
7
|
+
flexDirection: "row";
|
|
8
|
+
justifyContent: "space-between";
|
|
9
|
+
};
|
|
10
|
+
descriptionContainer: {
|
|
11
|
+
justifyContent: "space-between";
|
|
12
|
+
marginLeft: number;
|
|
13
|
+
};
|
|
14
|
+
discarded: {
|
|
15
|
+
height: string;
|
|
16
|
+
left: number;
|
|
17
|
+
position: "absolute";
|
|
18
|
+
top: number;
|
|
19
|
+
width: string;
|
|
20
|
+
zIndex: number;
|
|
21
|
+
};
|
|
22
|
+
media: {
|
|
23
|
+
borderColor: string;
|
|
24
|
+
borderWidth: number;
|
|
25
|
+
height: number;
|
|
26
|
+
width: number;
|
|
27
|
+
};
|
|
28
|
+
priceAndColor: {
|
|
29
|
+
color: string;
|
|
30
|
+
};
|
|
31
|
+
row: {
|
|
32
|
+
flexDirection: "row";
|
|
33
|
+
};
|
|
34
|
+
sizeChange: {
|
|
35
|
+
width?: string | undefined;
|
|
36
|
+
backgroundColor: string;
|
|
37
|
+
marginTop: number;
|
|
38
|
+
paddingHorizontal: number;
|
|
39
|
+
paddingVertical: number;
|
|
40
|
+
};
|
|
41
|
+
sizeChangeText: {
|
|
42
|
+
textTransform: "uppercase";
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
export { style, IMAGE_WIDTH };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Platform, StyleSheet } from "react-native";
|
|
2
|
+
import { theme } from "../../../../theme/theme";
|
|
3
|
+
const { borderSize, colorGrayscaleL, colorInfo, spaceXS, spaceS, spaceM } = theme();
|
|
4
|
+
const IMAGE_WIDTH = 96;
|
|
5
|
+
const IMAGE_HEIGHT = 120;
|
|
6
|
+
const style = StyleSheet.create({
|
|
7
|
+
brand: {
|
|
8
|
+
color: colorGrayscaleL,
|
|
9
|
+
},
|
|
10
|
+
container: {
|
|
11
|
+
flexDirection: "row",
|
|
12
|
+
justifyContent: "space-between",
|
|
13
|
+
},
|
|
14
|
+
descriptionContainer: {
|
|
15
|
+
justifyContent: "space-between",
|
|
16
|
+
marginLeft: spaceS,
|
|
17
|
+
},
|
|
18
|
+
discarded: {
|
|
19
|
+
height: "100%",
|
|
20
|
+
left: 0,
|
|
21
|
+
position: "absolute",
|
|
22
|
+
top: 0,
|
|
23
|
+
width: "100%",
|
|
24
|
+
zIndex: 10,
|
|
25
|
+
},
|
|
26
|
+
media: {
|
|
27
|
+
borderColor: colorGrayscaleL,
|
|
28
|
+
borderWidth: borderSize,
|
|
29
|
+
height: IMAGE_HEIGHT,
|
|
30
|
+
width: IMAGE_WIDTH,
|
|
31
|
+
},
|
|
32
|
+
priceAndColor: {
|
|
33
|
+
color: colorGrayscaleL,
|
|
34
|
+
},
|
|
35
|
+
row: {
|
|
36
|
+
flexDirection: "row",
|
|
37
|
+
},
|
|
38
|
+
sizeChange: {
|
|
39
|
+
backgroundColor: colorInfo,
|
|
40
|
+
marginTop: spaceM,
|
|
41
|
+
paddingHorizontal: spaceS,
|
|
42
|
+
paddingVertical: spaceXS,
|
|
43
|
+
...Platform.select({
|
|
44
|
+
web: {
|
|
45
|
+
width: "fit-content",
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
sizeChangeText: {
|
|
50
|
+
textTransform: "uppercase",
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
export { style, IMAGE_WIDTH };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Button } from "@lookiero/aurora-next/build/components/atoms/Button/Button";
|
|
2
|
+
import { Text } from "@lookiero/aurora-next/build/components/primitives/Text/Text";
|
|
3
|
+
import { useI18nMessage } from "@lookiero/i18n-react";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { View } from "react-native";
|
|
6
|
+
import { Modal } from "../../../../../../shared/ui/components/layouts/modal/Modal";
|
|
7
|
+
import { I18nMessages } from "../../../../i18n/i18n";
|
|
8
|
+
import { style } from "./SizeChangeModal.style";
|
|
9
|
+
const SizeChangeModal = ({ visible, onDismiss }) => {
|
|
10
|
+
const titleText = useI18nMessage({ id: I18nMessages.SIZE_CHANGE_MODAL_TITLE });
|
|
11
|
+
const descriptionText = useI18nMessage({ id: I18nMessages.SIZE_CHANGE_MODAL_DESCRIPTION });
|
|
12
|
+
const buttonText = useI18nMessage({ id: I18nMessages.SIZE_CHANGE_MODAL_BUTTON });
|
|
13
|
+
return (React.createElement(Modal, { visible: visible, onClose: onDismiss },
|
|
14
|
+
React.createElement(View, { style: style.modal },
|
|
15
|
+
React.createElement(Text, { level: 1, style: style.modalTitle, body: true }, titleText),
|
|
16
|
+
React.createElement(Text, { level: 3, style: style.modalDescription, body: true }, descriptionText),
|
|
17
|
+
React.createElement(Button
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
, {
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
22
|
+
// @ts-ignore
|
|
23
|
+
style: style.button, onPress: onDismiss }, buttonText))));
|
|
24
|
+
};
|
|
25
|
+
export { SizeChangeModal };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { StyleSheet } from "react-native";
|
|
2
|
+
import { theme } from "../../../../theme/theme";
|
|
3
|
+
const { spaceXL } = theme();
|
|
4
|
+
const style = StyleSheet.create({
|
|
5
|
+
button: {
|
|
6
|
+
flex: 0,
|
|
7
|
+
},
|
|
8
|
+
modal: {
|
|
9
|
+
padding: spaceXL,
|
|
10
|
+
},
|
|
11
|
+
modalDescription: {
|
|
12
|
+
marginVertical: spaceXL,
|
|
13
|
+
textAlign: "center",
|
|
14
|
+
},
|
|
15
|
+
modalTitle: {
|
|
16
|
+
textAlign: "center",
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
export { style };
|
|
@@ -11,11 +11,10 @@ interface TabBarProps {
|
|
|
11
11
|
}
|
|
12
12
|
type TabBar = FC<TabBarProps>;
|
|
13
13
|
interface NavigationProps {
|
|
14
|
+
readonly children: ReactNode;
|
|
14
15
|
readonly customerId: string;
|
|
15
16
|
readonly country: Country;
|
|
16
|
-
readonly
|
|
17
|
-
readonly tabBar: TabBar;
|
|
18
|
-
readonly children: ReactNode;
|
|
17
|
+
readonly onOpenMenu: () => void;
|
|
19
18
|
}
|
|
20
19
|
declare const Navigation: FC<NavigationProps>;
|
|
21
20
|
export type { Menu, TabBar };
|
|
@@ -1,33 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import React, { useCallback, useState } from "react";
|
|
3
|
-
import { Platform } from "react-native";
|
|
1
|
+
import React, { useCallback } from "react";
|
|
4
2
|
import { useTrackPressBack } from "../../../../shared/tracking/infrastructure/useTrackPressBack";
|
|
5
3
|
import { useTrackPressMenu } from "../../../../shared/tracking/infrastructure/useTrackPressMenu";
|
|
6
4
|
import { useTrackPressNext } from "../../../../shared/tracking/infrastructure/useTrackPressNext";
|
|
7
5
|
import { useTrackPressPrevious } from "../../../../shared/tracking/infrastructure/useTrackPressPrevious";
|
|
8
6
|
import { style } from "./Navigation.style";
|
|
9
7
|
import { Header } from "./components/header/Header";
|
|
10
|
-
const Navigation = ({ customerId, country,
|
|
11
|
-
const [menuVisible, setMenuVisible] = useState(false);
|
|
8
|
+
const Navigation = ({ children, customerId, country, onOpenMenu }) => {
|
|
12
9
|
const trackPressMenu = useTrackPressMenu();
|
|
13
10
|
const handleOnOpenMenu = useCallback(({ page, checkoutId, collapse }) => {
|
|
14
|
-
|
|
11
|
+
onOpenMenu();
|
|
15
12
|
trackPressMenu({
|
|
16
13
|
page,
|
|
17
14
|
country,
|
|
18
15
|
checkoutId,
|
|
19
16
|
collapse,
|
|
20
17
|
});
|
|
21
|
-
}, [country, trackPressMenu]);
|
|
22
|
-
const handleOnCloseMenu = useCallback(({ page, checkoutId, collapse }) => {
|
|
23
|
-
setMenuVisible(false);
|
|
24
|
-
trackPressMenu({
|
|
25
|
-
page,
|
|
26
|
-
country,
|
|
27
|
-
checkoutId,
|
|
28
|
-
collapse,
|
|
29
|
-
});
|
|
30
|
-
}, [country, trackPressMenu]);
|
|
18
|
+
}, [country, onOpenMenu, trackPressMenu]);
|
|
31
19
|
const trackPressBack = useTrackPressBack();
|
|
32
20
|
const handleOnBack = useCallback(({ page, checkoutId }) => trackPressBack({
|
|
33
21
|
page,
|
|
@@ -50,10 +38,8 @@ const Navigation = ({ customerId, country, menu, tabBar: TabBar, children }) =>
|
|
|
50
38
|
from,
|
|
51
39
|
to,
|
|
52
40
|
}), [country, trackPressPrevious]);
|
|
53
|
-
const { busy } = usePortal();
|
|
54
41
|
return (React.createElement(React.Fragment, null,
|
|
55
|
-
React.createElement(Header, { customerId: customerId,
|
|
56
|
-
children
|
|
57
|
-
Platform.OS !== "web" && React.createElement(TabBar, { isPortalBusy: busy, panel: menuVisible })));
|
|
42
|
+
React.createElement(Header, { customerId: customerId, style: style.header, onBack: handleOnBack, onNext: handleOnNext, onOpenMenu: handleOnOpenMenu, onPrevious: handleOnPrevious }),
|
|
43
|
+
children));
|
|
58
44
|
};
|
|
59
45
|
export { Navigation };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { FC } from "react";
|
|
2
2
|
import { StyleProp, ViewStyle } from "react-native";
|
|
3
3
|
import { TrackingPage } from "../../../../../tracking/tracking";
|
|
4
|
-
import { Menu } from "../../Navigation";
|
|
5
4
|
interface OnOpenMenuFunctionArgs {
|
|
6
5
|
readonly page: TrackingPage;
|
|
7
6
|
readonly checkoutId: string | undefined;
|
|
@@ -10,14 +9,6 @@ interface OnOpenMenuFunctionArgs {
|
|
|
10
9
|
interface OnOpenMenuFunction {
|
|
11
10
|
(args: OnOpenMenuFunctionArgs): void;
|
|
12
11
|
}
|
|
13
|
-
interface OnCloseMenuFunctionArgs {
|
|
14
|
-
readonly page: TrackingPage;
|
|
15
|
-
readonly checkoutId: string | undefined;
|
|
16
|
-
readonly collapse: boolean;
|
|
17
|
-
}
|
|
18
|
-
interface OnCloseMenuFunction {
|
|
19
|
-
(args: OnCloseMenuFunctionArgs): void;
|
|
20
|
-
}
|
|
21
12
|
interface OnBackFunctionArgs {
|
|
22
13
|
readonly page: TrackingPage;
|
|
23
14
|
readonly checkoutId: string | undefined;
|
|
@@ -45,10 +36,7 @@ interface OnPreviousFunction {
|
|
|
45
36
|
}
|
|
46
37
|
interface HeaderProps {
|
|
47
38
|
readonly customerId: string;
|
|
48
|
-
readonly menu: Menu;
|
|
49
|
-
readonly menuVisible: boolean;
|
|
50
39
|
readonly onOpenMenu: OnOpenMenuFunction;
|
|
51
|
-
readonly onCloseMenu: OnCloseMenuFunction;
|
|
52
40
|
readonly onBack?: OnBackFunction;
|
|
53
41
|
readonly onNext?: OnNextFunction;
|
|
54
42
|
readonly onPrevious?: OnPreviousFunction;
|
|
@@ -66,7 +66,7 @@ const CheckoutHeader = ({ onBack }) => {
|
|
|
66
66
|
React.createElement(Text, { level: 3, detail: true, upperCase: true }, title),
|
|
67
67
|
React.createElement(ButtonIconPlaceholder, null)));
|
|
68
68
|
};
|
|
69
|
-
const Header = ({ customerId,
|
|
69
|
+
const Header = ({ customerId, onOpenMenu, onBack, onNext, onPrevious, style: customStyle }) => {
|
|
70
70
|
const { id: itemId } = useParams();
|
|
71
71
|
const [checkout] = useViewFirstAvailableCheckoutByCustomerId({ customerId });
|
|
72
72
|
const currentItem = checkout?.items.find((item) => item.id === itemId);
|
|
@@ -97,7 +97,6 @@ const Header = ({ customerId, menu: Menu, menuVisible, onOpenMenu, onCloseMenu,
|
|
|
97
97
|
]);
|
|
98
98
|
const { top: safeAreaInsetTop } = useSafeAreaInsets();
|
|
99
99
|
const handleOnOpenMenu = useCallback(() => onOpenMenu({ page: trackingPage(), checkoutId: checkout?.id, collapse: false }), [checkout?.id, onOpenMenu, trackingPage]);
|
|
100
|
-
const handleOnCloseMenu = useCallback(() => onCloseMenu({ page: trackingPage(), checkoutId: checkout?.id, collapse: true }), [checkout?.id, onCloseMenu, trackingPage]);
|
|
101
100
|
const handleOnBack = useCallback(() => onBack?.({ page: trackingPage(), checkoutId: checkout?.id }), [checkout?.id, onBack, trackingPage]);
|
|
102
101
|
const handleOnNext = useCallback(({ from, to }) => onNext?.({ page: trackingPage(), checkoutId: checkout?.id, from, to }), [checkout?.id, onNext, trackingPage]);
|
|
103
102
|
const handleOnPrevious = useCallback(({ from, to }) => onPrevious?.({ page: trackingPage(), checkoutId: checkout?.id, from, to }), [checkout?.id, onPrevious, trackingPage]);
|
|
@@ -116,8 +115,6 @@ const Header = ({ customerId, menu: Menu, menuVisible, onOpenMenu, onCloseMenu,
|
|
|
116
115
|
else {
|
|
117
116
|
header = React.createElement(DefaultHeader, { onOpenMenu: handleOnOpenMenu });
|
|
118
117
|
}
|
|
119
|
-
return (React.createElement(
|
|
120
|
-
React.createElement(SafeAreaView, { edges: ["top"], style: [style.container, { height: HEADER_HEIGHT + safeAreaInsetTop }, customStyle] }, header),
|
|
121
|
-
React.createElement(Menu, { isVisible: menuVisible, onClose: handleOnCloseMenu })));
|
|
118
|
+
return (React.createElement(SafeAreaView, { edges: ["top"], style: [style.container, { height: HEADER_HEIGHT + safeAreaInsetTop }, customStyle] }, header));
|
|
122
119
|
};
|
|
123
120
|
export { Header };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from "@lookiero/messaging";
|
|
2
|
+
import { NotificationLevel } from "../model/notification";
|
|
3
|
+
declare const CREATE_NOTIFICATION = "create_notification";
|
|
4
|
+
interface CreateNotificationPayload {
|
|
5
|
+
readonly aggregateId: string;
|
|
6
|
+
readonly level: NotificationLevel;
|
|
7
|
+
readonly bodyI18nKey: string;
|
|
8
|
+
readonly titleI18nKey: string | undefined;
|
|
9
|
+
}
|
|
10
|
+
interface CreateNotification extends Command<typeof CREATE_NOTIFICATION>, CreateNotificationPayload {
|
|
11
|
+
}
|
|
12
|
+
interface CreateNotificationFunction {
|
|
13
|
+
(payload: CreateNotificationPayload): CreateNotification;
|
|
14
|
+
}
|
|
15
|
+
declare const createNotification: CreateNotificationFunction;
|
|
16
|
+
export type { CreateNotification };
|
|
17
|
+
export { CREATE_NOTIFICATION, createNotification };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { command } from "@lookiero/messaging";
|
|
2
|
+
const CREATE_NOTIFICATION = "create_notification";
|
|
3
|
+
const createNotification = ({ aggregateId, ...payload }) => ({
|
|
4
|
+
...command({ aggregateId, name: CREATE_NOTIFICATION }),
|
|
5
|
+
...payload,
|
|
6
|
+
});
|
|
7
|
+
export { CREATE_NOTIFICATION, createNotification };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CommandStatus } from "@lookiero/messaging-react";
|
|
2
|
+
import { Logger } from "../../../../../logging/Logger";
|
|
3
|
+
import { NotificationLevel } from "../../../../domain/notification/model/notification";
|
|
4
|
+
interface CreateNotificationFunctionArgs {
|
|
5
|
+
readonly aggregateId?: string;
|
|
6
|
+
readonly level: NotificationLevel;
|
|
7
|
+
readonly bodyI18nKey: string;
|
|
8
|
+
readonly titleI18nKey?: string;
|
|
9
|
+
}
|
|
10
|
+
interface CreateNotificationFunction {
|
|
11
|
+
(args: CreateNotificationFunctionArgs): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
interface UseCreateNotificationFunctionArgs {
|
|
14
|
+
readonly contextId: string;
|
|
15
|
+
readonly logger: Logger;
|
|
16
|
+
}
|
|
17
|
+
interface UseCreateNotificationFunction {
|
|
18
|
+
(args: UseCreateNotificationFunctionArgs): [create: CreateNotificationFunction, status: CommandStatus];
|
|
19
|
+
}
|
|
20
|
+
declare const useCreateNotification: UseCreateNotificationFunction;
|
|
21
|
+
export { useCreateNotification };
|
package/dist/shared/notifications/infrastructure/domain/notification/react/useCreateNotification.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useCommand } from "@lookiero/messaging-react";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { v4 as uuid } from "uuid";
|
|
4
|
+
import { createNotification } from "../../../../domain/notification/command/createNotification";
|
|
5
|
+
const useCreateNotification = ({ contextId, logger }) => {
|
|
6
|
+
const [commandBus, status] = useCommand({ contextId });
|
|
7
|
+
const create = useCallback(async ({ aggregateId, level, titleI18nKey, bodyI18nKey }) => {
|
|
8
|
+
try {
|
|
9
|
+
await commandBus(createNotification({
|
|
10
|
+
aggregateId: aggregateId || uuid(),
|
|
11
|
+
level,
|
|
12
|
+
titleI18nKey,
|
|
13
|
+
bodyI18nKey,
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
logger.captureException(error);
|
|
18
|
+
}
|
|
19
|
+
}, [commandBus, logger]);
|
|
20
|
+
return [create, status];
|
|
21
|
+
};
|
|
22
|
+
export { useCreateNotification };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
import { ToastStyle } from "../../../../ui/components/molecules/toast/Toast";
|
|
3
|
+
import { NotificationProjection } from "../../../projection/notification/notification";
|
|
4
|
+
interface NotificationItemProps {
|
|
5
|
+
readonly notification: NotificationProjection;
|
|
6
|
+
readonly onRemove: (notificationId: string) => void;
|
|
7
|
+
readonly autoHideTimeout?: number;
|
|
8
|
+
readonly testID?: string;
|
|
9
|
+
readonly style?: ToastStyle;
|
|
10
|
+
}
|
|
11
|
+
declare const NotificationItem: FC<NotificationItemProps>;
|
|
12
|
+
export { NotificationItem };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useI18nMessage } from "@lookiero/i18n-react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { useCallback, useEffect } from "react";
|
|
4
|
+
import { Toast, ToastVariant } from "../../../../ui/components/molecules/toast/Toast";
|
|
5
|
+
import { NotificationLevel } from "../../../domain/notification/model/notification";
|
|
6
|
+
const toastVariantForNotificationLevel = {
|
|
7
|
+
[NotificationLevel.INFO]: ToastVariant.INFO,
|
|
8
|
+
[NotificationLevel.ERROR]: ToastVariant.ERROR,
|
|
9
|
+
[NotificationLevel.SUCCESS]: ToastVariant.SUCCESS,
|
|
10
|
+
};
|
|
11
|
+
const NotificationItem = ({ notification, onRemove, autoHideTimeout, testID = "notification-item", style: customStyle, }) => {
|
|
12
|
+
const title = useI18nMessage({ id: notification.titleI18nKey }) || undefined;
|
|
13
|
+
const body = useI18nMessage({ id: notification.bodyI18nKey || "" });
|
|
14
|
+
const handleOnClose = useCallback(() => onRemove(notification.id), [notification.id, onRemove]);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!autoHideTimeout) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const hideTimeout = setTimeout(handleOnClose, autoHideTimeout);
|
|
20
|
+
return () => clearTimeout(hideTimeout);
|
|
21
|
+
}, [autoHideTimeout, handleOnClose]);
|
|
22
|
+
return (React.createElement(Toast, { body: body, style: customStyle, testID: testID, title: title, variant: toastVariantForNotificationLevel[notification.level], onClose: handleOnClose }));
|
|
23
|
+
};
|
|
24
|
+
export { NotificationItem };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
import { NotificationProjection } from "../../../projection/notification/notification";
|
|
3
|
+
interface NotificationsProps {
|
|
4
|
+
readonly notifications?: NotificationProjection[];
|
|
5
|
+
readonly onRemove: (notificationId: string) => void;
|
|
6
|
+
readonly autoHideTimeout?: number;
|
|
7
|
+
}
|
|
8
|
+
declare const Notifications: FC<NotificationsProps>;
|
|
9
|
+
export { Notifications };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
4
|
+
import { Column } from "../../../../ui/components/layouts/column/Column";
|
|
5
|
+
import { Row } from "../../../../ui/components/layouts/row/Row";
|
|
6
|
+
import { Stack } from "../../../../ui/components/layouts/stack/Stack";
|
|
7
|
+
import { useScreenSize } from "../../../../ui/hooks/useScreenSize";
|
|
8
|
+
import { NotificationItem } from "./NotificationItem";
|
|
9
|
+
import { style } from "./Notifications.style";
|
|
10
|
+
const NOTIFICATIONS_AUTOHIDE_TIMEOUT = 10000;
|
|
11
|
+
const Notifications = ({ notifications = [], onRemove, autoHideTimeout = NOTIFICATIONS_AUTOHIDE_TIMEOUT, }) => {
|
|
12
|
+
const { top: safeAreaInsetTop } = useSafeAreaInsets();
|
|
13
|
+
const screenSize = useScreenSize();
|
|
14
|
+
const isSmallDevice = screenSize === "S";
|
|
15
|
+
return (React.createElement(View, { pointerEvents: "box-none", style: style.notifications, testID: "notifications" },
|
|
16
|
+
React.createElement(Row, { style: [style.row, isSmallDevice && style.rowSmall, { paddingTop: safeAreaInsetTop }] },
|
|
17
|
+
React.createElement(Column, null,
|
|
18
|
+
React.createElement(Stack, null, notifications.map((notification) => (React.createElement(NotificationItem, { key: notification.id, autoHideTimeout: autoHideTimeout, notification: notification, style: { toast: style.toast }, testID: notification.id, onRemove: onRemove }))))))));
|
|
19
|
+
};
|
|
20
|
+
export { Notifications };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { StyleSheet } from "react-native";
|
|
2
|
+
declare const style: StyleSheet.NamedStyles<any> | StyleSheet.NamedStyles<{
|
|
3
|
+
notifications: {
|
|
4
|
+
flexDirection: "column";
|
|
5
|
+
top: number;
|
|
6
|
+
width: string;
|
|
7
|
+
zIndex: number;
|
|
8
|
+
left: number;
|
|
9
|
+
} | {
|
|
10
|
+
flexDirection: "column";
|
|
11
|
+
top: number;
|
|
12
|
+
width: string;
|
|
13
|
+
zIndex: number;
|
|
14
|
+
position: "fixed";
|
|
15
|
+
left: number;
|
|
16
|
+
} | {
|
|
17
|
+
flexDirection: "column";
|
|
18
|
+
top: number;
|
|
19
|
+
width: string;
|
|
20
|
+
zIndex: number;
|
|
21
|
+
position: "absolute";
|
|
22
|
+
left: number;
|
|
23
|
+
};
|
|
24
|
+
row: {
|
|
25
|
+
flex: number;
|
|
26
|
+
justifyContent: "center";
|
|
27
|
+
};
|
|
28
|
+
rowSmall: {
|
|
29
|
+
paddingLeft: number;
|
|
30
|
+
paddingRight: number;
|
|
31
|
+
};
|
|
32
|
+
toast: {
|
|
33
|
+
marginTop: number;
|
|
34
|
+
};
|
|
35
|
+
}>;
|
|
36
|
+
export { style };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Platform, StyleSheet } from "react-native";
|
|
2
|
+
import { theme } from "../../../../../infrastructure/ui/theme/theme";
|
|
3
|
+
const { spaceM } = theme();
|
|
4
|
+
const style = StyleSheet.create({
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
notifications: {
|
|
8
|
+
left: 0,
|
|
9
|
+
...Platform.select({
|
|
10
|
+
web: { position: "fixed" },
|
|
11
|
+
ios: { position: "absolute" },
|
|
12
|
+
android: { position: "absolute" },
|
|
13
|
+
}),
|
|
14
|
+
flexDirection: "column",
|
|
15
|
+
top: 0,
|
|
16
|
+
width: "100%",
|
|
17
|
+
zIndex: 20,
|
|
18
|
+
},
|
|
19
|
+
row: {
|
|
20
|
+
flex: 1,
|
|
21
|
+
justifyContent: "center",
|
|
22
|
+
},
|
|
23
|
+
rowSmall: {
|
|
24
|
+
paddingLeft: spaceM,
|
|
25
|
+
paddingRight: spaceM,
|
|
26
|
+
},
|
|
27
|
+
toast: {
|
|
28
|
+
marginTop: spaceM,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
export { style };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { TrackingPage } from "../../../infrastructure/tracking/tracking";
|
|
2
|
+
import { Country } from "../../../projection/shared/country";
|
|
3
|
+
interface UsePageViewFunctionArgs {
|
|
4
|
+
readonly page: TrackingPage;
|
|
5
|
+
readonly country: Country;
|
|
6
|
+
readonly checkoutId?: string;
|
|
7
|
+
readonly checkoutItemId?: string;
|
|
8
|
+
}
|
|
9
|
+
interface UsePageViewFunction {
|
|
10
|
+
(agrs: UsePageViewFunctionArgs): void;
|
|
11
|
+
}
|
|
12
|
+
declare const usePageView: UsePageViewFunction;
|
|
13
|
+
export { usePageView };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useLayoutEffect } from "react";
|
|
2
|
+
import { PROJECT } from "../../../infrastructure/tracking/tracking";
|
|
3
|
+
import { TrackingEvent, TrackingEventCategory } from "../tracking";
|
|
4
|
+
import { useEmitUserEvent } from "./useEmitUserEvent";
|
|
5
|
+
const usePageView = ({ page, country, checkoutId, checkoutItemId }) => {
|
|
6
|
+
const emitUserEvent = useEmitUserEvent();
|
|
7
|
+
useLayoutEffect(() => {
|
|
8
|
+
if (!checkoutId) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const baseTrackingEvent = {
|
|
12
|
+
event: TrackingEvent.PAGEVIEW,
|
|
13
|
+
eventCategory: TrackingEventCategory.NAVIGATION,
|
|
14
|
+
section: `${PROJECT}_${page}`,
|
|
15
|
+
store: country,
|
|
16
|
+
checkoutId,
|
|
17
|
+
};
|
|
18
|
+
const pageViewTrackingEvent = checkoutItemId
|
|
19
|
+
? {
|
|
20
|
+
...baseTrackingEvent,
|
|
21
|
+
checkoutItemId,
|
|
22
|
+
}
|
|
23
|
+
: baseTrackingEvent;
|
|
24
|
+
emitUserEvent(pageViewTrackingEvent);
|
|
25
|
+
}, [checkoutId, checkoutItemId, country, emitUserEvent, page]);
|
|
26
|
+
};
|
|
27
|
+
export { usePageView };
|