@wf-financing/ui 3.13.4 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/index.es.js +5237 -5299
  3. package/package.json +4 -4
  4. package/sdk/index.ts +2 -2
  5. package/src/App.tsx +2 -2
  6. package/src/CtaWidget.tsx +2 -1
  7. package/src/api/continueHostedApplication.ts +2 -2
  8. package/src/api/ctaBanner.ts +2 -2
  9. package/src/api/dismissCta.ts +2 -2
  10. package/src/api/fetchCtaBanner.test.ts +1 -2
  11. package/src/api/getHeadlessSdkInstance.test.ts +1 -2
  12. package/src/api/getHeadlessSdkInstance.ts +2 -2
  13. package/src/api/startHostedApplication.test.ts +1 -2
  14. package/src/api/startHostedApplication.ts +2 -2
  15. package/src/components/banner/CtaBanner.tsx +1 -4
  16. package/src/components/modal/BulletList.tsx +24 -0
  17. package/src/components/modal/ConsentModal.snapshot.stories.tsx +6 -18
  18. package/src/components/modal/ConsentModal.tsx +7 -4
  19. package/src/components/modal/ConsentModalContent.tsx +13 -40
  20. package/src/components/modal/Modal.tsx +14 -6
  21. package/src/components/modal/ModalFooter.tsx +6 -11
  22. package/src/components/modal/ModalHeader.tsx +21 -0
  23. package/src/components/modal/TemplateWithUrl.tsx +1 -1
  24. package/src/components/modal/modalEnhancements.ts +20 -5
  25. package/src/config/index.ts +0 -3
  26. package/src/hooks/useContinueHostedApplication.ts +2 -1
  27. package/src/hooks/useCopy.ts +4 -10
  28. package/src/hooks/useCtaBanner.ts +2 -1
  29. package/src/hooks/useDismissCta.ts +2 -1
  30. package/src/hooks/usePreloadImage.ts +30 -10
  31. package/src/hooks/useStartHostedApplication.ts +2 -1
  32. package/src/main.tsx +2 -2
  33. package/src/utils/index.ts +0 -1
  34. package/src/utils/initializeHeadlessSdk.ts +2 -2
  35. package/src/utils/loadScriptAndInitializeSdk.ts +2 -2
  36. package/src/utils/partnerContext.ts +2 -2
  37. package/src/components/modal/FundingSteps.tsx +0 -34
  38. package/src/config/fontParameters.ts +0 -31
  39. package/src/config/staticUrls.ts +0 -5
  40. package/src/utils/applyFont.ts +0 -22
  41. package/src/utils/loadFont.ts +0 -33
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wf-financing/ui",
3
- "version": "3.13.4",
3
+ "version": "4.0.0",
4
4
  "exports": {
5
5
  ".": {
6
6
  "import": "./dist/index.es.js",
@@ -37,9 +37,9 @@
37
37
  "react-intl": "^6.2.5",
38
38
  "react-markdown": "^10.1.0",
39
39
  "styled-components": "^6.1.19",
40
- "@wf-financing/embedded-types": "0.7.1",
41
- "@wf-financing/ui-assets": "0.3.4",
42
- "@wf-financing/logger": "1.1.3"
40
+ "@wf-financing/embedded-types": "1.0.0",
41
+ "@wf-financing/ui-assets": "1.0.0",
42
+ "@wf-financing/logger": "2.0.0"
43
43
  },
44
44
  "publishConfig": {
45
45
  "access": "public"
package/sdk/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { PartnerCallbackType, SdkOptionsType } from '@wf-financing/embedded-types';
1
+ import { PartnerCallbackType, UiSdkOptions } from '@wf-financing/embedded-types';
2
2
 
3
3
  import { PartnerTheme } from '../src/config';
4
4
  import { mountToTarget } from '../src/main';
@@ -9,7 +9,7 @@ export class WayflyerUiSdk {
9
9
 
10
10
  constructor(
11
11
  readonly companyToken: string,
12
- readonly options?: SdkOptionsType,
12
+ readonly options?: UiSdkOptions,
13
13
  ) {
14
14
  const partnerId = getPartnerIdFromToken(companyToken);
15
15
  this.partnerTheme = getPartnerThemeById(partnerId);
package/src/App.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
2
2
  import { FlyUIProvider } from '@wayflyer/flyui';
3
- import { PartnerCallbackType, SdkOptionsType } from '@wf-financing/embedded-types';
3
+ import { PartnerCallbackType, UiSdkOptions } from '@wf-financing/embedded-types';
4
4
  import { Logger } from '@wf-financing/logger';
5
5
  import { useState } from 'react';
6
6
  import { createIntl, createIntlCache, IntlShape } from 'react-intl';
@@ -13,7 +13,7 @@ import { PartnerContext } from './utils';
13
13
  type AppPropsType = {
14
14
  theme: PartnerTheme;
15
15
  companyToken: string;
16
- options?: SdkOptionsType;
16
+ options?: UiSdkOptions;
17
17
  partnerCallback: PartnerCallbackType;
18
18
  onWidgetClose: () => void;
19
19
  portalContainer: HTMLElement;
package/src/CtaWidget.tsx CHANGED
@@ -5,7 +5,7 @@ import { ContinueHostedApplicationResponseType } from '@wf-financing/embedded-ty
5
5
  import { AnimationWrapper } from './components/banner/AnimationWrapper';
6
6
  import { CtaBanner } from './components/banner/CtaBanner';
7
7
  import { ConsentModal } from './components/modal/ConsentModal.tsx';
8
- import { useCopy, useCtaBanner, usePartnerContext, useContinueHostedApplication } from './hooks';
8
+ import { useCopy, useCtaBanner, usePartnerContext, useContinueHostedApplication, usePreloadImage } from './hooks';
9
9
  import { getBannerConfig } from './utils';
10
10
 
11
11
  export const CtaWidget = () => {
@@ -15,6 +15,7 @@ export const CtaWidget = () => {
15
15
  const [skipAnimation] = useState(() => !!options?.skipAnimations || !isLoading);
16
16
  const { mutate: continueHostedApplicationMutaion } = useContinueHostedApplication();
17
17
  const [isModalOpen, setIsModalOpen] = useState(false);
18
+ usePreloadImage(copy);
18
19
 
19
20
  const showBanner = cta && copy && !isWidgetDismissed;
20
21
 
@@ -1,10 +1,10 @@
1
- import { ContinueHostedApplicationResponseType, SdkOptionsType } from '@wf-financing/embedded-types';
1
+ import { ContinueHostedApplicationResponseType, HeadlessSdkOptions } from '@wf-financing/embedded-types';
2
2
 
3
3
  import { getHeadlessSdkInstance } from './getHeadlessSdkInstance';
4
4
 
5
5
  export const continueHostedApplication = async (
6
6
  companyToken: string,
7
- options?: SdkOptionsType,
7
+ options?: HeadlessSdkOptions,
8
8
  ): Promise<ContinueHostedApplicationResponseType> => {
9
9
  const sdk = await getHeadlessSdkInstance(companyToken, options);
10
10
 
@@ -1,8 +1,8 @@
1
- import { SdkOptionsType } from '@wf-financing/embedded-types';
1
+ import { HeadlessSdkOptions } from '@wf-financing/embedded-types';
2
2
 
3
3
  import { getHeadlessSdkInstance } from './getHeadlessSdkInstance';
4
4
 
5
- export const fetchCtaBanner = async (companyToken: string, options?: SdkOptionsType) => {
5
+ export const fetchCtaBanner = async (companyToken: string, options?: HeadlessSdkOptions) => {
6
6
  const sdk = await getHeadlessSdkInstance(companyToken, options);
7
7
 
8
8
  return await sdk.getCta();
@@ -1,8 +1,8 @@
1
- import { SdkOptionsType } from '@wf-financing/embedded-types';
1
+ import { HeadlessSdkOptions } from '@wf-financing/embedded-types';
2
2
 
3
3
  import { getHeadlessSdkInstance } from './getHeadlessSdkInstance';
4
4
 
5
- export const dismissCta = async (companyToken: string, options?: SdkOptionsType) => {
5
+ export const dismissCta = async (companyToken: string, options?: HeadlessSdkOptions) => {
6
6
  const sdk = await getHeadlessSdkInstance(companyToken, options);
7
7
 
8
8
  await sdk.dismissCta();
@@ -1,4 +1,3 @@
1
- import { SdkOptionsType } from '@wf-financing/embedded-types';
2
1
  import { afterEach, describe, expect, test, vi } from 'vitest';
3
2
 
4
3
  import { fetchCtaBanner } from './ctaBanner';
@@ -22,7 +21,7 @@ describe('fetchCtaBanner', () => {
22
21
  const mockCta = { banner: 'hello' };
23
22
  (getHeadlessSdkInstance as any).mockResolvedValueOnce(fakeSdk);
24
23
  fakeSdk.getCta.mockResolvedValueOnce(mockCta);
25
- const options: SdkOptionsType = {
24
+ const options = {
26
25
  isSandbox: true,
27
26
  };
28
27
 
@@ -1,4 +1,3 @@
1
- import { SdkOptionsType } from '@wf-financing/embedded-types';
2
1
  import { afterEach, describe, expect, Mock, test, vi } from 'vitest';
3
2
 
4
3
  vi.mock('./getHeadlessSdkInstance', () => ({
@@ -13,7 +12,7 @@ const fakeSdk = {
13
12
  startHostedApplication: vi.fn(),
14
13
  };
15
14
 
16
- const options: SdkOptionsType = {
15
+ const options = {
17
16
  isSandbox: true,
18
17
  };
19
18
 
@@ -1,4 +1,4 @@
1
- import { IWayflyerHeadlessSdk, SdkOptionsType } from '@wf-financing/embedded-types';
1
+ import { IWayflyerHeadlessSdk, HeadlessSdkOptions } from '@wf-financing/embedded-types';
2
2
 
3
3
  import { Logger } from '@wf-financing/logger';
4
4
  import { HEADLESS_SDK_URL, WAYFLYER_HEADLESS_SDK_ID } from '../config';
@@ -6,7 +6,7 @@ import { initializeHeadlessSdk, loadScriptAndInitializeSdk } from '../utils';
6
6
 
7
7
  let cachedSdkInstance: IWayflyerHeadlessSdk | null = null;
8
8
 
9
- export const getHeadlessSdkInstance = async (companyToken: string, options?: SdkOptionsType) => {
9
+ export const getHeadlessSdkInstance = async (companyToken: string, options?: HeadlessSdkOptions) => {
10
10
  try {
11
11
  if (cachedSdkInstance) {
12
12
  cachedSdkInstance.setCompanyToken(companyToken);
@@ -1,4 +1,3 @@
1
- import { SdkOptionsType } from '@wf-financing/embedded-types';
2
1
  import { afterEach, describe, expect, test, vi } from 'vitest';
3
2
 
4
3
  vi.mock('./getHeadlessSdkInstance', () => ({
@@ -25,7 +24,7 @@ describe('startHostedApplication', () => {
25
24
 
26
25
  (getHeadlessSdkInstance as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce(fakeSdk);
27
26
  fakeSdk.startHostedApplication.mockResolvedValueOnce(fakeNextUrl);
28
- const options: SdkOptionsType = {
27
+ const options = {
29
28
  isSandbox: true,
30
29
  };
31
30
 
@@ -1,11 +1,11 @@
1
- import { PartnerCallbackType, SdkOptionsType } from '@wf-financing/embedded-types';
1
+ import { PartnerCallbackType, HeadlessSdkOptions } from '@wf-financing/embedded-types';
2
2
 
3
3
  import { getHeadlessSdkInstance } from './getHeadlessSdkInstance';
4
4
 
5
5
  export const startHostedApplication = async (
6
6
  companyToken: string,
7
7
  partnerCallback: PartnerCallbackType,
8
- options?: SdkOptionsType,
8
+ options?: HeadlessSdkOptions,
9
9
  ) => {
10
10
  let partnerData = await partnerCallback();
11
11
  const sdk = await getHeadlessSdkInstance(companyToken, options);
@@ -3,8 +3,7 @@ import { getThemeFlags, type ThemeOverrides } from '@wayflyer/flyui-tokens';
3
3
  import { motion, MotionProps } from 'framer-motion';
4
4
  import { css, styled } from 'styled-components';
5
5
 
6
- import { MODAL_LOGO_IMAGE_URL, STATIC_BASE_URL } from '../../config';
7
- import { usePartnerContext, usePreloadImage } from '../../hooks';
6
+ import { usePartnerContext } from '../../hooks';
8
7
  import { BannerActions } from './BannerActions';
9
8
  import { CtaMainText } from './CtaMainText';
10
9
  import { CloseButton } from './CloseButton';
@@ -64,8 +63,6 @@ type Props = {
64
63
 
65
64
  export const CtaBanner = ({ bannerConfig, skipAnimation }: Props) => {
66
65
  const { isMobile } = useDetectDeviceSize();
67
- const logoImageUrl = `${STATIC_BASE_URL}${MODAL_LOGO_IMAGE_URL}`;
68
- usePreloadImage(logoImageUrl);
69
66
  const { portalContainer } = usePartnerContext();
70
67
  const { cta: themeFlags } = getThemeFlags({ rootElement: portalContainer });
71
68
 
@@ -0,0 +1,24 @@
1
+ import { Flex, Divider, Text } from '@wayflyer/flyui';
2
+ import { IconCheck16Line } from '@wayflyer/flyui-icons/16/line';
3
+
4
+ type BulletListProps = {
5
+ bulletPoints: string[];
6
+ };
7
+
8
+ export const BulletList = ({ bulletPoints }: BulletListProps) => {
9
+ return (
10
+ <Flex direction="column" gap="3">
11
+ {bulletPoints.map((point, index) => (
12
+ <>
13
+ {index !== 0 && <Divider />}
14
+ <Flex direction="row" align="center" gap="2">
15
+ <IconCheck16Line color="#7F8592" />
16
+ <Text fontWeight="regular" fontStyle="regular" lineHeight="tight" size="sm">
17
+ {point}
18
+ </Text>
19
+ </Flex>
20
+ </>
21
+ ))}
22
+ </Flex>
23
+ );
24
+ };
@@ -35,26 +35,14 @@ export const ConsentModal: Story = {
35
35
  isModalOpen: true,
36
36
  setIsModalOpen: fn,
37
37
  title: 'Fuel your growth with capital from Wayflyer',
38
- buttons: {
39
- accept: 'Continue to Wayflyer',
40
- close: 'Cancel',
41
- },
42
- imageUrl: 'https://static.wayflyer.com/flyui-assets/logos/wayflyer-ef.png',
38
+ button: 'Continue to Wayflyer',
39
+ logoUrl: 'https://static.wayflyer.com/flyui-assets/favicons/white-logo-with-background.png',
43
40
  termsAndConditions:
44
41
  'By proceeding, you consent to us sharing your information with Wayflyer so they can assess your eligibility for financing, in accordance with the [Wayflyer Privacy Policy]({termsUrl}).',
45
- descriptions: [
46
- {
47
- title: 'Built for businesses like yours',
48
- description: '$5B+ deployed to 5,000+ companies',
49
- },
50
- {
51
- title: 'Your growth, your way',
52
- description: 'Flexible terms that fit your business',
53
- },
54
- {
55
- title: 'Quick to get started',
56
- description: 'Apply in minutes and get capital in hours',
57
- },
42
+ bulletPoints: [
43
+ '$5B deployed with 4.9 stars on Trustpilot',
44
+ 'Apply in minutes and get capital in hours',
45
+ 'Flexible offers designed to fit your business',
58
46
  ],
59
47
  },
60
48
  render: (args) => (
@@ -11,17 +11,20 @@ type ConsentModalProps = {
11
11
  export const ConsentModal = ({ isModalOpen, setIsModalOpen }: ConsentModalProps) => {
12
12
  const { data: copy } = useCopy();
13
13
  const consentModal = copy?.consentModal;
14
+ const partnerTemplate = copy?.partnerTemplate;
14
15
 
15
16
  if (!consentModal) return null;
16
17
 
17
- const { imageUrl, buttons, termsAndConditions, descriptions, title } = consentModal;
18
+ const { logoUrl, button, termsAndConditions, bulletPoints, title } = consentModal;
19
+ const partnerLogoUrl = partnerTemplate?.partnerLogoUrl;
18
20
 
19
21
  return (
20
22
  <ConsentModalContent
21
- imageUrl={imageUrl}
23
+ logoUrl={logoUrl}
24
+ partnerLogoUrl={partnerLogoUrl}
22
25
  title={title}
23
- descriptions={descriptions}
24
- buttons={buttons}
26
+ bulletPoints={bulletPoints}
27
+ button={button}
25
28
  termsAndConditions={termsAndConditions}
26
29
  setIsModalOpen={setIsModalOpen}
27
30
  isModalOpen={isModalOpen}
@@ -1,63 +1,36 @@
1
1
  import type { Dispatch, SetStateAction } from 'react';
2
- import { Flex, Image, Text, useDetectDeviceSize } from '@wayflyer/flyui';
3
2
 
4
- import { useDetectSmallScreen } from '../../hooks';
5
- import { FundingSteps } from './FundingSteps.tsx';
6
3
  import { ModalFooter } from './ModalFooter.tsx';
4
+ import { ModalHeader } from './ModalHeader.tsx';
5
+ import { BulletList } from './BulletList';
7
6
  import { Modal } from './Modal.tsx';
8
- import { styled } from 'styled-components';
9
-
10
- type ModalDescriptions = {
11
- title: string;
12
- description: string;
13
- };
14
-
15
- type Buttons = {
16
- accept: string;
17
- close: string;
18
- };
19
7
 
20
8
  type ConsentModalContentProps = {
21
- imageUrl: string;
9
+ logoUrl: string;
10
+ partnerLogoUrl?: string;
22
11
  title: string;
23
- descriptions: ModalDescriptions[];
24
- buttons: Buttons;
12
+ bulletPoints: string[];
13
+ button: string;
25
14
  termsAndConditions: string;
26
15
  setIsModalOpen: Dispatch<SetStateAction<boolean>>;
27
16
  isModalOpen: boolean;
28
17
  };
29
18
 
30
- const ImageContainer = styled(Image)`
31
- width: 100%;
32
- max-width: none;
33
- `;
34
-
35
19
  export const ConsentModalContent = ({
36
- imageUrl,
37
20
  title,
38
- descriptions,
21
+ bulletPoints,
39
22
  termsAndConditions,
40
- buttons,
23
+ button,
24
+ logoUrl,
25
+ partnerLogoUrl,
41
26
  setIsModalOpen,
42
27
  isModalOpen,
43
28
  }: ConsentModalContentProps) => {
44
- const isSmallScreen = useDetectSmallScreen();
45
- const { isMobile } = useDetectDeviceSize();
46
-
47
- const isReducedSpacing = isSmallScreen || isMobile;
48
-
49
29
  return (
50
30
  <Modal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen}>
51
- {!isSmallScreen && <ImageContainer src={imageUrl} />}
52
- <Flex direction="column" gap="8" padding={isReducedSpacing ? '4' : '6'}>
53
- <Flex direction="column" gap="6">
54
- <Text fontStyle="regular" fontWeight="medium" lineHeight="tight" size="2xl">
55
- {title}
56
- </Text>
57
- <FundingSteps descriptions={descriptions} />
58
- </Flex>
59
- <ModalFooter setOpen={setIsModalOpen} buttons={buttons} termsAndConditions={termsAndConditions} />
60
- </Flex>
31
+ <ModalHeader title={title} logoUrl={logoUrl} partnerLogoUrl={partnerLogoUrl} />
32
+ <BulletList bulletPoints={bulletPoints} />
33
+ <ModalFooter setIsModalOpen={setIsModalOpen} button={button} termsAndConditions={termsAndConditions} />
61
34
  </Modal>
62
35
  );
63
36
  };
@@ -1,12 +1,12 @@
1
- import { Dialog } from '@wayflyer/flyui';
1
+ import { Button, Dialog, Flex, useDetectDeviceSize } from '@wayflyer/flyui';
2
+ import { IconX16Line } from '@wayflyer/flyui-icons/16/line';
2
3
  import { AnimatePresence, LazyMotion, domAnimation } from 'framer-motion';
3
4
  import { ReactNode, useId, useRef } from 'react';
4
5
  import { UNSAFE_PortalProvider as PortalProvider } from 'react-aria';
5
- import { usePartnerContext } from '../../hooks';
6
- import { DialogBody } from './modalEnhancements';
7
6
 
7
+ import { usePartnerContext, useRemoveInerted } from '../../hooks';
8
+ import { DialogBody } from './modalEnhancements';
8
9
  import { MotionModalOverlay, MotionModalPrimitive, fullSize, opaque, small, transparent } from './modalEnhancements';
9
- import { useRemoveInerted } from '../../hooks';
10
10
 
11
11
  type ModalProps = {
12
12
  isModalOpen: boolean;
@@ -18,6 +18,7 @@ export const Modal = ({ isModalOpen, setIsModalOpen, children }: ModalProps) =>
18
18
  const modalId = useId();
19
19
  const { portalContainer } = usePartnerContext();
20
20
  const scrollableElRef = useRef(null);
21
+ const { isMobile } = useDetectDeviceSize();
21
22
 
22
23
  useRemoveInerted(isModalOpen, portalContainer);
23
24
 
@@ -36,7 +37,7 @@ export const Modal = ({ isModalOpen, setIsModalOpen, children }: ModalProps) =>
36
37
  animate={fullSize}
37
38
  exit={small}
38
39
  transition={{ duration: 0.2 }}
39
- style={{ width: '438px', border: 'none', maxWidth: '90vw', maxHeight: '100vh' }}
40
+ style={{ width: '420px', border: 'none', maxWidth: '90vw', maxHeight: '100vh' }}
40
41
  >
41
42
  <MotionModalOverlay
42
43
  data-id="modal-mask"
@@ -47,7 +48,14 @@ export const Modal = ({ isModalOpen, setIsModalOpen, children }: ModalProps) =>
47
48
  transition={{ duration: 0.2 }}
48
49
  />
49
50
  <Dialog role="dialog" aria-labelledby={modalId}>
50
- <DialogBody ref={scrollableElRef} data-testid="modal-body">
51
+ <div style={{ position: 'absolute', right: '10px', top: '10px' }}>
52
+ <Button variant="Tertiary" onClick={() => setIsModalOpen(false)}>
53
+ <Flex padding="2">
54
+ <IconX16Line />
55
+ </Flex>
56
+ </Button>
57
+ </div>
58
+ <DialogBody $isMobile={isMobile} ref={scrollableElRef} data-testid="modal-body">
51
59
  {children}
52
60
  </DialogBody>
53
61
  </Dialog>
@@ -1,3 +1,4 @@
1
+ import type { Dispatch, SetStateAction } from 'react';
1
2
  import { Button, Flex, useDetectDeviceSize } from '@wayflyer/flyui';
2
3
  import { IconArrowOnSquareUpRight16Line } from '@wayflyer/flyui-icons/16/line';
3
4
  import { StartHostedApplicationResponseType } from '@wf-financing/embedded-types';
@@ -7,15 +8,12 @@ import { TemplateWithUrl } from './TemplateWithUrl';
7
8
  import { useDetectSmallScreen, useStartHostedApplication } from '../../hooks';
8
9
 
9
10
  type ModalFooterProps = {
10
- buttons: {
11
- accept: string;
12
- close: string;
13
- };
11
+ button: string;
14
12
  termsAndConditions: string;
15
- setOpen: (isOpen: boolean) => void;
13
+ setIsModalOpen: Dispatch<SetStateAction<boolean>>;
16
14
  };
17
15
 
18
- export const ModalFooter = ({ setOpen, buttons, termsAndConditions }: ModalFooterProps) => {
16
+ export const ModalFooter = ({ button, termsAndConditions, setIsModalOpen }: ModalFooterProps) => {
19
17
  const { isMobile } = useDetectDeviceSize();
20
18
  const { mutate, isPending: isLoading } = useStartHostedApplication();
21
19
  const isSmallScreen = useDetectSmallScreen();
@@ -25,8 +23,8 @@ export const ModalFooter = ({ setOpen, buttons, termsAndConditions }: ModalFoote
25
23
  mutate(undefined, {
26
24
  onSuccess: (nextUrl: StartHostedApplicationResponseType) => {
27
25
  const { next } = nextUrl;
28
- setOpen(false);
29
26
  window.open(next);
27
+ setIsModalOpen(false);
30
28
  },
31
29
  onError: (error) => {
32
30
  Logger.logError(`Failed to start application ${error.message}`);
@@ -38,12 +36,9 @@ export const ModalFooter = ({ setOpen, buttons, termsAndConditions }: ModalFoote
38
36
  <Flex direction="column" gap="3">
39
37
  <Flex gap="2" direction={isSmallScreen || isMobile ? 'column' : 'row'}>
40
38
  <Button fullWidth onClick={handleStartApplication} loading={isLoading} variant="Primary" external>
41
- {buttons.accept}
39
+ {button}
42
40
  {!isLoading && <IconArrowOnSquareUpRight16Line />}
43
41
  </Button>
44
- <Button fullWidth variant="Secondary" onClick={() => setOpen(false)}>
45
- {buttons.close}
46
- </Button>
47
42
  </Flex>
48
43
  <TemplateWithUrl templateString={termsAndConditions} />
49
44
  </Flex>
@@ -0,0 +1,21 @@
1
+ import { Flex, Heading, Image } from '@wayflyer/flyui';
2
+
3
+ type ModalHeaderProps = {
4
+ title: string;
5
+ logoUrl: string;
6
+ partnerLogoUrl?: string;
7
+ };
8
+
9
+ export const ModalHeader = ({ title, logoUrl, partnerLogoUrl }: ModalHeaderProps) => {
10
+ return (
11
+ <Flex direction="column" gap="6">
12
+ <Flex direction="row" align="center" justify="center" gap="2">
13
+ <Image src={logoUrl} height={64} width={64} />
14
+ {partnerLogoUrl && <Image src={partnerLogoUrl} height={64} width={64} />}
15
+ </Flex>
16
+ <Heading align="center" fontWeight="medium" lineHeight="tight" size="2xl">
17
+ {title}
18
+ </Heading>
19
+ </Flex>
20
+ );
21
+ };
@@ -21,7 +21,7 @@ export const TemplateWithUrl = ({ templateString }: TemplateWithUrlProps) => {
21
21
  <ReactMarkdown
22
22
  components={{
23
23
  a: ({ children }) => <Link onClick={handleOpenExternalLink} children={children} />,
24
- p: ({ children }) => <Text size="sm" lineHeight="normal" children={children} />,
24
+ p: ({ children }) => <Text color="placeholder" size="xs" lineHeight="normal" children={children} />,
25
25
  }}
26
26
  >
27
27
  {text}
@@ -1,6 +1,6 @@
1
1
  import { ModalOverlay as OriginalModalOverlay, ModalPrimitive as OriginalModalPrimitive } from '@wayflyer/flyui';
2
2
  import { m } from 'framer-motion';
3
- import styled from 'styled-components';
3
+ import { styled, css } from 'styled-components';
4
4
 
5
5
  export const MotionModalOverlay = m(OriginalModalOverlay);
6
6
  export const MotionModalPrimitive = m(OriginalModalPrimitive);
@@ -14,15 +14,30 @@ export const opaque = {
14
14
  opacity: 0.6,
15
15
  };
16
16
 
17
- export const DialogBody = styled.div`
17
+ export const DialogBody = styled.div<{ $isMobile: boolean }>`
18
18
  scrollbar-width: thin;
19
19
  scrollbar-color: var(--palette-border-neutral-subtle);
20
20
  flex-shrink: 1;
21
21
  overflow-y: auto;
22
22
  background-color: var(--palette-bg-neutral-base);
23
- border-radius: var(--sizes-radius-md);
23
+ border-radius: var(--sizes-radius-lg);
24
24
  max-height: 80vh;
25
- padding-bottom: 10px;
25
+ padding: ${({ $isMobile }) => {
26
+ if ($isMobile) {
27
+ return css`var(--sizes-spacing-8) var(--sizes-spacing-4)`;
28
+ }
29
+
30
+ return css`var(--sizes-spacing-8)`;
31
+ }};
32
+ gap: ${({ $isMobile }) => {
33
+ if ($isMobile) {
34
+ return css`var(--sizes-spacing-8)`;
35
+ }
36
+
37
+ return css`var(--sizes-spacing-10)`;
38
+ }};
39
+ display: flex;
40
+ flex-direction: column;
26
41
 
27
42
  &::-webkit-scrollbar {
28
43
  width: 12px;
@@ -34,6 +49,6 @@ export const DialogBody = styled.div`
34
49
 
35
50
  &::-webkit-scrollbar-thumb {
36
51
  background-color: var(--palette-border-neutral-subtle);
37
- border-radius: var(--sizes-radius-sm);
52
+ border-radius: var(--sizes-radius-lg);
38
53
  }
39
54
  `;
@@ -1,9 +1,6 @@
1
- export { FONT_PARAMS } from './fontParameters';
2
- export type { FontParamsType } from './fontParameters';
3
1
  export { ROOTS_CONFIG } from './rootsParameters';
4
2
  export { WAYFLYER_HEADLESS_SDK_ID } from './scriptId';
5
3
  export { HEADLESS_SDK_URL } from './url';
6
4
  export { WHITELISTED_PARTNER_IDS } from './whitelistedPartnerIds';
7
5
  export type { PartnerId, PartnerTheme } from './whitelistedPartnerIds';
8
- export { STATIC_BASE_URL, DM_SANS_URL, MERRION_SANS_URL, MODAL_LOGO_IMAGE_URL } from './staticUrls';
9
6
  export { APPLICATION_VERSION } from './applicationVersion';
@@ -5,8 +5,9 @@ import { usePartnerContext } from './usePartnerContext';
5
5
 
6
6
  export const useContinueHostedApplication = () => {
7
7
  const { companyToken, options } = usePartnerContext();
8
+ const headlessOptions = { isSandbox: options?.isSandbox };
8
9
 
9
10
  return useMutation({
10
- mutationFn: () => continueHostedApplication(companyToken, options),
11
+ mutationFn: () => continueHostedApplication(companyToken, headlessOptions),
11
12
  });
12
13
  };
@@ -1,4 +1,3 @@
1
- import { SdkOptionsType } from '@wf-financing/embedded-types';
2
1
  import { useQuery, keepPreviousData } from '@tanstack/react-query';
3
2
  import { loadAndConfigureCopy } from '@wf-financing/ui-assets';
4
3
 
@@ -6,25 +5,20 @@ import { usePartnerContext } from './usePartnerContext.ts';
6
5
  import { APPLICATION_VERSION } from '../config';
7
6
  import { getPartnerIdFromToken } from '../utils';
8
7
 
9
- type Options = SdkOptionsType & {
10
- language?: string;
11
- cohort?: string;
12
- };
13
-
14
8
  export const useCopy = () => {
15
9
  const { options, companyToken } = usePartnerContext();
10
+ const language = options?.language;
16
11
 
17
12
  const partnerId = getPartnerIdFromToken(companyToken);
18
- // TODO: remove after extension of SdkOptionType
19
- const copyOptions = options as Options;
20
13
 
21
14
  return useQuery({
22
- queryKey: ['partnerCopy'],
23
- queryFn: () => loadAndConfigureCopy({ version: APPLICATION_VERSION, partnerId, options: copyOptions }),
15
+ queryKey: ['partnerCopy', language],
16
+ queryFn: () => loadAndConfigureCopy({ version: APPLICATION_VERSION, partnerId, options: options }),
24
17
  placeholderData: keepPreviousData,
25
18
  refetchOnWindowFocus: false,
26
19
  refetchOnMount: false,
27
20
  staleTime: 10 * 60 * 1000,
28
21
  gcTime: 10 * 60 * 1000,
22
+ retry: 1,
29
23
  });
30
24
  };
@@ -5,10 +5,11 @@ import { usePartnerContext } from './usePartnerContext';
5
5
 
6
6
  export const useCtaBanner = () => {
7
7
  const { companyToken, options } = usePartnerContext();
8
+ const headlessOptions = { isSandbox: options?.isSandbox };
8
9
 
9
10
  return useQuery({
10
11
  queryKey: ['cta', companyToken],
11
- queryFn: () => fetchCtaBanner(companyToken, options),
12
+ queryFn: () => fetchCtaBanner(companyToken, headlessOptions),
12
13
  enabled: !!companyToken,
13
14
  placeholderData: keepPreviousData,
14
15
  refetchOnMount: false,
@@ -5,10 +5,11 @@ import { usePartnerContext } from './usePartnerContext';
5
5
 
6
6
  export const useDismissCta = () => {
7
7
  const { companyToken, options } = usePartnerContext();
8
+ const headlessOptions = { isSandbox: options?.isSandbox };
8
9
 
9
10
  return useMutation({
10
11
  mutationFn: async () => {
11
- await dismissCta(companyToken, options);
12
+ await dismissCta(companyToken, headlessOptions);
12
13
  },
13
14
  });
14
15
  };