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.
@@ -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: 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
  }
@@ -147,6 +158,43 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
147
158
  const [isPopoverOpen, setIsPopoverOpen] = useState(false)
148
159
  const [showAlert, setShowAlert] = useState(true)
149
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
+ }
197
+
150
198
  // Format category name for display
151
199
  const formatCategoryName = (name: string) => {
152
200
  return name
@@ -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
- navigate('/link-accounts/discovery', {
261
- state: {
262
- category,
263
- selectedFips: state?.selectedFips,
264
- fromDiscovery: true
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-[380px]'
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: 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,6 +468,28 @@ 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'>
@@ -474,15 +598,15 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
474
598
  <>
475
599
  <SectionTitle className='mt-6' title={`DOB*`} />
476
600
  <div className='flex flex-col gap-1 md:w-[50%] w-full gap-2'>
477
- <Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
601
+ <Popover open={isPopoverOpen && !decodedInfo?.dob} onOpenChange={setIsPopoverOpen}>
478
602
  <PopoverTrigger asChild>
479
- <div 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-consent-primary dark:border-gray-700'>
603
+ <div 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-consent-primary dark:border-gray-700 ${decodedInfo?.dob ? 'cursor-not-allowed opacity-60' : 'cursor-pointer'}`}>
480
604
  {identifiers?.find(i => i.type === 'DOB')?.value ? (
481
605
  <span>{new Date(identifiers.find(i => i.type === 'DOB')?.value || '').toLocaleDateString('en-IN', { year: 'numeric', month: 'long', day: 'numeric' })}</span>
482
606
  ) : (
483
607
  <span className='text-muted-foreground'>Pick a date</span>
484
608
  )}
485
- <CalendarIcon className='h-4 w-4 opacity-50' />
609
+ <CalendarIcon className={`h-4 w-4 ${decodedInfo?.dob ? 'opacity-30' : 'opacity-50'}`} />
486
610
  </div>
487
611
 
488
612
  </PopoverTrigger>
@@ -529,16 +653,22 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
529
653
  selected={identifiers?.find(i => i.type === 'DOB')?.value ? new Date(identifiers.find(i => i.type === 'DOB')?.value || '') : undefined}
530
654
  onSelect={(date) => {
531
655
  if (date) {
656
+ const dateValue = date.toLocaleString().split(',')[0]
532
657
  const result = [...identifiers]
533
658
  result.splice(
534
659
  result.findIndex(i => i.type === 'DOB'),
535
660
  1,
536
661
  {
537
662
  ...identifiers?.find(i => i.type === 'DOB'),
538
- value: date.toLocaleString().split(',')[0]
663
+ value: dateValue
539
664
  }
540
665
  )
541
666
  setIdentifiers(result)
667
+
668
+ // Also update redirect store immediately
669
+ const { updateIdentifiers } = useRedirectStore.getState()
670
+ updateIdentifiers(undefined, dateValue || undefined)
671
+
542
672
  setIsPopoverOpen(false)
543
673
  }
544
674
  }}
@@ -557,21 +687,27 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
557
687
  type='text'
558
688
  className={`h-[56px] bg-white border-gray-200 dark:border-gray-700 text-consent-primary text-md ${validationError.PAN ? 'border-red-500' : ''}`}
559
689
  placeholder={`Enter PAN`}
560
- value={identifiers?.find(i => i.type === 'PAN')?.value}
561
- readOnly={decodedInfo?.pan ? true : false}
690
+ value={identifiers?.find(i => i.type === 'PAN')?.value || ''}
691
+ // readOnly={decodedInfo?.pan ? true : false}
562
692
  onChange={e => {
563
693
  setValidationError(prev => ({ ...prev, PAN: null }))
564
694
  const panValue = e.target.value.replace(/[^A-Za-z0-9]/g, '').toUpperCase()
695
+
696
+ // Update identifiers array more safely
565
697
  const result = [...identifiers]
566
- result.splice(
567
- result.findIndex(i => i.type === 'PAN'),
568
- 1,
569
- {
570
- ...identifiers?.find(i => i.type === 'PAN'),
698
+ const panIndex = result.findIndex(i => i.type === 'PAN')
699
+
700
+ if (panIndex !== -1) {
701
+ result[panIndex] = {
702
+ ...result[panIndex],
571
703
  value: panValue
572
704
  }
573
- )
574
- setIdentifiers(result)
705
+ setIdentifiers(result)
706
+
707
+ // Also update redirect store immediately
708
+ const { updateIdentifiers } = useRedirectStore.getState()
709
+ updateIdentifiers(panValue || undefined, undefined)
710
+ }
575
711
 
576
712
  // Only validate when input reaches max length
577
713
  if (panValue.length >= 10) {
@@ -601,20 +737,22 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
601
737
  className={`h-[56px] bg-white border-gray-200 dark:border-gray-700 text-consent-primary text-md ${validationError.AADHAAR ? 'border-red-500' : ''}`}
602
738
  placeholder={`Enter AADHAAR`}
603
739
  readOnly={decodedInfo?.aadhaar ? true : false}
604
- value={identifiers?.find(i => i.type === 'AADHAAR')?.value}
740
+ value={identifiers?.find(i => i.type === 'AADHAAR')?.value || ''}
605
741
  onChange={e => {
606
742
  setValidationError(prev => ({ ...prev, AADHAAR: null }))
607
743
  const aadhaarValue = e.target.value.replace(/[^0-9]/g, '')
744
+
745
+ // Update identifiers array more safely
608
746
  const result = [...identifiers]
609
- result.splice(
610
- result.findIndex(i => i.type === 'AADHAAR'),
611
- 1,
612
- {
613
- ...identifiers?.find(i => i.type === 'AADHAAR'),
747
+ const aadhaarIndex = result.findIndex(i => i.type === 'AADHAAR')
748
+
749
+ if (aadhaarIndex !== -1) {
750
+ result[aadhaarIndex] = {
751
+ ...result[aadhaarIndex],
614
752
  value: aadhaarValue
615
753
  }
616
- )
617
- setIdentifiers(result)
754
+ setIdentifiers(result)
755
+ }
618
756
 
619
757
  // Only validate when input reaches max length
620
758
  if (aadhaarValue.length >= 12) {
@@ -710,13 +848,14 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
710
848
  title=''
711
849
  editable={true}
712
850
  mobileNumber={showModal.number}
851
+ eyeClassName="translate-x-2"
713
852
  onResend={() => {
714
853
  setShowModal(old => ({ ...old, otp: null, error: '' }))
715
854
  addNewNoQuery.mutate(undefined, {
716
855
  onSuccess: () => {
717
856
  setShowModal(old => ({ ...old, otp: null, error: '' }))
718
857
  },
719
- onError: (error: Error) => {
858
+ onError: (error: ApiError) => {
720
859
  setShowModal(old => ({ ...old, otp: null, error: error.response?.data?.errorMsg || 'Something went wrong' }))
721
860
  // console.error(error)
722
861
  }
@@ -743,8 +882,7 @@ const DiscoverAccount = ({ state, currentCategory }: DiscoverAccountProps) => {
743
882
  </AnimatedButton>
744
883
  </div> : showModal.content}
745
884
  </BottomSheet>
746
-
747
- </div >
885
+ </div>
748
886
  )
749
887
  }
750
888
 
@@ -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: any[] }) =>
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]?.['DiscoveredAccounts']
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]?.['DiscoveredAccounts']
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?.['DiscoveredAccounts']
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?.['DiscoveredAccounts']
563
+ accounts: account?.DiscoveredAccounts || [],
564
+ signature: account?.signature || ''
559
565
  })
560
566
  setOTPResent(true)
561
567
  }}