@wf-financing/ui 3.9.2 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/global.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { IWayflyerUiSdkConstructor, IWayflyerHeadlessSdkConstructor } from '@wf-financing/embedded-types';
1
+ import { IWayflyerHeadlessSdkConstructor, IWayflyerUiSdkConstructor } from '@wf-financing/embedded-types';
2
2
 
3
3
  declare global {
4
4
  interface Window {
package/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { WayflyerUiSdk } from './sdk';
2
2
 
3
- const addHeadlessSdkToWindow = () => {
3
+ const addUiSdkToWindow = () => {
4
4
  window.WayflyerUiSdk = WayflyerUiSdk;
5
5
  };
6
6
 
7
- addHeadlessSdkToWindow();
7
+ addUiSdkToWindow();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wf-financing/ui",
3
- "version": "3.9.2",
3
+ "version": "3.11.0",
4
4
  "exports": {
5
5
  ".": {
6
6
  "import": "./dist/index.es.js",
@@ -36,7 +36,8 @@
36
36
  "react-aria": "^3.41.1",
37
37
  "react-intl": "^6.2.5",
38
38
  "styled-components": "^6.1.19",
39
- "@wf-financing/embedded-types": "0.6.0"
39
+ "@wf-financing/embedded-types": "0.6.0",
40
+ "@wf-financing/logger": "1.1.0"
40
41
  },
41
42
  "publishConfig": {
42
43
  "access": "public"
package/src/App.tsx CHANGED
@@ -1,6 +1,7 @@
1
1
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
2
2
  import { FlyUIProvider } from '@wayflyer/flyui';
3
3
  import { PartnerCallbackType, SdkOptionsType } from '@wf-financing/embedded-types';
4
+ import { Logger } from '@wf-financing/logger';
4
5
  import { useState } from 'react';
5
6
  import { createIntl, createIntlCache, IntlShape } from 'react-intl';
6
7
 
@@ -46,6 +47,7 @@ export const App = ({
46
47
 
47
48
  const onWidgetDismiss = () => {
48
49
  setIsWidgetDismissed(true);
50
+ Logger.logEvent('ui_sdk_banner_dismissed');
49
51
  };
50
52
 
51
53
  return (
@@ -65,8 +67,9 @@ export const App = ({
65
67
  theme={theme}
66
68
  intl={intl}
67
69
  onThemeLoad={() => setIsThemeLoaded(true)}
68
- // TODO: implement error notification event after required BE endpoints become available
69
- onThemeLoadError={() => console.error('Error while loading theme')}
70
+ onThemeLoadError={() => {
71
+ Logger.logError(`Error while loading theme: ${theme}`);
72
+ }}
70
73
  >
71
74
  {isThemeLoaded && <CtaWidget />}
72
75
  </FlyUIProvider>
package/src/CtaWidget.tsx CHANGED
@@ -22,7 +22,7 @@ export const CtaWidget = () => {
22
22
  <AnimatePresence mode="wait" onExitComplete={handleExitComplete}>
23
23
  {showBanner && (
24
24
  <AnimationWrapper skipAnimation={skipAnimation}>
25
- <CtaBanner skipAnimation={skipAnimation} />
25
+ <CtaBanner skipAnimation={skipAnimation} bannerState={data.state} />
26
26
  </AnimationWrapper>
27
27
  )}
28
28
  </AnimatePresence>
@@ -1,5 +1,6 @@
1
1
  import { IWayflyerHeadlessSdk, SdkOptionsType } from '@wf-financing/embedded-types';
2
2
 
3
+ import { Logger } from '@wf-financing/logger';
3
4
  import { HEADLESS_SDK_URL, WAYFLYER_HEADLESS_SDK_ID } from '../config';
4
5
  import { initializeHeadlessSdk, loadScriptAndInitializeSdk } from '../utils';
5
6
 
@@ -28,14 +29,15 @@ export const getHeadlessSdkInstance = async (companyToken: string, options?: Sdk
28
29
  script.type = 'module';
29
30
  script.id = WAYFLYER_HEADLESS_SDK_ID;
30
31
  script.async = true;
32
+ script.onerror = () => Logger.logError('Error in loading headless SDK script');
31
33
 
32
34
  document.head.appendChild(script);
33
35
  const headlessSdk: IWayflyerHeadlessSdk = await loadScriptAndInitializeSdk(script, companyToken, options);
34
36
  cachedSdkInstance = headlessSdk;
35
37
 
36
38
  return headlessSdk;
37
- } catch (error) {
38
- console.error('Error in loading headless SDK:', error);
39
+ } catch {
40
+ Logger.logError('Error in loading headless SDK');
39
41
  throw new Error('Failed to load script');
40
42
  }
41
43
  };
@@ -1,6 +1,7 @@
1
1
  import { Button, Flex } from '@wayflyer/flyui';
2
2
  import { IconX16Line } from '@wayflyer/flyui-icons/16/line';
3
3
 
4
+ import { Logger } from '@wf-financing/logger';
4
5
  import { useDismissCta, usePartnerContext } from '../../hooks';
5
6
 
6
7
  export const CloseButton = ({ isOnDarkTheme }: { isOnDarkTheme: boolean }) => {
@@ -12,7 +13,7 @@ export const CloseButton = ({ isOnDarkTheme }: { isOnDarkTheme: boolean }) => {
12
13
 
13
14
  dismissCta.mutate(undefined, {
14
15
  onError: (error) => {
15
- console.error('Failed to dismiss CTA', error);
16
+ Logger.logError(`Failed to dismiss CTA ${error.message}`);
16
17
  },
17
18
  });
18
19
  };
@@ -12,6 +12,7 @@ const defaultArgs = {
12
12
  onWidgetDismiss: () => {},
13
13
  isWidgetDismissed: false,
14
14
  skipAnimation: false,
15
+ bannerState: 'test',
15
16
  portalContainer,
16
17
  };
17
18
 
@@ -19,7 +20,7 @@ type CtaBannerStoryArgs = typeof defaultArgs;
19
20
 
20
21
  const Template = (args: CtaBannerStoryArgs) => (
21
22
  <PartnerContext.Provider value={{ ...args }}>
22
- <CtaBanner skipAnimation={false} />
23
+ <CtaBanner skipAnimation={false} bannerState="test" />
23
24
  </PartnerContext.Provider>
24
25
  );
25
26
 
@@ -2,6 +2,8 @@ import { Flex, useDetectDeviceSize, useTheme } from '@wayflyer/flyui';
2
2
  import { motion, MotionProps } from 'framer-motion';
3
3
  import { css, styled } from 'styled-components';
4
4
 
5
+ import { Logger } from '@wf-financing/logger';
6
+ import { useEffect } from 'react';
5
7
  import { MODAL_LOGO_IMAGE_URL, STATIC_BASE_URL } from '../../config';
6
8
  import { usePreloadImage } from '../../hooks';
7
9
  import { CtaBannerContent } from './CtaBannerContent';
@@ -60,9 +62,10 @@ const bannerAnimationProps: MotionProps = {
60
62
 
61
63
  type Props = {
62
64
  skipAnimation: boolean;
65
+ bannerState: string;
63
66
  };
64
67
 
65
- export const CtaBanner = ({ skipAnimation }: Props) => {
68
+ export const CtaBanner = ({ skipAnimation, bannerState }: Props) => {
66
69
  const { isMobile, isTablet } = useDetectDeviceSize();
67
70
  const logoImageUrl = `${STATIC_BASE_URL}${MODAL_LOGO_IMAGE_URL}`;
68
71
  const { themeName } = useTheme();
@@ -71,6 +74,10 @@ export const CtaBanner = ({ skipAnimation }: Props) => {
71
74
  const isTertiaryTheme = TERTIARY_THEMES.includes(themeName);
72
75
  usePreloadImage(logoImageUrl);
73
76
 
77
+ useEffect(() => {
78
+ Logger.logEvent('banner_cta_visible', { bannerState });
79
+ }, []);
80
+
74
81
  return (
75
82
  <BannerContainer
76
83
  $isLightBgTheme={isLightBgTheme}
@@ -1,14 +1,15 @@
1
1
  import { Button } from '@wayflyer/flyui';
2
2
  import {
3
+ ContinueHostedApplicationResponseType,
3
4
  CtaContinueFundingType,
4
5
  CtaGenericOfferType,
5
6
  CtaIndicativeOfferType,
6
7
  CtaStateType,
7
- ContinueHostedApplicationResponseType,
8
8
  } from '@wf-financing/embedded-types';
9
+ import { Logger } from '@wf-financing/logger';
9
10
  import { useState } from 'react';
10
11
 
11
- import { useCtaBanner, useContinueHostedApplication } from '../../hooks';
12
+ import { useContinueHostedApplication, useCtaBanner } from '../../hooks';
12
13
  import { ConsentModal } from '../modal/ConsentModal';
13
14
 
14
15
  type CtaResponseType = CtaGenericOfferType | CtaIndicativeOfferType | CtaContinueFundingType;
@@ -29,13 +30,14 @@ export const ProceedFundingButton = ({ isOnDarkTheme }: { isOnDarkTheme: boolean
29
30
  const handleContinueHostedApplication = () => {
30
31
  switch (state) {
31
32
  case CtaStateType.CONTINUE_APPLICATION:
33
+ Logger.logEvent('cta_continue_hosted_application');
32
34
  mutate(undefined, {
33
35
  onSuccess: (nextUrl: ContinueHostedApplicationResponseType) => {
34
36
  const { next } = nextUrl;
35
37
  window.open(next);
36
38
  },
37
39
  onError: (error) => {
38
- console.error('Failed to continue application', error);
40
+ Logger.logError(`Failed to continue application ${error.message}`);
39
41
  },
40
42
  });
41
43
  break;
@@ -1,6 +1,7 @@
1
1
  import { Button, Flex, Link, Text, useDetectDeviceSize } from '@wayflyer/flyui';
2
2
  import { IconArrowOnSquareUpRight16Line } from '@wayflyer/flyui-icons/16/line';
3
3
  import { StartHostedApplicationResponseType } from '@wf-financing/embedded-types';
4
+ import { Logger } from '@wf-financing/logger';
4
5
  import { FormattedMessage } from 'react-intl';
5
6
 
6
7
  import { useDetectSmallScreen, useStartHostedApplication } from '../../hooks';
@@ -15,6 +16,7 @@ export const ModalFooter = ({ setOpen }: ModalFooterType) => {
15
16
  const isSmallScreen = useDetectSmallScreen();
16
17
 
17
18
  const handleStartApplication = () => {
19
+ Logger.logEvent('cta_start_hosted_application');
18
20
  mutate(undefined, {
19
21
  onSuccess: (nextUrl: StartHostedApplicationResponseType) => {
20
22
  const { next } = nextUrl;
@@ -22,12 +24,13 @@ export const ModalFooter = ({ setOpen }: ModalFooterType) => {
22
24
  window.open(next);
23
25
  },
24
26
  onError: (error) => {
25
- console.error('Failed to start application', error);
27
+ Logger.logError(`Failed to continue application ${error.message}`);
26
28
  },
27
29
  });
28
30
  };
29
31
 
30
32
  const handleOpenExternalLink = () => {
33
+ Logger.logEvent('cta_open_privacy_note');
31
34
  window.open('https://wayflyer.com/en/privacy-notice', '_blank', 'noopener,noreferrer');
32
35
  };
33
36
 
package/src/main.tsx CHANGED
@@ -1,11 +1,12 @@
1
1
  import { PartnerCallbackType, SdkOptionsType } from '@wf-financing/embedded-types';
2
+ import { Logger } from '@wf-financing/logger';
2
3
  import ReactDOM from 'react-dom/client';
3
4
  import { StyleSheetManager } from 'styled-components';
4
5
 
5
6
  import { MotionGlobalConfig } from 'framer-motion';
6
7
  import { App } from './App';
7
8
  import { PartnerTheme } from './config';
8
- import { applyFont, createRoots } from './utils';
9
+ import { applyFont, applyStyles, createRoots } from './utils';
9
10
 
10
11
  let root: ReactDOM.Root | undefined;
11
12
  let savedTargetId: string | undefined;
@@ -24,11 +25,16 @@ export const mountToTarget = async (
24
25
  }
25
26
 
26
27
  const hostEl = document.getElementById(savedTargetId as string);
27
- if (!hostEl) throw new Error(`Target element with id "${savedTargetId}" not found.`);
28
+ if (!hostEl) {
29
+ Logger.logError(`Target element with id "${savedTargetId}" not found.`);
30
+ throw new Error(`Target element with id "${savedTargetId}" not found.`);
31
+ }
28
32
 
29
- const shadow = hostEl.shadowRoot ?? hostEl.attachShadow({ mode: 'open' });
33
+ const shadow = hostEl.shadowRoot ?? (hostEl.attachShadow({ mode: 'open' }) as ShadowRoot);
30
34
 
35
+ // TODO: remove font loading from SDK
31
36
  try {
37
+ applyStyles({ shadow });
32
38
  await applyFont(shadow, partnerTheme);
33
39
  } catch (error) {
34
40
  console.error(error);
@@ -60,4 +66,6 @@ export const mountToTarget = async (
60
66
  />
61
67
  </StyleSheetManager>,
62
68
  );
69
+
70
+ Logger.logEvent('cta_mounted_to_target');
63
71
  };
@@ -0,0 +1,41 @@
1
+ import assetsManifest from '@wayflyer/flyui/manifest.json';
2
+
3
+ const LINK_TAG_ID = 'wf-flyui-styles';
4
+
5
+ type ApplyFontsType = ({
6
+ shadow,
7
+ onStylesLoad,
8
+ onStylesLoadError,
9
+ }: {
10
+ shadow: ShadowRoot;
11
+ onStylesLoad?: () => void;
12
+ onStylesLoadError?: (error: unknown) => void;
13
+ }) => void;
14
+
15
+ export const applyStyles: ApplyFontsType = async ({ shadow, onStylesLoad, onStylesLoadError }) => {
16
+ if (!assetsManifest || !('styles' in assetsManifest)) {
17
+ return onStylesLoadError?.('Style manifest not found');
18
+ }
19
+
20
+ const stylesUrl = assetsManifest.styles as string;
21
+
22
+ const linkTag = document.createElement('link');
23
+ linkTag.href = stylesUrl;
24
+ linkTag.rel = 'stylesheet';
25
+ linkTag.id = LINK_TAG_ID;
26
+
27
+ linkTag.onload = () => {
28
+ onStylesLoad?.();
29
+ };
30
+
31
+ linkTag.onerror = (error) => {
32
+ onStylesLoadError?.(error);
33
+ };
34
+
35
+ const existingLinkTag = document.getElementById(LINK_TAG_ID);
36
+ if (existingLinkTag) {
37
+ existingLinkTag.replaceWith(linkTag);
38
+ } else {
39
+ shadow.prepend(linkTag);
40
+ }
41
+ };
@@ -1,4 +1,5 @@
1
1
  export { applyFont } from './applyFont';
2
+ export { applyStyles } from './applyStyles';
2
3
  export { createRoots } from './createRoots';
3
4
  export { getPartnerIdFromToken } from './getPartnerIdFromToken';
4
5
  export { getPartnerThemeById } from './getPartnerThemeById';
@@ -1,3 +1,5 @@
1
+ import { Logger } from '@wf-financing/logger';
2
+
1
3
  type CompanyToken = {
2
4
  sub: string;
3
5
  };
@@ -5,8 +7,8 @@ type CompanyToken = {
5
7
  export const parseJwt = (token: string): CompanyToken | null => {
6
8
  try {
7
9
  return JSON.parse(atob(token.split('.')[1]));
8
- } catch (error) {
9
- console.error('Error parsing a company token:', error);
10
+ } catch {
11
+ Logger.logError('Error parsing a company token');
10
12
  return null;
11
13
  }
12
14
  };