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
@@ -8,13 +8,23 @@ import { feedbackService } from "@/services/api";
|
|
8
8
|
import { StarFilledIcon } from "@radix-ui/react-icons";
|
9
9
|
import { useNavigationBlock } from "@/store/NavigationBlockContext";
|
10
10
|
import { MobileAppDownload } from "@/components/mobileAppDownload";
|
11
|
+
import { useLocation } from "react-router-dom";
|
12
|
+
import AlertComp from "@/components/alert/alert";
|
13
|
+
import { X } from "lucide-react";
|
14
|
+
import { useFipStore } from "@/store/fip.store";
|
15
|
+
|
16
|
+
import { HelpModal } from "@/components/modal/HelpModal";
|
11
17
|
|
12
18
|
const Success = () => {
|
19
|
+
const location = useLocation();
|
20
|
+
const consentResult: any[] = location.state?.consentResult;
|
21
|
+
const consents: any[] = location.state?.consents;
|
13
22
|
const [rating, setRating] = useState<number | null>(null);
|
14
23
|
const [hoveredRating, setHoveredRating] = useState<number | null>(null);
|
15
24
|
const [isSubmitted, setIsSubmitted] = useState(false);
|
16
25
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
17
26
|
const [countdown, setCountdown] = useState(10);
|
27
|
+
const { selectedAccountToLink } = useFipStore()
|
18
28
|
const { decodedInfo } = useRedirectStore();
|
19
29
|
const { allowNextNavigation } = useNavigationBlock();
|
20
30
|
const consentHandle = decodedInfo?.srcref || '';
|
@@ -24,9 +34,9 @@ const Success = () => {
|
|
24
34
|
if (countdown === 1) {
|
25
35
|
console.log("Sending message to parent -->> From AA");
|
26
36
|
window.parent.postMessage(
|
27
|
-
{
|
28
|
-
type: 'AA',
|
29
|
-
status: 'approved',
|
37
|
+
{
|
38
|
+
type: 'AA',
|
39
|
+
status: 'approved',
|
30
40
|
flowCompleted: true,
|
31
41
|
consentHandle: consentHandle,
|
32
42
|
userRating: rating,
|
@@ -94,8 +104,9 @@ const Success = () => {
|
|
94
104
|
};
|
95
105
|
|
96
106
|
return (
|
97
|
-
<div className="flex flex-col
|
107
|
+
<div className="flex flex-col h-full">
|
98
108
|
<MobileBackground
|
109
|
+
className="h-full min-h-screen"
|
99
110
|
blobConfig={{
|
100
111
|
count: 8,
|
101
112
|
color: "rgba(255, 255, 255, 0.4)",
|
@@ -104,104 +115,142 @@ const Success = () => {
|
|
104
115
|
speed: 4,
|
105
116
|
}}
|
106
117
|
>
|
107
|
-
<div className="
|
108
|
-
{/*
|
109
|
-
|
110
|
-
<
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
118
|
+
<div className="flex flex-col h-full min-h-screen p-4 sm:p-6">
|
119
|
+
{/* Header with help and timer */}
|
120
|
+
<div className="flex justify-between items-start mb-4 sm:mb-6">
|
121
|
+
<HelpModal
|
122
|
+
variant="light"
|
123
|
+
iconSize={16}
|
124
|
+
textSize="text-xs sm:text-sm"
|
125
|
+
buttonClassName="self-start"
|
126
|
+
/>
|
127
|
+
{decodedInfo?.redirect && (
|
128
|
+
<div className="text-center">
|
129
|
+
<p className="text-white/80 text-xs sm:text-sm">
|
130
|
+
Redirecting in <span className="font-semibold">00:{countdown < 10 ? `0${countdown}` : countdown}</span>
|
131
|
+
</p>
|
132
|
+
</div>
|
133
|
+
)}
|
134
|
+
</div>
|
116
135
|
|
117
136
|
{/* Main content - centered vertically and horizontally */}
|
118
137
|
<div className="flex-1 flex flex-col justify-center items-center pt-10 sm:pt-16 pb-10 sm:pb-20">
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
138
|
+
<motion.div
|
139
|
+
initial={{ y: 20, opacity: 0 }}
|
140
|
+
animate={{ y: 0, opacity: 1 }}
|
141
|
+
transition={{ delay: 0.2, duration: 0.5 }}
|
123
142
|
className="text-white flex flex-col items-center w-full max-w-md mx-auto px-4"
|
124
|
-
|
143
|
+
>
|
125
144
|
<div className="rounded-full mb-10 sm:mb-16">
|
126
|
-
|
127
|
-
|
128
|
-
|
145
|
+
<motion.img
|
146
|
+
src={checkIcon}
|
147
|
+
alt="Check Icon"
|
129
148
|
className="w-32 h-32 sm:w-40 sm:h-40 md:w-48 md:h-48"
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
149
|
+
initial={{ scale: 0, rotate: -10 }}
|
150
|
+
animate={{ scale: 1, rotate: 0 }}
|
151
|
+
transition={{
|
152
|
+
type: "spring",
|
153
|
+
damping: 8,
|
154
|
+
stiffness: 100,
|
155
|
+
delay: 0.9,
|
156
|
+
duration: 4
|
157
|
+
}}
|
158
|
+
/>
|
159
|
+
</div>
|
141
160
|
|
142
|
-
|
161
|
+
<motion.h1
|
143
162
|
className="text-xl md:text-3xl font-semibold mb-5 sm:mb-6 text-center"
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
className="text-white/80 text-center mb-10 sm:mb-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
163
|
+
initial={{ opacity: 0 }}
|
164
|
+
animate={{ opacity: 1 }}
|
165
|
+
transition={{ delay: 1.5 }}
|
166
|
+
>
|
167
|
+
Approved Successfully
|
168
|
+
</motion.h1>
|
169
|
+
|
170
|
+
<motion.p
|
171
|
+
className="text-white/80 text-center mb-10 sm:mb-8 text-sm sm:text-base md:text-lg max-w-sm"
|
172
|
+
initial={{ opacity: 0 }}
|
173
|
+
animate={{ opacity: 1 }}
|
174
|
+
transition={{ delay: 1.5 }}
|
175
|
+
>
|
176
|
+
You can pause/revoke this consent anytime using Saafe app
|
177
|
+
</motion.p>
|
159
178
|
|
179
|
+
{consentResult?.filter(item => item.ConsentStatus === 'FAILED')?.length > 0 && (
|
180
|
+
<AlertComp
|
181
|
+
type="error"
|
182
|
+
icon={
|
183
|
+
<div className='flex bg-red-600 p-2 rounded-full'>
|
184
|
+
<X
|
185
|
+
strokeWidth='2.3'
|
186
|
+
className='h-[18px] w-[18px] text-white'
|
187
|
+
/>
|
188
|
+
</div>
|
189
|
+
}
|
190
|
+
title={`Out of ${consents?.length} consents, ${consentResult?.filter(item => item.ConsentStatus === 'FAILED')?.length} was rejected`}
|
191
|
+
description={
|
192
|
+
<div className="text-left mt-1">
|
193
|
+
{
|
194
|
+
(() => {
|
195
|
+
const fipCodes = consentResult?.filter(item => item.ConsentStatus === 'FAILED')?.map(item =>
|
196
|
+
item.reasonForRejection.match(/\[(.*?)\]/)?.[1]
|
197
|
+
);
|
198
|
+
const fipNames = selectedAccountToLink?.filter(item => fipCodes?.join(", ").split(", ").includes(item.fipHandle))?.map(item => item.fipName)
|
199
|
+
// remove the duplicate fipCodes
|
200
|
+
const uniqueFipCodes = [...new Set((fipNames))];
|
201
|
+
const result = `Consent rejected by FIP: ${uniqueFipCodes?.join(", ")}`;
|
202
|
+
return <div>{result}</div>;
|
203
|
+
})()
|
204
|
+
}
|
205
|
+
</div>
|
206
|
+
}
|
207
|
+
/>
|
208
|
+
)}
|
160
209
|
{/* Mobile App Download - animated to appear last */}
|
161
210
|
<motion.div
|
162
|
-
className="w-full max-w-xs mx-auto"
|
211
|
+
className="w-full max-w-xs mx-auto mt-5"
|
163
212
|
initial={{ opacity: 0, y: 20 }}
|
164
213
|
animate={{ opacity: 1, y: 0 }}
|
165
214
|
transition={{ delay: 2.5, duration: 0.7 }}
|
166
215
|
>
|
167
|
-
|
216
|
+
<MobileAppDownload />
|
168
217
|
</motion.div>
|
169
|
-
|
170
|
-
|
171
|
-
|
218
|
+
|
219
|
+
{/* Rating Component */}
|
220
|
+
<motion.div
|
172
221
|
className="mt-12 sm:mt-16 w-full max-w-xs mx-auto px-4 text-center"
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
222
|
+
initial={{ opacity: 0, y: 20 }}
|
223
|
+
animate={{ opacity: 1, y: 0 }}
|
224
|
+
transition={{ delay: 2.0 }}
|
225
|
+
>
|
177
226
|
<h3 className="text-white text-sm sm:text-base md:text-lg mb-4 sm:mb-6">Rate your experience</h3>
|
178
227
|
<div className="flex justify-center gap-3 sm:gap-5">
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
228
|
+
{[1, 2, 3, 4, 5].map((star) => (
|
229
|
+
<button
|
230
|
+
key={star}
|
231
|
+
onClick={() => handleRatingClick(star)}
|
232
|
+
onMouseEnter={() => setHoveredRating(star)}
|
233
|
+
onMouseLeave={() => setHoveredRating(null)}
|
185
234
|
className="focus:outline-none transition-transform hover:scale-110 p-1 sm:p-2"
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
235
|
+
disabled={isSubmitting}
|
236
|
+
>
|
237
|
+
<StarFilledIcon
|
238
|
+
className={`
|
190
239
|
transition-colors duration-200 w-6 h-6 sm:w-8 sm:h-8 md:w-9 md:h-9
|
191
240
|
${(hoveredRating !== null ? star <= hoveredRating : star <= (rating || 0))
|
192
|
-
|
193
|
-
|
241
|
+
? "fill-white text-white"
|
242
|
+
: "text-white/40 fill-transparent"}
|
194
243
|
${isSubmitting ? "opacity-70" : ""}
|
195
244
|
`}
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
245
|
+
/>
|
246
|
+
</button>
|
247
|
+
))}
|
248
|
+
</div>
|
249
|
+
{isSubmitted && (
|
201
250
|
<p className="mt-3 text-white/80 text-xs sm:text-sm">Thank you for your feedback!</p>
|
202
|
-
|
251
|
+
)}
|
252
|
+
</motion.div>
|
203
253
|
</motion.div>
|
204
|
-
</motion.div>
|
205
254
|
</div>
|
206
255
|
|
207
256
|
{/* Footer */}
|
package/src/store/fip.store.ts
CHANGED
@@ -8,6 +8,48 @@ import { fiTypeCategoryMap } from '@/const/fiTypeCategoryMap'
|
|
8
8
|
export interface Identifier {
|
9
9
|
category: string
|
10
10
|
type: string
|
11
|
+
value?: string
|
12
|
+
}
|
13
|
+
|
14
|
+
export interface DiscoveredAccount {
|
15
|
+
FIType: string
|
16
|
+
accType: string
|
17
|
+
accRefNumber: string
|
18
|
+
maskedAccNumber: string
|
19
|
+
state: string | null
|
20
|
+
amcName: string | null
|
21
|
+
logoUrl: string | null
|
22
|
+
}
|
23
|
+
|
24
|
+
export interface AccountToLink {
|
25
|
+
id: string
|
26
|
+
type: string
|
27
|
+
maskedAccountNumber: string
|
28
|
+
bankName: string
|
29
|
+
logoUrl?: string
|
30
|
+
isNew?: boolean
|
31
|
+
fipId: string
|
32
|
+
fipName: string
|
33
|
+
signature: string
|
34
|
+
DiscoveredAccounts?: DiscoveredAccount[]
|
35
|
+
}
|
36
|
+
|
37
|
+
export interface AccountStatus {
|
38
|
+
id: string
|
39
|
+
fipId: string
|
40
|
+
status: string
|
41
|
+
accounts?: string
|
42
|
+
otp?: string
|
43
|
+
}
|
44
|
+
|
45
|
+
export interface AccountForConsent {
|
46
|
+
linkRefNumber: string
|
47
|
+
fiType: string
|
48
|
+
bankName: string
|
49
|
+
accountType: string
|
50
|
+
maskedAccNumber: string
|
51
|
+
logoUrl?: string
|
52
|
+
fipHandle: string
|
11
53
|
}
|
12
54
|
|
13
55
|
export interface FipData {
|
@@ -32,14 +74,16 @@ export const MAIN_STEPS = {
|
|
32
74
|
LOGIN: 'login',
|
33
75
|
LINK_ACCOUNTS: 'link_accounts',
|
34
76
|
REVIEW_CONSENT: 'review_consent'
|
35
|
-
}
|
77
|
+
} as const
|
36
78
|
|
37
79
|
// Define flow steps for each category
|
38
80
|
export const CATEGORY_STEPS = {
|
39
81
|
LIST: 'list',
|
40
82
|
DISCOVERY: 'discovery',
|
41
83
|
LINKING: 'linking'
|
42
|
-
}
|
84
|
+
} as const
|
85
|
+
|
86
|
+
type CategoryStepType = typeof CATEGORY_STEPS[keyof typeof CATEGORY_STEPS]
|
43
87
|
|
44
88
|
export interface FipState {
|
45
89
|
fips: FipData[]
|
@@ -49,16 +93,15 @@ export interface FipState {
|
|
49
93
|
activeCategory: string | null
|
50
94
|
categories: string[]
|
51
95
|
completedCategories: string[]
|
52
|
-
currentCategoryStep:
|
96
|
+
currentCategoryStep: CategoryStepType
|
53
97
|
isLoading: boolean
|
54
98
|
error: string | null
|
55
|
-
selectedAccountToLink:
|
56
|
-
signature: string | null
|
99
|
+
selectedAccountToLink: AccountToLink[]
|
57
100
|
originalAccounts: AutoDiscoveryResponse['Accounts']
|
58
|
-
accountsStatusArray:
|
59
|
-
identifiers:
|
60
|
-
accountForConsent:
|
61
|
-
accountsToNotify:
|
101
|
+
accountsStatusArray: AccountStatus[]
|
102
|
+
identifiers: Identifier[]
|
103
|
+
accountForConsent: AccountForConsent[]
|
104
|
+
accountsToNotify: string[]
|
62
105
|
|
63
106
|
// Navigation state
|
64
107
|
currentStep: string
|
@@ -76,21 +119,20 @@ export interface FipState {
|
|
76
119
|
removeSelectedFip: (category: string, fipId: string) => void
|
77
120
|
setSelectedMobileNumber: (mobileNumber: string) => void
|
78
121
|
setActiveCategory: (category: string | null) => void
|
79
|
-
setCategoryStep: (step:
|
122
|
+
setCategoryStep: (step: CategoryStepType) => void
|
80
123
|
completeCategory: (category: string) => void
|
81
124
|
setCurrentStep: (stepId: string) => void
|
82
125
|
setCurrentChildStep: (childStepId: string | null) => void
|
83
126
|
clearSelections: () => void
|
84
127
|
setIsLoading: (isLoading: boolean) => void
|
85
128
|
setError: (error: string | null) => void
|
86
|
-
|
87
|
-
setSelectedAccountToLink: (account: any[]) => void
|
129
|
+
setSelectedAccountToLink: (account: AccountToLink[]) => void
|
88
130
|
setOriginalAccounts: (
|
89
131
|
originalAccounts: AutoDiscoveryResponse['Accounts']
|
90
132
|
) => void
|
91
|
-
setAccountsStatusArray: (data:
|
92
|
-
setIdentifiers: (identifiersList:
|
93
|
-
setAccountForConsent: (accountForConsent:
|
133
|
+
setAccountsStatusArray: (data: AccountStatus[]) => void
|
134
|
+
setIdentifiers: (identifiersList: Identifier[]) => void
|
135
|
+
setAccountForConsent: (accountForConsent: AccountForConsent[]) => void
|
94
136
|
setAccountsToNotify: (data: string) => void
|
95
137
|
}
|
96
138
|
|
@@ -119,7 +161,7 @@ const initialNavigationSteps: StepItem[] = [
|
|
119
161
|
|
120
162
|
// Helper function to extract categories
|
121
163
|
export const extractCategories = (groupedFips: string[]): string[] => {
|
122
|
-
const category = new Set()
|
164
|
+
const category = new Set<string>()
|
123
165
|
console.log('groupedFips', groupedFips);
|
124
166
|
|
125
167
|
groupedFips.forEach((i) => {
|
@@ -131,13 +173,13 @@ export const extractCategories = (groupedFips: string[]): string[] => {
|
|
131
173
|
})
|
132
174
|
})
|
133
175
|
|
134
|
-
const result =
|
176
|
+
const result = Array.from(category)
|
135
177
|
if (result.length > 1) {
|
136
178
|
const order = Object.keys(fiTypeCategoryMap)
|
137
|
-
return result.sort((a, b) => order.indexOf(a) - order.indexOf(b))
|
179
|
+
return result.sort((a, b) => order.indexOf(a) - order.indexOf(b))
|
138
180
|
}
|
139
181
|
|
140
|
-
return result
|
182
|
+
return result
|
141
183
|
}
|
142
184
|
|
143
185
|
// Helper function to create child steps from categories
|
@@ -179,6 +221,7 @@ export const useFipStore = create<FipState>()(
|
|
179
221
|
fips: [],
|
180
222
|
groupedFips: {},
|
181
223
|
selectedFips: {},
|
224
|
+
autoDiscoveryFips: {},
|
182
225
|
selectedMobileNumber: undefined,
|
183
226
|
activeCategory: null,
|
184
227
|
categories: [],
|
@@ -187,7 +230,6 @@ export const useFipStore = create<FipState>()(
|
|
187
230
|
isLoading: false,
|
188
231
|
error: null,
|
189
232
|
selectedAccountToLink: [],
|
190
|
-
signature: null,
|
191
233
|
originalAccounts: [],
|
192
234
|
accountsStatusArray: [],
|
193
235
|
identifiers: [],
|
@@ -207,10 +249,16 @@ export const useFipStore = create<FipState>()(
|
|
207
249
|
|
208
250
|
// Initialize selectedFips with empty arrays for each category
|
209
251
|
const selectedFips: { [category: string]: string[] } = {}
|
252
|
+
// const autoDiscoveryFips: { [category: string]: string[] } = {}
|
210
253
|
categories.forEach(category => {
|
211
254
|
selectedFips[category] = []
|
255
|
+
// autoDiscoveryFips[category] = []
|
256
|
+
// const fipsList = fips[category]
|
257
|
+
// autoDiscoveryFips[category] = fipsList.filter(fip => fip.isEnabled && fip.isOnBoarded)
|
212
258
|
})
|
213
259
|
|
260
|
+
|
261
|
+
|
214
262
|
// Create child steps from categories
|
215
263
|
const childSteps = createChildSteps(categories)
|
216
264
|
const currentChildStep = categories.length > 0 ? categories[0] : null
|
@@ -245,8 +293,6 @@ export const useFipStore = create<FipState>()(
|
|
245
293
|
setSelectedAccountToLink: accountIds =>
|
246
294
|
set({ selectedAccountToLink: accountIds }),
|
247
295
|
|
248
|
-
setSignature: signature => set({ signature }),
|
249
|
-
|
250
296
|
setIdentifiers: identifiers => set({ identifiers }),
|
251
297
|
|
252
298
|
setOriginalAccounts: originalAccounts => set({ originalAccounts }),
|
@@ -363,12 +409,13 @@ export const useFipStore = create<FipState>()(
|
|
363
409
|
|
364
410
|
setIsLoading: isLoading => set({ isLoading }),
|
365
411
|
|
366
|
-
setAccountsToNotify: (data: string
|
412
|
+
setAccountsToNotify: (data: string) => {
|
367
413
|
console.log('data', data);
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
414
|
+
if (data) {
|
415
|
+
set(state => ({
|
416
|
+
accountsToNotify: [...(state.accountsToNotify || []), data]
|
417
|
+
}))
|
418
|
+
}
|
372
419
|
},
|
373
420
|
|
374
421
|
setError: error => set({ error })
|
@@ -388,7 +435,6 @@ export const useFipStore = create<FipState>()(
|
|
388
435
|
selectedAccountToLink: state.selectedAccountToLink,
|
389
436
|
originalAccounts: state.originalAccounts,
|
390
437
|
identifiers: state.identifiers,
|
391
|
-
signature: state.signature,
|
392
438
|
accountForConsent: state.accountForConsent
|
393
439
|
})
|
394
440
|
}
|
@@ -13,6 +13,7 @@ export interface RedirectState {
|
|
13
13
|
setDecodingError: (error: string) => void;
|
14
14
|
clearDecodedInfo: () => void;
|
15
15
|
getCustomStyle: () => Record<string, string> | null;
|
16
|
+
updateIdentifiers: (pan?: string, dob?: string) => void;
|
16
17
|
}
|
17
18
|
|
18
19
|
export const useRedirectStore = create<RedirectState>()(
|
@@ -60,6 +61,18 @@ export const useRedirectStore = create<RedirectState>()(
|
|
60
61
|
return null;
|
61
62
|
}
|
62
63
|
},
|
64
|
+
|
65
|
+
updateIdentifiers: (pan?: string, dob?: string) => {
|
66
|
+
const { decodedInfo } = get();
|
67
|
+
if (decodedInfo) {
|
68
|
+
const updatedInfo = {
|
69
|
+
...decodedInfo,
|
70
|
+
pan: pan || decodedInfo.pan,
|
71
|
+
dob: dob || decodedInfo.dob,
|
72
|
+
};
|
73
|
+
set({ decodedInfo: updatedInfo });
|
74
|
+
}
|
75
|
+
},
|
63
76
|
}),
|
64
77
|
{
|
65
78
|
name: "saafe-redirect-storage",
|
@@ -0,0 +1,126 @@
|
|
1
|
+
export interface AutoDiscoveryConfig {
|
2
|
+
isEnabled: boolean;
|
3
|
+
showDiscoverMore: boolean;
|
4
|
+
}
|
5
|
+
|
6
|
+
export interface AutoDiscoveryConfigs {
|
7
|
+
INSURANCE: AutoDiscoveryConfig;
|
8
|
+
GST: AutoDiscoveryConfig;
|
9
|
+
INVESTMENTS: AutoDiscoveryConfig;
|
10
|
+
}
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Parse the auto discovery configuration from the decoded info
|
14
|
+
*/
|
15
|
+
export const parseAutoDiscoveryConfig = (otherAppCustomization?: string): AutoDiscoveryConfigs => {
|
16
|
+
const defaultConfig: AutoDiscoveryConfigs = {
|
17
|
+
INSURANCE: { isEnabled: false, showDiscoverMore: true },
|
18
|
+
GST: { isEnabled: false, showDiscoverMore: true },
|
19
|
+
INVESTMENTS: { isEnabled: false, showDiscoverMore: true }
|
20
|
+
};
|
21
|
+
|
22
|
+
if (!otherAppCustomization) {
|
23
|
+
return defaultConfig;
|
24
|
+
}
|
25
|
+
|
26
|
+
try {
|
27
|
+
const config = JSON.parse(otherAppCustomization);
|
28
|
+
|
29
|
+
return {
|
30
|
+
INSURANCE: {
|
31
|
+
isEnabled: config.INSURANCE_AutoDiscovery === 'true',
|
32
|
+
showDiscoverMore: config.INSURANCE_ShowDiscoverMore === undefined ? true : config.INSURANCE_ShowDiscoverMore === 'true'
|
33
|
+
},
|
34
|
+
GST: {
|
35
|
+
isEnabled: config.GST_AutoDiscovery === 'true',
|
36
|
+
showDiscoverMore: config.GST_ShowDiscoverMore === undefined ? true : config.GST_ShowDiscoverMore === 'true'
|
37
|
+
},
|
38
|
+
INVESTMENTS: {
|
39
|
+
isEnabled: config.INVESTMENTS_AutoDiscovery === 'true',
|
40
|
+
showDiscoverMore: config.INVESTMENTS_ShowDiscoverMore === undefined ? true : config.INVESTMENTS_ShowDiscoverMore === 'true'
|
41
|
+
}
|
42
|
+
};
|
43
|
+
} catch (error) {
|
44
|
+
console.error('Failed to parse auto discovery configuration:', error);
|
45
|
+
return defaultConfig;
|
46
|
+
}
|
47
|
+
};
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Check if auto discovery is enabled for a specific category
|
51
|
+
*/
|
52
|
+
export const isAutoDiscoveryEnabled = (category: string, otherAppCustomization?: string): boolean => {
|
53
|
+
const config = parseAutoDiscoveryConfig(otherAppCustomization);
|
54
|
+
const categoryKey = category.toUpperCase() as keyof AutoDiscoveryConfigs;
|
55
|
+
|
56
|
+
// Only enable auto discovery for supported categories
|
57
|
+
if (!['INSURANCE', 'GST', 'INVESTMENTS'].includes(categoryKey)) {
|
58
|
+
return false;
|
59
|
+
}
|
60
|
+
|
61
|
+
return config[categoryKey]?.isEnabled || false;
|
62
|
+
};
|
63
|
+
|
64
|
+
/**
|
65
|
+
* Get FIP IDs for auto discovery
|
66
|
+
* If FIP IDs are configured in decodedInfo, use those
|
67
|
+
* Otherwise, use all FIPs for the category
|
68
|
+
*/
|
69
|
+
export const getAutoDiscoveryFipIds = (
|
70
|
+
category: string,
|
71
|
+
configuredFipIds?: string,
|
72
|
+
groupedFips?: Record<string, any[]>
|
73
|
+
): string[] => {
|
74
|
+
// If FIP IDs are configured and not empty, use them
|
75
|
+
if (configuredFipIds && configuredFipIds.trim()) {
|
76
|
+
return configuredFipIds.split(',').map(fipId => fipId.trim()).filter(fipId => fipId.length > 0);
|
77
|
+
}
|
78
|
+
|
79
|
+
// Otherwise, get all FIPs for the category from the store
|
80
|
+
if (groupedFips) {
|
81
|
+
// Import fiTypeCategoryMap to get the correct FI types for the category
|
82
|
+
const fiTypeCategoryMap: Record<string, string[]> = {
|
83
|
+
BANK: ['DEPOSIT', 'RECURRING_DEPOSIT', 'TERM_DEPOSIT', 'BANK'],
|
84
|
+
INVESTMENTS: [
|
85
|
+
'EQUITIES',
|
86
|
+
'MUTUAL_FUNDS',
|
87
|
+
'OTHER_INVESTMENTS',
|
88
|
+
'IDR',
|
89
|
+
'REIT',
|
90
|
+
'CIS',
|
91
|
+
'INVIT',
|
92
|
+
'AIF',
|
93
|
+
'ETF',
|
94
|
+
'SIP',
|
95
|
+
'NPS (Retirement focused)',
|
96
|
+
'PENSION'
|
97
|
+
],
|
98
|
+
INSURANCE: ['INSURANCE_POLICIES', 'LIFE_INSURANCE', 'GENERAL_INSURANCE', 'INSURANCE'],
|
99
|
+
GST: ['GSTR1_3B', 'GST']
|
100
|
+
};
|
101
|
+
|
102
|
+
const categoryUpper = category.toUpperCase();
|
103
|
+
const fiTypesForCategory = fiTypeCategoryMap[categoryUpper] || [];
|
104
|
+
|
105
|
+
// Get all FIPs that match the FI types for this category
|
106
|
+
const categoryFips = fiTypesForCategory
|
107
|
+
.flatMap(fiType => groupedFips[fiType] || [])
|
108
|
+
.reduce((unique: any[], fip: any) => {
|
109
|
+
// Remove duplicates based on ID
|
110
|
+
if (!unique.find((existingFip: any) => existingFip.id === fip.id)) {
|
111
|
+
unique.push(fip);
|
112
|
+
}
|
113
|
+
return unique;
|
114
|
+
}, [])
|
115
|
+
// IMPORTANT: Filter to only include enabled and onboarded FIPs
|
116
|
+
.filter((fip: any) => fip.isEnabled === true && fip.isOnBoarded === true);
|
117
|
+
|
118
|
+
console.log(`Auto Discovery: Found ${categoryFips.length} enabled & onboarded FIPs for category ${categoryUpper}:`,
|
119
|
+
categoryFips.map((fip: any) => ({ id: fip.id, name: fip.name, isEnabled: fip.isEnabled, isOnBoarded: fip.isOnBoarded }))
|
120
|
+
);
|
121
|
+
|
122
|
+
return categoryFips.map((fip: any) => fip.id);
|
123
|
+
}
|
124
|
+
|
125
|
+
return [];
|
126
|
+
};
|
Binary file
|