@wf-financing/ui 3.10.0 → 3.12.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/CHANGELOG.md +24 -0
- package/dist/index.es.js +21897 -12225
- package/global.d.ts +1 -1
- package/index.ts +2 -2
- package/package.json +5 -2
- package/src/App.tsx +5 -2
- package/src/CtaWidget.tsx +31 -9
- package/src/api/ctaBanner.ts +1 -2
- package/src/api/getHeadlessSdkInstance.ts +4 -2
- package/src/components/banner/BannerActions.tsx +26 -0
- package/src/components/banner/BulletList.tsx +34 -21
- package/src/components/banner/CloseButton.tsx +22 -2
- package/src/components/banner/CtaBanner.snapshot.stories.tsx +11 -1
- package/src/components/banner/CtaBanner.tsx +23 -8
- package/src/components/banner/CtaMainText.tsx +23 -0
- package/src/components/modal/ConsentModal.snapshot.stories.tsx +28 -25
- package/src/components/modal/ConsentModal.tsx +18 -30
- package/src/components/modal/ConsentModalContent.tsx +63 -0
- package/src/components/modal/FundingSteps.tsx +16 -8
- package/src/components/modal/ModalFooter.tsx +15 -17
- package/src/components/modal/TemplateWithUrl.tsx +30 -0
- package/src/config/applicationVersion.ts +1 -0
- package/src/config/index.ts +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useCopy.ts +30 -0
- package/src/main.tsx +8 -1
- package/src/utils/buildBannerConfig.ts +26 -0
- package/src/utils/getBannerConfig.ts +44 -0
- package/src/utils/getModalItems.ts +19 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/injectUrlInTemplate.ts +6 -0
- package/src/utils/parseJwt.ts +4 -2
- package/src/utils/replacePlaceholdersForMainText.ts +29 -0
- package/tsconfig.json +2 -1
- package/vite-env.d.ts +1 -0
- package/vite.config.mts +3 -0
- package/src/components/banner/BannerActionsDesktop.tsx +0 -13
- package/src/components/banner/CtaBannerContent.tsx +0 -37
- package/src/components/banner/FooterActions.tsx +0 -8
- package/src/components/banner/HeaderActions.tsx +0 -21
- package/src/components/banner/ProceedFundingButton.tsx +0 -61
- package/src/constants/index.ts +0 -1
- package/src/constants/modalItems.ts +0 -26
package/global.d.ts
CHANGED
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wf-financing/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.12.0",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": {
|
|
6
6
|
"import": "./dist/index.es.js",
|
|
@@ -35,8 +35,11 @@
|
|
|
35
35
|
"framer-motion": "^12.23.0",
|
|
36
36
|
"react-aria": "^3.41.1",
|
|
37
37
|
"react-intl": "^6.2.5",
|
|
38
|
+
"react-markdown": "^10.1.0",
|
|
38
39
|
"styled-components": "^6.1.19",
|
|
39
|
-
"@wf-financing/embedded-types": "0.
|
|
40
|
+
"@wf-financing/embedded-types": "0.7.0",
|
|
41
|
+
"@wf-financing/ui-assets": "0.2.0",
|
|
42
|
+
"@wf-financing/logger": "1.1.1"
|
|
40
43
|
},
|
|
41
44
|
"publishConfig": {
|
|
42
45
|
"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
|
-
|
|
69
|
-
|
|
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
|
@@ -1,30 +1,52 @@
|
|
|
1
1
|
import { AnimatePresence } from 'framer-motion';
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
+
import { ContinueHostedApplicationResponseType } from '@wf-financing/embedded-types';
|
|
3
4
|
|
|
4
5
|
import { AnimationWrapper } from './components/banner/AnimationWrapper';
|
|
5
6
|
import { CtaBanner } from './components/banner/CtaBanner';
|
|
6
|
-
import {
|
|
7
|
+
import { ConsentModal } from './components/modal/ConsentModal.tsx';
|
|
8
|
+
import { useCopy, useCtaBanner, usePartnerContext, useContinueHostedApplication } from './hooks';
|
|
9
|
+
import { getBannerConfig } from './utils';
|
|
7
10
|
|
|
8
11
|
export const CtaWidget = () => {
|
|
9
|
-
const { isLoading, data } = useCtaBanner();
|
|
12
|
+
const { isLoading, data: cta } = useCtaBanner();
|
|
13
|
+
const { data: copy } = useCopy();
|
|
10
14
|
const { onWidgetClose, isWidgetDismissed, options } = usePartnerContext();
|
|
11
15
|
const [skipAnimation] = useState(() => !!options?.skipAnimations);
|
|
16
|
+
const { mutate: continueHostedApplicationMutaion } = useContinueHostedApplication();
|
|
17
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
12
18
|
|
|
13
|
-
const showBanner =
|
|
19
|
+
const showBanner = cta && copy && !isWidgetDismissed;
|
|
20
|
+
|
|
21
|
+
if (!showBanner) return null;
|
|
14
22
|
|
|
15
23
|
const handleExitComplete = () => {
|
|
16
|
-
if ((!isLoading && !
|
|
24
|
+
if ((!isLoading && !cta) || isWidgetDismissed) {
|
|
17
25
|
onWidgetClose();
|
|
18
26
|
}
|
|
19
27
|
};
|
|
20
28
|
|
|
29
|
+
const handleIsModalOpen = () => setIsModalOpen((isModalOpen) => !isModalOpen);
|
|
30
|
+
const handleContinueHostedApplication = () => {
|
|
31
|
+
continueHostedApplicationMutaion(undefined, {
|
|
32
|
+
onSuccess: (nextUrl: ContinueHostedApplicationResponseType) => {
|
|
33
|
+
const { next } = nextUrl;
|
|
34
|
+
window.open(next);
|
|
35
|
+
},
|
|
36
|
+
onError: (error) => {
|
|
37
|
+
console.error('Failed to continue application', error);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const bannerConfig = getBannerConfig(cta, copy, handleIsModalOpen, handleContinueHostedApplication);
|
|
43
|
+
|
|
21
44
|
return (
|
|
22
45
|
<AnimatePresence mode="wait" onExitComplete={handleExitComplete}>
|
|
23
|
-
{
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
)}
|
|
46
|
+
<AnimationWrapper skipAnimation={skipAnimation}>
|
|
47
|
+
<CtaBanner bannerConfig={bannerConfig} skipAnimation={skipAnimation} />
|
|
48
|
+
<ConsentModal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} />
|
|
49
|
+
</AnimationWrapper>
|
|
28
50
|
</AnimatePresence>
|
|
29
51
|
);
|
|
30
52
|
};
|
package/src/api/ctaBanner.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { getHeadlessSdkInstance } from './getHeadlessSdkInstance';
|
|
|
4
4
|
|
|
5
5
|
export const fetchCtaBanner = async (companyToken: string, options?: SdkOptionsType) => {
|
|
6
6
|
const sdk = await getHeadlessSdkInstance(companyToken, options);
|
|
7
|
-
const cta = await sdk.getCta();
|
|
8
7
|
|
|
9
|
-
return
|
|
8
|
+
return await sdk.getCta();
|
|
10
9
|
};
|
|
@@ -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
|
|
38
|
-
|
|
39
|
+
} catch {
|
|
40
|
+
Logger.logError('Error in loading headless SDK');
|
|
39
41
|
throw new Error('Failed to load script');
|
|
40
42
|
}
|
|
41
43
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Button, Flex, useDetectDeviceSize } from '@wayflyer/flyui';
|
|
2
|
+
|
|
3
|
+
type BannerActionsProps = {
|
|
4
|
+
isOnDarkTheme: boolean;
|
|
5
|
+
buttonText: string;
|
|
6
|
+
buttonAction: () => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const BannerActions = ({ isOnDarkTheme, buttonAction, buttonText }: BannerActionsProps) => {
|
|
10
|
+
const { isMobile } = useDetectDeviceSize();
|
|
11
|
+
|
|
12
|
+
if (isMobile)
|
|
13
|
+
return (
|
|
14
|
+
<Button variant={isOnDarkTheme ? 'PrimaryOnDark' : 'Primary'} fullWidth onClick={buttonAction}>
|
|
15
|
+
{buttonText}
|
|
16
|
+
</Button>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Flex gap="4">
|
|
21
|
+
<Button variant={isOnDarkTheme ? 'PrimaryOnDark' : 'Primary'} fullWidth onClick={buttonAction}>
|
|
22
|
+
{buttonText}
|
|
23
|
+
</Button>
|
|
24
|
+
</Flex>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
@@ -1,23 +1,36 @@
|
|
|
1
|
-
import { Flex, Text, Icon } from '@wayflyer/flyui';
|
|
1
|
+
import { Flex, Text, Icon, useDetectDeviceSize } from '@wayflyer/flyui';
|
|
2
2
|
import { IconCheck12Line } from '@wayflyer/flyui-icons/12/line';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
4
|
+
type BulletListProps = {
|
|
5
|
+
isOnDarkTheme: boolean;
|
|
6
|
+
bulletPoints?: string[];
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const BulletList = ({ isOnDarkTheme, bulletPoints }: BulletListProps) => {
|
|
10
|
+
const { isMobile, isTablet } = useDetectDeviceSize();
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<>
|
|
14
|
+
{!(isTablet || isMobile) && bulletPoints && (
|
|
15
|
+
<Flex gap="4">
|
|
16
|
+
{bulletPoints.map((bulletPoint: string) => (
|
|
17
|
+
<Flex key={bulletPoint} gap="1" align="center">
|
|
18
|
+
<Icon color={isOnDarkTheme ? 'onDark' : 'default'}>
|
|
19
|
+
<IconCheck12Line />
|
|
20
|
+
</Icon>
|
|
21
|
+
<Text
|
|
22
|
+
size="sm"
|
|
23
|
+
fontWeight="medium"
|
|
24
|
+
fontStyle="regular"
|
|
25
|
+
lineHeight="tight"
|
|
26
|
+
color={isOnDarkTheme ? 'onDark' : 'default'}
|
|
27
|
+
>
|
|
28
|
+
{bulletPoint}
|
|
29
|
+
</Text>
|
|
30
|
+
</Flex>
|
|
31
|
+
))}
|
|
32
|
+
</Flex>
|
|
33
|
+
)}
|
|
34
|
+
</>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import { Button, Flex } from '@wayflyer/flyui';
|
|
1
|
+
import { Button, Flex, useDetectDeviceSize } 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';
|
|
6
|
+
import styled from 'styled-components';
|
|
7
|
+
|
|
8
|
+
const MobileCloseContainer = styled.div`
|
|
9
|
+
margin-top: calc(0px - var(--sizes-spacing-3));
|
|
10
|
+
margin-right: calc(0px - var(--sizes-spacing-2));
|
|
11
|
+
`;
|
|
5
12
|
|
|
6
13
|
export const CloseButton = ({ isOnDarkTheme }: { isOnDarkTheme: boolean }) => {
|
|
7
14
|
const { onWidgetDismiss } = usePartnerContext();
|
|
15
|
+
const { isMobile } = useDetectDeviceSize();
|
|
8
16
|
const dismissCta = useDismissCta();
|
|
9
17
|
|
|
10
18
|
const handleCloseWidget = () => {
|
|
@@ -12,11 +20,23 @@ export const CloseButton = ({ isOnDarkTheme }: { isOnDarkTheme: boolean }) => {
|
|
|
12
20
|
|
|
13
21
|
dismissCta.mutate(undefined, {
|
|
14
22
|
onError: (error) => {
|
|
15
|
-
|
|
23
|
+
Logger.logError(`Failed to dismiss CTA ${error.message}`);
|
|
16
24
|
},
|
|
17
25
|
});
|
|
18
26
|
};
|
|
19
27
|
|
|
28
|
+
if (isMobile) {
|
|
29
|
+
return (
|
|
30
|
+
<MobileCloseContainer>
|
|
31
|
+
<Button onClick={handleCloseWidget} variant={isOnDarkTheme ? 'TertiaryOnDark' : 'Tertiary'}>
|
|
32
|
+
<Flex padding="2">
|
|
33
|
+
<IconX16Line />
|
|
34
|
+
</Flex>
|
|
35
|
+
</Button>
|
|
36
|
+
</MobileCloseContainer>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
20
40
|
return (
|
|
21
41
|
<Button onClick={handleCloseWidget} variant={isOnDarkTheme ? 'TertiaryOnDark' : 'Tertiary'}>
|
|
22
42
|
<Flex padding="2">
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
|
|
2
3
|
import { PartnerContext } from '../../utils';
|
|
3
4
|
import { CtaBanner } from './CtaBanner';
|
|
4
5
|
|
|
5
6
|
const portalContainer = document.createElement('div');
|
|
6
7
|
document.body.append(portalContainer);
|
|
7
8
|
|
|
9
|
+
const bannerConfig = {
|
|
10
|
+
mainText: 'Business financing made easy. Unlock your next stage of growth with Wayflyer today.',
|
|
11
|
+
bulletPoints: ['Funds in 24 hours', 'No personal guarantees', '$5B+ in funding delivered'],
|
|
12
|
+
buttonText: 'Get funding',
|
|
13
|
+
buttonAction: () => {},
|
|
14
|
+
};
|
|
15
|
+
|
|
8
16
|
const defaultArgs = {
|
|
9
17
|
companyToken: 'demo-token',
|
|
10
18
|
partnerCallback: () => {},
|
|
@@ -12,6 +20,8 @@ const defaultArgs = {
|
|
|
12
20
|
onWidgetDismiss: () => {},
|
|
13
21
|
isWidgetDismissed: false,
|
|
14
22
|
skipAnimation: false,
|
|
23
|
+
bannerState: 'test',
|
|
24
|
+
bannerConfig,
|
|
15
25
|
portalContainer,
|
|
16
26
|
};
|
|
17
27
|
|
|
@@ -19,7 +29,7 @@ type CtaBannerStoryArgs = typeof defaultArgs;
|
|
|
19
29
|
|
|
20
30
|
const Template = (args: CtaBannerStoryArgs) => (
|
|
21
31
|
<PartnerContext.Provider value={{ ...args }}>
|
|
22
|
-
<CtaBanner skipAnimation={false} />
|
|
32
|
+
<CtaBanner bannerConfig={bannerConfig} skipAnimation={false} />
|
|
23
33
|
</PartnerContext.Provider>
|
|
24
34
|
);
|
|
25
35
|
|
|
@@ -4,9 +4,11 @@ import { css, styled } from 'styled-components';
|
|
|
4
4
|
|
|
5
5
|
import { MODAL_LOGO_IMAGE_URL, STATIC_BASE_URL } from '../../config';
|
|
6
6
|
import { usePreloadImage } from '../../hooks';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { BannerActions } from './BannerActions';
|
|
8
|
+
import { CtaMainText } from './CtaMainText';
|
|
9
|
+
import { CloseButton } from './CloseButton';
|
|
10
|
+
import type { BannerConfig } from '../../utils';
|
|
11
|
+
import { BulletList } from './BulletList.tsx';
|
|
10
12
|
|
|
11
13
|
const ON_DARK_THEMES = ['athena'];
|
|
12
14
|
const LIGHT_BG_THEMES = ['conjura', 'rocketFuel'];
|
|
@@ -60,10 +62,11 @@ const bannerAnimationProps: MotionProps = {
|
|
|
60
62
|
|
|
61
63
|
type Props = {
|
|
62
64
|
skipAnimation: boolean;
|
|
65
|
+
bannerConfig: BannerConfig;
|
|
63
66
|
};
|
|
64
67
|
|
|
65
|
-
export const CtaBanner = ({ skipAnimation }: Props) => {
|
|
66
|
-
const { isMobile
|
|
68
|
+
export const CtaBanner = ({ bannerConfig, skipAnimation }: Props) => {
|
|
69
|
+
const { isMobile } = useDetectDeviceSize();
|
|
67
70
|
const logoImageUrl = `${STATIC_BASE_URL}${MODAL_LOGO_IMAGE_URL}`;
|
|
68
71
|
const { themeName } = useTheme();
|
|
69
72
|
const isOnDarkTheme = ON_DARK_THEMES.includes(themeName);
|
|
@@ -71,6 +74,8 @@ export const CtaBanner = ({ skipAnimation }: Props) => {
|
|
|
71
74
|
const isTertiaryTheme = TERTIARY_THEMES.includes(themeName);
|
|
72
75
|
usePreloadImage(logoImageUrl);
|
|
73
76
|
|
|
77
|
+
const { buttonAction, buttonText, mainText, bulletPoints } = bannerConfig;
|
|
78
|
+
|
|
74
79
|
return (
|
|
75
80
|
<BannerContainer
|
|
76
81
|
$isLightBgTheme={isLightBgTheme}
|
|
@@ -79,10 +84,20 @@ export const CtaBanner = ({ skipAnimation }: Props) => {
|
|
|
79
84
|
initial={skipAnimation ? bannerAnimationProps.animate : bannerAnimationProps.initial}
|
|
80
85
|
>
|
|
81
86
|
<Flex gap="4" align={isMobile ? 'flex-start' : 'center'} justify="space-between" width="100%">
|
|
82
|
-
<
|
|
83
|
-
|
|
87
|
+
<Flex direction="column" gap="1">
|
|
88
|
+
<CtaMainText mainText={mainText} isOnDarkTheme={isOnDarkTheme} />
|
|
89
|
+
<BulletList isOnDarkTheme={isOnDarkTheme} bulletPoints={bulletPoints} />
|
|
90
|
+
</Flex>
|
|
91
|
+
{!isMobile ? (
|
|
92
|
+
<Flex gap="4">
|
|
93
|
+
<BannerActions isOnDarkTheme={isOnDarkTheme} buttonText={buttonText} buttonAction={buttonAction} />
|
|
94
|
+
<CloseButton isOnDarkTheme={isOnDarkTheme} />
|
|
95
|
+
</Flex>
|
|
96
|
+
) : (
|
|
97
|
+
<CloseButton isOnDarkTheme={isOnDarkTheme} />
|
|
98
|
+
)}
|
|
84
99
|
</Flex>
|
|
85
|
-
<
|
|
100
|
+
{isMobile && <BannerActions isOnDarkTheme={isOnDarkTheme} buttonText={buttonText} buttonAction={buttonAction} />}
|
|
86
101
|
</BannerContainer>
|
|
87
102
|
);
|
|
88
103
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Text, Heading, useDetectDeviceSize } from '@wayflyer/flyui';
|
|
2
|
+
|
|
3
|
+
type CtaMainTextProps = {
|
|
4
|
+
mainText: string;
|
|
5
|
+
isOnDarkTheme: boolean;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const CtaMainText = ({ mainText, isOnDarkTheme }: CtaMainTextProps) => {
|
|
9
|
+
const { isMobile } = useDetectDeviceSize();
|
|
10
|
+
|
|
11
|
+
const TextComponent = isMobile ? Text : Heading;
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<TextComponent
|
|
15
|
+
size={isMobile ? 'base' : 'lg'}
|
|
16
|
+
fontWeight="medium"
|
|
17
|
+
lineHeight="normal"
|
|
18
|
+
color={isOnDarkTheme ? 'onDark' : 'default'}
|
|
19
|
+
>
|
|
20
|
+
{mainText}
|
|
21
|
+
</TextComponent>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
|
|
3
3
|
import { PartnerContext } from '../../utils';
|
|
4
|
-
import {
|
|
4
|
+
import { ConsentModalContent } from './ConsentModalContent.tsx';
|
|
5
5
|
|
|
6
6
|
const portalContainer = document.createElement('div');
|
|
7
7
|
document.body.append(portalContainer);
|
|
@@ -17,9 +17,9 @@ const appArgs = {
|
|
|
17
17
|
|
|
18
18
|
const fn = (): void => {};
|
|
19
19
|
|
|
20
|
-
const meta: Meta<typeof
|
|
20
|
+
const meta: Meta<typeof ConsentModalContent> = {
|
|
21
21
|
title: 'ConsentModal',
|
|
22
|
-
component:
|
|
22
|
+
component: ConsentModalContent,
|
|
23
23
|
argTypes: {
|
|
24
24
|
isModalOpen: { control: 'boolean', defaultValue: true },
|
|
25
25
|
setIsModalOpen: { action: 'setIsModalOpen' },
|
|
@@ -28,36 +28,39 @@ const meta: Meta<typeof ConsentModal> = {
|
|
|
28
28
|
|
|
29
29
|
export default meta;
|
|
30
30
|
|
|
31
|
-
type Story = StoryObj<typeof
|
|
31
|
+
type Story = StoryObj<typeof ConsentModalContent>;
|
|
32
32
|
|
|
33
|
-
export const
|
|
34
|
-
args: {
|
|
35
|
-
isModalOpen: true,
|
|
36
|
-
setIsModalOpen: fn,
|
|
37
|
-
},
|
|
38
|
-
render: (args) => (
|
|
39
|
-
<PartnerContext.Provider value={{ ...appArgs }}>
|
|
40
|
-
<ConsentModal {...args} />
|
|
41
|
-
</PartnerContext.Provider>
|
|
42
|
-
),
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export const WithContent: Story = {
|
|
33
|
+
export const ConsentModal: Story = {
|
|
46
34
|
args: {
|
|
47
35
|
isModalOpen: true,
|
|
48
36
|
setIsModalOpen: fn,
|
|
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',
|
|
43
|
+
termsAndConditions:
|
|
44
|
+
'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
|
+
},
|
|
58
|
+
],
|
|
49
59
|
},
|
|
50
60
|
render: (args) => (
|
|
51
61
|
<>
|
|
52
62
|
<PartnerContext.Provider value={{ ...appArgs }}>
|
|
53
|
-
<
|
|
54
|
-
<div style={{ padding: '20px', background: 'white' }}>
|
|
55
|
-
<h2>Consent Agreement</h2>
|
|
56
|
-
<p>Please agree to the terms to proceed.</p>
|
|
57
|
-
<button>Continue to Wayflyer</button>
|
|
58
|
-
<button>Cancel</button>
|
|
59
|
-
</div>
|
|
60
|
-
</ConsentModal>
|
|
63
|
+
<ConsentModalContent {...args} />
|
|
61
64
|
</PartnerContext.Provider>
|
|
62
65
|
</>
|
|
63
66
|
),
|
|
@@ -1,42 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useIntl } from 'react-intl';
|
|
1
|
+
import type { Dispatch, SetStateAction } from 'react';
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { FundingSteps } from './FundingSteps';
|
|
7
|
-
import { Modal } from './Modal';
|
|
8
|
-
import { ModalFooter } from './ModalFooter';
|
|
9
|
-
import { STATIC_BASE_URL, MODAL_LOGO_IMAGE_URL } from '../../config';
|
|
3
|
+
import { useCopy } from '../../hooks';
|
|
4
|
+
import { ConsentModalContent } from './ConsentModalContent';
|
|
10
5
|
|
|
11
6
|
type ConsentModalProps = {
|
|
12
7
|
isModalOpen: boolean;
|
|
13
|
-
setIsModalOpen:
|
|
8
|
+
setIsModalOpen: Dispatch<SetStateAction<boolean>>;
|
|
14
9
|
};
|
|
15
10
|
|
|
16
|
-
const ImageContainer = styled(Image)`
|
|
17
|
-
width: 100%;
|
|
18
|
-
max-width: none;
|
|
19
|
-
`;
|
|
20
|
-
|
|
21
11
|
export const ConsentModal = ({ isModalOpen, setIsModalOpen }: ConsentModalProps) => {
|
|
22
|
-
const {
|
|
23
|
-
const
|
|
24
|
-
|
|
12
|
+
const { data: copy } = useCopy();
|
|
13
|
+
const consentModal = copy?.consentModal;
|
|
14
|
+
|
|
15
|
+
if (!consentModal) return null;
|
|
25
16
|
|
|
26
|
-
const
|
|
17
|
+
const { imageUrl, buttons, termsAndConditions, descriptions, title } = consentModal;
|
|
27
18
|
|
|
28
19
|
return (
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<ModalFooter setOpen={setIsModalOpen} />
|
|
39
|
-
</Flex>
|
|
40
|
-
</Modal>
|
|
20
|
+
<ConsentModalContent
|
|
21
|
+
imageUrl={imageUrl}
|
|
22
|
+
title={title}
|
|
23
|
+
descriptions={descriptions}
|
|
24
|
+
buttons={buttons}
|
|
25
|
+
termsAndConditions={termsAndConditions}
|
|
26
|
+
setIsModalOpen={setIsModalOpen}
|
|
27
|
+
isModalOpen={isModalOpen}
|
|
28
|
+
/>
|
|
41
29
|
);
|
|
42
30
|
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Dispatch, SetStateAction } from 'react';
|
|
2
|
+
import { Flex, Image, Text, useDetectDeviceSize } from '@wayflyer/flyui';
|
|
3
|
+
|
|
4
|
+
import { useDetectSmallScreen } from '../../hooks';
|
|
5
|
+
import { FundingSteps } from './FundingSteps.tsx';
|
|
6
|
+
import { ModalFooter } from './ModalFooter.tsx';
|
|
7
|
+
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
|
+
|
|
20
|
+
type ConsentModalContentProps = {
|
|
21
|
+
imageUrl: string;
|
|
22
|
+
title: string;
|
|
23
|
+
descriptions: ModalDescriptions[];
|
|
24
|
+
buttons: Buttons;
|
|
25
|
+
termsAndConditions: string;
|
|
26
|
+
setIsModalOpen: Dispatch<SetStateAction<boolean>>;
|
|
27
|
+
isModalOpen: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const ImageContainer = styled(Image)`
|
|
31
|
+
width: 100%;
|
|
32
|
+
max-width: none;
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
export const ConsentModalContent = ({
|
|
36
|
+
imageUrl,
|
|
37
|
+
title,
|
|
38
|
+
descriptions,
|
|
39
|
+
termsAndConditions,
|
|
40
|
+
buttons,
|
|
41
|
+
setIsModalOpen,
|
|
42
|
+
isModalOpen,
|
|
43
|
+
}: ConsentModalContentProps) => {
|
|
44
|
+
const isSmallScreen = useDetectSmallScreen();
|
|
45
|
+
const { isMobile } = useDetectDeviceSize();
|
|
46
|
+
|
|
47
|
+
const isReducedSpacing = isSmallScreen || isMobile;
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<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>
|
|
61
|
+
</Modal>
|
|
62
|
+
);
|
|
63
|
+
};
|