@lookiero/checkout 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +5 -15
  2. package/dist/domain/checkout/command/markCheckoutAsPaid.d.ts +13 -0
  3. package/dist/domain/checkout/command/markCheckoutAsPaid.js +4 -0
  4. package/dist/domain/checkout/model/checkout.d.ts +5 -2
  5. package/dist/domain/checkout/model/checkout.js +13 -1
  6. package/dist/domain/checkout/model/checkoutPaid.d.ts +13 -0
  7. package/dist/domain/checkout/model/checkoutPaid.js +4 -0
  8. package/dist/infrastructure/delivery/baseBootstrap.d.ts +2 -0
  9. package/dist/infrastructure/delivery/baseBootstrap.js +8 -2
  10. package/dist/infrastructure/delivery/bootstrap.js +4 -0
  11. package/dist/infrastructure/domain/checkout/model/httpCheckouts.js +7 -1
  12. package/dist/infrastructure/domain/checkout/model/httpCheckoutsMarkAsPaid.d.ts +5 -0
  13. package/dist/infrastructure/domain/checkout/model/httpCheckoutsMarkAsPaid.js +13 -0
  14. package/dist/infrastructure/domain/checkout/react/useMarkCheckoutAsPaid.d.ts +13 -0
  15. package/dist/infrastructure/domain/checkout/react/useMarkCheckoutAsPaid.js +12 -0
  16. package/dist/infrastructure/projection/payment/httpPaymentFlowPayloadByCheckoutIdView.d.ts +12 -0
  17. package/dist/infrastructure/projection/payment/httpPaymentFlowPayloadByCheckoutIdView.js +9 -0
  18. package/dist/infrastructure/projection/payment/react/useViewPaymentFlowPayloadByCheckoutId.d.ts +10 -0
  19. package/dist/infrastructure/projection/payment/react/useViewPaymentFlowPayloadByCheckoutId.js +17 -0
  20. package/dist/infrastructure/ui/components/templates/SafeAreaScrollView.d.ts +3 -0
  21. package/dist/infrastructure/ui/components/templates/SafeAreaScrollView.js +9 -6
  22. package/dist/infrastructure/ui/i18n/i18n.d.ts +3 -0
  23. package/dist/infrastructure/ui/i18n/i18n.js +3 -0
  24. package/dist/infrastructure/ui/routing/CheckoutMiddleware.js +4 -3
  25. package/dist/infrastructure/ui/routing/Routing.js +5 -0
  26. package/dist/infrastructure/ui/routing/routes.d.ts +1 -0
  27. package/dist/infrastructure/ui/routing/routes.js +1 -0
  28. package/dist/infrastructure/ui/shared/components/layouts/sticky/Sticky.js +2 -2
  29. package/dist/infrastructure/ui/shared/components/layouts/sticky/Sticky.style.js +2 -2
  30. package/dist/infrastructure/ui/views/summary/Summary.js +33 -9
  31. package/dist/infrastructure/ui/views/summary/Summary.style.d.ts +18 -0
  32. package/dist/infrastructure/ui/views/summary/Summary.style.js +21 -0
  33. package/dist/infrastructure/ui/views/summary/components/checkoutItemsTabs/CheckoutItemsTabs.d.ts +2 -1
  34. package/dist/infrastructure/ui/views/summary/components/checkoutItemsTabs/CheckoutItemsTabs.js +5 -8
  35. package/dist/infrastructure/ui/views/summary/components/fiveItemsDiscountBanner/FiveItemsDiscountBanner.d.ts +6 -0
  36. package/dist/infrastructure/ui/views/summary/components/fiveItemsDiscountBanner/FiveItemsDiscountBanner.js +15 -0
  37. package/dist/infrastructure/ui/views/summary/components/fiveItemsDiscountBanner/FiveItemsDiscountBanner.style.d.ts +9 -0
  38. package/dist/infrastructure/ui/views/summary/components/fiveItemsDiscountBanner/FiveItemsDiscountBanner.style.js +12 -0
  39. package/dist/infrastructure/ui/views/summary/components/pricing/Pricing.d.ts +1 -1
  40. package/dist/infrastructure/ui/views/summary/components/pricing/Pricing.js +9 -9
  41. package/dist/infrastructure/ui/views/summary/components/pricing/Pricing.style.d.ts +5 -5
  42. package/dist/infrastructure/ui/views/summary/components/pricing/Pricing.style.js +6 -6
  43. package/dist/infrastructure/ui/views/summary/components/pricing/icons/ArrowDown.d.ts +4 -0
  44. package/dist/infrastructure/ui/views/summary/components/pricing/icons/ArrowDown.js +5 -0
  45. package/dist/infrastructure/ui/views/summary/components/pricing/icons/ArrowUp.d.ts +4 -0
  46. package/dist/infrastructure/ui/views/summary/components/pricing/icons/ArrowUp.js +5 -0
  47. package/dist/infrastructure/ui/views/summary/components/stickyPricing/StickyPricing.d.ts +11 -0
  48. package/dist/infrastructure/ui/views/summary/components/stickyPricing/StickyPricing.js +9 -0
  49. package/dist/infrastructure/ui/views/summary/components/stickyPricing/StickyPricing.style.d.ts +6 -0
  50. package/dist/infrastructure/ui/views/summary/components/stickyPricing/StickyPricing.style.js +9 -0
  51. package/dist/projection/payment/paymentFlowPayload.d.ts +39 -0
  52. package/dist/projection/payment/paymentFlowPayload.js +1 -0
  53. package/dist/projection/payment/viewPaymentFlowPayloadByCheckoutId.d.ts +25 -0
  54. package/dist/projection/payment/viewPaymentFlowPayloadByCheckoutId.js +8 -0
  55. package/package.json +4 -3
  56. package/dist/infrastructure/projection/pricing/pricing.mock.d.ts +0 -3
  57. package/dist/infrastructure/projection/pricing/pricing.mock.js +0 -37
package/README.md CHANGED
@@ -26,25 +26,15 @@ Once we can replace the build of Jenkins for NPM one, we are going to be able to
26
26
  ```json
27
27
  {
28
28
  (...)
29
- "devDependencies": {
29
+ "dependencies": {
30
30
  (...)
31
- "pino-std-serializers": "^6.0.0",
31
+ "@expo/dev-server": "0.1.120",
32
32
  (...)
33
- }
34
- }
35
- ```
36
-
37
- We are hiding the node compatibility deprecation warnings in order to obtain a success message from
38
- the terminal. Remove it once we drop Yarn compatibility.
39
-
40
- ```json
41
- {
42
- (...)
43
- "scripts": {
33
+ },
34
+ "devDependencies": {
44
35
  (...)
45
- "build:app": "tsc -p ./tsconfig.build-app.json --noemit && ENTRY_POINT='./src/Expo.tsx' NODE_ENV=production expo export:web --clear 2> /dev/null"
36
+ "pino-std-serializers": "^6.0.0",
46
37
  (...)
47
38
  }
48
- (...)
49
39
  }
50
40
  ```
@@ -0,0 +1,13 @@
1
+ import { Command } from "@lookiero/messaging";
2
+ declare const MARK_CHECKOUT_AS_PAID = "mark_checkout_as_paid";
3
+ interface MarkCheckoutAsPaidPayload {
4
+ readonly aggregateId: string;
5
+ }
6
+ interface MarkCheckoutAsPaid extends Command<typeof MARK_CHECKOUT_AS_PAID>, MarkCheckoutAsPaidPayload {
7
+ }
8
+ interface MarkCheckoutAsPaidFunction {
9
+ (payload: MarkCheckoutAsPaidPayload): MarkCheckoutAsPaid;
10
+ }
11
+ declare const markCheckoutAsPaid: MarkCheckoutAsPaidFunction;
12
+ export type { MarkCheckoutAsPaid };
13
+ export { MARK_CHECKOUT_AS_PAID, markCheckoutAsPaid };
@@ -0,0 +1,4 @@
1
+ import { command } from "@lookiero/messaging";
2
+ const MARK_CHECKOUT_AS_PAID = "mark_checkout_as_paid";
3
+ const markCheckoutAsPaid = ({ aggregateId }) => command({ aggregateId, name: MARK_CHECKOUT_AS_PAID });
4
+ export { MARK_CHECKOUT_AS_PAID, markCheckoutAsPaid };
@@ -1,10 +1,12 @@
1
1
  import { AggregateRoot, CommandHandlerFunction } from "@lookiero/messaging";
2
+ import { MarkCheckoutAsPaid } from "../command/markCheckoutAsPaid";
2
3
  import { StartCheckout } from "../command/startCheckout";
3
4
  declare enum CheckoutStatus {
4
5
  AVAILABLE = "AVAILABLE",
5
6
  NOTIFIED = "NOTIFIED",
6
7
  STARTED = "STARTED",
7
- COMPLETED = "COMPLETED"
8
+ COMPLETED = "COMPLETED",
9
+ PAID = "PAID"
8
10
  }
