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.
@@ -22,10 +22,13 @@ import { useSetPageTitle } from '@/hooks/use-page-title'
22
22
  import DummyFooter from '@/components/dummyFooter'
23
23
  import { useRedirectStore } from '@/store/redirect.store'
24
24
  import { fiTypeCategoryMap } from '@/const/fiTypeCategoryMap'
25
+ import { useAutoDiscovery } from '@/hooks/use-auto-discovery'
26
+ import { parseAutoDiscoveryConfig } from '@/utils/auto-discovery'
25
27
  interface AccountsToProceedProps {
26
28
  state?: {
27
29
  category: string
28
30
  selectedFips: string[]
31
+ isAutoDiscovery?: boolean
29
32
  }
30
33
  currentCategory?: string | null
31
34
  }
@@ -42,7 +45,6 @@ const AccountsToProceed = ({
42
45
  groupedFips,
43
46
  activeCategory,
44
47
  setSelectedAccountToLink,
45
- setSignature,
46
48
  setOriginalAccounts,
47
49
  originalAccounts
48
50
  } = useFipStore()
@@ -56,6 +58,7 @@ const AccountsToProceed = ({
56
58
 
57
59
  // Account discovery with enhanced error handling
58
60
  const { mutate, errorMessage, clearError } = useAccountDiscovery()
61
+ const autoDiscovery = useAutoDiscovery()
59
62
 
60
63
  // Format category name for display
61
64
  const formatCategoryName = (name: string) => {
@@ -75,11 +78,141 @@ const AccountsToProceed = ({
75
78
 
76
79
  const requiredCategory = activeCategory ? fiTypeCategoryMap[activeCategory]?.filter((fiType: string) => decodedInfo?.fiTypesRequiredForConsent?.includes(fiType)) || [] : [];
77
80
 
81
+ // Check if "Discover More" should be shown based on auto discovery configuration
82
+ const autoDiscoveryConfig = parseAutoDiscoveryConfig(decodedInfo?.customization?.otherAppCustomization)
83
+ const shouldShowDiscoverMore = state?.isAutoDiscovery
84
+ ? autoDiscoveryConfig[activeCategory?.toUpperCase() as keyof typeof autoDiscoveryConfig]?.showDiscoverMore
85
+ : true // Always show for manual discovery
86
+
87
+ console.log('AccountsToProceed Debug:', {
88
+ state,
89
+ isAutoDiscovery: state?.isAutoDiscovery,
90
+ category,
91
+ activeCategory,
92
+ fipIds,
93
+ shouldShowDiscoverMore,
94
+ autoDiscoveryConfig: autoDiscoveryConfig[activeCategory?.toUpperCase() as keyof typeof autoDiscoveryConfig]
95
+ });
96
+
78
97
  // Discover accounts when component loads
79
98
  useEffect(() => {
99
+ console.log('AccountsToProceed useEffect:', {
100
+ isAutoDiscovery: state?.isAutoDiscovery,
101
+ category,
102
+ fipIds: fipIds.length,
103
+ groupedFipsKeys: Object.keys(groupedFips || {})
104
+ });
105
+
80
106
  let mounted = true
81
107
 
82
- if (fipIds.length > 0) {
108
+ if (state?.isAutoDiscovery) {
109
+ console.log('Using AUTO DISCOVERY for category:', category);
110
+
111
+ // First validate identifiers before proceeding with auto discovery
112
+ const { fips, identifiers: storeIdentifiers } = useFipStore.getState();
113
+ const identifiersList: Array<{
114
+ type: string;
115
+ value: string | undefined;
116
+ category: string;
117
+ }> = [];
118
+
119
+ // For auto discovery, we need to check identifiers across all FIPs in the category
120
+ const categoryFips = fips.filter(fip =>
121
+ fip.fiTypeList.some(fiType =>
122
+ fiTypeCategoryMap[category]?.includes(fiType)
123
+ ) && fip.isEnabled && fip.isOnBoarded
124
+ );
125
+
126
+ // Collect required identifiers from category FIPs
127
+ categoryFips.forEach(fip => {
128
+ fip.Identifiers?.forEach(identifier => {
129
+ if (!identifiersList.find(i => i.type === identifier.type)) {
130
+ // First check if we already have this identifier in store
131
+ const storeIdentifier = storeIdentifiers.find(i => i.type === identifier.type);
132
+ let value: string | undefined = undefined;
133
+ if (storeIdentifier?.value !== undefined) {
134
+ value = storeIdentifier.value;
135
+ }
136
+
137
+ // If not in store, fall back to decodedInfo
138
+ if (!value) {
139
+ value = identifier.type === 'PAN'
140
+ ? (decodedInfo?.pan || undefined)
141
+ : identifier.type === 'MOBILE'
142
+ ? (decodedInfo?.phoneNumber || undefined)
143
+ : identifier.type === 'DOB'
144
+ ? (decodedInfo?.dob || undefined)
145
+ : undefined;
146
+ }
147
+
148
+ identifiersList.push({
149
+ type: identifier.type,
150
+ value,
151
+ category: identifier.category
152
+ });
153
+ }
154
+ });
155
+ });
156
+
157
+ console.log('Auto Discovery - Checking identifiers:', identifiersList);
158
+ console.log('Auto Discovery - Store identifiers:', storeIdentifiers);
159
+
160
+ // Check if any required identifiers are missing
161
+ const missingIdentifiers = identifiersList.filter(identifier =>
162
+ !identifier.value || identifier.value.trim() === ''
163
+ );
164
+
165
+ if (missingIdentifiers.length > 0) {
166
+ console.log('Auto Discovery - Missing identifiers found, redirecting to collect:', missingIdentifiers);
167
+ // Redirect to DiscoverAccount to collect missing identifiers
168
+ navigate(`/link-accounts/discover-account`, {
169
+ state: {
170
+ category,
171
+ selectedFips: fipIds, // Use the auto discovery FIP IDs
172
+ fromAutoDiscovery: true
173
+ }
174
+ });
175
+ return; // Don't proceed with auto discovery
176
+ }
177
+
178
+ console.log('Auto Discovery - All identifiers available, proceeding with discovery');
179
+ // All identifiers are available, set them in store and proceed with auto discovery
180
+ const { setIdentifiers } = useFipStore.getState();
181
+ setIdentifiers(identifiersList);
182
+
183
+ // Use auto discovery
184
+ setIsLoading(true)
185
+ autoDiscovery.clearError()
186
+
187
+ autoDiscovery.mutate(category, {
188
+ onSuccess: result => {
189
+ if (!mounted) return
190
+ console.log('Auto discovery success:', result);
191
+ const groupedAccounts = result.accounts.reduce(
192
+ (acc: Record<string, typeof result.accounts>, account) => {
193
+ const key = account.type
194
+ if (!acc[key]) {
195
+ acc[key] = []
196
+ }
197
+ acc[key].push(account)
198
+ return acc
199
+ },
200
+ {}
201
+ )
202
+ setDiscoveryResult({ ...result, groupedAccounts })
203
+ setOriginalAccounts(result.originalAccounts)
204
+
205
+ setIsLoading(false)
206
+ },
207
+ onError: (error) => {
208
+ if (!mounted) return
209
+ console.log('Auto discovery error:', error);
210
+ setIsLoading(false)
211
+ }
212
+ })
213
+ } else if (fipIds.length > 0) {
214
+ console.log('Using MANUAL DISCOVERY for FIPs:', fipIds);
215
+ // Use manual discovery
83
216
  setIsLoading(true)
84
217
  clearError()
85
218
 
@@ -98,7 +231,6 @@ const AccountsToProceed = ({
98
231
  {}
99
232
  )
100
233
  setDiscoveryResult({ ...result, groupedAccounts })
101
- setSignature(result.signature)
102
234
  setOriginalAccounts(result.originalAccounts)
103
235
 
104
236
  setIsLoading(false)
@@ -108,24 +240,65 @@ const AccountsToProceed = ({
108
240
  setIsLoading(false)
109
241
  }
110
242
  })
243
+ } else {
244
+ console.log('No discovery triggered - no FIPs and not auto discovery');
111
245
  }
112
246
 
113
247
  // Cleanup function to handle unmounting
114
248
  return () => {
115
249
  mounted = false
116
250
  }
117
- }, [fipIds]) // Only depend on fipIds changing
251
+ }, [fipIds, state?.isAutoDiscovery, category]) // Depend on auto discovery flag and category
118
252
 
119
253
  const handleProceed = () => {
120
- const result: typeof originalAccounts = []
254
+ console.log('handleProceed called with:', { selectedAccounts, originalAccounts })
255
+
256
+ const result: Array<{
257
+ id: string;
258
+ fipId: string;
259
+ fipName: string;
260
+ signature: string;
261
+ type: string;
262
+ maskedAccountNumber: string;
263
+ bankName: string;
264
+ logoUrl?: string;
265
+ isNew?: boolean;
266
+ DiscoveredAccounts: Array<{
267
+ FIType: string;
268
+ accType: string;
269
+ accRefNumber: string;
270
+ maskedAccNumber: string;
271
+ state: string | null;
272
+ amcName: string | null;
273
+ logoUrl: string | null;
274
+ }>;
275
+ }> = []
276
+
121
277
  originalAccounts.forEach((item, index) => {
122
- const data = item?.["DiscoveredAccounts"].filter(account => selectedAccounts.includes(account.accRefNumber))
278
+ const data = item?.["DiscoveredAccounts"].filter(account =>
279
+ selectedAccounts.includes(account.accRefNumber)
280
+ )
123
281
  if (data.length > 0) {
124
- result.push({ ...item, DiscoveredAccounts: data, id: `${index} - ${item.fipName}` })
282
+ // For each FIP with selected accounts, create one entry
283
+ result.push({
284
+ id: `${index} - ${item.fipName}`,
285
+ fipId: item.fipId,
286
+ fipName: item.fipName,
287
+ signature: (item as { signature?: string }).signature || '', // Use signature from the specific FIP
288
+ type: data[0]?.FIType || 'DEFAULT',
289
+ maskedAccountNumber: data[0]?.maskedAccNumber || '',
290
+ bankName: item.fipName,
291
+ logoUrl: data[0]?.logoUrl || undefined,
292
+ isNew: false,
293
+ DiscoveredAccounts: data
294
+ })
125
295
  }
126
296
  })
127
297
 
298
+ console.log('handleProceed result:', result)
299
+ console.log('Setting selectedAccountToLink with result length:', result.length)
128
300
  setSelectedAccountToLink(result)
301
+
129
302
  // Navigate to account linking screen with selected accounts and signature
130
303
  navigate('/link-accounts/link', {
131
304
  state: {
@@ -188,7 +361,28 @@ const AccountsToProceed = ({
188
361
 
189
362
 
190
363
  const retryDiscovery = () => {
191
- if (fipIds.length > 0) {
364
+ if (state?.isAutoDiscovery) {
365
+ // Retry auto discovery
366
+ setIsLoading(true)
367
+ autoDiscovery.clearError()
368
+
369
+ autoDiscovery.mutate(category, {
370
+ onSuccess: result => {
371
+ setDiscoveryResult(result)
372
+
373
+ // Auto-select all accounts by default
374
+ if (result.accounts.length > 0) {
375
+ setSelectedAccounts(result.accounts.map(account => account.id))
376
+ }
377
+
378
+ setIsLoading(false)
379
+ },
380
+ onError: () => {
381
+ setIsLoading(false)
382
+ }
383
+ })
384
+ } else if (fipIds.length > 0) {
385
+ // Retry manual discovery
192
386
  setIsLoading(true)
193
387
  clearError()
194
388
 
@@ -210,6 +404,82 @@ const AccountsToProceed = ({
210
404
  }
211
405
  }
212
406
 
407
+ // Function to validate identifiers before showing discover more
408
+ const validateIdentifiersBeforeDiscoverMore = () => {
409
+ // Get the FIP store state to access fips data
410
+ const { fips } = useFipStore.getState();
411
+
412
+ if (!fipIds || fipIds.length === 0) {
413
+ return true; // No FIPs selected, can't validate
414
+ }
415
+
416
+ const identifiersList: Array<{
417
+ type: string;
418
+ value: string | null | undefined;
419
+ categoryType: string;
420
+ }> = [];
421
+
422
+ // Collect required identifiers from selected FIPs
423
+ fips
424
+ .filter(fip => fipIds.includes(fip.id))
425
+ .forEach(fip => {
426
+ fip.Identifiers?.forEach(identifier => {
427
+ if (!identifiersList.find(i => i.type === identifier.type)) {
428
+ const value =
429
+ identifier.type === 'PAN'
430
+ ? decodedInfo?.pan
431
+ : identifier.type === 'MOBILE'
432
+ ? decodedInfo?.phoneNumber
433
+ : identifier.type === 'DOB'
434
+ ? decodedInfo?.dob
435
+ : null;
436
+ identifiersList.push({
437
+ type: identifier.type,
438
+ value,
439
+ categoryType: identifier.category
440
+ });
441
+ }
442
+ });
443
+ });
444
+
445
+ console.log('AccountsToProceed - Checking identifiers for discover more:', identifiersList);
446
+
447
+ // Check if any required identifiers are missing
448
+ const missingIdentifiers = identifiersList.filter(identifier =>
449
+ !identifier.value || identifier.value.trim() === ''
450
+ );
451
+
452
+ if (missingIdentifiers.length > 0) {
453
+ console.log('AccountsToProceed - Missing identifiers found, redirecting to collect:', missingIdentifiers);
454
+ // Redirect to DiscoverAccount to collect missing identifiers
455
+ navigate(`/link-accounts/discover-account`, {
456
+ state: {
457
+ category,
458
+ selectedFips: fipIds,
459
+ fromDiscoverMore: true
460
+ }
461
+ });
462
+ return false; // Don't show discover more button
463
+ }
464
+
465
+ return true; // All identifiers available, can show discover more
466
+ };
467
+
468
+ const handleDiscoverMore = () => {
469
+ // Validate identifiers first
470
+ if (!validateIdentifiersBeforeDiscoverMore()) {
471
+ return; // Don't proceed if identifiers are missing
472
+ }
473
+
474
+ // Proceed with original navigation
475
+ navigate(`/link-accounts/${category.toLowerCase()}`, {
476
+ state: {
477
+ category,
478
+ selectedFips: fipIds
479
+ }
480
+ });
481
+ };
482
+
213
483
  const renderAccountGroups = () => {
214
484
  if (isLoading) {
215
485
  return Array(3)
@@ -263,7 +533,7 @@ const AccountsToProceed = ({
263
533
 
264
534
  return (
265
535
  <div className='text-center p-4 text-gray-500'>
266
- {errorMessage
536
+ {(errorMessage || autoDiscovery.errorMessage)
267
537
  ? 'Failed to discover accounts'
268
538
  : 'No accounts found for the selected banks'}
269
539
  </div>
@@ -301,11 +571,11 @@ const AccountsToProceed = ({
301
571
  }
302
572
  />
303
573
 
304
- {errorMessage && (
574
+ {(errorMessage || autoDiscovery.errorMessage) && (
305
575
  <Alert variant='destructive' className='flex items-center'>
306
576
  <AlertCircle className='w-4 h-4' />
307
577
  <AlertDescription className='flex items-center justify-between'>
308
- {errorMessage}
578
+ {errorMessage || autoDiscovery.errorMessage}
309
579
  <Button
310
580
  variant='outline'
311
581
  size='sm'
@@ -324,18 +594,11 @@ const AccountsToProceed = ({
324
594
  {renderAccountGroups()}
325
595
  </div>
326
596
  </div>
327
- {requiredCategory?.every(fiType => totalCategory?.includes(fiType)) ? null :
597
+ {(requiredCategory?.every(fiType => totalCategory?.includes(fiType)) || !shouldShowDiscoverMore) ? null :
328
598
  <Button
329
599
  variant={'outline'}
330
600
  className='bg-primary-foreground text-primary border-primary hover:bg-primary/10 h-[50px] dark:bg-ring'
331
- onClick={() =>
332
- navigate(`/link-accounts/${category.toLowerCase()}`, {
333
- state: {
334
- category,
335
- selectedFips: fipIds
336
- }
337
- })
338
- }
601
+ onClick={handleDiscoverMore}
339
602
  size={'lg'}
340
603
  >
341
604
  <div className='flex items-center gap-2 text-md text-primary font-[600] dark:text-muted-secondary'>
@@ -6,14 +6,20 @@ import { useLocation, useParams } from "react-router-dom";
6
6
  import AccountsToProceed from "./AccountsToProceed";
7
7
  import LinkSelectedAccounts from "./LinkSelectedAccounts";
8
8
  import DiscoverAccount from "./DiscoverAccount";
9
+
9
10
  import { useFipStore } from "@/store/fip.store";
11
+ import { useRedirectStore } from "@/store/redirect.store";
10
12
  import { useEffect } from "react";
13
+ import { useNavigate } from "react-router-dom";
14
+ import { isAutoDiscoveryEnabled, getAutoDiscoveryFipIds } from "@/utils/auto-discovery";
11
15
 
12
16
  const Discover = () => {
13
17
  const { category } = useParams();
14
18
  const location = useLocation();
15
19
  const path = location.pathname.split('/').pop() || '';
16
- const { activeCategory, setActiveCategory, categories } = useFipStore();
20
+ const navigate = useNavigate();
21
+ const { activeCategory, setActiveCategory, categories, groupedFips, setSelectedFips } = useFipStore();
22
+ const { decodedInfo } = useRedirectStore();
17
23
 
18
24
  // For step navigation in the sidebar - detect category from path
19
25
  useEffect(() => {
@@ -37,19 +43,131 @@ const Discover = () => {
37
43
  }
38
44
  }, [path, category, categories, setActiveCategory, location.state]);
39
45
 
46
+ // Handle auto discovery logic
47
+ useEffect(() => {
48
+ // Only run auto discovery logic on category pages (not flow pages)
49
+ const flowPaths = ['discovery', 'link', 'discover-account'];
50
+ const isFlowPath = flowPaths.includes(path);
51
+
52
+ console.log('Auto Discovery Debug:', {
53
+ path,
54
+ isFlowPath,
55
+ activeCategory,
56
+ hasDecodedInfo: !!decodedInfo,
57
+ otherAppCustomization: decodedInfo?.customization?.otherAppCustomization,
58
+ fipId: decodedInfo?.fipId,
59
+ locationPathname: location.pathname,
60
+ groupedFipsKeys: Object.keys(groupedFips || {}),
61
+ groupedFipsEmpty: !groupedFips || Object.keys(groupedFips).length === 0,
62
+ categoriesLoaded: categories.length > 0
63
+ });
64
+
65
+ // Guard clauses to prevent unnecessary execution
66
+ if (isFlowPath || !activeCategory || !decodedInfo) {
67
+ console.log('Auto Discovery: Early return due to guard clauses', {
68
+ isFlowPath,
69
+ activeCategory,
70
+ hasDecodedInfo: !!decodedInfo
71
+ });
72
+ return;
73
+ }
74
+
75
+ // Check if groupedFips is loaded
76
+ if (!groupedFips || Object.keys(groupedFips).length === 0) {
77
+ console.log('Auto Discovery: groupedFips not loaded yet, waiting...');
78
+ return;
79
+ }
80
+
81
+ // Additional guard: only run for supported auto discovery categories
82
+ const supportedCategories = ['INVESTMENTS', 'GST', 'INSURANCE'];
83
+ if (!supportedCategories.includes(activeCategory.toUpperCase())) {
84
+ console.log('Auto Discovery: Category not supported:', activeCategory);
85
+ return;
86
+ }
87
+
88
+ const autoDiscoveryEnabled = isAutoDiscoveryEnabled(
89
+ activeCategory,
90
+ decodedInfo?.customization?.otherAppCustomization
91
+ );
92
+
93
+ console.log('Auto Discovery: Enabled?', autoDiscoveryEnabled, 'for category:', activeCategory);
94
+
95
+ if (autoDiscoveryEnabled) {
96
+ // Get FIP IDs for auto discovery
97
+ const fipIds = getAutoDiscoveryFipIds(
98
+ activeCategory,
99
+ decodedInfo?.fipId,
100
+ groupedFips
101
+ );
102
+
103
+ console.log('Auto Discovery: FIP IDs:', fipIds, 'groupedFips keys:', Object.keys(groupedFips || {}));
104
+
105
+ if (fipIds.length > 0) {
106
+ // Additional guard: check if we're already navigating or have navigated
107
+ if (location.pathname === '/link-accounts/discovery') {
108
+ console.log('Auto Discovery: Already on discovery page, skipping navigation');
109
+ return;
110
+ }
111
+
112
+ console.log('Auto Discovery: Navigating to discovery with FIPs:', fipIds);
113
+
114
+ // Set selected FIPs in the store
115
+ setSelectedFips(activeCategory, fipIds);
116
+
117
+ // IMPORTANT: Also populate identifiers in the store for manual discovery hooks to use
118
+ // This mirrors the logic from link-accounts.tsx handleNext function
119
+ const { fips, setIdentifiers, selectedMobileNumber } = useFipStore.getState();
120
+ const identifiersList: { type: string; value: string | null; category: string }[] = [];
121
+
122
+ fips
123
+ .filter(fip => fipIds.includes(fip.id))
124
+ .forEach(fip => {
125
+ fip.Identifiers?.forEach(identifier => {
126
+ if (!identifiersList.find(i => i.type === identifier.type)) {
127
+ const value =
128
+ identifier.type === 'PAN'
129
+ ? decodedInfo?.pan
130
+ : identifier.type === 'MOBILE'
131
+ ? selectedMobileNumber || decodedInfo?.phoneNumber
132
+ : identifier.type === 'DOB'
133
+ ? decodedInfo?.dob
134
+ : null;
135
+ identifiersList.push({ type: identifier.type, value, category: identifier.category });
136
+ }
137
+ });
138
+ });
139
+
140
+ console.log('Auto Discovery: Setting identifiers in store:', identifiersList);
141
+ setIdentifiers(identifiersList);
142
+
143
+ // Navigate to discovery page
144
+ navigate('/link-accounts/discovery', {
145
+ state: {
146
+ category: activeCategory,
147
+ selectedFips: fipIds,
148
+ isAutoDiscovery: true
149
+ },
150
+ replace: true // Use replace to avoid back button issues
151
+ });
152
+ } else {
153
+ console.log('Auto Discovery: No FIP IDs found, staying on FIP listing');
154
+ }
155
+ }
156
+ }, [activeCategory, path, decodedInfo?.customization?.otherAppCustomization, decodedInfo?.fipId, location.pathname, groupedFips, categories]);
157
+
40
158
  const getCurrentStep = (newPath: string) => {
41
159
  // Flow paths
42
160
  switch (newPath || path) {
43
161
  case 'discovery':
44
162
  case 'proceed':
45
163
  return <AccountsToProceed
46
- state={location.state as { category: string; selectedFips: string[] }}
164
+ state={location.state as { category: string; selectedFips: string[]; isAutoDiscovery?: boolean }}
47
165
  currentCategory={activeCategory}
48
- />;
49
-
50
- case 'link':
166
+ />;
167
+
168
+ case 'link':
51
169
  return <LinkSelectedAccounts
52
- state={location.state as { category: string; selectedFips: string[] }}
170
+ state={location.state as { category: string; selectedFips: string[]; selectedAccounts: string[]; signature?: string }}
53
171
  currentCategory={activeCategory}
54
172
  />;
55
173
 
@@ -60,15 +178,15 @@ const Discover = () => {
60
178
  />;
61
179
 
62
180
  default:
63
- // If we're on a category path or any other path, show FIP listing
64
- // This handles all category paths like /link-accounts/banks, /link-accounts/investment etc.
181
+ // Default: show FIP listing (manual flow)
182
+ // Auto discovery logic is now handled in useEffect above
65
183
  return <LinkAccounts />;
66
184
  }
67
185
  }
68
186
 
69
187
  return (
70
188
  <FrostedLayout>
71
- {getCurrentStep()}
189
+ {getCurrentStep(path)}
72
190
  </FrostedLayout>
73
191
  )
74
192
  }