@wf-financing/ui 4.4.0 → 4.5.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 +6 -0
- package/dist/index.es.js +3902 -3919
- package/package.json +3 -3
- package/src/App.tsx +2 -18
- package/src/CtaWidget.snapshot.stories.tsx +276 -0
- package/src/CtaWidget.tsx +26 -39
- package/src/CtaWidgetContainer.tsx +32 -0
- package/src/components/banner/CtaBanner.snapshot.stories.tsx +8 -3
- package/src/components/banner/CtaBanner.tsx +3 -8
- package/src/components/modal/ConsentModal.snapshot.stories.tsx +8 -8
- package/src/components/modal/ConsentModal.tsx +21 -28
- package/src/hooks/useNotificationOnRender.ts +5 -5
- package/src/utils/{buildBannerConfig.ts → buildBannerProps.ts} +4 -4
- package/src/utils/buildCtaUiProps.ts +81 -0
- package/src/utils/index.ts +2 -2
- package/src/components/modal/ConsentModalContent.tsx +0 -36
- package/src/locales/en.json +0 -17
- package/src/utils/getBannerConfig.ts +0 -44
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wf-financing/ui",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": {
|
|
6
6
|
"import": "./dist/index.es.js",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"react-markdown": "^10.1.0",
|
|
39
39
|
"styled-components": "^6.1.19",
|
|
40
40
|
"@wf-financing/embedded-types": "1.1.0",
|
|
41
|
-
"@wf-financing/
|
|
42
|
-
"@wf-financing/
|
|
41
|
+
"@wf-financing/ui-assets": "1.0.2",
|
|
42
|
+
"@wf-financing/logger": "2.1.0"
|
|
43
43
|
},
|
|
44
44
|
"publishConfig": {
|
|
45
45
|
"access": "public"
|
package/src/App.tsx
CHANGED
|
@@ -5,10 +5,8 @@ import { Logger } from '@wf-financing/logger';
|
|
|
5
5
|
import { fetchThemeOverrides } from '@wayflyer/flyui-tokens';
|
|
6
6
|
import { getPartnerIdFromToken } from './utils';
|
|
7
7
|
import { useState } from 'react';
|
|
8
|
-
import { createIntl, createIntlCache, IntlShape } from 'react-intl';
|
|
9
8
|
|
|
10
|
-
import {
|
|
11
|
-
import enMessages from './locales/en.json';
|
|
9
|
+
import { CtaWidgetContainer } from './CtaWidgetContainer.tsx';
|
|
12
10
|
import { PartnerContext } from './utils';
|
|
13
11
|
|
|
14
12
|
type AppPropsType = {
|
|
@@ -21,9 +19,6 @@ type AppPropsType = {
|
|
|
21
19
|
};
|
|
22
20
|
|
|
23
21
|
const queryClient = new QueryClient();
|
|
24
|
-
const messages = {
|
|
25
|
-
en: enMessages,
|
|
26
|
-
};
|
|
27
22
|
|
|
28
23
|
export const App = ({
|
|
29
24
|
companyToken,
|
|
@@ -36,16 +31,6 @@ export const App = ({
|
|
|
36
31
|
const [isThemeLoaded, setIsThemeLoaded] = useState(false);
|
|
37
32
|
const [isWidgetDismissed, setIsWidgetDismissed] = useState(false);
|
|
38
33
|
|
|
39
|
-
const locale = 'en';
|
|
40
|
-
|
|
41
|
-
const intl: IntlShape = createIntl(
|
|
42
|
-
{
|
|
43
|
-
locale,
|
|
44
|
-
messages: messages[locale],
|
|
45
|
-
},
|
|
46
|
-
createIntlCache(),
|
|
47
|
-
);
|
|
48
|
-
|
|
49
34
|
const onWidgetDismiss = () => {
|
|
50
35
|
setIsWidgetDismissed(true);
|
|
51
36
|
Logger.logEvent('ui_sdk_banner_dismissed');
|
|
@@ -73,13 +58,12 @@ export const App = ({
|
|
|
73
58
|
Logger.logError(`Error while loading theme overridesfor partner: ${companyToken}`);
|
|
74
59
|
},
|
|
75
60
|
})}
|
|
76
|
-
intl={intl}
|
|
77
61
|
onThemeLoad={() => setIsThemeLoaded(true)}
|
|
78
62
|
onThemeLoadError={() => {
|
|
79
63
|
Logger.logError(`Error while loading theme for partner: ${companyToken}`);
|
|
80
64
|
}}
|
|
81
65
|
>
|
|
82
|
-
{isThemeLoaded && <
|
|
66
|
+
{isThemeLoaded && <CtaWidgetContainer />}
|
|
83
67
|
</FlyUIProvider>
|
|
84
68
|
</PartnerContext.Provider>
|
|
85
69
|
</QueryClientProvider>
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import {
|
|
3
|
+
type CtaGenericOfferType,
|
|
4
|
+
type CtaIndicativeOfferType,
|
|
5
|
+
type CtaContinueFundingType,
|
|
6
|
+
CtaStateType,
|
|
7
|
+
ApplicationRequiredActions,
|
|
8
|
+
} from '@wf-financing/embedded-types';
|
|
9
|
+
import type { Copy } from '@wf-financing/ui-assets';
|
|
10
|
+
|
|
11
|
+
import { PartnerContext, EventContext, type PartnerContextType } from './utils';
|
|
12
|
+
import { CtaWidget } from './CtaWidget.tsx';
|
|
13
|
+
|
|
14
|
+
const portalContainer = document.createElement('div');
|
|
15
|
+
document.body.append(portalContainer);
|
|
16
|
+
const partnerContextValue: PartnerContextType = {
|
|
17
|
+
companyToken: 'demo-token',
|
|
18
|
+
partnerCallback: () => {},
|
|
19
|
+
onWidgetClose: () => {},
|
|
20
|
+
onWidgetDismiss: () => {},
|
|
21
|
+
isWidgetDismissed: false,
|
|
22
|
+
mountToTargetTimestamp: 10,
|
|
23
|
+
portalContainer,
|
|
24
|
+
};
|
|
25
|
+
const eventContextValue = {
|
|
26
|
+
logEvent: () => {},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const genericOfferCta: CtaGenericOfferType = {
|
|
30
|
+
state: CtaStateType.GENERIC_OFFER,
|
|
31
|
+
data: {
|
|
32
|
+
config: {
|
|
33
|
+
text: 'Business financing made easy. Unlock your next stage of growth with Wayflyer today.',
|
|
34
|
+
bullet_points: ['Funds in 24 hours', 'No personal guarantees', '$5B+ in funding delivered'],
|
|
35
|
+
button_label: 'Get funding',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
dismissal_key: 'generic_offer|0',
|
|
39
|
+
};
|
|
40
|
+
const indicativeOfferCta: CtaIndicativeOfferType = {
|
|
41
|
+
state: CtaStateType.INDICATIVE_OFFER,
|
|
42
|
+
data: {
|
|
43
|
+
offer: {
|
|
44
|
+
id: 'id',
|
|
45
|
+
amount: 100000,
|
|
46
|
+
currency: 'USD',
|
|
47
|
+
},
|
|
48
|
+
config: {
|
|
49
|
+
text: "You've pre-qualified for up to $100,000 in financing with Wayflyer. Unlock your next stage of growth.",
|
|
50
|
+
bullet_points: ['Funds in 24 hours', 'No personal guarantees', '$5B+ in funding delivered'],
|
|
51
|
+
button_label: 'Get funding',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
dismissal_key: 'indicative_offer|0',
|
|
55
|
+
};
|
|
56
|
+
const completeApplicationCta: CtaContinueFundingType = {
|
|
57
|
+
state: CtaStateType.CONTINUE_APPLICATION,
|
|
58
|
+
data: {
|
|
59
|
+
config: {
|
|
60
|
+
text: 'Almost there! Continue your application for financing with Wayflyer.',
|
|
61
|
+
bullet_points: [],
|
|
62
|
+
button_label: 'Continue applying',
|
|
63
|
+
required_action: ApplicationRequiredActions.COMPLETE_APPLICATION,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
dismissal_key: 'continue_application|1c0020f7-5c01-403d-8b9d-d65d27606c08|complete_application',
|
|
67
|
+
};
|
|
68
|
+
const provideAdditionalInfoCta: CtaContinueFundingType = {
|
|
69
|
+
state: CtaStateType.CONTINUE_APPLICATION,
|
|
70
|
+
data: {
|
|
71
|
+
config: {
|
|
72
|
+
text: 'Your Wayflyer financing application needs additional information to proceed.',
|
|
73
|
+
bullet_points: [],
|
|
74
|
+
button_label: 'Login to Wayflyer',
|
|
75
|
+
required_action: ApplicationRequiredActions.PROVIDE_ADDITIONAL_INFO,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
dismissal_key: 'continue_application|10a2c08f-d4ff-4e7f-b4ca-37a7f6d442ae|provide_additional_info',
|
|
79
|
+
};
|
|
80
|
+
const selectOfferCta: CtaContinueFundingType = {
|
|
81
|
+
state: CtaStateType.CONTINUE_APPLICATION,
|
|
82
|
+
data: {
|
|
83
|
+
offer: {
|
|
84
|
+
id: '1',
|
|
85
|
+
amount: 100000,
|
|
86
|
+
currency: 'USD',
|
|
87
|
+
},
|
|
88
|
+
config: {
|
|
89
|
+
text: "You've been approved for Wayflyer financing up to $100000! Choose your offer.",
|
|
90
|
+
bullet_points: [],
|
|
91
|
+
button_label: 'View offer',
|
|
92
|
+
required_action: ApplicationRequiredActions.SELECT_OFFER,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
dismissal_key: 'continue_application|1c0020f7-5c01-403d-8b9d-d65d27606c08|select_offer',
|
|
96
|
+
};
|
|
97
|
+
const completeKycCta: CtaContinueFundingType = {
|
|
98
|
+
state: CtaStateType.CONTINUE_APPLICATION,
|
|
99
|
+
data: {
|
|
100
|
+
config: {
|
|
101
|
+
text: 'Almost there. Wayflyer just needs a few more details to unlock your financing.',
|
|
102
|
+
bullet_points: [],
|
|
103
|
+
button_label: 'Continue application',
|
|
104
|
+
required_action: ApplicationRequiredActions.COMPLETE_KYC,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
dismissal_key: 'continue_application|10a2c08f-d4ff-4e7f-b4ca-37a7f6d442ae|complete_kyc',
|
|
108
|
+
};
|
|
109
|
+
const signContractCta: CtaContinueFundingType = {
|
|
110
|
+
state: CtaStateType.CONTINUE_APPLICATION,
|
|
111
|
+
data: {
|
|
112
|
+
config: {
|
|
113
|
+
text: 'Almost there! Review and sign your Wayflyer contract to receive funds.',
|
|
114
|
+
bullet_points: [],
|
|
115
|
+
button_label: 'View contract',
|
|
116
|
+
required_action: ApplicationRequiredActions.COMPLETE_KYC,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
dismissal_key: 'continue_application|10a2c08f-d4ff-4e7f-b4ca-37a7f6d442ae|sign_contract',
|
|
120
|
+
};
|
|
121
|
+
const copy: Copy = {
|
|
122
|
+
genericOffer: {
|
|
123
|
+
type: 'genericOffer',
|
|
124
|
+
version: '1.0',
|
|
125
|
+
language: 'en',
|
|
126
|
+
cohort: 'default',
|
|
127
|
+
mainText: 'Business financing made easy. Unlock your next stage of growth with Wayflyer today.',
|
|
128
|
+
bulletPoints: ['Funds in 24 hours', 'No personal guarantees', '$5B+ in funding delivered'],
|
|
129
|
+
buttonText: 'Get financing',
|
|
130
|
+
},
|
|
131
|
+
indicativeOffer: {
|
|
132
|
+
type: 'indicativeOffer',
|
|
133
|
+
version: '1.0',
|
|
134
|
+
language: 'en',
|
|
135
|
+
cohort: 'default',
|
|
136
|
+
mainText: 'You’ve pre-qualified for up to {amount} in financing with Wayflyer. Unlock your growth.',
|
|
137
|
+
bulletPoints: ['Funds in 24 hours', 'No personal guarantees', '$5B+ in funding delivered'],
|
|
138
|
+
buttonText: 'Get financing',
|
|
139
|
+
},
|
|
140
|
+
selectOffer: {
|
|
141
|
+
type: 'selectOffer',
|
|
142
|
+
version: '1.0',
|
|
143
|
+
language: 'en',
|
|
144
|
+
cohort: 'default',
|
|
145
|
+
mainText: "You've been approved for Wayflyer financing up to {amount}! Choose your offer.",
|
|
146
|
+
buttonText: 'View offer',
|
|
147
|
+
},
|
|
148
|
+
completeKyc: {
|
|
149
|
+
type: 'completeKyc',
|
|
150
|
+
version: '1.0',
|
|
151
|
+
language: 'en',
|
|
152
|
+
cohort: 'default',
|
|
153
|
+
mainText: 'Almost there. Wayflyer just needs a few more details to unlock your financing.',
|
|
154
|
+
buttonText: 'Continue application',
|
|
155
|
+
},
|
|
156
|
+
signContract: {
|
|
157
|
+
type: 'signContract',
|
|
158
|
+
version: '1.0',
|
|
159
|
+
language: 'en',
|
|
160
|
+
cohort: 'default',
|
|
161
|
+
mainText: 'Almost there! Review and sign your Wayflyer contract to receive funds.',
|
|
162
|
+
buttonText: 'View contract',
|
|
163
|
+
},
|
|
164
|
+
provideAdditionalInfo: {
|
|
165
|
+
type: 'provideAdditionalInfo',
|
|
166
|
+
version: '1.0',
|
|
167
|
+
language: 'en',
|
|
168
|
+
cohort: 'default',
|
|
169
|
+
mainText: 'Your Wayflyer financing application needs additional information to proceed.',
|
|
170
|
+
buttonText: 'Continue application',
|
|
171
|
+
},
|
|
172
|
+
completeApplication: {
|
|
173
|
+
type: 'completeApplication',
|
|
174
|
+
version: '1.0',
|
|
175
|
+
language: 'en',
|
|
176
|
+
cohort: 'default',
|
|
177
|
+
mainText: 'Almost there! Complete your application to access capital with Wayflyer.',
|
|
178
|
+
buttonText: 'Continue application',
|
|
179
|
+
},
|
|
180
|
+
consentModal: {
|
|
181
|
+
type: 'consentModal',
|
|
182
|
+
version: '1.0',
|
|
183
|
+
language: 'en',
|
|
184
|
+
cohort: 'default',
|
|
185
|
+
logoUrl: 'https://static.wayflyer.com/flyui-assets/logos/wayflyer-icon-logo-v3.svg',
|
|
186
|
+
title: 'Fuel your growth with capital from Wayflyer',
|
|
187
|
+
bulletPoints: [
|
|
188
|
+
'$5B deployed with 4.9 stars on Trustpilot',
|
|
189
|
+
'Apply in minutes and get capital in hours',
|
|
190
|
+
'Flexible offers designed to fit your business',
|
|
191
|
+
],
|
|
192
|
+
button: 'Continue to Wayflyer',
|
|
193
|
+
termsAndConditions:
|
|
194
|
+
'By proceeding, you consent to us sharing your information with Wayflyer in accordance with Wayflyer’s [Privacy Policy]({termsUrl}).',
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const meta = {
|
|
199
|
+
title: 'CtaWidget',
|
|
200
|
+
component: CtaWidget,
|
|
201
|
+
decorators: [
|
|
202
|
+
(Story, context) => (
|
|
203
|
+
<EventContext.Provider value={eventContextValue}>
|
|
204
|
+
<PartnerContext.Provider value={partnerContextValue}>
|
|
205
|
+
<Story {...context.args} />
|
|
206
|
+
</PartnerContext.Provider>
|
|
207
|
+
</EventContext.Provider>
|
|
208
|
+
),
|
|
209
|
+
],
|
|
210
|
+
} satisfies Meta<typeof CtaWidget>;
|
|
211
|
+
export default meta;
|
|
212
|
+
|
|
213
|
+
type Story = StoryObj<typeof meta>;
|
|
214
|
+
|
|
215
|
+
export const GenericOffer: Story = {
|
|
216
|
+
name: 'Generic Offer',
|
|
217
|
+
args: {
|
|
218
|
+
skipAnimation: false,
|
|
219
|
+
cta: genericOfferCta,
|
|
220
|
+
copy,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
export const IndicativeOffer: Story = {
|
|
225
|
+
name: 'Indicative Offer',
|
|
226
|
+
args: {
|
|
227
|
+
skipAnimation: false,
|
|
228
|
+
cta: indicativeOfferCta,
|
|
229
|
+
copy,
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
export const CompleteApplication: Story = {
|
|
234
|
+
name: 'Complete Application',
|
|
235
|
+
args: {
|
|
236
|
+
skipAnimation: false,
|
|
237
|
+
cta: completeApplicationCta,
|
|
238
|
+
copy,
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export const ProvideAdditionalInfo: Story = {
|
|
243
|
+
name: 'Provide Additional Info',
|
|
244
|
+
args: {
|
|
245
|
+
skipAnimation: false,
|
|
246
|
+
cta: provideAdditionalInfoCta,
|
|
247
|
+
copy,
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
export const SelectOffer: Story = {
|
|
252
|
+
name: 'Select Offer',
|
|
253
|
+
args: {
|
|
254
|
+
skipAnimation: false,
|
|
255
|
+
cta: selectOfferCta,
|
|
256
|
+
copy,
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
export const CompleteKyc: Story = {
|
|
261
|
+
name: 'Complete Kyc',
|
|
262
|
+
args: {
|
|
263
|
+
skipAnimation: false,
|
|
264
|
+
cta: completeKycCta,
|
|
265
|
+
copy,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
export const SignContract: Story = {
|
|
270
|
+
name: 'Sign Contract',
|
|
271
|
+
args: {
|
|
272
|
+
skipAnimation: false,
|
|
273
|
+
cta: signContractCta,
|
|
274
|
+
copy,
|
|
275
|
+
},
|
|
276
|
+
};
|
package/src/CtaWidget.tsx
CHANGED
|
@@ -1,43 +1,26 @@
|
|
|
1
|
-
import { AnimatePresence } from 'framer-motion';
|
|
2
1
|
import { useState } from 'react';
|
|
3
|
-
import { ContinueHostedApplicationResponseType } from '@wf-financing/embedded-types';
|
|
2
|
+
import type { CtaResponseType, ContinueHostedApplicationResponseType } from '@wf-financing/embedded-types';
|
|
3
|
+
import type { Copy } from '@wf-financing/ui-assets';
|
|
4
4
|
|
|
5
|
-
import { AnimationWrapper } from './components/banner/AnimationWrapper';
|
|
6
5
|
import { CtaBanner } from './components/banner/CtaBanner';
|
|
7
6
|
import { ConsentModal } from './components/modal/ConsentModal.tsx';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
useCtaBanner,
|
|
11
|
-
usePartnerContext,
|
|
12
|
-
useContinueHostedApplication,
|
|
13
|
-
usePreloadImage,
|
|
14
|
-
useNotificationOnRender,
|
|
15
|
-
} from './hooks';
|
|
16
|
-
import { getBannerConfig } from './utils';
|
|
7
|
+
import { buildCtaUiProps } from './utils';
|
|
8
|
+
import { useContinueHostedApplication, useNotificationOnRender } from './hooks';
|
|
17
9
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
type CtaWidgetProps = {
|
|
11
|
+
cta: Exclude<CtaResponseType, null>;
|
|
12
|
+
copy: Copy;
|
|
13
|
+
skipAnimation: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const CtaWidget = ({ cta, copy, skipAnimation }: CtaWidgetProps) => {
|
|
24
17
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
25
|
-
|
|
18
|
+
const { mutate: continueHostedApplicationMutation } = useContinueHostedApplication();
|
|
26
19
|
const sendNotification = useNotificationOnRender();
|
|
27
20
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
if (!showBanner) return null;
|
|
31
|
-
|
|
32
|
-
const handleExitComplete = () => {
|
|
33
|
-
if ((!isLoading && !cta) || isWidgetDismissed) {
|
|
34
|
-
onWidgetClose();
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const handleIsModalOpen = () => setIsModalOpen((isModalOpen) => !isModalOpen);
|
|
21
|
+
const handleIsModalOpen = () => setIsModalOpen(() => !isModalOpen);
|
|
39
22
|
const handleContinueHostedApplication = () => {
|
|
40
|
-
|
|
23
|
+
continueHostedApplicationMutation(undefined, {
|
|
41
24
|
onSuccess: (nextUrl: ContinueHostedApplicationResponseType) => {
|
|
42
25
|
const { next } = nextUrl;
|
|
43
26
|
window.open(next);
|
|
@@ -48,15 +31,19 @@ export const CtaWidget = () => {
|
|
|
48
31
|
});
|
|
49
32
|
};
|
|
50
33
|
|
|
51
|
-
const
|
|
52
|
-
|
|
34
|
+
const { bannerProps, consentModalProps, partnerTemplateProps } = buildCtaUiProps(
|
|
35
|
+
cta,
|
|
36
|
+
copy,
|
|
37
|
+
handleIsModalOpen,
|
|
38
|
+
handleContinueHostedApplication,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
sendNotification(cta, bannerProps);
|
|
53
42
|
|
|
54
43
|
return (
|
|
55
|
-
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
</AnimationWrapper>
|
|
60
|
-
</AnimatePresence>
|
|
44
|
+
<>
|
|
45
|
+
<CtaBanner skipAnimation={skipAnimation} {...bannerProps} />
|
|
46
|
+
{consentModalProps && <ConsentModal {...consentModalProps} {...partnerTemplateProps} isModalOpen={isModalOpen} />}
|
|
47
|
+
</>
|
|
61
48
|
);
|
|
62
49
|
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { AnimatePresence } from 'framer-motion';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { AnimationWrapper } from './components/banner/AnimationWrapper';
|
|
5
|
+
import { CtaWidget } from './CtaWidget.tsx';
|
|
6
|
+
import { useCopy, useCtaBanner, usePartnerContext, usePreloadImage } from './hooks';
|
|
7
|
+
|
|
8
|
+
export const CtaWidgetContainer = () => {
|
|
9
|
+
const { isLoading, data: cta } = useCtaBanner();
|
|
10
|
+
const { data: copy } = useCopy();
|
|
11
|
+
const { onWidgetClose, isWidgetDismissed, options } = usePartnerContext();
|
|
12
|
+
const [skipAnimation] = useState(() => !!options?.skipAnimations || !isLoading);
|
|
13
|
+
usePreloadImage(copy);
|
|
14
|
+
|
|
15
|
+
const showBanner = !!cta && !!copy && !isWidgetDismissed;
|
|
16
|
+
|
|
17
|
+
if (!showBanner) return null;
|
|
18
|
+
|
|
19
|
+
const handleExitComplete = () => {
|
|
20
|
+
if ((!isLoading && !cta) || isWidgetDismissed) {
|
|
21
|
+
onWidgetClose();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<AnimatePresence mode="wait" onExitComplete={handleExitComplete}>
|
|
27
|
+
<AnimationWrapper skipAnimation={skipAnimation}>
|
|
28
|
+
<CtaWidget cta={cta} copy={copy} skipAnimation={skipAnimation} />
|
|
29
|
+
</AnimationWrapper>
|
|
30
|
+
</AnimatePresence>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
@@ -6,7 +6,7 @@ import { CtaBanner } from './CtaBanner';
|
|
|
6
6
|
const portalContainer = document.createElement('div');
|
|
7
7
|
document.body.append(portalContainer);
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const bannerProps = {
|
|
10
10
|
mainText: 'Business financing made easy. Unlock your next stage of growth with Wayflyer today.',
|
|
11
11
|
bulletPoints: ['Funds in 24 hours', 'No personal guarantees', '$5B+ in funding delivered'],
|
|
12
12
|
buttonText: 'Get funding',
|
|
@@ -24,7 +24,12 @@ const defaultArgs = {
|
|
|
24
24
|
skipAnimation: false,
|
|
25
25
|
bannerState: 'test',
|
|
26
26
|
mountToTargetTimestamp: 10,
|
|
27
|
-
|
|
27
|
+
mainText: 'Business financing made easy. Unlock your next stage of growth with Wayflyer today.',
|
|
28
|
+
bulletPoints: ['Funds in 24 hours', 'No personal guarantees', '$5B+ in funding delivered'],
|
|
29
|
+
buttonText: 'Get funding',
|
|
30
|
+
buttonAction: () => {},
|
|
31
|
+
version: '1.0.0',
|
|
32
|
+
type: 'genericOffer',
|
|
28
33
|
portalContainer,
|
|
29
34
|
};
|
|
30
35
|
|
|
@@ -32,7 +37,7 @@ type CtaBannerStoryArgs = typeof defaultArgs;
|
|
|
32
37
|
|
|
33
38
|
const Template = (args: CtaBannerStoryArgs) => (
|
|
34
39
|
<PartnerContext.Provider value={{ ...args }}>
|
|
35
|
-
<CtaBanner
|
|
40
|
+
<CtaBanner {...bannerProps} skipAnimation={false} />
|
|
36
41
|
</PartnerContext.Provider>
|
|
37
42
|
);
|
|
38
43
|
|
|
@@ -7,7 +7,7 @@ import { usePartnerContext } from '../../hooks';
|
|
|
7
7
|
import { BannerActions } from './BannerActions';
|
|
8
8
|
import { CtaMainText } from './CtaMainText';
|
|
9
9
|
import { CloseButton } from './CloseButton';
|
|
10
|
-
import type {
|
|
10
|
+
import type { BannerProps } from '../../utils';
|
|
11
11
|
import { BulletList } from './BulletList.tsx';
|
|
12
12
|
|
|
13
13
|
const BannerContainer = styled(motion.aside)<{ $bgVariant: ThemeOverrides['cta']['surface'] }>`
|
|
@@ -56,19 +56,14 @@ const bannerAnimationProps: MotionProps = {
|
|
|
56
56
|
},
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
type
|
|
60
|
-
skipAnimation: boolean;
|
|
61
|
-
bannerConfig: BannerConfig;
|
|
62
|
-
};
|
|
59
|
+
type CtaBannerProps = BannerProps & { skipAnimation: boolean };
|
|
63
60
|
|
|
64
|
-
export const CtaBanner = ({
|
|
61
|
+
export const CtaBanner = ({ buttonAction, buttonText, mainText, bulletPoints, skipAnimation }: CtaBannerProps) => {
|
|
65
62
|
const { isMobile } = useDetectDeviceSize();
|
|
66
63
|
const { portalContainer } = usePartnerContext();
|
|
67
64
|
|
|
68
65
|
const { cta: themeFlags } = getThemeFlags({ rootElement: portalContainer });
|
|
69
66
|
|
|
70
|
-
const { buttonAction, buttonText, mainText, bulletPoints } = bannerConfig;
|
|
71
|
-
|
|
72
67
|
const isOnDarkTheme = themeFlags.content === 'onDark';
|
|
73
68
|
|
|
74
69
|
return (
|
|
@@ -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 { ConsentModal } from './ConsentModal.tsx';
|
|
5
5
|
|
|
6
6
|
const portalContainer = document.createElement('div');
|
|
7
7
|
document.body.append(portalContainer);
|
|
@@ -18,23 +18,23 @@ const appArgs = {
|
|
|
18
18
|
|
|
19
19
|
const fn = (): void => {};
|
|
20
20
|
|
|
21
|
-
const meta: Meta<typeof
|
|
21
|
+
const meta: Meta<typeof ConsentModal> = {
|
|
22
22
|
title: 'ConsentModal',
|
|
23
|
-
component:
|
|
23
|
+
component: ConsentModal,
|
|
24
24
|
argTypes: {
|
|
25
25
|
isModalOpen: { control: 'boolean', defaultValue: true },
|
|
26
|
-
|
|
26
|
+
onModalStateChange: { action: 'setIsModalOpen' },
|
|
27
27
|
},
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
export default meta;
|
|
31
31
|
|
|
32
|
-
type Story = StoryObj<typeof
|
|
32
|
+
type Story = StoryObj<typeof ConsentModal>;
|
|
33
33
|
|
|
34
|
-
export const
|
|
34
|
+
export const ConsentModalStory: Story = {
|
|
35
35
|
args: {
|
|
36
36
|
isModalOpen: true,
|
|
37
|
-
|
|
37
|
+
onModalStateChange: fn,
|
|
38
38
|
title: 'Fuel your growth with capital from Wayflyer',
|
|
39
39
|
button: 'Continue to Wayflyer',
|
|
40
40
|
logoUrl: 'https://static.wayflyer.com/flyui-assets/logos/wayflyer-icon-logo-v3.svg',
|
|
@@ -49,7 +49,7 @@ export const ConsentModal: Story = {
|
|
|
49
49
|
render: (args) => (
|
|
50
50
|
<>
|
|
51
51
|
<PartnerContext.Provider value={{ ...appArgs }}>
|
|
52
|
-
<
|
|
52
|
+
<ConsentModal {...args} />
|
|
53
53
|
</PartnerContext.Provider>
|
|
54
54
|
</>
|
|
55
55
|
),
|
|
@@ -1,33 +1,26 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ConsentModalProps as ModalProps } from '../../utils';
|
|
2
|
+
import { ModalHeader } from './ModalHeader.tsx';
|
|
3
|
+
import { BulletList } from './BulletList.tsx';
|
|
4
|
+
import { ModalFooter } from './ModalFooter.tsx';
|
|
5
|
+
import { Modal } from './Modal.tsx';
|
|
2
6
|
|
|
3
|
-
|
|
4
|
-
import { ConsentModalContent } from './ConsentModalContent';
|
|
5
|
-
|
|
6
|
-
type ConsentModalProps = {
|
|
7
|
-
isModalOpen: boolean;
|
|
8
|
-
setIsModalOpen: Dispatch<SetStateAction<boolean>>;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export const ConsentModal = ({ isModalOpen, setIsModalOpen }: ConsentModalProps) => {
|
|
12
|
-
const { data: copy } = useCopy();
|
|
13
|
-
const consentModal = copy?.consentModal;
|
|
14
|
-
const partnerTemplate = copy?.partnerTemplate;
|
|
15
|
-
|
|
16
|
-
if (!consentModal) return null;
|
|
17
|
-
|
|
18
|
-
const { logoUrl, button, termsAndConditions, bulletPoints, title } = consentModal;
|
|
19
|
-
const partnerLogoUrl = partnerTemplate?.partnerLogoUrl;
|
|
7
|
+
type ConsentModalProps = ModalProps & { isModalOpen: boolean; partnerLogoUrl?: string };
|
|
20
8
|
|
|
9
|
+
export const ConsentModal = ({
|
|
10
|
+
isModalOpen,
|
|
11
|
+
logoUrl,
|
|
12
|
+
button,
|
|
13
|
+
termsAndConditions,
|
|
14
|
+
bulletPoints,
|
|
15
|
+
title,
|
|
16
|
+
onModalStateChange,
|
|
17
|
+
partnerLogoUrl,
|
|
18
|
+
}: ConsentModalProps) => {
|
|
21
19
|
return (
|
|
22
|
-
<
|
|
23
|
-
logoUrl={logoUrl}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
button={button}
|
|
28
|
-
termsAndConditions={termsAndConditions}
|
|
29
|
-
setIsModalOpen={setIsModalOpen}
|
|
30
|
-
isModalOpen={isModalOpen}
|
|
31
|
-
/>
|
|
20
|
+
<Modal isModalOpen={isModalOpen} setIsModalOpen={onModalStateChange}>
|
|
21
|
+
<ModalHeader title={title} logoUrl={logoUrl} partnerLogoUrl={partnerLogoUrl} />
|
|
22
|
+
<BulletList bulletPoints={bulletPoints} />
|
|
23
|
+
<ModalFooter setIsModalOpen={onModalStateChange} button={button} termsAndConditions={termsAndConditions} />
|
|
24
|
+
</Modal>
|
|
32
25
|
);
|
|
33
26
|
};
|
|
@@ -2,23 +2,23 @@ import { CtaResponseType, CtaStateType } from '@wf-financing/embedded-types';
|
|
|
2
2
|
|
|
3
3
|
import { useEventContext } from './useEventContext';
|
|
4
4
|
import { usePartnerContext } from './usePartnerContext';
|
|
5
|
-
import { type
|
|
5
|
+
import { type BannerProps } from '../utils';
|
|
6
6
|
|
|
7
7
|
export const useNotificationOnRender = () => {
|
|
8
8
|
const { logEvent } = useEventContext();
|
|
9
9
|
const { mountToTargetTimestamp } = usePartnerContext();
|
|
10
10
|
|
|
11
|
-
return (cta: CtaResponseType,
|
|
12
|
-
if (!cta || !
|
|
11
|
+
return (cta: CtaResponseType, bannerProps: BannerProps) => {
|
|
12
|
+
if (!cta || !bannerProps) return;
|
|
13
13
|
|
|
14
14
|
const bannerRenderingLatency = Date.now() - mountToTargetTimestamp;
|
|
15
15
|
|
|
16
16
|
const { state, data } = cta;
|
|
17
17
|
const attrs: Record<string, string | number> = {
|
|
18
18
|
cta_state: state,
|
|
19
|
-
banner_copy_version:
|
|
19
|
+
banner_copy_version: bannerProps.version,
|
|
20
20
|
banner_rendering_latency: bannerRenderingLatency,
|
|
21
|
-
copy_type:
|
|
21
|
+
copy_type: bannerProps.type,
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
if (state === CtaStateType.CONTINUE_APPLICATION) {
|