9
11
  interface Checkout extends AggregateRoot {
10
12
  readonly status: CheckoutStatus;
@@ -14,5 +16,6 @@ interface Checkout extends AggregateRoot {
14
16
  readonly expiresOn: Date;
15
17
  }
16
18
  declare const startCheckoutHandler: CommandHandlerFunction<StartCheckout, Checkout>;
19
+ declare const markCheckoutAsPaidHandler: CommandHandlerFunction<MarkCheckoutAsPaid, Checkout>;
17
20
  export type { Checkout };
18
- export { CheckoutStatus, startCheckoutHandler };
21
+ export { CheckoutStatus, startCheckoutHandler, markCheckoutAsPaidHandler };
@@ -1,4 +1,5 @@
1
1
  import invariant from "tiny-invariant";
2
+ import { checkoutPaid } from "./checkoutPaid";
2
3
  import { checkoutStarted } from "./checkoutStarted";
3
4
  var CheckoutStatus;
4
5
  (function (CheckoutStatus) {
@@ -6,6 +7,7 @@ var CheckoutStatus;
6
7
  CheckoutStatus["NOTIFIED"] = "NOTIFIED";
7
8
  CheckoutStatus["STARTED"] = "STARTED";
8
9
  CheckoutStatus["COMPLETED"] = "COMPLETED";
10
+ CheckoutStatus["PAID"] = "PAID";
9
11
  })(CheckoutStatus || (CheckoutStatus = {}));
10
12
  const startCheckoutHandler = () => async ({ aggregateRoot, command }) => {
11
13
  const { aggregateId } = command;
@@ -17,4 +19,14 @@ const startCheckoutHandler = () => async ({ aggregateRoot, command }) => {
17
19
  domainEvents: [checkoutStarted({ aggregateId })],
18
20
  };
19
21
  };
20
- export { CheckoutStatus, startCheckoutHandler };
22
+ const markCheckoutAsPaidHandler = () => async ({ aggregateRoot, command }) => {
23
+ const { aggregateId } = command;
24
+ invariant(aggregateRoot.status !== CheckoutStatus.PAID, "Checkout is already paid");
25
+ invariant(aggregateRoot.status !== CheckoutStatus.COMPLETED, "Checkout is already completed");
26
+ return {
27
+ ...aggregateRoot,
28
+ status: CheckoutStatus.PAID,
29
+ domainEvents: [checkoutPaid({ aggregateId })],
30
+ };
31
+ };
32
+ export { CheckoutStatus, startCheckoutHandler, markCheckoutAsPaidHandler };
@@ -0,0 +1,13 @@
1
+ import { DomainEvent } from "@lookiero/messaging";
2
+ declare const CHECKOUT_PAID = "checkout_paid";
3
+ interface CheckoutPaidPayload {
4
+ readonly aggregateId: string;
5
+ }
6
+ interface CheckoutPaid extends DomainEvent<typeof CHECKOUT_PAID>, CheckoutPaidPayload {
7
+ }
8
+ interface CheckoutPaidFunction {
9
+ (payload: CheckoutPaidPayload): CheckoutPaid;
10
+ }
11
+ declare const checkoutPaid: CheckoutPaidFunction;
12
+ export type { CheckoutPaid };
13
+ export { CHECKOUT_PAID, checkoutPaid };
@@ -0,0 +1,4 @@
1
+ import { domainEvent } from "@lookiero/messaging";
2
+ const CHECKOUT_PAID = "checkout_paid";
3
+ const checkoutPaid = ({ aggregateId }) => domainEvent({ aggregateId, name: CHECKOUT_PAID });
4
+ export { CHECKOUT_PAID, checkoutPaid };
@@ -14,6 +14,7 @@ import { FirstAvailableCheckoutByCustomerIdView } from "../../projection/checkou
14
14
  import { FiveItemsDiscountByCustomerIdView } from "../../projection/checkout/viewFiveItemsDiscountByCustomerId";
15
15
  import { IsCheckoutEnabledByCustomerIdView } from "../../projection/checkout/viewIsCheckoutEnabledByCustomerId";
16
16
  import { CheckoutItemByIdView } from "../../projection/checkoutItem/viewCheckoutItemById";
17
+ import { PaymentFlowPayloadByCheckoutIdView } from "../../projection/payment/viewPaymentFlowPayloadByCheckoutId";
17
18
  import { PricingByCheckoutIdView } from "../../projection/pricing/viewPricingByCheckoutId";
18
19
  import { ReturnQuestionsByCheckoutItemIdView } from "../../projection/returnQuestion/listReturnQuestionsByCheckoutItemId";
19
20
  import { UiSettingByKeyView } from "../../projection/uiSetting/viewUiSettingByKey";
@@ -30,6 +31,7 @@ interface BaseBootstrapFunctionArgs<UiSettingGetFunctionArgs extends RepositoryG
30
31
  readonly returnQuestionsByCheckoutItemIdView: ReturnQuestionsByCheckoutItemIdView;
31
32
  readonly bookedSizeChangeProductsVariantsForCheckoutItemView: BookedSizeChangeProductsVariantsForCheckoutItemView;
32
33
  readonly pricingByCheckoutIdView: PricingByCheckoutIdView;
34
+ readonly paymentFlowPayloadByCheckoutIdView: PaymentFlowPayloadByCheckoutIdView;
33
35
  readonly getUiSetting: UiSettingGetFunction<UiSettingGetFunctionArgs>;
34
36
  readonly saveUiSetting: UiSettingSaveFunction<UiSettingSaveFunctionArgs>;
35
37
  readonly uiSettingsDependencies: NeverWhenEmptyRecord<[
@@ -1,6 +1,7 @@
1
1
  import { bootstrap as messagingBootstrap } from "@lookiero/messaging-react";
2
+ import { MARK_CHECKOUT_AS_PAID } from "../../domain/checkout/command/markCheckoutAsPaid";
2
3
  import { START_CHECKOUT } from "../../domain/checkout/command/startCheckout";
3
- import { startCheckoutHandler } from "../../domain/checkout/model/checkout";
4
+ import { markCheckoutAsPaidHandler, startCheckoutHandler } from "../../domain/checkout/model/checkout";
4
5
  import { BOOK_CHECKOUT_BOOKING_FOR_CHECKOUT_ITEM } from "../../domain/checkoutBooking/command/bookCheckoutBookingForCheckoutItem";
5
6
  import { bookCheckoutBookingForCheckoutItemHandler, } from "../../domain/checkoutBooking/model/checkoutBooking";
6
7
  import { KEEP_CHECKOUT_ITEM } from "../../domain/checkoutItem/command/keepCheckoutItem";
@@ -16,11 +17,12 @@ import { viewFiveItemsDiscountByCustomerIdHandler, VIEW_FIVE_ITEMS_DISCOUNT_BY_C
16
17
  import { viewIsCheckoutAccessibleByCustomerIdHandler, VIEW_IS_CHECKOUT_ACCESSIBLE_BY_CUSTOMER_ID, } from "../../projection/checkout/viewIsCheckoutAccessibleByCustomerId";
17
18
  import { viewIsCheckoutEnabledByCustomerIdHandler, VIEW_IS_CHECKOUT_ENABLED_BY_CUSTOMER_ID, } from "../../projection/checkout/viewIsCheckoutEnabledByCustomerId";
18
19
  import { viewCheckoutItemByIdHandler, VIEW_CHECKOUT_ITEM_BY_ID, } from "../../projection/checkoutItem/viewCheckoutItemById";
20
+ import { viewPaymentFlowPayloadByCheckoutIdHandler, VIEW_PAYMENT_FLOW_PAYLOAD_BY_CHECKOUT_ID, } from "../../projection/payment/viewPaymentFlowPayloadByCheckoutId";
19
21
  import { viewPricingByCheckoutIdHandler, VIEW_PRICING_BY_CHECKOUT_ID, } from "../../projection/pricing/viewPricingByCheckoutId";
20
22
  import { listReturnQuestionsByCheckoutItemIdHandler, LIST_RETURN_QUESTIONS_BY_CHECKOUT_ITEM_ID, } from "../../projection/returnQuestion/listReturnQuestionsByCheckoutItemId";
21
23
  import { viewUiSettingByKeyHandler, VIEW_UI_SETTING_BY_KEY, } from "../../projection/uiSetting/viewUiSettingByKey";
22
24
  const MESSAGING_CONTEXT_ID = "Checkout";
23
- const baseBootstrap = ({ checkoutByIdView, firstAvailableCheckoutByCustomerIdView, isCheckoutEnabledByCustomerIdView, fiveItemsDiscountByCustomerIdView, uiSettingByKeyView, checkoutItemByIdView, returnQuestionsByCheckoutItemIdView, bookedSizeChangeProductsVariantsForCheckoutItemView, pricingByCheckoutIdView, getUiSetting, saveUiSetting, uiSettingsDependencies, getCheckout, saveCheckout, checkoutsDependencies, getCheckoutItem, saveCheckoutItem, checkoutItemsDependencies, getCheckoutBooking, saveCheckoutBooking, checkoutBookingsDependencies, }) => messagingBootstrap({ id: MESSAGING_CONTEXT_ID })
25
+ const baseBootstrap = ({ checkoutByIdView, firstAvailableCheckoutByCustomerIdView, isCheckoutEnabledByCustomerIdView, fiveItemsDiscountByCustomerIdView, uiSettingByKeyView, checkoutItemByIdView, returnQuestionsByCheckoutItemIdView, bookedSizeChangeProductsVariantsForCheckoutItemView, pricingByCheckoutIdView, paymentFlowPayloadByCheckoutIdView, getUiSetting, saveUiSetting, uiSettingsDependencies, getCheckout, saveCheckout, checkoutsDependencies, getCheckoutItem, saveCheckoutItem, checkoutItemsDependencies, getCheckoutBooking, saveCheckoutBooking, checkoutBookingsDependencies, }) => messagingBootstrap({ id: MESSAGING_CONTEXT_ID })
24
26
  .query(VIEW_FIVE_ITEMS_DISCOUNT_BY_CUSTOMER_ID, viewFiveItemsDiscountByCustomerIdHandler, {
25
27
  view: fiveItemsDiscountByCustomerIdView,
26
28
  })
@@ -35,6 +37,7 @@ const baseBootstrap = ({ checkoutByIdView, firstAvailableCheckoutByCustomerIdVie
35
37
  })
36
38
  .query(VIEW_IS_CHECKOUT_ACCESSIBLE_BY_CUSTOMER_ID, viewIsCheckoutAccessibleByCustomerIdHandler)
37
39
  .command(START_CHECKOUT, startCheckoutHandler)(getCheckout, saveCheckout, ...checkoutsDependencies)
40
+ .command(MARK_CHECKOUT_AS_PAID, markCheckoutAsPaidHandler)(getCheckout, saveCheckout, ...checkoutsDependencies)
38
41
  .query(VIEW_CHECKOUT_ITEM_BY_ID, viewCheckoutItemByIdHandler, {
39
42
  view: checkoutItemByIdView,
40
43
  })
@@ -46,6 +49,9 @@ const baseBootstrap = ({ checkoutByIdView, firstAvailableCheckoutByCustomerIdVie
46
49
  .command(REPLACE_CHECKOUT_ITEM, replaceCheckoutItemHandler)(getCheckoutItem, saveCheckoutItem, ...checkoutItemsDependencies)
47
50
  .query(VIEW_PRICING_BY_CHECKOUT_ID, viewPricingByCheckoutIdHandler, {
48
51
  view: pricingByCheckoutIdView,
52
+ })
53
+ .query(VIEW_PAYMENT_FLOW_PAYLOAD_BY_CHECKOUT_ID, viewPaymentFlowPayloadByCheckoutIdHandler, {
54
+ view: paymentFlowPayloadByCheckoutIdView,
49
55
  })
50
56
  .query(VIEW_BOOKED_SIZE_CHANGE_PRODUCT_VARIANTS_FOR_CHECKOUT_ITEM, viewBookedSizeChangeProductsVariantsForCheckoutItemHandler, {
51
57
  view: bookedSizeChangeProductsVariantsForCheckoutItemView,
@@ -9,6 +9,7 @@ import { httpFirstAvailableCheckoutByCustomerIdView } from "../projection/checko
9
9
  import { httpFiveItemsDiscountByCustomerIdView } from "../projection/checkout/httpFiveItemsDiscountByCustomerIdView";
10
10
  import { httpIsCheckoutEnabledByCustomerIdView } from "../projection/checkout/httpIsCheckoutEnabledByCustomerIdView";
11
11
  import { httpCheckoutItemByIdView } from "../projection/checkoutItem/httpCheckoutItemByIdView";
12
+ import { httpPaymentFlowPayloadByCheckoutIdView } from "../projection/payment/httpPaymentFlowPayloadByCheckoutIdView";
12
13
  import { httpPricingByCheckoutIdView } from "../projection/pricing/httpPricingByCheckoutIdView";
13
14
  import { httpReturnQuestionsByCheckoutItemIdView } from "../projection/returnQuestion/httpReturnQuestionsByCheckoutItemIdView";
14
15
  import { storageUiSettingByKeyView } from "../projection/uiSetting/storageUiSettingByKeyView";
@@ -31,6 +32,9 @@ const bootstrap = ({ apiUrl, getAuthToken }) => {
31
32
  pricingByCheckoutIdView: httpPricingByCheckoutIdView({
32
33
  httpPost,
33
34
  }),
35
+ paymentFlowPayloadByCheckoutIdView: httpPaymentFlowPayloadByCheckoutIdView({
36
+ httpPost,
37
+ }),
34
38
  getUiSetting,
35
39
  saveUiSetting,
36
40
  uiSettingsDependencies: [{ read, write }],
@@ -1,5 +1,6 @@
1
1
  import invariant from "tiny-invariant";
2
2
  import { viewCheckoutById, } from "../../../../projection/checkout/viewCheckoutById";
3
+ import { httpCheckoutsMarkAsPaid } from "./httpCheckoutsMarkAsPaid";
3
4
  import { httpCheckoutsStart } from "./httpCheckoutsStart";
4
5
  const toDomain = (checkout) => {
5
6
  invariant(checkout, "No checkout found!");
@@ -15,5 +16,10 @@ const toDomain = (checkout) => {
15
16
  };
16
17
  };
17
18
  const getCheckout = ({ queryBus }) => async (aggregateId) => toDomain(await queryBus(viewCheckoutById({ checkoutId: aggregateId })));
18
- const saveCheckout = ({ httpPost }) => async (aggregateRoot) => await httpCheckoutsStart({ httpPost })(aggregateRoot);
19
+ const saveCheckout = ({ httpPost }) => async (aggregateRoot) => {
20
+ await Promise.all([
21
+ httpCheckoutsStart({ httpPost })(aggregateRoot),
22
+ httpCheckoutsMarkAsPaid({ httpPost })(aggregateRoot),
23
+ ]);
24
+ };
19
25
  export { getCheckout, saveCheckout };
@@ -0,0 +1,5 @@
1
+ import { HttpCheckoutsSaveFunction } from "./httpCheckouts";
2
+ interface HttpCheckoutsMarkAsPaidFunction extends HttpCheckoutsSaveFunction {
3
+ }
4
+ declare const httpCheckoutsMarkAsPaid: HttpCheckoutsMarkAsPaidFunction;
5
+ export { httpCheckoutsMarkAsPaid };
@@ -0,0 +1,13 @@
1
+ import { CHECKOUT_PAID } from "../../../../domain/checkout/model/checkoutPaid";
2
+ const isCheckoutPaid = (event) => event.name === CHECKOUT_PAID;
3
+ const httpCheckoutsMarkAsPaid = ({ httpPost }) => async ({ aggregateId, domainEvents }) => {
4
+ const checkoutPaid = domainEvents.find(isCheckoutPaid);
5
+ if (!checkoutPaid) {
6
+ return;
7
+ }
8
+ await httpPost({
9
+ endpoint: "/mark-checkout-as-paid",
10
+ body: { checkoutId: aggregateId },
11
+ });
12
+ };
13
+ export { httpCheckoutsMarkAsPaid };
@@ -0,0 +1,13 @@
1
+ import { CommandStatus } from "@lookiero/messaging-react";
2
+ interface MarkAsPaidFunction {
3
+ (): Promise<void>;
4
+ }
5
+ declare type UseMarkCheckoutAsPaid = [markAsPaid: MarkAsPaidFunction, status: CommandStatus];
6
+ interface UseMarkCheckoutAsPaidFunctionArgs {
7
+ readonly checkoutId: string;
8
+ }
9
+ interface UseMarkCheckoutAsPaidFunction {
10
+ (args: UseMarkCheckoutAsPaidFunctionArgs): UseMarkCheckoutAsPaid;
11
+ }
12
+ declare const useMarkCheckoutAsPaid: UseMarkCheckoutAsPaidFunction;
13
+ export { useMarkCheckoutAsPaid };
@@ -0,0 +1,12 @@
1
+ import { useCommand } from "@lookiero/messaging-react";
2
+ import { useCallback } from "react";
3
+ import { markCheckoutAsPaid } from "../../../../domain/checkout/command/markCheckoutAsPaid";
4
+ import { MESSAGING_CONTEXT_ID } from "../../../delivery/baseBootstrap";
5
+ const useMarkCheckoutAsPaid = ({ checkoutId }) => {
6
+ const [commandBus, status] = useCommand({ contextId: MESSAGING_CONTEXT_ID });
7
+ const markAsPaid = useCallback(() => commandBus(markCheckoutAsPaid({
8
+ aggregateId: checkoutId,
9
+ })), [checkoutId, commandBus]);
10
+ return [markAsPaid, status];
11
+ };
12
+ export { useMarkCheckoutAsPaid };
@@ -0,0 +1,12 @@
1
+ import { PaymentFlowPayloadByCheckoutIdView } from "../../../projection/payment/viewPaymentFlowPayloadByCheckoutId";
2
+ import { HttpPostFunction } from "../../delivery/http/httpClient";
3
+ interface HttpPaymentFlowPayloadByCheckoutIdView extends PaymentFlowPayloadByCheckoutIdView {
4
+ }
5
+ interface HttpPaymentFlowPayloadByCheckoutIdViewFunctionArgs {
6
+ readonly httpPost: HttpPostFunction;
7
+ }
8
+ interface HttpPaymentFlowPayloadByCheckoutIdViewFunction {
9
+ (args: HttpPaymentFlowPayloadByCheckoutIdViewFunctionArgs): HttpPaymentFlowPayloadByCheckoutIdView;
10
+ }
11
+ declare const httpPaymentFlowPayloadByCheckoutIdView: HttpPaymentFlowPayloadByCheckoutIdViewFunction;
12
+ export { httpPaymentFlowPayloadByCheckoutIdView };
@@ -0,0 +1,9 @@
1
+ const httpPaymentFlowPayloadByCheckoutIdView = ({ httpPost }) => async ({ checkoutId, signal }) => {
2
+ const response = await httpPost({
3
+ endpoint: "/view-payment-flow-payload-by-checkout-id",
4
+ body: { checkoutId },
5
+ signal,
6
+ });
7
+ return !response.ok || response.status === 404 ? null : (await response.json()).result;
8
+ };
9
+ export { httpPaymentFlowPayloadByCheckoutIdView };
@@ -0,0 +1,10 @@
1
+ import { UseQueryFunctionResult } from "@lookiero/messaging-react";
2
+ import { PaymentFlowPayloadProjection } from "../../../../projection/payment/paymentFlowPayload";
3
+ interface UseViewPaymentFlowPayloadByCheckoutIdFunctionArgs {
4
+ readonly checkoutId: string;
5
+ }
6
+ interface UseViewPaymentFlowPayloadByCheckoutIdFunction {
7
+ (args: UseViewPaymentFlowPayloadByCheckoutIdFunctionArgs): UseQueryFunctionResult<PaymentFlowPayloadProjection>;
8
+ }
9
+ declare const useViewPaymentFlowPayloadByCheckoutId: UseViewPaymentFlowPayloadByCheckoutIdFunction;
10
+ export { useViewPaymentFlowPayloadByCheckoutId };
@@ -0,0 +1,17 @@
1
+ import { useQuery } from "@lookiero/messaging-react";
2
+ import { CHECKOUT_ITEM_KEPT } from "../../../../domain/checkoutItem/model/checkoutItemKept";
3
+ import { CHECKOUT_ITEM_REPLACED, } from "../../../../domain/checkoutItem/model/checkoutItemReplaced";
4
+ import { CHECKOUT_ITEM_RETURNED, } from "../../../../domain/checkoutItem/model/checkoutItemReturned";
5
+ import { viewPaymentFlowPayloadByCheckoutId } from "../../../../projection/payment/viewPaymentFlowPayloadByCheckoutId";
6
+ import { MESSAGING_CONTEXT_ID } from "../../../delivery/baseBootstrap";
7
+ const isCheckoutItemKept = (event) => event.name === CHECKOUT_ITEM_KEPT;
8
+ const isCheckoutItemReturned = (event) => event.name === CHECKOUT_ITEM_RETURNED;
9
+ const isCheckoutItemReplaced = (event) => event.name === CHECKOUT_ITEM_REPLACED;
10
+ const shouldInvalidate = (event) => isCheckoutItemKept(event) || isCheckoutItemReplaced(event) || isCheckoutItemReturned(event);
11
+ const useViewPaymentFlowPayloadByCheckoutId = ({ checkoutId }) => useQuery({
12
+ query: viewPaymentFlowPayloadByCheckoutId({ checkoutId }),
13
+ contextId: MESSAGING_CONTEXT_ID,
14
+ invalidation: shouldInvalidate,
15
+ options: { staleTime: Infinity, retry: false, refetchOnWindowFocus: false },
16
+ });
17
+ export { useViewPaymentFlowPayloadByCheckoutId };
@@ -1,8 +1,11 @@
1
1
  import { FC, ReactNode } from "react";
2
+ import { StyleProp, ViewStyle } from "react-native";
2
3
  import { Edge } from "react-native-safe-area-context";
4
+ declare type SafeAreaScrollViewStyle = "safeAreaView" | "scrollView";
3
5
  interface SafeAreaScrollViewProps {
4
6
  readonly children: ReactNode;
5
7
  readonly edges?: Edge[];
8
+ readonly style?: Partial<Record<SafeAreaScrollViewStyle, StyleProp<ViewStyle>>>;
6
9
  }
7
10
  declare const SafeAreaScrollView: FC<SafeAreaScrollViewProps>;
8
11
  export { SafeAreaScrollView };
@@ -3,12 +3,15 @@ import { ScrollView } from "react-native";
3
3
  import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context";
4
4
  import { theme } from "../../theme/theme";
5
5
  const { spaceXL } = theme();
6
- const SafeAreaScrollView = ({ children, edges = ["top", "bottom", "left", "right"] }) => {
6
+ const SafeAreaScrollView = ({ children, edges = ["top", "bottom", "left", "right"], style: customStyle, }) => {
7
7
  const { top: safeAreaInsetTop, bottom: safeAreaInsetBottom } = useSafeAreaInsets();
8
- return (React.createElement(SafeAreaView, { edges: edges },
9
- React.createElement(ScrollView, { contentContainerStyle: {
10
- paddingTop: !safeAreaInsetTop ? spaceXL : 0,
11
- paddingBottom: !safeAreaInsetBottom ? spaceXL : 0,
12
- } }, children)));
8
+ return (React.createElement(SafeAreaView, { edges: edges, style: customStyle?.safeAreaView },
9
+ React.createElement(ScrollView, { contentContainerStyle: [
10
+ {
11
+ paddingTop: !safeAreaInsetTop ? spaceXL : 0,
12
+ paddingBottom: !safeAreaInsetBottom ? spaceXL : 0,
13
+ },
14
+ customStyle?.scrollView,
15
+ ] }, children)));
13
16
  };
14
17
  export { SafeAreaScrollView };
@@ -33,6 +33,9 @@ declare enum I18nMessages {
33
33
  FEEDBACK_UNANSWERED = "feedback.unanswered",
34
34
  RETURN_QUESTIONS_TITLE = "return_questions.title",
35
35
  RETURN_QUESTIONS_SUBMIT_BUTTON = "return_questions.submit_button",
36
+ SUMMARY_TITLE = "summary.title",
37
+ SUMMARY_DESCRIPTION = "summary.description",
38
+ SUMMARY_BANNER = "summary.banner",
36
39
  SUMMARY_KEEP_TAB = "summary.keep_tab",
37
40
  SUMMARY_RETURN_TAB = "summary.return_tab",
38
41
  SUMMARY_KEEP_EMPTY = "summary.keep_empty",
@@ -34,6 +34,9 @@ var I18nMessages;
34
34
  I18nMessages["FEEDBACK_UNANSWERED"] = "feedback.unanswered";
35
35
  I18nMessages["RETURN_QUESTIONS_TITLE"] = "return_questions.title";
36
36
  I18nMessages["RETURN_QUESTIONS_SUBMIT_BUTTON"] = "return_questions.submit_button";
37
+ I18nMessages["SUMMARY_TITLE"] = "summary.title";
38
+ I18nMessages["SUMMARY_DESCRIPTION"] = "summary.description";
39
+ I18nMessages["SUMMARY_BANNER"] = "summary.banner";
37
40
  I18nMessages["SUMMARY_KEEP_TAB"] = "summary.keep_tab";
38
41
  I18nMessages["SUMMARY_RETURN_TAB"] = "summary.return_tab";
39
42
  I18nMessages["SUMMARY_KEEP_EMPTY"] = "summary.keep_empty";
@@ -13,6 +13,7 @@ const CheckoutMiddleware = ({ basePath, customerId, loader = React.createElement
13
13
  const firstItemWithoutCustomerDecision = checkout?.items.find((item) => [CheckoutItemStatus.EXPIRED, CheckoutItemStatus.INITIAL].includes(item.status));
14
14
  const introRouteMatch = useMatch(`${basePath}/${Routes.INTRO}`);
15
15
  const itemRouteMatch = useMatch(`${basePath}/${Routes.ITEM}`);
16
+ const itemDetailRouteMatch = useMatch(`${basePath}/${Routes.ITEM_DETAIL}`);
16
17
  const summaryRouteMatch = useMatch(`${basePath}/${Routes.SUMMARY}`);
17
18
  const dependenciesLoadedStatuses = [QueryStatus.ERROR, QueryStatus.SUCCESS];
18
19
  const dependenciesLoaded = dependenciesLoadedStatuses.includes(shouldIntroBeShownStatus) &&
@@ -29,8 +30,8 @@ const CheckoutMiddleware = ({ basePath, customerId, loader = React.createElement
29
30
  }
30
31
  }
31
32
  else {
32
- if (itemRouteMatch) {
33
- const checkoutItem = checkout?.items.find((item) => item.id === itemRouteMatch.params.id);
33
+ if (itemRouteMatch || itemDetailRouteMatch) {
34
+ const checkoutItem = checkout?.items.find((item) => item.id === itemRouteMatch?.params.id || itemDetailRouteMatch?.params.id);
34
35
  if (!checkoutItem) {
35
36
  return React.createElement(Navigate, { to: `${basePath}/${Routes.HOME}`, replace: true });
36
37
  }
@@ -38,7 +39,7 @@ const CheckoutMiddleware = ({ basePath, customerId, loader = React.createElement
38
39
  if (firstItemWithoutCustomerDecision && !itemRouteMatch) {
39
40
  return (React.createElement(Navigate, { to: generatePath(`${basePath}/${Routes.ITEM}`, { id: firstItemWithoutCustomerDecision.id }), replace: true }));
40
41
  }
41
- if (firstItemWithoutCustomerDecision === undefined && !summaryRouteMatch) {
42
+ if (firstItemWithoutCustomerDecision === undefined && !summaryRouteMatch && !itemDetailRouteMatch) {
42
43
  return React.createElement(Navigate, { to: `${basePath}/${Routes.SUMMARY}`, replace: true });
43
44
  }
44
45
  }
@@ -28,6 +28,11 @@ const Routing = ({ basePath = "", onNotAccessible, customerId, locale, I18n, onI
28
28
  element: (React.createElement(Suspense, { fallback: React.createElement(Spinner, null) },
29
29
  React.createElement(Item, { customerId: customerId }))),
30
30
  },
31
+ {
32
+ path: Routes.ITEM_DETAIL,
33
+ element: (React.createElement(Suspense, { fallback: React.createElement(Spinner, null) },
34
+ React.createElement(Item, { customerId: customerId }))),
35
+ },
31
36
  {
32
37
  path: Routes.SUMMARY,
33
38
  element: (React.createElement(Suspense, { fallback: React.createElement(Spinner, null) },
@@ -1,6 +1,7 @@
1
1
  export declare enum Routes {
2
2
  HOME = "",
3
3
  INTRO = "intro",
4
+ ITEM_DETAIL = "item/:id/detail",
4
5
  ITEM = "item/:id",
5
6
  SUMMARY = "summary"
6
7
  }
@@ -2,6 +2,7 @@ export var Routes;
2
2
  (function (Routes) {
3
3
  Routes["HOME"] = "";
4
4
  Routes["INTRO"] = "intro";
5
+ Routes["ITEM_DETAIL"] = "item/:id/detail";
5
6
  Routes["ITEM"] = "item/:id";
6
7
  Routes["SUMMARY"] = "summary";
7
8
  })(Routes || (Routes = {}));
@@ -3,10 +3,10 @@ import { View } from "react-native";
3
3
  import { useSafeAreaInsets } from "react-native-safe-area-context";
4
4
  import { theme } from "../../../../theme/theme";
5
5
  import { style } from "./Sticky.style";
6
- const { spaceL } = theme();
6
+ const { spaceXL } = theme();
7
7
  const Sticky = ({ children, style: customStyle, onLayout }) => {
8
8
  const { bottom: safeAreaInsetBottom } = useSafeAreaInsets();
9
9
  const handleOnLayout = useCallback(({ nativeEvent: { layout: { width, height }, }, }) => onLayout?.({ width, height }), [onLayout]);
10
- return (React.createElement(View, { style: [style.sticky, { paddingBottom: safeAreaInsetBottom + spaceL }, customStyle], onLayout: handleOnLayout }, children));
10
+ return (React.createElement(View, { style: [style.sticky, { paddingBottom: safeAreaInsetBottom + spaceXL }, customStyle], onLayout: handleOnLayout }, children));
11
11
  };
12
12
  export { Sticky };
@@ -1,6 +1,6 @@
1
1
  import { Platform, StyleSheet } from "react-native";
2
2
  import { theme } from "../../../../theme/theme";
3
- const { colorBase, elevationLayerL, elevationRadius, spaceL } = theme();
3
+ const { colorBase, elevationLayerL, elevationRadius, spaceXL } = theme();
4
4
  const style = StyleSheet.create({
5
5
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
6
6
  // @ts-ignore
@@ -8,7 +8,7 @@ const style = StyleSheet.create({
8
8
  backgroundColor: colorBase,
9
9
  bottom: 0,
10
10
  elevation: elevationLayerL,
11
- paddingTop: spaceL,
11
+ paddingTop: spaceXL,
12
12
  shadowColor: colorBase,
13
13
  shadowOffset: {
14
14
  height: -40,
@@ -1,25 +1,49 @@
1
+ import { Text } from "@lookiero/aurora-next/build/components/primitives/Text/Text";
2
+ import { useI18nMessage } from "@lookiero/i18n-react";
1
3
  import { QueryStatus } from "@lookiero/messaging-react";
2
- import React, { useCallback, useState } from "react";
4
+ import React, { useCallback, useMemo, useState } from "react";
5
+ import { View } from "react-native";
6
+ import { generatePath, useNavigate } from "react-router-native";
7
+ import { CheckoutItemStatus } from "../../../../domain/checkoutItem/model/checkoutItem";
3
8
  import { useViewFirstAvailableCheckoutByCustomerId } from "../../../projection/checkout/react/useViewFirstAvailableCheckoutByCustomerId";
4
- import { pricing } from "../../../projection/pricing/pricing.mock";
9
+ import { useViewFiveItemsDiscountByCustomerId } from "../../../projection/checkout/react/useViewFiveItemsDiscountByCustomerId";
10
+ import { useViewPricingByCheckoutId } from "../../../projection/pricing/react/useViewPricingByCheckoutId";
5
11
  import { Body } from "../../components/layouts/body/Body";
6
12
  import { SafeAreaScrollView } from "../../components/templates/SafeAreaScrollView";
13
+ import { I18nMessages } from "../../i18n/i18n";
14
+ import { Routes } from "../../routing/routes";
7
15
  import { Spinner } from "../../shared/components/atoms/spinner/Spinner";
16
+ import { style } from "./Summary.style";
8
17
  import { CheckoutItemsTabs } from "./components/checkoutItemsTabs/CheckoutItemsTabs";
9
- import { Pricing } from "./components/pricing/Pricing";
18
+ import { FiveItemsDiscountBanner } from "./components/fiveItemsDiscountBanner/FiveItemsDiscountBanner";
19
+ import { StickyPricing } from "./components/stickyPricing/StickyPricing";
10
20
  const Summary = ({ customerId }) => {
21
+ const titleText = useI18nMessage({ id: I18nMessages.SUMMARY_TITLE });
22
+ const descriptionText = useI18nMessage({ id: I18nMessages.SUMMARY_DESCRIPTION });
11
23
  const [checkout, checkoutStatus] = useViewFirstAvailableCheckoutByCustomerId({ customerId });
24
+ const [pricing, pricingStatus] = useViewPricingByCheckoutId({ checkoutId: checkout?.id });
25
+ const [fiveItemsDiscount = 0, fiveItemsDiscountStatus] = useViewFiveItemsDiscountByCustomerId({ customerId });
12
26
  const [pricingCollapsed, setPricingCollapsed] = useState(true);
13
27
  const handleOnPressPricing = useCallback(() => setPricingCollapsed(!pricingCollapsed), [pricingCollapsed]);
14
28
  const handleOnSubmit = useCallback(() => void 0, []);
15
- const handleOnPressItem = useCallback(() => void 0, []);
29
+ const navigate = useNavigate();
30
+ const handleOnPressItem = useCallback((checkoutItemId) => navigate(generatePath(Routes.ITEM_DETAIL, { id: checkoutItemId })), [navigate]);
31
+ const checkoutItemsKept = useMemo(() => checkout?.items.filter((checkoutItem) => checkoutItem.status === CheckoutItemStatus.KEPT || checkoutItem.status === CheckoutItemStatus.REPLACED), [checkout?.items]);
32
+ const checkoutItemsReturned = useMemo(() => checkout?.items.filter((checkoutItem) => checkoutItem.status === CheckoutItemStatus.RETURNED || checkoutItem.status === CheckoutItemStatus.REPLACED), [checkout?.items]);
16
33
  const dependenciesLoadedStatuses = [QueryStatus.ERROR, QueryStatus.SUCCESS];
17
- const dependenciesLoaded = dependenciesLoadedStatuses.includes(checkoutStatus);
34
+ const dependenciesLoaded = dependenciesLoadedStatuses.includes(checkoutStatus) &&
35
+ dependenciesLoadedStatuses.includes(pricingStatus) &&
36
+ dependenciesLoadedStatuses.includes(fiveItemsDiscountStatus);
18
37
  if (!dependenciesLoaded)
19
38
  return React.createElement(Spinner, null);
20
- return (React.createElement(SafeAreaScrollView, null,
21
- React.createElement(Body, null,
22
- React.createElement(CheckoutItemsTabs, { checkoutItems: checkout?.items || [], onPressItem: handleOnPressItem }),
23
- React.createElement(Pricing, { balanceDiscount: pricing.balanceDiscount, collapsed: pricingCollapsed, discount: pricing.discount, discountPercentage: pricing.discountPercentage, orderTotal: pricing.orderTotal, service: pricing.service, subtotal: pricing.subtotal, totalItemsKept: 3, onPress: handleOnPressPricing, onSubmit: handleOnSubmit }))));
39
+ return (React.createElement(React.Fragment, null,
40
+ React.createElement(SafeAreaScrollView, { style: { safeAreaView: style.container } },
41
+ React.createElement(Body, null,
42
+ fiveItemsDiscount !== 0 && React.createElement(FiveItemsDiscountBanner, { fiveItemsDiscount: fiveItemsDiscount }),
43
+ React.createElement(View, { style: style.content },
44
+ React.createElement(Text, { level: 3, style: style.title, heading: true }, titleText),
45
+ React.createElement(Text, { level: 3, style: style.description, body: true }, descriptionText)),
46
+ React.createElement(CheckoutItemsTabs, { checkoutItemsKept: checkoutItemsKept || [], checkoutItemsReturned: checkoutItemsReturned || [], onPressItem: handleOnPressItem }))),
47
+ pricing && (React.createElement(StickyPricing, { collapsed: pricingCollapsed, pricing: pricing, totalCheckoutItemsKept: checkoutItemsKept?.length || 0, onPress: handleOnPressPricing, onSubmit: handleOnSubmit }))));
24
48
  };
25
49
  export { Summary };
@@ -0,0 +1,18 @@
1
+ declare const style: {
2
+ container: {
3
+ flex: number;
4
+ };
5
+ content: {
6
+ paddingHorizontal: number;
7
+ };
8
+ description: {
9
+ marginBottom: number;
10
+ marginTop: number;
11
+ textAlign: "center";
12
+ };
13
+ title: {
14
+ marginTop: number;
15
+ textAlign: "center";
16
+ };
17
+ };
18
+ export { style };
@@ -0,0 +1,21 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { theme } from "../../theme/theme";
3
+ const { spaceM, spaceL, spaceXL } = theme();
4
+ const style = StyleSheet.create({
5
+ container: {
6
+ flex: 1,
7
+ },
8
+ content: {
9
+ paddingHorizontal: spaceXL,
10
+ },
11
+ description: {
12
+ marginBottom: spaceXL,
13
+ marginTop: spaceM,
14
+ textAlign: "center",
15
+ },
16
+ title: {
17
+ marginTop: spaceL,
18
+ textAlign: "center",
19
+ },
20
+ });
21
+ export { style };
@@ -1,7 +1,8 @@
1
1
  import { FC } from "react";
2
2
  import { CheckoutItemProjection } from "../../../../../../projection/checkoutItem/checkoutItem";
3
3
  interface CheckoutItemsTabsProps {
4
- readonly checkoutItems: CheckoutItemProjection[];
4
+ readonly checkoutItemsKept: CheckoutItemProjection[];
5
+ readonly checkoutItemsReturned: CheckoutItemProjection[];
5
6
  readonly onPressItem: (checkoutItemId: string) => void;
6
7
  }
7
8
  declare const CheckoutItemsTabs: FC<CheckoutItemsTabsProps>;
@@ -1,24 +1,21 @@
1
1
  import { Text } from "@lookiero/aurora-next/build/components/primitives/Text/Text";
2
2
  import { useI18nMessage } from "@lookiero/i18n-react";
3
- import React, { useCallback, useMemo } from "react";
3
+ import React, { useCallback } from "react";
4
4
  import { View } from "react-native";
5
- import { CheckoutItemStatus } from "../../../../../../domain/checkoutItem/model/checkoutItem";
6
5
  import { Tabs } from "../../../../components/layouts/tabs/Tabs";
7
6
  import { I18nMessages } from "../../../../i18n/i18n";
8
7
  import { ProductVariant } from "../../../item/components/productVariant/ProductVariant";
9
8
  import { style } from "./CheckoutItemsTabs.style";
10
9
  const CheckoutItem = ({ checkoutItem, discarded = false, testID, style: customStyle, onPress, }) => (React.createElement(View, { style: style.checkoutItem, testID: testID },
11
10
  React.createElement(ProductVariant, { brand: checkoutItem.productVariant.brand, color: checkoutItem.productVariant.color, discarded: discarded, media: checkoutItem.productVariant.media, name: checkoutItem.productVariant.name, price: checkoutItem.price, size: checkoutItem.productVariant.size, status: checkoutItem.status, style: customStyle, onPress: onPress })));
12
- const CheckoutItemsTabs = ({ checkoutItems, onPressItem }) => {
11
+ const CheckoutItemsTabs = ({ checkoutItemsKept, checkoutItemsReturned, onPressItem }) => {
13
12
  const keepTabText = useI18nMessage({ id: I18nMessages.SUMMARY_KEEP_TAB });
14
13
  const returnTabText = useI18nMessage({ id: I18nMessages.SUMMARY_RETURN_TAB });
15
14
  const keepEmptyText = useI18nMessage({ id: I18nMessages.SUMMARY_KEEP_EMPTY });
16
15
  const returnEmptyText = useI18nMessage({ id: I18nMessages.SUMMARY_RETURN_EMPTY });
17
- const keepCheckoutItems = useMemo(() => checkoutItems.filter((checkoutItem) => checkoutItem.status === CheckoutItemStatus.KEPT || checkoutItem.status === CheckoutItemStatus.REPLACED), [checkoutItems]);
18
- const returnCheckoutItems = useMemo(() => checkoutItems.filter((checkoutItem) => checkoutItem.status === CheckoutItemStatus.RETURNED || checkoutItem.status === CheckoutItemStatus.REPLACED), [checkoutItems]);
19
16
  const handleOnPressItem = useCallback((checkoutItemId) => onPressItem(checkoutItemId), [onPressItem]);
20
- return (React.createElement(Tabs, { style: { sliderContainer: style.sliderContainer }, tabLabels: [`${keepTabText} (${keepCheckoutItems.length})`, `${returnTabText} (${returnCheckoutItems.length})`] },
21
- React.createElement(React.Fragment, null, keepCheckoutItems.length === 0 ? (React.createElement(Text, null, keepEmptyText)) : (keepCheckoutItems.map((checkoutItem) => (React.createElement(CheckoutItem, { key: checkoutItem.id, checkoutItem: checkoutItem, testID: "keep-checkout-item", onPress: () => handleOnPressItem(checkoutItem.id) }))))),
22
- React.createElement(React.Fragment, null, returnCheckoutItems.length === 0 ? (React.createElement(Text, null, returnEmptyText)) : (returnCheckoutItems.map((checkoutItem) => (React.createElement(CheckoutItem, { key: checkoutItem.id, checkoutItem: checkoutItem, style: { image: style.returnCheckoutItem }, testID: "return-checkout-item", discarded: true, onPress: () => handleOnPressItem(checkoutItem.id) })))))));
17
+ return (React.createElement(Tabs, { style: { sliderContainer: style.sliderContainer }, tabLabels: [`${keepTabText} (${checkoutItemsKept.length})`, `${returnTabText} (${checkoutItemsReturned.length})`] },
18
+ React.createElement(React.Fragment, null, checkoutItemsKept.length === 0 ? (React.createElement(Text, null, keepEmptyText)) : (checkoutItemsKept.map((checkoutItem) => (React.createElement(CheckoutItem, { key: checkoutItem.id, checkoutItem: checkoutItem, testID: "keep-checkout-item", onPress: () => handleOnPressItem(checkoutItem.id) }))))),
19
+ React.createElement(React.Fragment, null, checkoutItemsReturned.length === 0 ? (React.createElement(Text, null, returnEmptyText)) : (checkoutItemsReturned.map((checkoutItem) => (React.createElement(CheckoutItem, { key: checkoutItem.id, checkoutItem: checkoutItem, style: { image: style.returnCheckoutItem }, testID: "return-checkout-item", discarded: true, onPress: () => handleOnPressItem(checkoutItem.id) })))))));
23
20
  };
24
21
  export { CheckoutItemsTabs };
@@ -0,0 +1,6 @@
1
+ import { FC } from "react";
2
+ interface FiveItemsDiscountBannerProps {
3
+ readonly fiveItemsDiscount: number;
4
+ }
5
+ declare const FiveItemsDiscountBanner: FC<FiveItemsDiscountBannerProps>;
6
+ export { FiveItemsDiscountBanner };
@@ -0,0 +1,15 @@
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 { View } from "react-native";
5
+ import { I18nMessages } from "../../../../i18n/i18n";
6
+ import { style } from "./FiveItemsDiscountBanner.style";
7
+ const FiveItemsDiscountBanner = ({ fiveItemsDiscount }) => {
8
+ const bannerText = useI18nMessage({
9
+ id: I18nMessages.SUMMARY_BANNER,
10
+ values: { discount: fiveItemsDiscount.toString() },
11
+ });
12
+ return (React.createElement(View, { style: style.container },
13
+ React.createElement(Text, { level: 3, body: true }, bannerText)));
14
+ };
15
+ export { FiveItemsDiscountBanner };
@@ -0,0 +1,9 @@
1
+ declare const style: {
2
+ container: {
3
+ alignItems: "center";
4
+ backgroundColor: string;
5
+ paddingHorizontal: number;
6
+ paddingVertical: number;
7
+ };
8
+ };
9
+ export { style };
@@ -0,0 +1,12 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { theme } from "../../../../theme/theme";
3
+ const { colorAccent, spaceXL } = theme();
4
+ const style = StyleSheet.create({
5
+ container: {
6
+ alignItems: "center",
7
+ backgroundColor: colorAccent,
8
+ paddingHorizontal: spaceXL,
9
+ paddingVertical: 12,
10
+ },
11
+ });
12
+ export { style };
@@ -7,7 +7,7 @@ interface PricingProps {
7
7
  readonly balanceDiscount?: PriceProjection;
8
8
  readonly discount?: PriceProjection;
9
9
  readonly discountPercentage?: number;
10
- readonly totalItemsKept: number;
10
+ readonly totalCheckoutItemsKept: number;
11
11
  readonly service: ServiceProjection;
12
12
  readonly collapsible?: boolean;
13
13
  readonly collapsed?: boolean;
@@ -6,17 +6,18 @@ import React from "react";
6
6
  import { Pressable, View } from "react-native";
7
7
  import { Price } from "../../../../components/atoms/price/Price";
8
8
  import { I18nMessages } from "../../../../i18n/i18n";
9
- import { Icon } from "../../../../shared/components/atoms/icon/Icon";
10
9
  import { style } from "./Pricing.style";
10
+ import { ArrowDown } from "./icons/ArrowDown";
11
+ import { ArrowUp } from "./icons/ArrowUp";
11
12
  const PS_SERVICE_FREE_INDICATOR = 100;
12
13
  const Row = ({ text, level = 3, children }) => (React.createElement(View, { style: style.row },
13
14
  React.createElement(Text, { level: level, style: style.uppercase, action: true }, text),
14
15
  children));
15
- const Pricing = ({ orderTotal, subtotal, balanceDiscount, discount, discountPercentage = 0, totalItemsKept, service, collapsible = true, collapsed = false, onPress = () => void 0, onSubmit, }) => {
16
+ const Pricing = ({ orderTotal, subtotal, balanceDiscount, discount, discountPercentage = 0, totalCheckoutItemsKept, service, collapsible = true, collapsed = false, onPress = () => void 0, onSubmit, }) => {
16
17
  const totalText = useI18nMessage({ id: I18nMessages.SUMMARY_TOTAL }) || "";
17
- const totalItemsKeptText = useI18nMessage({
18
+ const totalCheckoutItemsKeptText = useI18nMessage({
18
19
  id: I18nMessages.SUMMARY_TOTAL_ITEMS_KEPT,
19
- values: { items: totalItemsKept.toString() },
20
+ values: { items: totalCheckoutItemsKept.toString() },
20
21
  });
21
22
  const subtotalText = useI18nMessage({ id: I18nMessages.SUMMARY_SUBTOTAL });
22
23
  const submitButtonText = useI18nMessage({ id: I18nMessages.SUMMARY_SUBMIT_BUTTON });
@@ -30,19 +31,18 @@ const Pricing = ({ orderTotal, subtotal, balanceDiscount, discount, discountPerc
30
31
  const isPSServiceFree = service.discount.amount === PS_SERVICE_FREE_INDICATOR;
31
32
  const collapsedStyle = useSpring({ opacitiy: collapsed ? 1 : 0 });
32
33
  const notCollapsedStyle = useSpring({ opacitiy: collapsed ? 0 : 1 });
33
- return (React.createElement(Pressable, { disabled: !collapsible, style: style.container, testID: "pricing", onPress: onPress },
34
- collapsible && (React.createElement(View, { style: style.iconContainer },
35
- React.createElement(Icon, { name: collapsed ? "arrow-up" : "arrow-down" }))),
34
+ return (React.createElement(Pressable, { disabled: !collapsible, testID: "pricing", onPress: onPress },
35
+ collapsible && (React.createElement(View, { style: style.iconContainer }, collapsed ? (React.createElement(ArrowUp, { style: style.icon, testID: "arrow-up" })) : (React.createElement(ArrowDown, { style: style.icon, testID: "arrow-down" })))),
36
36
  collapsed && collapsible ? (React.createElement(animated.View, { style: [style.collapsed, { opacity: collapsedStyle.opacitiy }] },
37
37
  React.createElement(View, null,
38
38
  React.createElement(Text, { level: 1, style: style.totalCollapsed, detail: true },
39
39
  totalText,
40
40
  " ",
41
- totalItemsKeptText),
41
+ totalCheckoutItemsKeptText),
42
42
  React.createElement(Price, { price: orderTotal, variant: "detail" })),
43
43
  React.createElement(View, null,
44
44
  React.createElement(Button, { small: true, onPress: onSubmit }, submitButtonText)))) : (React.createElement(animated.View, { style: { opacity: notCollapsedStyle.opacitiy } },
45
- React.createElement(Row, { text: `${subtotalText} ${totalItemsKeptText}` },
45
+ React.createElement(Row, { text: `${subtotalText} ${totalCheckoutItemsKeptText}` },
46
46
  React.createElement(Price, { price: subtotal, variant: "subtotal" })),
47
47
  discount && (React.createElement(Row, { text: discountText },
48
48
  React.createElement(Price, { price: discount, variant: "subtotal" }))),
@@ -3,16 +3,16 @@ declare const style: {
3
3
  flexDirection: "row";
4
4
  justifyContent: "space-between";
5
5
  };
6
- container: {
7
- paddingBottom: number;
8
- paddingHorizontal: number;
9
- paddingTop: number;
10
- };
11
6
  divider: {
12
7
  backgroundColor: string;
13
8
  height: number;
14
9
  marginBottom: number;
15
10
  };
11
+ icon: {
12
+ height: number;
13
+ stroke: string;
14
+ width: number;
15
+ };
16
16
  iconContainer: {
17
17
  alignItems: "center";
18
18
  marginBottom: number;
@@ -1,21 +1,21 @@
1
1
  import { StyleSheet } from "react-native";
2
2
  import { theme } from "../../../../theme/theme";
3
- const { colorContent, spaceXS, spaceM, spaceL, spaceXL } = theme();
3
+ const { colorContent, colorGrayscaleS, spaceXS, spaceL } = theme();
4
4
  const style = StyleSheet.create({
5
5
  collapsed: {
6
6
  flexDirection: "row",
7
7
  justifyContent: "space-between",
8
8
  },
9
- container: {
10
- paddingBottom: spaceXL,
11
- paddingHorizontal: spaceXL,
12
- paddingTop: spaceM,
13
- },
14
9
  divider: {
15
10
  backgroundColor: colorContent,
16
11
  height: 1,
17
12
  marginBottom: 19,
18
13
  },
14
+ icon: {
15
+ height: 10,
16
+ stroke: colorGrayscaleS,
17
+ width: 58,
18
+ },
19
19
  iconContainer: {
20
20
  alignItems: "center",
21
21
  marginBottom: spaceL,
@@ -0,0 +1,4 @@
1
+ /// <reference types="react" />
2
+ import { SvgProps } from "react-native-svg";
3
+ declare const ArrowDown: (props: SvgProps) => JSX.Element;
4
+ export { ArrowDown };
@@ -0,0 +1,5 @@
1
+ import * as React from "react";
2
+ import Svg, { Path } from "react-native-svg";
3
+ const ArrowDown = (props) => (React.createElement(Svg, { viewBox: "0 0 58 10", ...props },
4
+ React.createElement(Path, { d: "M1.198 1.23 28.27 8.698M55.802 1.23 28.73 8.698", strokeLinecap: "round", strokeWidth: 2 })));
5
+ export { ArrowDown };
@@ -0,0 +1,4 @@
1
+ /// <reference types="react" />
2
+ import { SvgProps } from "react-native-svg";
3
+ declare const ArrowUp: (props: SvgProps) => JSX.Element;
4
+ export { ArrowUp };
@@ -0,0 +1,5 @@
1
+ import React from "react";
2
+ import Svg, { Path } from "react-native-svg";
3
+ const ArrowUp = (props) => (React.createElement(Svg, { viewBox: "0 0 58 10", ...props },
4
+ React.createElement(Path, { d: "M1.698 8.77 28.77 1.302M56.302 8.77 29.23 1.302", strokeLinecap: "round", strokeWidth: 2 })));
5
+ export { ArrowUp };
@@ -0,0 +1,11 @@
1
+ import { FC } from "react";
2
+ import { PricingProjection } from "../../../../../../projection/pricing/pricing";
3
+ interface StickyPricingProps {
4
+ readonly pricing: PricingProjection;
5
+ readonly totalCheckoutItemsKept: number;
6
+ readonly collapsed: boolean;
7
+ readonly onPress: () => void;
8
+ readonly onSubmit: () => void;
9
+ }
10
+ declare const StickyPricing: FC<StickyPricingProps>;
11
+ export { StickyPricing };
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import { Body } from "../../../../components/layouts/body/Body";
3
+ import { Sticky } from "../../../../shared/components/layouts/sticky/Sticky";
4
+ import { Pricing } from "../pricing/Pricing";
5
+ import { style } from "./StickyPricing.style";
6
+ const StickyPricing = ({ pricing, totalCheckoutItemsKept, collapsed, onPress, onSubmit }) => (React.createElement(Sticky, null,
7
+ React.createElement(Body, { style: { column: style.container } },
8
+ React.createElement(Pricing, { balanceDiscount: pricing.balanceDiscount, collapsed: collapsed, discount: pricing.discount, discountPercentage: pricing.discountPercentage, orderTotal: pricing.orderTotal, service: pricing.service, subtotal: pricing.subtotal, totalCheckoutItemsKept: totalCheckoutItemsKept, onPress: onPress, onSubmit: onSubmit }))));
9
+ export { StickyPricing };
@@ -0,0 +1,6 @@
1
+ declare const style: {
2
+ container: {
3
+ paddingHorizontal: number;
4
+ };
5
+ };
6
+ export { style };
@@ -0,0 +1,9 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { theme } from "../../../../theme/theme";
3
+ const { spaceXL } = theme();
4
+ const style = StyleSheet.create({
5
+ container: {
6
+ paddingHorizontal: spaceXL,
7
+ },
8
+ });
9
+ export { style };
@@ -0,0 +1,39 @@
1
+ interface PaymentFlowPayloadItem {
2
+ readonly reference: string;
3
+ }
4
+ interface PaymentFlowPayloadReturnedItemFeedback {
5
+ readonly name: string;
6
+ readonly value: string;
7
+ }
8
+ interface PaymentFlowPayloadReturnedItem extends PaymentFlowPayloadItem {
9
+ readonly feedback: {
10
+ readonly comment?: PaymentFlowPayloadReturnedItemFeedback;
11
+ readonly price?: PaymentFlowPayloadReturnedItemFeedback;
12
+ readonly quality?: PaymentFlowPayloadReturnedItemFeedback;
13
+ readonly shape?: PaymentFlowPayloadReturnedItemFeedback;
14
+ readonly size?: PaymentFlowPayloadReturnedItemFeedback;
15
+ readonly style?: PaymentFlowPayloadReturnedItemFeedback;
16
+ };
17
+ }
18
+ interface PaymentFlowPayloadReplacedItem extends PaymentFlowPayloadItem {
19
+ readonly sizeChange: {
20
+ readonly brand: string;
21
+ readonly color: string;
22
+ readonly description: string;
23
+ readonly id: string;
24
+ readonly reference: string;
25
+ readonly size: string;
26
+ readonly title: string;
27
+ };
28
+ }
29
+ interface PaymentFlowPayloadKeptItem extends PaymentFlowPayloadItem {
30
+ }
31
+ interface PaymentFlowPayloadProjection {
32
+ readonly checkout: {
33
+ readonly bookingId: string;
34
+ readonly orderId: string;
35
+ readonly comment: string;
36
+ readonly items: (PaymentFlowPayloadKeptItem | PaymentFlowPayloadReplacedItem | PaymentFlowPayloadReturnedItem)[];
37
+ };
38
+ }
39
+ export type { PaymentFlowPayloadProjection };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import { CancelableQueryViewArgs, Query, QueryHandlerFunction, QueryHandlerFunctionArgs } from "@lookiero/messaging";
2
+ import { PaymentFlowPayloadProjection } from "./paymentFlowPayload";
3
+ declare const VIEW_PAYMENT_FLOW_PAYLOAD_BY_CHECKOUT_ID = "view_payment_flow_payload_by_checkout_id";
4
+ interface ViewPaymentFlowPayloadByCheckoutIdPayload {
5
+ readonly checkoutId: string;
6
+ }
7
+ interface ViewPaymentFlowPayloadByCheckoutId extends Query<typeof VIEW_PAYMENT_FLOW_PAYLOAD_BY_CHECKOUT_ID>, ViewPaymentFlowPayloadByCheckoutIdPayload {
8
+ }
9
+ interface ViewPaymentFlowPayloadByCheckoutIdFunction {
10
+ (payload: ViewPaymentFlowPayloadByCheckoutIdPayload): ViewPaymentFlowPayloadByCheckoutId;
11
+ }
12
+ declare const viewPaymentFlowPayloadByCheckoutId: ViewPaymentFlowPayloadByCheckoutIdFunction;
13
+ declare type ViewPaymentFlowPayloadByCheckoutIdResult = PaymentFlowPayloadProjection | null;
14
+ interface PaymentFlowPayloadByCheckoutIdViewArgs extends CancelableQueryViewArgs {
15
+ readonly checkoutId: string;
16
+ }
17
+ interface PaymentFlowPayloadByCheckoutIdView {
18
+ (args: PaymentFlowPayloadByCheckoutIdViewArgs): Promise<ViewPaymentFlowPayloadByCheckoutIdResult>;
19
+ }
20
+ interface ViewPaymentFlowPayloadByCheckoutIdHandlerFunctionArgs extends QueryHandlerFunctionArgs {
21
+ readonly view: PaymentFlowPayloadByCheckoutIdView;
22
+ }
23
+ declare const viewPaymentFlowPayloadByCheckoutIdHandler: QueryHandlerFunction<ViewPaymentFlowPayloadByCheckoutId, ViewPaymentFlowPayloadByCheckoutIdResult, ViewPaymentFlowPayloadByCheckoutIdHandlerFunctionArgs>;
24
+ export type { ViewPaymentFlowPayloadByCheckoutId, PaymentFlowPayloadByCheckoutIdView, ViewPaymentFlowPayloadByCheckoutIdResult, };
25
+ export { VIEW_PAYMENT_FLOW_PAYLOAD_BY_CHECKOUT_ID, viewPaymentFlowPayloadByCheckoutId, viewPaymentFlowPayloadByCheckoutIdHandler, };
@@ -0,0 +1,8 @@
1
+ import { query, } from "@lookiero/messaging";
2
+ const VIEW_PAYMENT_FLOW_PAYLOAD_BY_CHECKOUT_ID = "view_payment_flow_payload_by_checkout_id";
3
+ const viewPaymentFlowPayloadByCheckoutId = (payload) => ({
4
+ ...query({ name: VIEW_PAYMENT_FLOW_PAYLOAD_BY_CHECKOUT_ID }),
5
+ ...payload,
6
+ });
7
+ const viewPaymentFlowPayloadByCheckoutIdHandler = ({ view, signal }) => async ({ checkoutId }) => view({ checkoutId, signal });
8
+ export { VIEW_PAYMENT_FLOW_PAYLOAD_BY_CHECKOUT_ID, viewPaymentFlowPayloadByCheckoutId, viewPaymentFlowPayloadByCheckoutIdHandler, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lookiero/checkout",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [
@@ -16,7 +16,7 @@
16
16
  "web": "expo start --web",
17
17
  "export-analyze-web": "BUNDLE_ANALYZE=true NODE_ENV=production expo export:web",
18
18
  "build": "tsc -b tsconfig.build.json",
19
- "build:app": "tsc -p ./tsconfig.build-app.json --noemit && ENTRY_POINT='./src/Expo.tsx' NODE_ENV=production expo export:web --clear 2> /dev/null",
19
+ "build:app": "tsc -p ./tsconfig.build-app.json --noemit && ENTRY_POINT='./src/Expo.tsx' NODE_ENV=production expo export:web --clear",
20
20
  "lint": "eslint --fix --cache --cache-location ./node_modules/.cache/.eslintcache --ext ts,tsx .",
21
21
  "format": "prettier --write \"**/*.+(json|css|ts|tsx|md)\"",
22
22
  "test": "tsc -p ./tsconfig.test.json --noemit && jest",
@@ -26,6 +26,7 @@
26
26
  "prepare": "husky install"
27
27
  },
28
28
  "dependencies": {
29
+ "@expo/dev-server": "0.1.120",
29
30
  "@gorhom/portal": "^1.0.14",
30
31
  "@lookiero/i18n": "^0.8.1",
31
32
  "@lookiero/i18n-react": "^0.8.1",
@@ -33,7 +34,7 @@
33
34
  "@lookiero/messaging-react": "^8.0.0",
34
35
  "@react-spring/native": "^9.5.5",
35
36
  "react-native-safe-area-context": "^4.4.1",
36
- "react-native-svg": "^13.5.0",
37
+ "react-native-svg": "12.3.0",
37
38
  "react-router-dom": "^6.4.2",
38
39
  "react-router-native": "^6.4.2",
39
40
  "tiny-invariant": "^1.2.0",
@@ -1,3 +0,0 @@
1
- import { PricingProjection } from "../../../projection/pricing/pricing";
2
- declare const pricing: PricingProjection;
3
- export { pricing };
@@ -1,37 +0,0 @@
1
- import { Currency } from "../../../domain/checkoutItem/model/currency";
2
- const pricing = {
3
- orderTotal: {
4
- amount: 14323,
5
- currency: Currency.EUR,
6
- },
7
- subtotal: {
8
- amount: 21801,
9
- currency: Currency.EUR,
10
- },
11
- discount: {
12
- amount: -2500,
13
- currency: Currency.EUR,
14
- },
15
- discountPercentage: 25,
16
- balanceDiscount: {
17
- amount: 1000,
18
- currency: Currency.EUR,
19
- },
20
- service: {
21
- reference: "LK-service",
22
- originalPrice: {
23
- amount: 10.0,
24
- currency: Currency.EUR,
25
- },
26
- discount: {
27
- amount: 0,
28
- currency: Currency.EUR,
29
- },
30
- finalPrice: {
31
- amount: -1000,
32
- currency: Currency.EUR,
33
- },
34
- prepaid: true,
35
- },
36
- };
37
- export { pricing };