saafe-redirection-flow 2.0.0 → 2.1.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 +16 -0
- package/package.json +1 -1
- package/src/components/alert/alert.tsx +10 -4
- package/src/components/modal/HelpModal.tsx +122 -0
- package/src/components/session/SessionTimer.tsx +20 -33
- package/src/components/ui/bottom-sheet.tsx +1 -1
- package/src/components/ui/frosted-panel.tsx +0 -2
- package/src/components/ui/otp-input.tsx +5 -3
- package/src/hooks/use-account-discovery.ts +13 -6
- package/src/hooks/use-auto-discovery.ts +226 -0
- package/src/index.css +3 -0
- package/src/pages/accounts/AccountsToProceed.tsx +283 -20
- package/src/pages/accounts/Discover.tsx +127 -9
- package/src/pages/accounts/DiscoverAccount.tsx +176 -38
- package/src/pages/accounts/LinkSelectedAccounts.tsx +15 -9
- package/src/pages/accounts/OldUser.tsx +192 -40
- package/src/pages/accounts/link-accounts.tsx +19 -11
- package/src/pages/consent/ReviewConsent.tsx +18 -10
- package/src/pages/consent/rejected.tsx +17 -8
- package/src/pages/consent/success.tsx +125 -76
- package/src/store/fip.store.ts +74 -28
- package/src/store/redirect.store.ts +13 -0
- package/src/utils/auto-discovery.ts +126 -0
- package/stage-aa-0407251720.zip +0 -0
@@ -24,6 +24,7 @@ import { useSetPageTitle } from '@/hooks/use-page-title'
|
|
24
24
|
import { useNavigationBlock } from '@/store/NavigationBlockContext'
|
25
25
|
import DummyFooter from '@/components/dummyFooter'
|
26
26
|
import { useFipQuery } from '@/hooks/use-fip-query'
|
27
|
+
import { parseAutoDiscoveryConfig } from '@/utils/auto-discovery'
|
27
28
|
|
28
29
|
interface BankAccount {
|
29
30
|
linkRefNumber: string;
|
@@ -44,6 +45,17 @@ const OldUser = () => {
|
|
44
45
|
const { shouldAllowNavigation } = useNavigationBlock();
|
45
46
|
const navigate = useNavigate();
|
46
47
|
|
48
|
+
// Parse auto discovery configuration for showDiscoverMore
|
49
|
+
const autoDiscoveryConfig = parseAutoDiscoveryConfig(decodedInfo?.customization?.otherAppCustomization);
|
50
|
+
|
51
|
+
// Function to check if discover more should be shown for a category
|
52
|
+
const shouldShowDiscoverMore = (category: string) => {
|
53
|
+
const categoryKey = category.toUpperCase() as keyof typeof autoDiscoveryConfig;
|
54
|
+
return autoDiscoveryConfig[categoryKey]?.showDiscoverMore !== false;
|
55
|
+
};
|
56
|
+
|
57
|
+
console.log('OldUser Debug - Auto Discovery Config:', autoDiscoveryConfig);
|
58
|
+
|
47
59
|
// Fetch FIP data with React Query - use dynamic consent handle
|
48
60
|
const { refetch } = useFipQuery(consentHandle)
|
49
61
|
|
@@ -101,7 +113,7 @@ const OldUser = () => {
|
|
101
113
|
onError: () => {
|
102
114
|
if (!mounted) return
|
103
115
|
}
|
104
|
-
})
|
116
|
+
});
|
105
117
|
|
106
118
|
const accounts = getLinkedAccountsList.data;
|
107
119
|
return accounts.reduce((acc: any, account) => {
|
@@ -114,6 +126,64 @@ const OldUser = () => {
|
|
114
126
|
}, {});
|
115
127
|
}, [getLinkedAccountsList?.data]);
|
116
128
|
|
129
|
+
// Check for missing identifiers before account discovery - moved to useEffect
|
130
|
+
React.useEffect(() => {
|
131
|
+
if (!getLinkedAccountsList?.data || getLinkedAccountsList.data.length === 0) {
|
132
|
+
return; // Don't check identifiers if no linked accounts
|
133
|
+
}
|
134
|
+
|
135
|
+
const fipIdsArray = Array.from(new Set(getLinkedAccountsList.data.map((account: BankAccount) => account.fipHandle)));
|
136
|
+
|
137
|
+
// Get the FIP store state to access fips data
|
138
|
+
const { fips } = useFipStore.getState();
|
139
|
+
|
140
|
+
const identifiersList: any[] = [];
|
141
|
+
|
142
|
+
// Collect required identifiers from selected FIPs
|
143
|
+
fips
|
144
|
+
.filter(fip => fipIdsArray.includes(fip.id))
|
145
|
+
.forEach(fip => {
|
146
|
+
fip.Identifiers?.forEach(identifier => {
|
147
|
+
if (!identifiersList.find(i => i.type === identifier.type)) {
|
148
|
+
const value =
|
149
|
+
identifier.type === 'PAN'
|
150
|
+
? decodedInfo?.pan
|
151
|
+
: identifier.type === 'MOBILE'
|
152
|
+
? decodedInfo?.phoneNumber
|
153
|
+
: identifier.type === 'DOB'
|
154
|
+
? decodedInfo?.dob
|
155
|
+
: null;
|
156
|
+
identifiersList.push({ ...identifier, value });
|
157
|
+
}
|
158
|
+
});
|
159
|
+
});
|
160
|
+
|
161
|
+
console.log('OldUser - Checking identifiers:', identifiersList);
|
162
|
+
|
163
|
+
// Check if any required identifiers are missing (null/undefined/empty)
|
164
|
+
const missingIdentifiers = identifiersList.filter(identifier =>
|
165
|
+
!identifier.value || identifier.value.trim() === ''
|
166
|
+
);
|
167
|
+
|
168
|
+
if (missingIdentifiers.length > 0) {
|
169
|
+
console.log('OldUser - Missing identifiers found:', missingIdentifiers);
|
170
|
+
// Redirect to DiscoverAccount to collect missing identifiers
|
171
|
+
shouldAllowNavigation();
|
172
|
+
navigate(`/link-accounts/discover-account`, {
|
173
|
+
state: {
|
174
|
+
category: activeCategory || categories[0],
|
175
|
+
selectedFips: fipIdsArray,
|
176
|
+
fromOldUser: true
|
177
|
+
}
|
178
|
+
});
|
179
|
+
return;
|
180
|
+
}
|
181
|
+
|
182
|
+
// All identifiers are available, set them in store
|
183
|
+
const { setIdentifiers } = useFipStore.getState();
|
184
|
+
setIdentifiers(identifiersList);
|
185
|
+
}, [getLinkedAccountsList?.data, decodedInfo?.pan, decodedInfo?.phoneNumber, decodedInfo?.dob, activeCategory, categories, navigate, shouldAllowNavigation]);
|
186
|
+
|
117
187
|
const toggleBankSelection = (bankId: string) => {
|
118
188
|
setAccountForConsent(accountForConsent.includes(bankId)
|
119
189
|
? accountForConsent.filter(id => id !== bankId)
|
@@ -156,6 +226,95 @@ const OldUser = () => {
|
|
156
226
|
}
|
157
227
|
};
|
158
228
|
|
229
|
+
// Function to validate identifiers before navigation
|
230
|
+
const validateIdentifiersBeforeNavigation = (targetFiType: string, selectedFipIds?: string[]) => {
|
231
|
+
console.log('OldUser - Validating identifiers before navigation');
|
232
|
+
|
233
|
+
// Get the FIP store state to access fips data
|
234
|
+
const { fips } = useFipStore.getState();
|
235
|
+
const identifiersList: any[] = [];
|
236
|
+
|
237
|
+
// Use provided fipIds or get from linked accounts
|
238
|
+
const fipIdsToCheck = selectedFipIds || Array.from(new Set(getLinkedAccountsList.data?.map((account: BankAccount) => account.fipHandle) || []));
|
239
|
+
|
240
|
+
// Collect required identifiers from FIPs
|
241
|
+
fips
|
242
|
+
.filter(fip => fipIdsToCheck.includes(fip.id))
|
243
|
+
.forEach(fip => {
|
244
|
+
fip.Identifiers?.forEach(identifier => {
|
245
|
+
if (!identifiersList.find(i => i.type === identifier.type)) {
|
246
|
+
const value =
|
247
|
+
identifier.type === 'PAN'
|
248
|
+
? decodedInfo?.pan
|
249
|
+
: identifier.type === 'MOBILE'
|
250
|
+
? decodedInfo?.phoneNumber
|
251
|
+
: identifier.type === 'DOB'
|
252
|
+
? decodedInfo?.dob
|
253
|
+
: null;
|
254
|
+
identifiersList.push({ ...identifier, value });
|
255
|
+
}
|
256
|
+
});
|
257
|
+
});
|
258
|
+
|
259
|
+
console.log('OldUser - Identifiers to validate:', identifiersList);
|
260
|
+
|
261
|
+
// Check if any required identifiers are missing
|
262
|
+
const missingIdentifiers = identifiersList.filter(identifier =>
|
263
|
+
!identifier.value || identifier.value.trim() === ''
|
264
|
+
);
|
265
|
+
|
266
|
+
if (missingIdentifiers.length > 0) {
|
267
|
+
console.log('OldUser - Missing identifiers found, redirecting to collect:', missingIdentifiers);
|
268
|
+
// Redirect to DiscoverAccount to collect missing identifiers
|
269
|
+
shouldAllowNavigation();
|
270
|
+
navigate(`/link-accounts/discover-account`, {
|
271
|
+
state: {
|
272
|
+
category: targetFiType.toUpperCase(),
|
273
|
+
selectedFips: fipIdsToCheck,
|
274
|
+
fromOldUser: true
|
275
|
+
}
|
276
|
+
});
|
277
|
+
return false; // Don't proceed with original navigation
|
278
|
+
}
|
279
|
+
|
280
|
+
console.log('OldUser - All identifiers available, proceeding with navigation');
|
281
|
+
// All identifiers are available, set them in store
|
282
|
+
const { setIdentifiers } = useFipStore.getState();
|
283
|
+
setIdentifiers(identifiersList);
|
284
|
+
return true; // Proceed with original navigation
|
285
|
+
};
|
286
|
+
|
287
|
+
// Enhanced discover more handler
|
288
|
+
const handleDiscoverMore = (fiType: string) => {
|
289
|
+
// Validate identifiers first
|
290
|
+
if (!validateIdentifiersBeforeNavigation(fiType)) {
|
291
|
+
return; // Don't proceed if identifiers are missing
|
292
|
+
}
|
293
|
+
|
294
|
+
// Proceed with original navigation
|
295
|
+
refetch();
|
296
|
+
navigate(`/link-accounts/${fiType.toLowerCase()}`, {
|
297
|
+
state: {
|
298
|
+
fiType,
|
299
|
+
}
|
300
|
+
});
|
301
|
+
setAccountForConsent([]);
|
302
|
+
};
|
303
|
+
|
304
|
+
// Enhanced fallback discover handler
|
305
|
+
const handleFallbackDiscover = () => {
|
306
|
+
const categoryToUse = activeCategory || categories[0] || 'BANK';
|
307
|
+
|
308
|
+
// Validate identifiers first
|
309
|
+
if (!validateIdentifiersBeforeNavigation(categoryToUse)) {
|
310
|
+
return; // Don't proceed if identifiers are missing
|
311
|
+
}
|
312
|
+
|
313
|
+
// Proceed with original navigation
|
314
|
+
refetch();
|
315
|
+
navigate(`/link-accounts/${categoryToUse.toLowerCase()}`);
|
316
|
+
};
|
317
|
+
|
159
318
|
return (
|
160
319
|
<FrostedLayout>
|
161
320
|
<div className='w-full px-0 md:px-14'>
|
@@ -218,28 +377,22 @@ const OldUser = () => {
|
|
218
377
|
</div>
|
219
378
|
))}
|
220
379
|
</ScrollArea>
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
strokeWidth='2.3'
|
238
|
-
className='size-5 text-primary dark:text-muted-secondary'
|
239
|
-
/>{' '}
|
240
|
-
{t('discoverMore')}
|
241
|
-
</div>
|
242
|
-
</Button>
|
380
|
+
{shouldShowDiscoverMore(fiType) && (
|
381
|
+
<Button
|
382
|
+
variant={'outline'}
|
383
|
+
className='bg-primary-foreground text-primary border-primary hover:bg-primary/10 h-[50px] w-full mt-4 mb-6'
|
384
|
+
onClick={() => handleDiscoverMore(fiType)}
|
385
|
+
size={'lg'}
|
386
|
+
>
|
387
|
+
<div className='flex items-center gap-2 text-md text-primary font-[600] dark:text-muted-secondary'>
|
388
|
+
<PlusIcon
|
389
|
+
strokeWidth='2.3'
|
390
|
+
className='size-5 text-primary dark:text-muted-secondary'
|
391
|
+
/>{' '}
|
392
|
+
{t('discoverMore')}
|
393
|
+
</div>
|
394
|
+
</Button>
|
395
|
+
)}
|
243
396
|
</div>
|
244
397
|
));
|
245
398
|
}
|
@@ -262,23 +415,22 @@ const OldUser = () => {
|
|
262
415
|
</Button>
|
263
416
|
</AlertDescription>
|
264
417
|
</Alert>
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
</Button>
|
418
|
+
{shouldShowDiscoverMore(activeCategory || categories[0] || 'BANK') && (
|
419
|
+
<Button
|
420
|
+
variant={'secondary'}
|
421
|
+
className='bg-gray-200 dark:bg-gray-800 text-gray-700 hover:bg-gray-300 h-[50px] w-full mt-4'
|
422
|
+
onClick={handleFallbackDiscover}
|
423
|
+
size={'lg'}
|
424
|
+
>
|
425
|
+
<div className='flex items-center gap-2 text-md text-consent-primary font-[600] capitalize'>
|
426
|
+
<PlusIcon
|
427
|
+
strokeWidth='2.3'
|
428
|
+
className='size-5 text-consent-primary'
|
429
|
+
/>{' '}
|
430
|
+
{t('discover')}
|
431
|
+
</div>
|
432
|
+
</Button>
|
433
|
+
)}
|
282
434
|
</>
|
283
435
|
);
|
284
436
|
})()}
|
@@ -370,25 +370,31 @@ const LinkAccounts = () => {
|
|
370
370
|
identifier.type == 'PAN'
|
371
371
|
? decodedInfo?.pan
|
372
372
|
: identifier.type == 'AADHAAR'
|
373
|
-
? decodedInfo?.aadhaar
|
373
|
+
? decodedInfo?.pan // Use PAN as fallback since aadhaar doesn't exist in DecodedInfo
|
374
374
|
: identifier.type == 'MOBILE'
|
375
375
|
? selectedMobileNumber
|
376
|
-
:
|
376
|
+
: identifier.type == 'DOB'
|
377
|
+
? decodedInfo?.dob
|
378
|
+
: null
|
377
379
|
identifiersList.push({ ...identifier, value })
|
378
380
|
}
|
379
381
|
})
|
380
382
|
})
|
381
383
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
384
|
+
console.log('link-accounts - Identifiers to validate:', identifiersList);
|
385
|
+
|
386
|
+
// Check if ANY required identifiers are missing (not just PAN/AADHAAR)
|
387
|
+
const missingIdentifiers = identifiersList.filter(identifier =>
|
388
|
+
!identifier.value || identifier.value.trim() === ''
|
389
|
+
);
|
390
|
+
|
391
|
+
if (missingIdentifiers.length > 0) {
|
392
|
+
console.log('link-accounts - Missing identifiers found, redirecting to collect:', missingIdentifiers);
|
393
|
+
redirect = true
|
390
394
|
}
|
391
395
|
|
396
|
+
setIdentifiers(identifiersList)
|
397
|
+
|
392
398
|
if (redirect) {
|
393
399
|
navigate(`/link-accounts/discover-account`, {
|
394
400
|
state: {
|
@@ -681,7 +687,7 @@ const LinkAccounts = () => {
|
|
681
687
|
<Modal
|
682
688
|
title={showModal.title}
|
683
689
|
open={showModal.isOpen}
|
684
|
-
className='w-[
|
690
|
+
className='w-[450px]'
|
685
691
|
onOpenChange={open =>
|
686
692
|
!open &&
|
687
693
|
setShowModal({
|
@@ -703,6 +709,7 @@ const LinkAccounts = () => {
|
|
703
709
|
title=''
|
704
710
|
editable={true}
|
705
711
|
mobileNumber={showModal.number}
|
712
|
+
eyeClassName="translate-x-4"
|
706
713
|
onResend={() => {
|
707
714
|
setShowModal(old => ({ ...old, otp: null, error: '' }))
|
708
715
|
addNewNoQuery.mutate(undefined, {
|
@@ -836,6 +843,7 @@ const LinkAccounts = () => {
|
|
836
843
|
title=''
|
837
844
|
editable={true}
|
838
845
|
mobileNumber={showModal.number}
|
846
|
+
eyeClassName="translate-x-2"
|
839
847
|
onResend={() => {
|
840
848
|
setShowModal(old => ({ ...old, otp: null, error: '' }))
|
841
849
|
addNewNoQuery.mutate(undefined, {
|
@@ -279,13 +279,21 @@ const ReviewConsent = () => {
|
|
279
279
|
}
|
280
280
|
|
281
281
|
const consentResult = await consentService.submitConsentVerification(approvalData)
|
282
|
-
console.log("navigatig")
|
283
282
|
|
284
|
-
if (consentResult?.length && consentResult?.
|
285
|
-
navigate('/rejected', {
|
283
|
+
if (consentResult?.length && consentResult?.filter(item => item.ConsentStatus === 'FAILED')?.length === consentResult?.length) {
|
284
|
+
navigate('/rejected', {
|
285
|
+
state: {
|
286
|
+
consentResult: consentResult?.filter(item => item.ConsentStatus === 'FAILED')
|
287
|
+
}
|
288
|
+
})
|
286
289
|
return
|
287
290
|
} else if (consentResult?.length) {
|
288
|
-
navigate('/success'
|
291
|
+
navigate('/success', {
|
292
|
+
state: {
|
293
|
+
consentResult: consentResult,
|
294
|
+
consents: selectedConsent
|
295
|
+
}
|
296
|
+
})
|
289
297
|
} else {
|
290
298
|
setError('Failed to approve consent. Please try again.')
|
291
299
|
}
|
@@ -423,22 +431,22 @@ const ReviewConsent = () => {
|
|
423
431
|
{linkedAccounts.length === 0 ?
|
424
432
|
<div className='mb-8'>
|
425
433
|
<Alert>
|
426
|
-
<AlertTitle className='flex items-center justify-between gap-4'>
|
427
|
-
<div className='flex items-center gap-4'>
|
428
|
-
<div className='flex items-center justify-center bg-yellow-600 p-2 rounded-full w-fit'>
|
434
|
+
<AlertTitle className='flex flex-col md:flex-row md:items-center md:justify-between gap-4'>
|
435
|
+
<div className='flex items-center gap-3 md:gap-4'>
|
436
|
+
<div className='flex items-center justify-center bg-yellow-600 p-2 rounded-full w-fit flex-shrink-0'>
|
429
437
|
<Info
|
430
438
|
strokeWidth='2.3'
|
431
|
-
className='h-[18px] w-[18px] text-white'
|
439
|
+
className='h-[16px] w-[16px] md:h-[18px] md:w-[18px] text-white'
|
432
440
|
/>
|
433
441
|
</div>
|
434
|
-
<p className='text-lg font-medium'>
|
442
|
+
<p className='text-sm md:text-lg font-medium leading-tight'>
|
435
443
|
No accounts selected! You need to select accounts to approve this consent
|
436
444
|
</p>
|
437
445
|
</div>
|
438
446
|
<AnimatedButton
|
439
447
|
variant={'outline'}
|
440
448
|
size={'lg'}
|
441
|
-
className='text-yellow-600 border-yellow-600 border-2 hover:bg-yellow-200 hover:text-yellow-900'
|
449
|
+
className='text-yellow-600 border-yellow-600 border-2 hover:bg-yellow-200 hover:text-yellow-900 w-full md:w-auto md:flex-shrink-0 text-sm md:text-base'
|
442
450
|
onClick={() => { trackEvent(EVENTS.DISCOVER_MORE); navigate(`/link-accounts/${activeCategory}`) }}
|
443
451
|
>
|
444
452
|
Add accounts
|
@@ -6,6 +6,7 @@ import { useState, useEffect } from "react";
|
|
6
6
|
import { feedbackService } from "@/services/api";
|
7
7
|
import { useLocation } from 'react-router-dom';
|
8
8
|
import { useFipStore } from '@/store/fip.store';
|
9
|
+
import { HelpModal } from "@/components/modal/HelpModal";
|
9
10
|
|
10
11
|
interface ConsentResultItem {
|
11
12
|
reasonForRejection: string;
|
@@ -116,14 +117,22 @@ const Rejected = () => {
|
|
116
117
|
|
117
118
|
return (
|
118
119
|
<div className="flex flex-col min-h-screen bg-background">
|
119
|
-
{/*
|
120
|
-
|
121
|
-
<
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
120
|
+
{/* Header with help and timer */}
|
121
|
+
<div className="flex justify-between items-start p-4">
|
122
|
+
<HelpModal
|
123
|
+
variant="dark"
|
124
|
+
iconSize={16}
|
125
|
+
textSize="text-xs sm:text-sm"
|
126
|
+
buttonClassName="self-start"
|
127
|
+
/>
|
128
|
+
{decodedInfo?.redirect && (
|
129
|
+
<div className="text-center">
|
130
|
+
<p className="text-muted-secondary text-xs sm:text-sm">
|
131
|
+
Redirecting in <span className="font-semibold">00:{countdown < 10 ? `0${countdown}` : countdown}</span>
|
132
|
+
</p>
|
133
|
+
</div>
|
134
|
+
)}
|
135
|
+
</div>
|
127
136
|
|
128
137
|
{/* Main Content - Center */}
|
129
138
|
<div className="flex-1 flex items-center justify-center p-6">
|