saafe-redirection-flow 2.0.0 → 2.2.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 +23 -0
- package/aa-redirection-1607251826.zip +0 -0
- package/docs/features/account-discovery.md +803 -0
- package/docs/features/authentication.md +583 -0
- package/docs/features/consent-management.md +740 -0
- package/docs/features/index.md +206 -0
- package/docs/features/navigation-routing.md +846 -0
- package/docs/features/overview.md +554 -0
- package/docs/features/theming-internationalization.md +982 -0
- package/docs/index.md +1 -1
- package/package.json +1 -1
- package/src/components/AutoDiscoveryLoader.tsx +29 -0
- package/src/components/LinkedAccountTypeAccordion.tsx +154 -0
- 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/title/AppBar.tsx +1 -1
- 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 +38 -6
- 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 +598 -59
- package/src/pages/accounts/Discover.tsx +127 -9
- package/src/pages/accounts/DiscoverAccount.tsx +195 -42
- package/src/pages/accounts/LinkSelectedAccounts.tsx +15 -9
- package/src/pages/accounts/OldUser.tsx +184 -79
- package/src/pages/accounts/link-accounts.tsx +19 -11
- package/src/pages/consent/ReviewConsent.tsx +19 -10
- package/src/pages/consent/rejected.tsx +17 -8
- package/src/pages/consent/success.tsx +125 -76
- package/src/services/api/account.service.ts +2 -2
- package/src/store/fip.store.ts +74 -28
- package/src/store/redirect.store.ts +19 -0
- package/src/utils/auto-discovery.ts +126 -0
- package/src/utils/toast-helpers.ts +5 -0
- package/stage-aa-2506251021.zip +0 -0
|
@@ -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
|
|
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 | undefined; 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 || undefined
|
|
130
|
+
: identifier.type === 'MOBILE'
|
|
131
|
+
? selectedMobileNumber || decodedInfo?.phoneNumber || undefined
|
|
132
|
+
: identifier.type === 'DOB'
|
|
133
|
+
? decodedInfo?.dob || undefined
|
|
134
|
+
: undefined;
|
|
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
|
-
|
|
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
|
-
//
|
|
64
|
-
//
|
|
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
|
}
|
|
@@ -18,7 +18,6 @@ import { useMutation, useQuery } from '@tanstack/react-query'
|
|
|
18
18
|
import { accountService } from '@/services/api'
|
|
19
19
|
import { useMediaQuery } from '@/hooks/use-media-query'
|
|
20
20
|
import { useFipStore } from '@/store/fip.store'
|
|
21
|
-
import logo from '../../assets/brand/saafe-logo.svg'
|
|
22
21
|
import { z } from 'zod'
|
|
23
22
|
import { Calendar } from '@/components/ui/calendar'
|
|
24
23
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
|
@@ -36,10 +35,22 @@ import { useSetPageTitle } from '@/hooks/use-page-title'
|
|
|
36
35
|
import { useRedirectStore } from '@/store/redirect.store'
|
|
37
36
|
import { BottomSheet } from '@/components/ui/bottom-sheet'
|
|
38
37
|
|
|
38
|
+
interface ApiError extends Error {
|
|
39
|
+
response?: {
|
|
40
|
+
data?: {
|
|
41
|
+
errorCode?: string
|
|
42
|
+
errorMsg?: string
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
39
47
|
interface DiscoverAccountProps {
|
|
40
48
|
state?: {
|
|
41
49
|
category: string
|
|
42
50
|
selectedFips?: string[]
|
|
51
|
+
fromOldUser?: boolean
|
|
52
|
+
fromDiscoverMore?: boolean
|
|
53
|
+
fromAutoDiscovery?: boolean
|
|
43
54
|
}
|
|
44
55
|
currentCategory?: string | null
|
|
45
56
|
}
|
|
@@ -107,7 +118,7 @@ const AddNumberModalContent = ({ showModal, setShowModal, t, addNewNoQuery, hand
|
|
|
107
118
|
onSuccess: () => {
|
|
108
119
|
handleAddNumber(inputValue.number)
|
|
109
120
|
},
|
|
110
|
-
onError: (error:
|
|
121
|
+
onError: (error: ApiError) => {
|
|
111
122
|
// console.error(error)
|
|
112
123
|
setInputValue({ number: inputValue.number, error: error.response?.data?.errorCode || 'Something went wrong' });
|
|
113
124
|
}
|
|
@@ -145,7 +156,44 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
145
156
|
AADHAAR: null
|
|
146
157
|
})
|
|
147
158
|
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
|
|
148
|
-
const [showAlert, setShowAlert] = useState(true)
|
|
159
|
+
// const [showAlert, setShowAlert] = useState(true)
|
|
160
|
+
|
|
161
|
+
// Utility function to parse various date formats
|
|
162
|
+
const parseDateFromDecodedInfo = (dateString: string | null): Date | null => {
|
|
163
|
+
if (!dateString) return null
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
// Handle URL-encoded date string
|
|
167
|
+
const decodedDateString = decodeURIComponent(dateString)
|
|
168
|
+
|
|
169
|
+
// Try parsing as ISO string first (handles formats like "1999-12-09T18:30:00.000Z")
|
|
170
|
+
let parsedDate = new Date(decodedDateString)
|
|
171
|
+
|
|
172
|
+
// If that fails, try other common formats
|
|
173
|
+
if (isNaN(parsedDate.getTime())) {
|
|
174
|
+
// Try DD/MM/YYYY format
|
|
175
|
+
if (decodedDateString.includes('/')) {
|
|
176
|
+
const [day, month, year] = decodedDateString.split('/')
|
|
177
|
+
parsedDate = new Date(parseInt(year), parseInt(month) - 1, parseInt(day))
|
|
178
|
+
}
|
|
179
|
+
// Try YYYY-MM-DD format
|
|
180
|
+
else if (decodedDateString.includes('-') && decodedDateString.length === 10) {
|
|
181
|
+
parsedDate = new Date(decodedDateString + 'T00:00:00.000Z')
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Validate the parsed date
|
|
186
|
+
if (isNaN(parsedDate.getTime())) {
|
|
187
|
+
console.warn('Could not parse date:', dateString)
|
|
188
|
+
return null
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return parsedDate
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.warn('Error parsing date:', error)
|
|
194
|
+
return null
|
|
195
|
+
}
|
|
196
|
+
}
|
|
149
197
|
|
|
150
198
|
// Format category name for display
|
|
151
199
|
const formatCategoryName = (name: string) => {
|
|
@@ -159,7 +207,8 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
159
207
|
title: t('addNewMobileNumber'),
|
|
160
208
|
content: null as React.ReactNode,
|
|
161
209
|
number: '',
|
|
162
|
-
otp: null as string | null
|
|
210
|
+
otp: null as string | null,
|
|
211
|
+
error: ''
|
|
163
212
|
})
|
|
164
213
|
|
|
165
214
|
// Detect mobile screens
|
|
@@ -189,7 +238,7 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
189
238
|
getMobileNumbersQuery.refetch()
|
|
190
239
|
setShowModal(old => ({ ...old, otp: null, error: '' }))
|
|
191
240
|
},
|
|
192
|
-
onError: error => {
|
|
241
|
+
onError: (error: ApiError) => {
|
|
193
242
|
setShowModal(old => ({ ...old, otp: null, error: error.response?.data?.errorCode || 'Something went wrong' }))
|
|
194
243
|
}
|
|
195
244
|
})
|
|
@@ -209,10 +258,11 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
209
258
|
title: 'Add new mobile number',
|
|
210
259
|
content: null,
|
|
211
260
|
number: '',
|
|
212
|
-
otp: null
|
|
261
|
+
otp: null,
|
|
262
|
+
error: ''
|
|
213
263
|
})
|
|
214
264
|
},
|
|
215
|
-
onError: error => {
|
|
265
|
+
onError: (error: ApiError) => {
|
|
216
266
|
setShowModal(old => ({ ...old, otp: null, error: error.response?.data?.errorCode || 'Something went wrong' }))
|
|
217
267
|
// console.error(error)
|
|
218
268
|
}
|
|
@@ -257,13 +307,64 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
257
307
|
...result[mobileIndex],
|
|
258
308
|
value: selectedMobileNumber
|
|
259
309
|
})
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
310
|
+
|
|
311
|
+
// Update identifiers in FIP store
|
|
312
|
+
setIdentifiers(result)
|
|
313
|
+
|
|
314
|
+
// Also update PAN and DOB in redirect store for API calls
|
|
315
|
+
const { updateIdentifiers } = useRedirectStore.getState()
|
|
316
|
+
const panIdentifier = result.find(i => i.type === 'PAN')
|
|
317
|
+
const dobIdentifier = result.find(i => i.type === 'DOB')
|
|
318
|
+
|
|
319
|
+
updateIdentifiers(
|
|
320
|
+
panIdentifier?.value || undefined,
|
|
321
|
+
dobIdentifier?.value || undefined
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
console.log('DiscoverAccount: Updated identifiers in both stores', {
|
|
325
|
+
pan: panIdentifier?.value,
|
|
326
|
+
dob: dobIdentifier?.value
|
|
266
327
|
})
|
|
328
|
+
|
|
329
|
+
// Check if we came from different sources and redirect accordingly
|
|
330
|
+
if (state?.fromOldUser) {
|
|
331
|
+
// Redirect back to old user page
|
|
332
|
+
navigate('/link-accounts/old-user', {
|
|
333
|
+
state: {
|
|
334
|
+
category,
|
|
335
|
+
selectedFips: state?.selectedFips,
|
|
336
|
+
fromDiscovery: true
|
|
337
|
+
}
|
|
338
|
+
})
|
|
339
|
+
} else if (state?.fromAutoDiscovery) {
|
|
340
|
+
// Redirect back to auto discovery page
|
|
341
|
+
navigate('/link-accounts/discovery', {
|
|
342
|
+
state: {
|
|
343
|
+
category,
|
|
344
|
+
selectedFips: state?.selectedFips,
|
|
345
|
+
isAutoDiscovery: true, // Important: preserve auto discovery flag
|
|
346
|
+
fromDiscovery: true
|
|
347
|
+
}
|
|
348
|
+
})
|
|
349
|
+
} else if (state?.fromDiscoverMore) {
|
|
350
|
+
// Redirect back to AccountsToProceed page
|
|
351
|
+
navigate('/link-accounts/discovery', {
|
|
352
|
+
state: {
|
|
353
|
+
category,
|
|
354
|
+
selectedFips: state?.selectedFips,
|
|
355
|
+
fromDiscovery: true
|
|
356
|
+
}
|
|
357
|
+
})
|
|
358
|
+
} else {
|
|
359
|
+
// Normal flow - go to discovery page
|
|
360
|
+
navigate('/link-accounts/discovery', {
|
|
361
|
+
state: {
|
|
362
|
+
category,
|
|
363
|
+
selectedFips: state?.selectedFips,
|
|
364
|
+
fromDiscovery: true
|
|
365
|
+
}
|
|
366
|
+
})
|
|
367
|
+
}
|
|
267
368
|
}
|
|
268
369
|
|
|
269
370
|
const handleCancel = () => {
|
|
@@ -308,7 +409,7 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
308
409
|
<Modal
|
|
309
410
|
title={showModal.title}
|
|
310
411
|
open={showModal.isOpen}
|
|
311
|
-
className='w-[
|
|
412
|
+
className='w-[450px]'
|
|
312
413
|
onOpenChange={open =>
|
|
313
414
|
!open &&
|
|
314
415
|
setShowModal({
|
|
@@ -330,13 +431,14 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
330
431
|
title=''
|
|
331
432
|
editable={true}
|
|
332
433
|
mobileNumber={showModal.number}
|
|
434
|
+
eyeClassName="translate-x-2"
|
|
333
435
|
onResend={() => {
|
|
334
436
|
setShowModal(old => ({ ...old, otp: null, error: '' }))
|
|
335
437
|
addNewNoQuery.mutate(undefined, {
|
|
336
438
|
onSuccess: () => {
|
|
337
439
|
setShowModal(old => ({ ...old, otp: null, error: '' }))
|
|
338
440
|
},
|
|
339
|
-
onError: (error:
|
|
441
|
+
onError: (error: ApiError) => {
|
|
340
442
|
setShowModal(old => ({ ...old, otp: null, error: error.response?.data?.errorMsg || 'Something went wrong' }))
|
|
341
443
|
// console.error(error)
|
|
342
444
|
}
|
|
@@ -366,10 +468,32 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
366
468
|
);
|
|
367
469
|
};
|
|
368
470
|
|
|
471
|
+
// Auto-fill DOB from decodedInfo when identifiers are set
|
|
472
|
+
useEffect(() => {
|
|
473
|
+
if (decodedInfo?.dob && identifiers?.find(i => i.type === 'DOB')) {
|
|
474
|
+
const parsedDate = parseDateFromDecodedInfo(decodedInfo.dob)
|
|
475
|
+
|
|
476
|
+
if (parsedDate) {
|
|
477
|
+
const dobIdentifier = identifiers.find(i => i.type === 'DOB')
|
|
478
|
+
if (dobIdentifier && !dobIdentifier.value) {
|
|
479
|
+
const result = [...identifiers]
|
|
480
|
+
const dobIndex = result.findIndex(i => i.type === 'DOB')
|
|
481
|
+
if (dobIndex !== -1) {
|
|
482
|
+
result[dobIndex] = {
|
|
483
|
+
...dobIdentifier,
|
|
484
|
+
value: parsedDate.toLocaleDateString('en-CA') // YYYY-MM-DD format
|
|
485
|
+
}
|
|
486
|
+
setIdentifiers(result)
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}, [decodedInfo?.dob, identifiers, setIdentifiers])
|
|
492
|
+
|
|
369
493
|
return (
|
|
370
494
|
<div>
|
|
371
495
|
<div className='flex flex-col gap-1 w-full md:px-14 px-0 gap-2 md:mt-6 mt-0'>
|
|
372
|
-
{showAlert &&
|
|
496
|
+
{/* {showAlert &&
|
|
373
497
|
<div className='mb-4'>
|
|
374
498
|
<AlertComp
|
|
375
499
|
icon={
|
|
@@ -389,7 +513,7 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
389
513
|
}
|
|
390
514
|
/>
|
|
391
515
|
</div>
|
|
392
|
-
}
|
|
516
|
+
} */}
|
|
393
517
|
<div className='flex items-center justify-between gap-2'>
|
|
394
518
|
<p className='text-md md:text-2xl font-semibold text-black dark:text-white'>
|
|
395
519
|
{t('keywords.Discover') +
|
|
@@ -476,15 +600,30 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
476
600
|
<div className='flex flex-col gap-1 md:w-[50%] w-full gap-2'>
|
|
477
601
|
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
|
|
478
602
|
<PopoverTrigger asChild>
|
|
479
|
-
|
|
603
|
+
{/* */}
|
|
604
|
+
<div
|
|
605
|
+
className={`flex h-[56px] w-full items-center justify-between rounded-md border bg-white border-gray-200 dark:border-gray-700 dark:bg-input/30 px-3 py-2 text-muted-foreground dark:border-gray-700 cursor-pointer`}
|
|
606
|
+
>
|
|
480
607
|
{identifiers?.find(i => i.type === 'DOB')?.value ? (
|
|
481
|
-
<span>
|
|
608
|
+
<span>
|
|
609
|
+
{
|
|
610
|
+
(() => {
|
|
611
|
+
const dobString = identifiers.find(i => i.type === 'DOB')?.value || '';
|
|
612
|
+
const [day, month, year] = dobString.split('/');
|
|
613
|
+
const dob = new Date(Number(year), Number(month) - 1, Number(day));
|
|
614
|
+
return dob.toLocaleDateString('en-IN', {
|
|
615
|
+
year: 'numeric',
|
|
616
|
+
month: 'long',
|
|
617
|
+
day: 'numeric'
|
|
618
|
+
});
|
|
619
|
+
})()
|
|
620
|
+
}
|
|
621
|
+
</span>
|
|
482
622
|
) : (
|
|
483
623
|
<span className='text-muted-foreground'>Pick a date</span>
|
|
484
624
|
)}
|
|
485
|
-
<CalendarIcon className=
|
|
625
|
+
<CalendarIcon className={`h-4 w-4 ${decodedInfo?.dob ? 'opacity-30' : 'opacity-50'}`} />
|
|
486
626
|
</div>
|
|
487
|
-
|
|
488
627
|
</PopoverTrigger>
|
|
489
628
|
<PopoverContent className='w-auto p-0' align='start'>
|
|
490
629
|
<Calendar
|
|
@@ -529,16 +668,22 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
529
668
|
selected={identifiers?.find(i => i.type === 'DOB')?.value ? new Date(identifiers.find(i => i.type === 'DOB')?.value || '') : undefined}
|
|
530
669
|
onSelect={(date) => {
|
|
531
670
|
if (date) {
|
|
671
|
+
const dateValue = date.toLocaleString().split(',')[0]
|
|
532
672
|
const result = [...identifiers]
|
|
533
673
|
result.splice(
|
|
534
674
|
result.findIndex(i => i.type === 'DOB'),
|
|
535
675
|
1,
|
|
536
676
|
{
|
|
537
677
|
...identifiers?.find(i => i.type === 'DOB'),
|
|
538
|
-
value:
|
|
678
|
+
value: dateValue
|
|
539
679
|
}
|
|
540
680
|
)
|
|
541
681
|
setIdentifiers(result)
|
|
682
|
+
|
|
683
|
+
// Also update redirect store immediately
|
|
684
|
+
const { updateIdentifiers } = useRedirectStore.getState()
|
|
685
|
+
updateIdentifiers(undefined, dateValue || undefined)
|
|
686
|
+
|
|
542
687
|
setIsPopoverOpen(false)
|
|
543
688
|
}
|
|
544
689
|
}}
|
|
@@ -557,21 +702,27 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
557
702
|
type='text'
|
|
558
703
|
className={`h-[56px] bg-white border-gray-200 dark:border-gray-700 text-consent-primary text-md ${validationError.PAN ? 'border-red-500' : ''}`}
|
|
559
704
|
placeholder={`Enter PAN`}
|
|
560
|
-
value={identifiers?.find(i => i.type === 'PAN')?.value}
|
|
561
|
-
readOnly={decodedInfo?.pan ? true : false}
|
|
705
|
+
value={identifiers?.find(i => i.type === 'PAN')?.value || ''}
|
|
706
|
+
// readOnly={decodedInfo?.pan ? true : false}
|
|
562
707
|
onChange={e => {
|
|
563
708
|
setValidationError(prev => ({ ...prev, PAN: null }))
|
|
564
709
|
const panValue = e.target.value.replace(/[^A-Za-z0-9]/g, '').toUpperCase()
|
|
710
|
+
|
|
711
|
+
// Update identifiers array more safely
|
|
565
712
|
const result = [...identifiers]
|
|
566
|
-
result.
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
{
|
|
570
|
-
...
|
|
713
|
+
const panIndex = result.findIndex(i => i.type === 'PAN')
|
|
714
|
+
|
|
715
|
+
if (panIndex !== -1) {
|
|
716
|
+
result[panIndex] = {
|
|
717
|
+
...result[panIndex],
|
|
571
718
|
value: panValue
|
|
572
719
|
}
|
|
573
|
-
|
|
574
|
-
|
|
720
|
+
setIdentifiers(result)
|
|
721
|
+
|
|
722
|
+
// Also update redirect store immediately
|
|
723
|
+
const { updateIdentifiers } = useRedirectStore.getState()
|
|
724
|
+
updateIdentifiers(panValue || undefined, undefined)
|
|
725
|
+
}
|
|
575
726
|
|
|
576
727
|
// Only validate when input reaches max length
|
|
577
728
|
if (panValue.length >= 10) {
|
|
@@ -601,20 +752,22 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
601
752
|
className={`h-[56px] bg-white border-gray-200 dark:border-gray-700 text-consent-primary text-md ${validationError.AADHAAR ? 'border-red-500' : ''}`}
|
|
602
753
|
placeholder={`Enter AADHAAR`}
|
|
603
754
|
readOnly={decodedInfo?.aadhaar ? true : false}
|
|
604
|
-
value={identifiers?.find(i => i.type === 'AADHAAR')?.value}
|
|
755
|
+
value={identifiers?.find(i => i.type === 'AADHAAR')?.value || ''}
|
|
605
756
|
onChange={e => {
|
|
606
757
|
setValidationError(prev => ({ ...prev, AADHAAR: null }))
|
|
607
758
|
const aadhaarValue = e.target.value.replace(/[^0-9]/g, '')
|
|
759
|
+
|
|
760
|
+
// Update identifiers array more safely
|
|
608
761
|
const result = [...identifiers]
|
|
609
|
-
result.
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
{
|
|
613
|
-
...
|
|
762
|
+
const aadhaarIndex = result.findIndex(i => i.type === 'AADHAAR')
|
|
763
|
+
|
|
764
|
+
if (aadhaarIndex !== -1) {
|
|
765
|
+
result[aadhaarIndex] = {
|
|
766
|
+
...result[aadhaarIndex],
|
|
614
767
|
value: aadhaarValue
|
|
615
768
|
}
|
|
616
|
-
|
|
617
|
-
|
|
769
|
+
setIdentifiers(result)
|
|
770
|
+
}
|
|
618
771
|
|
|
619
772
|
// Only validate when input reaches max length
|
|
620
773
|
if (aadhaarValue.length >= 12) {
|
|
@@ -710,13 +863,14 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
710
863
|
title=''
|
|
711
864
|
editable={true}
|
|
712
865
|
mobileNumber={showModal.number}
|
|
866
|
+
eyeClassName="translate-x-2"
|
|
713
867
|
onResend={() => {
|
|
714
868
|
setShowModal(old => ({ ...old, otp: null, error: '' }))
|
|
715
869
|
addNewNoQuery.mutate(undefined, {
|
|
716
870
|
onSuccess: () => {
|
|
717
871
|
setShowModal(old => ({ ...old, otp: null, error: '' }))
|
|
718
872
|
},
|
|
719
|
-
onError: (error:
|
|
873
|
+
onError: (error: ApiError) => {
|
|
720
874
|
setShowModal(old => ({ ...old, otp: null, error: error.response?.data?.errorMsg || 'Something went wrong' }))
|
|
721
875
|
// console.error(error)
|
|
722
876
|
}
|
|
@@ -743,8 +897,7 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
|
|
|
743
897
|
</AnimatedButton>
|
|
744
898
|
</div> : showModal.content}
|
|
745
899
|
</BottomSheet>
|
|
746
|
-
|
|
747
|
-
</div >
|
|
900
|
+
</div>
|
|
748
901
|
)
|
|
749
902
|
}
|
|
750
903
|
|
|
@@ -8,7 +8,7 @@ import { Repeat } from 'lucide-react'
|
|
|
8
8
|
import { Button } from '@/components/ui/button'
|
|
9
9
|
import { MobileFooter } from '@/components/ui/mobile-footer'
|
|
10
10
|
import WebFooter from '@/components/ui/web-footer'
|
|
11
|
-
import { useFipStore } from '@/store/fip.store'
|
|
11
|
+
import { useFipStore, DiscoveredAccount } from '@/store/fip.store'
|
|
12
12
|
import { Collapsible, CollapsibleContent } from '@/components/ui/collapsible'
|
|
13
13
|
import { Input } from '@/components/ui/input'
|
|
14
14
|
import { useMutation } from '@tanstack/react-query'
|
|
@@ -117,8 +117,11 @@ const SuccessAnimation = ({
|
|
|
117
117
|
interface Account {
|
|
118
118
|
id: string;
|
|
119
119
|
fipId: string;
|
|
120
|
+
fipName: string;
|
|
121
|
+
signature: string;
|
|
120
122
|
accounts: string;
|
|
121
123
|
otp: string;
|
|
124
|
+
DiscoveredAccounts?: DiscoveredAccount[];
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
interface LinkSelectedAccountsProps {
|
|
@@ -157,13 +160,12 @@ const LinkSelectedAccounts = ({
|
|
|
157
160
|
const {
|
|
158
161
|
selectedAccountToLink,
|
|
159
162
|
selectedMobileNumber,
|
|
160
|
-
signature,
|
|
161
163
|
accountsStatusArray,
|
|
162
164
|
setAccountsStatusArray
|
|
163
165
|
} = useFipStore()
|
|
164
166
|
|
|
165
167
|
const sendBankOTPQuery = useMutation({
|
|
166
|
-
mutationFn: ({ fipId, accounts }: { fipId: string; accounts:
|
|
168
|
+
mutationFn: ({ fipId, accounts, signature }: { fipId: string; accounts: DiscoveredAccount[]; signature: string }) =>
|
|
167
169
|
accountService.sendBankOTP({
|
|
168
170
|
signature: signature || '',
|
|
169
171
|
FipId: fipId || '',
|
|
@@ -236,9 +238,10 @@ const LinkSelectedAccounts = ({
|
|
|
236
238
|
) {
|
|
237
239
|
sendBankOTPQuery.mutate({
|
|
238
240
|
fipId: selectedAccountToLink[nextIndex].fipId,
|
|
239
|
-
accounts: selectedAccountToLink[nextIndex]?.[
|
|
241
|
+
accounts: selectedAccountToLink[nextIndex]?.DiscoveredAccounts || [],
|
|
242
|
+
signature: selectedAccountToLink[nextIndex]?.signature || ''
|
|
240
243
|
})
|
|
241
|
-
setIsExpanded({ item: selectedAccountToLink[nextIndex], expanded: true })
|
|
244
|
+
setIsExpanded({ item: { ...selectedAccountToLink[nextIndex], accounts: '', otp: '' }, expanded: true })
|
|
242
245
|
} else {
|
|
243
246
|
if (type != 'retry') { setIsExpanded({ item: null, expanded: false }) }
|
|
244
247
|
}
|
|
@@ -313,10 +316,11 @@ const LinkSelectedAccounts = ({
|
|
|
313
316
|
if (accountsStatusArray?.length) {
|
|
314
317
|
handleNextActiveAccount()
|
|
315
318
|
} else {
|
|
316
|
-
setIsExpanded({ item: selectedAccountToLink[0], expanded: true })
|
|
319
|
+
setIsExpanded({ item: { ...selectedAccountToLink[0], accounts: '', otp: '' }, expanded: true })
|
|
317
320
|
sendBankOTPQuery.mutate({
|
|
318
321
|
fipId: selectedAccountToLink[0].fipId,
|
|
319
|
-
accounts: selectedAccountToLink[0]?.[
|
|
322
|
+
accounts: selectedAccountToLink[0]?.DiscoveredAccounts || [],
|
|
323
|
+
signature: selectedAccountToLink[0]?.signature || ''
|
|
320
324
|
})
|
|
321
325
|
}
|
|
322
326
|
}
|
|
@@ -443,7 +447,8 @@ const LinkSelectedAccounts = ({
|
|
|
443
447
|
) {
|
|
444
448
|
sendBankOTPQuery.mutate({
|
|
445
449
|
fipId: account.fipId,
|
|
446
|
-
accounts: account?.[
|
|
450
|
+
accounts: account?.DiscoveredAccounts || [],
|
|
451
|
+
signature: account?.signature || ''
|
|
447
452
|
})
|
|
448
453
|
}
|
|
449
454
|
setIsExpanded({
|
|
@@ -555,7 +560,8 @@ const LinkSelectedAccounts = ({
|
|
|
555
560
|
onClick={() => {
|
|
556
561
|
sendBankOTPQuery.mutate({
|
|
557
562
|
fipId: account.fipId,
|
|
558
|
-
accounts: account?.[
|
|
563
|
+
accounts: account?.DiscoveredAccounts || [],
|
|
564
|
+
signature: account?.signature || ''
|
|
559
565
|
})
|
|
560
566
|
setOTPResent(true)
|
|
561
567
|
}}
|