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.
@@ -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
- <Button
222
- variant={'outline'}
223
- className='bg-primary-foreground text-primary border-primary hover:bg-primary/10 h-[50px] w-full mt-4 mb-6'
224
- onClick={() => {
225
- refetch()
226
- navigate(`/link-accounts/${fiType.toLowerCase()}`, {
227
- state: {
228
- fiType,
229
- }
230
- });
231
- setAccountForConsent([])
232
- }}
233
- size={'lg'}
234
- >
235
- <div className='flex items-center gap-2 text-md text-primary font-[600] dark:text-muted-secondary'>
236
- <PlusIcon
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
- <Button
266
- variant={'secondary'}
267
- className='bg-gray-200 dark:bg-gray-800 text-gray-700 hover:bg-gray-300 h-[50px] w-full mt-4'
268
- onClick={() => {
269
- refetch()
270
- navigate(`/link-accounts/${completedCategories?.[completedCategories.length - 1]}`);
271
- }}
272
- size={'lg'}
273
- >
274
- <div className='flex items-center gap-2 text-md text-consent-primary font-[600] capitalize'>
275
- <PlusIcon
276
- strokeWidth='2.3'
277
- className='size-5 text-consent-primary'
278
- />{' '}
279
- {t('discover')}
280
- </div>
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
- : null
376
+ : identifier.type == 'DOB'
377
+ ? decodedInfo?.dob
378
+ : null
377
379
  identifiersList.push({ ...identifier, value })
378
380
  }
379
381
  })
380
382
  })
381
383
 
382
- setIdentifiers(identifiersList)
383
- if (identifiersList?.length > 0 && Boolean(identifiersList.find(val => !val.value)?.type)) {
384
- if (identifiersList.find(i => i.type === 'PAN')) {
385
- redirect = true
386
- }
387
- if (identifiersList.find(i => i.type === 'AADHAAR')) {
388
- redirect = true
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-[380px]'
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?.find(item => item.ConsentStatus === 'FAILED')) {
285
- navigate('/rejected', { state: { consentResult } })
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
- {/* Timer Section - Top */}
120
- {decodedInfo?.redirect && (
121
- <div className="p-4 text-center">
122
- <p className="text-muted-secondary">
123
- Redirecting in <span className="font-semibold">00:{countdown < 10 ? `0${countdown}` : countdown}</span>
124
- </p>
125
- </div>
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">