saafe-redirection-flow 2.0.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.
Files changed (225) hide show
  1. package/.github/workflows/build-and-deploy.yml +41 -0
  2. package/.gitlab-ci.yml +108 -0
  3. package/.releaserc.json +18 -0
  4. package/.storybook/main.ts +28 -0
  5. package/.storybook/preview.ts +16 -0
  6. package/.storybook/vitest.setup.ts +9 -0
  7. package/.vite/deps/@radix-ui_react-avatar.js +230 -0
  8. package/.vite/deps/@radix-ui_react-avatar.js.map +7 -0
  9. package/.vite/deps/@radix-ui_react-slot.js +12 -0
  10. package/.vite/deps/@radix-ui_react-slot.js.map +7 -0
  11. package/.vite/deps/_metadata.json +79 -0
  12. package/.vite/deps/chunk-5VGQBUCU.js +597 -0
  13. package/.vite/deps/chunk-5VGQBUCU.js.map +7 -0
  14. package/.vite/deps/chunk-DC5AMYBS.js +38 -0
  15. package/.vite/deps/chunk-DC5AMYBS.js.map +7 -0
  16. package/.vite/deps/chunk-HUIEPYH7.js +11265 -0
  17. package/.vite/deps/chunk-HUIEPYH7.js.map +7 -0
  18. package/.vite/deps/chunk-TKHB4QMX.js +281 -0
  19. package/.vite/deps/chunk-TKHB4QMX.js.map +7 -0
  20. package/.vite/deps/chunk-YLDSBLSF.js +1139 -0
  21. package/.vite/deps/chunk-YLDSBLSF.js.map +7 -0
  22. package/.vite/deps/class-variance-authority.js +63 -0
  23. package/.vite/deps/class-variance-authority.js.map +7 -0
  24. package/.vite/deps/lucide-react.js +36984 -0
  25. package/.vite/deps/lucide-react.js.map +7 -0
  26. package/.vite/deps/package.json +3 -0
  27. package/.vite/deps/react-dom_client.js +17917 -0
  28. package/.vite/deps/react-dom_client.js.map +7 -0
  29. package/.vite/deps/react-router-dom.js +452 -0
  30. package/.vite/deps/react-router-dom.js.map +7 -0
  31. package/.vite/deps/react-router.js +234 -0
  32. package/.vite/deps/react-router.js.map +7 -0
  33. package/.vite/deps/react.js +5 -0
  34. package/.vite/deps/react.js.map +7 -0
  35. package/.vite/deps/react_jsx-dev-runtime.js +470 -0
  36. package/.vite/deps/react_jsx-dev-runtime.js.map +7 -0
  37. package/CHANGELOG.md +420 -0
  38. package/LICENSE +21 -0
  39. package/README.md +129 -0
  40. package/RELEASE_CHEATSHEET.md +93 -0
  41. package/RELEASE_NOTES.md +120 -0
  42. package/components.json +21 -0
  43. package/docs/DEPLOYMENT_WORKFLOW.md +262 -0
  44. package/docs/RELEASE_GUIDE.md +591 -0
  45. package/docs/architecture.md +432 -0
  46. package/docs/components.md +199 -0
  47. package/docs/index.md +69 -0
  48. package/docs/local-release-workflow.md +234 -0
  49. package/docs/routes.md +118 -0
  50. package/docs/sdk-integration.md +325 -0
  51. package/docs/semantic-release.md +124 -0
  52. package/docs/user-flow.md +206 -0
  53. package/eslint.config.js +28 -0
  54. package/index.html +19 -0
  55. package/install.sh +198 -0
  56. package/package.json +115 -0
  57. package/public/images/bank-logo.png +0 -0
  58. package/public/saafe-icon.svg +9 -0
  59. package/src/App.tsx +171 -0
  60. package/src/__tests__/url-parameters.test.ts +82 -0
  61. package/src/assets/brand/applestore.svg +13 -0
  62. package/src/assets/brand/playstore.svg +23 -0
  63. package/src/assets/brand/saafe-color-white-logo.svg +14 -0
  64. package/src/assets/brand/saafe-icon.svg +9 -0
  65. package/src/assets/brand/saafe-logo.svg +18 -0
  66. package/src/assets/icons/check-icon-dark.svg +27 -0
  67. package/src/assets/icons/check-icon.svg +23 -0
  68. package/src/components/ErrorBoundary.tsx +132 -0
  69. package/src/components/alert/alert.tsx +27 -0
  70. package/src/components/auth/AuthGuard.tsx +76 -0
  71. package/src/components/cards/BankCard.stories.tsx +69 -0
  72. package/src/components/cards/BankCard.tsx +227 -0
  73. package/src/components/cards/OuterCard.tsx +109 -0
  74. package/src/components/cards/WrapperCard.tsx +64 -0
  75. package/src/components/documents/PrivacyContent.tsx +1 -0
  76. package/src/components/dummyFooter.tsx +29 -0
  77. package/src/components/icons/github.tsx +12 -0
  78. package/src/components/language/LanguageSwitcher.tsx +44 -0
  79. package/src/components/layouts/FrostedLayout.stories.tsx +42 -0
  80. package/src/components/layouts/FrostedLayout.tsx +333 -0
  81. package/src/components/layouts/MobileLayout.tsx +403 -0
  82. package/src/components/mobile-background.tsx +136 -0
  83. package/src/components/mobileAppDownload.tsx +30 -0
  84. package/src/components/modal/ModalComp.tsx +27 -0
  85. package/src/components/mode-toggle.tsx +36 -0
  86. package/src/components/page-header.tsx +50 -0
  87. package/src/components/session/SessionTimeoutScreen.tsx +134 -0
  88. package/src/components/session/SessionTimer.tsx +173 -0
  89. package/src/components/step-navigation.tsx +87 -0
  90. package/src/components/title/AppBar.stories.tsx +50 -0
  91. package/src/components/title/AppBar.tsx +150 -0
  92. package/src/components/title/SectionTitle.tsx +31 -0
  93. package/src/components/ui/AnimatedButton.module.css +13 -0
  94. package/src/components/ui/alert.tsx +66 -0
  95. package/src/components/ui/animatedButton.tsx +111 -0
  96. package/src/components/ui/avatar.tsx +51 -0
  97. package/src/components/ui/badge.tsx +36 -0
  98. package/src/components/ui/bottom-sheet.tsx +122 -0
  99. package/src/components/ui/button.tsx +59 -0
  100. package/src/components/ui/calendar.tsx +86 -0
  101. package/src/components/ui/card.tsx +92 -0
  102. package/src/components/ui/checkbox.stories.tsx +49 -0
  103. package/src/components/ui/checkbox.tsx +67 -0
  104. package/src/components/ui/collapsible.tsx +45 -0
  105. package/src/components/ui/dialog.tsx +134 -0
  106. package/src/components/ui/document-link.tsx +26 -0
  107. package/src/components/ui/dot-stepper.tsx +57 -0
  108. package/src/components/ui/dropdown-menu.tsx +255 -0
  109. package/src/components/ui/form.tsx +165 -0
  110. package/src/components/ui/frosted-panel.stories.tsx +86 -0
  111. package/src/components/ui/frosted-panel.tsx +276 -0
  112. package/src/components/ui/input.tsx +39 -0
  113. package/src/components/ui/label.stories.tsx +67 -0
  114. package/src/components/ui/label.tsx +23 -0
  115. package/src/components/ui/mobile-footer.tsx +54 -0
  116. package/src/components/ui/modal.tsx +90 -0
  117. package/src/components/ui/otp-input.stories.tsx +62 -0
  118. package/src/components/ui/otp-input.tsx +221 -0
  119. package/src/components/ui/platform-specific-behavior.tsx +28 -0
  120. package/src/components/ui/popover.tsx +46 -0
  121. package/src/components/ui/progress.tsx +103 -0
  122. package/src/components/ui/radio-group.tsx +45 -0
  123. package/src/components/ui/scroll-area.tsx +56 -0
  124. package/src/components/ui/sdk-params-docs.tsx +53 -0
  125. package/src/components/ui/select.tsx +159 -0
  126. package/src/components/ui/separator.tsx +28 -0
  127. package/src/components/ui/sheet.tsx +137 -0
  128. package/src/components/ui/sidebar.tsx +724 -0
  129. package/src/components/ui/skeleton.stories.tsx +50 -0
  130. package/src/components/ui/skeleton.tsx +15 -0
  131. package/src/components/ui/sonner.tsx +23 -0
  132. package/src/components/ui/step.stories.tsx +132 -0
  133. package/src/components/ui/step.tsx +234 -0
  134. package/src/components/ui/stepper-progress.tsx +136 -0
  135. package/src/components/ui/stepper.tsx +259 -0
  136. package/src/components/ui/tabs.tsx +55 -0
  137. package/src/components/ui/tooltip.tsx +61 -0
  138. package/src/components/ui/url-decode-loader.tsx +36 -0
  139. package/src/components/ui/version-display.tsx +104 -0
  140. package/src/components/ui/web-footer.tsx +36 -0
  141. package/src/config/environments.ts +99 -0
  142. package/src/config/urls.ts +53 -0
  143. package/src/const/fiTypeCategoryMap.ts +19 -0
  144. package/src/contexts/LanguageContext.tsx +41 -0
  145. package/src/contexts/RTLContext.tsx +42 -0
  146. package/src/contexts/ThemeContext.tsx +93 -0
  147. package/src/hooks/use-account-discovery.ts +205 -0
  148. package/src/hooks/use-auth-query.ts +141 -0
  149. package/src/hooks/use-fip-query.ts +72 -0
  150. package/src/hooks/use-media-query.ts +32 -0
  151. package/src/hooks/use-mobile.ts +24 -0
  152. package/src/hooks/use-page-title.tsx +48 -0
  153. package/src/hooks/use-platform.ts +52 -0
  154. package/src/hooks/use-trusted-count.ts +21 -0
  155. package/src/hooks/use-url-decode.ts +90 -0
  156. package/src/hooks/useStep.ts +170 -0
  157. package/src/index.css +154 -0
  158. package/src/interfaces/app.interfaces.ts +39 -0
  159. package/src/interfaces/services.interfaces.ts +65 -0
  160. package/src/lib/i18n.ts +68 -0
  161. package/src/lib/utils.ts +6 -0
  162. package/src/locales/en/common.json +167 -0
  163. package/src/locales/hi/common.json +137 -0
  164. package/src/locales/kn/common.json +137 -0
  165. package/src/locales/ml/common.json +137 -0
  166. package/src/locales/ta/common.json +137 -0
  167. package/src/locales/te/common.json +137 -0
  168. package/src/locales/ur/common.json +138 -0
  169. package/src/main.tsx +46 -0
  170. package/src/pages/Login.tsx +363 -0
  171. package/src/pages/accounts/AccountsToProceed.tsx +396 -0
  172. package/src/pages/accounts/Discover.tsx +76 -0
  173. package/src/pages/accounts/DiscoverAccount.tsx +751 -0
  174. package/src/pages/accounts/LinkSelectedAccounts.tsx +638 -0
  175. package/src/pages/accounts/OldUser.tsx +329 -0
  176. package/src/pages/accounts/link-accounts.tsx +913 -0
  177. package/src/pages/consent/ReviewConsent.tsx +836 -0
  178. package/src/pages/consent/rejected.tsx +253 -0
  179. package/src/pages/consent/success.tsx +220 -0
  180. package/src/providers/query-provider.tsx +24 -0
  181. package/src/providers/toast-provider.tsx +26 -0
  182. package/src/services/api/account.service.ts +296 -0
  183. package/src/services/api/auth.service.ts +206 -0
  184. package/src/services/api/axios.ts +138 -0
  185. package/src/services/api/consent.service.ts +142 -0
  186. package/src/services/api/decode.service.ts +53 -0
  187. package/src/services/api/feedback.service.ts +34 -0
  188. package/src/services/api/fip.service.ts +187 -0
  189. package/src/services/api/index.ts +9 -0
  190. package/src/services/api/public.service.ts +18 -0
  191. package/src/services/api.ts +2 -0
  192. package/src/services/postMessage.service.ts +179 -0
  193. package/src/store/NavigationBlockContext.tsx +34 -0
  194. package/src/store/auth.store.ts +79 -0
  195. package/src/store/fip.store.ts +396 -0
  196. package/src/store/mandatoryConsent.store.ts +24 -0
  197. package/src/store/redirect.store.ts +73 -0
  198. package/src/store/step.store.ts +124 -0
  199. package/src/stories/Button.stories.ts +53 -0
  200. package/src/stories/Button.tsx +37 -0
  201. package/src/stories/Configure.mdx +364 -0
  202. package/src/stories/Header.stories.ts +33 -0
  203. package/src/stories/Header.tsx +56 -0
  204. package/src/stories/Page.stories.ts +32 -0
  205. package/src/stories/Page.tsx +73 -0
  206. package/src/stories/button.css +30 -0
  207. package/src/stories/header.css +32 -0
  208. package/src/stories/page.css +68 -0
  209. package/src/styles/rtl-utils.css +90 -0
  210. package/src/styles/rtl.css +105 -0
  211. package/src/utils/api-error.ts +26 -0
  212. package/src/utils/cn.ts +10 -0
  213. package/src/utils/error-callback.ts +116 -0
  214. package/src/utils/formatAccountNumber.ts +9 -0
  215. package/src/utils/handleIdentifiers.ts +90 -0
  216. package/src/utils/posthog.ts +67 -0
  217. package/src/utils/toast-helpers.ts +61 -0
  218. package/src/vite-env.d.ts +1 -0
  219. package/stage-aa-2506251021.zip +0 -0
  220. package/tsconfig.app.json +33 -0
  221. package/tsconfig.json +13 -0
  222. package/tsconfig.node.json +24 -0
  223. package/vite.config.ts +45 -0
  224. package/vitest.shims.d.ts +1 -0
  225. package/vitest.workspace.ts +46 -0
@@ -0,0 +1,32 @@
1
+ .storybook-header {
2
+ display: flex;
3
+ justify-content: space-between;
4
+ align-items: center;
5
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
6
+ padding: 15px 20px;
7
+ font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
8
+ }
9
+
10
+ .storybook-header svg {
11
+ display: inline-block;
12
+ vertical-align: top;
13
+ }
14
+
15
+ .storybook-header h1 {
16
+ display: inline-block;
17
+ vertical-align: top;
18
+ margin: 6px 0 6px 10px;
19
+ font-weight: 700;
20
+ font-size: 20px;
21
+ line-height: 1;
22
+ }
23
+
24
+ .storybook-header button + button {
25
+ margin-left: 10px;
26
+ }
27
+
28
+ .storybook-header .welcome {
29
+ margin-right: 10px;
30
+ color: #333;
31
+ font-size: 14px;
32
+ }
@@ -0,0 +1,68 @@
1
+ .storybook-page {
2
+ margin: 0 auto;
3
+ padding: 48px 20px;
4
+ max-width: 600px;
5
+ color: #333;
6
+ font-size: 14px;
7
+ line-height: 24px;
8
+ font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
9
+ }
10
+
11
+ .storybook-page h2 {
12
+ display: inline-block;
13
+ vertical-align: top;
14
+ margin: 0 0 4px;
15
+ font-weight: 700;
16
+ font-size: 32px;
17
+ line-height: 1;
18
+ }
19
+
20
+ .storybook-page p {
21
+ margin: 1em 0;
22
+ }
23
+
24
+ .storybook-page a {
25
+ color: inherit;
26
+ }
27
+
28
+ .storybook-page ul {
29
+ margin: 1em 0;
30
+ padding-left: 30px;
31
+ }
32
+
33
+ .storybook-page li {
34
+ margin-bottom: 8px;
35
+ }
36
+
37
+ .storybook-page .tip {
38
+ display: inline-block;
39
+ vertical-align: top;
40
+ margin-right: 10px;
41
+ border-radius: 1em;
42
+ background: #e7fdd8;
43
+ padding: 4px 12px;
44
+ color: #357a14;
45
+ font-weight: 700;
46
+ font-size: 11px;
47
+ line-height: 12px;
48
+ }
49
+
50
+ .storybook-page .tip-wrapper {
51
+ margin-top: 40px;
52
+ margin-bottom: 40px;
53
+ font-size: 13px;
54
+ line-height: 20px;
55
+ }
56
+
57
+ .storybook-page .tip-wrapper svg {
58
+ display: inline-block;
59
+ vertical-align: top;
60
+ margin-top: 3px;
61
+ margin-right: 4px;
62
+ width: 12px;
63
+ height: 12px;
64
+ }
65
+
66
+ .storybook-page .tip-wrapper svg path {
67
+ fill: #1ea7fd;
68
+ }
@@ -0,0 +1,90 @@
1
+ /* RTL utilities for Tailwind CSS */
2
+
3
+ /* These custom utilities will complement Tailwind classes */
4
+ .rtl\:flex-row-reverse {
5
+ flex-direction: row-reverse;
6
+ }
7
+
8
+ .rtl\:text-right {
9
+ text-align: right;
10
+ }
11
+
12
+ .rtl\:mr-auto {
13
+ margin-right: auto;
14
+ }
15
+
16
+ .rtl\:ml-auto {
17
+ margin-left: auto;
18
+ }
19
+
20
+ /* RTL specific padding and margin classes */
21
+ .rtl\:pl-0 {
22
+ padding-left: 0;
23
+ }
24
+
25
+ .rtl\:pr-0 {
26
+ padding-right: 0;
27
+ }
28
+
29
+ /* RTL transforms for icons */
30
+ .rtl\:rotate-180 {
31
+ transform: rotate(180deg);
32
+ }
33
+
34
+ .rtl\:scale-x-n1 {
35
+ transform: scaleX(-1);
36
+ }
37
+
38
+ /* RTL borders */
39
+ .rtl\:border-r {
40
+ border-right-width: 1px;
41
+ border-right-style: solid;
42
+ }
43
+
44
+ .rtl\:border-l {
45
+ border-left-width: 1px;
46
+ border-left-style: solid;
47
+ }
48
+
49
+ .rtl\:border-r-0 {
50
+ border-right-width: 0;
51
+ }
52
+
53
+ .rtl\:border-l-0 {
54
+ border-left-width: 0;
55
+ }
56
+
57
+ /* Typography direction */
58
+ .rtl\:direction-rtl {
59
+ direction: rtl;
60
+ }
61
+
62
+ .rtl\:direction-ltr {
63
+ direction: ltr;
64
+ }
65
+
66
+ /* RTL float classes */
67
+ .rtl\:float-right {
68
+ float: right;
69
+ }
70
+
71
+ .rtl\:float-left {
72
+ float: left;
73
+ }
74
+
75
+ /* Variable to manage padding and margin amounts based on size */
76
+ :root {
77
+ --padding-amount-1: 0.25rem;
78
+ --padding-amount-2: 0.5rem;
79
+ --padding-amount-3: 0.75rem;
80
+ --padding-amount-4: 1rem;
81
+ --padding-amount-5: 1.25rem;
82
+ --padding-amount-6: 1.5rem;
83
+
84
+ --margin-amount-1: 0.25rem;
85
+ --margin-amount-2: 0.5rem;
86
+ --margin-amount-3: 0.75rem;
87
+ --margin-amount-4: 1rem;
88
+ --margin-amount-5: 1.25rem;
89
+ --margin-amount-6: 1.5rem;
90
+ }
@@ -0,0 +1,105 @@
1
+ /* RTL Specific Styles */
2
+
3
+ /* Global RTL adjustments */
4
+ .rtl {
5
+ direction: rtl;
6
+ text-align: right;
7
+ }
8
+
9
+ /* Flip padding and margins for RTL */
10
+ .rtl .language-switcher {
11
+ margin-left: 0;
12
+ margin-right: auto;
13
+ }
14
+
15
+ /* Flip button icons */
16
+ .rtl button svg:not(.no-flip) {
17
+ transform: scaleX(-1);
18
+ }
19
+
20
+ /* Adjust flexbox direction */
21
+ .rtl .flex-row {
22
+ flex-direction: row-reverse;
23
+ }
24
+
25
+ /* Adjust text alignment in inputs and forms */
26
+ .rtl input,
27
+ .rtl textarea {
28
+ text-align: right;
29
+ }
30
+
31
+ /* Adjust dropdown menus */
32
+ .rtl .dropdown-menu-content {
33
+ transform-origin: top right;
34
+ }
35
+
36
+ /* Adjust borders */
37
+ .rtl .border-l {
38
+ border-left: none;
39
+ border-right: 1px solid;
40
+ }
41
+
42
+ .rtl .border-r {
43
+ border-right: none;
44
+ border-left: 1px solid;
45
+ }
46
+
47
+ /* Adjust any left/right padding or margins in common components */
48
+ .rtl .pl-1, .rtl .pl-2, .rtl .pl-3, .rtl .pl-4, .rtl .pl-5, .rtl .pl-6 {
49
+ padding-left: 0;
50
+ padding-right: var(--padding-amount);
51
+ }
52
+
53
+ .rtl .pr-1, .rtl .pr-2, .rtl .pr-3, .rtl .pr-4, .rtl .pr-5, .rtl .pr-6 {
54
+ padding-right: 0;
55
+ padding-left: var(--padding-amount);
56
+ }
57
+
58
+ .rtl .ml-1, .rtl .ml-2, .rtl .ml-3, .rtl .ml-4, .rtl .ml-5, .rtl .ml-6 {
59
+ margin-left: 0;
60
+ margin-right: var(--margin-amount);
61
+ }
62
+
63
+ .rtl .mr-1, .rtl .mr-2, .rtl .mr-3, .rtl .mr-4, .rtl .mr-5, .rtl .mr-6 {
64
+ margin-right: 0;
65
+ margin-left: var(--margin-amount);
66
+ }
67
+
68
+ /* Adjust input groups */
69
+ .rtl .input-group-prepend {
70
+ border-radius: 0 0.25rem 0.25rem 0;
71
+ }
72
+
73
+ .rtl .input-group-append {
74
+ border-radius: 0.25rem 0 0 0.25rem;
75
+ }
76
+
77
+ /* If using tailwind, we need to adjust text alignment for RTL */
78
+ .rtl .text-left {
79
+ text-align: right;
80
+ }
81
+
82
+ .rtl .text-right {
83
+ text-align: left;
84
+ }
85
+
86
+ /* Reset direction for elements that should not be flipped */
87
+ .rtl .no-rtl {
88
+ direction: ltr;
89
+ text-align: left;
90
+ }
91
+
92
+ /* Stepper specific RTL adjustments */
93
+ .rtl .rtl-stepper {
94
+ /* Ensure connectors align correctly in RTL */
95
+ direction: rtl;
96
+ }
97
+
98
+ .rtl .rtl-stepper .step-content {
99
+ text-align: right;
100
+ }
101
+
102
+ /* Fix for step icons in RTL mode */
103
+ .rtl .rtl-stepper svg:not(.no-flip) {
104
+ transform: scaleX(-1);
105
+ }
@@ -0,0 +1,26 @@
1
+ import { ErrorResponse } from '@/services/api/auth.service';
2
+
3
+ export class ApiError extends Error {
4
+ code: string;
5
+ txnid: string;
6
+ timestamp: string;
7
+
8
+ constructor(error: ErrorResponse) {
9
+ super(error.errorMsg || error.errorCode);
10
+ this.name = 'ApiError';
11
+ this.code = error.errorCode;
12
+ this.txnid = error.txnid;
13
+ this.timestamp = error.timestamp;
14
+ }
15
+ }
16
+
17
+ export const getErrorMessage = (errorCode: string): string => {
18
+ const errorMessages: Record<string, string> = {
19
+ 'Invalid Otp': 'The OTP you entered is invalid. Please try again.',
20
+ 'Token Expired': 'Your session has expired. Please log in again.',
21
+ 'Invalid Token': 'Authentication failed. Please log in again.',
22
+ 'User Not Found': 'User not found. Please check your credentials.'
23
+ };
24
+
25
+ return errorMessages[errorCode] || 'An unexpected error occurred. Please try again.';
26
+ };
@@ -0,0 +1,10 @@
1
+ import { clsx, type ClassValue } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ /**
5
+ * A utility function that merges multiple class names together
6
+ * It combines clsx for conditional classes with tailwind-merge to handle Tailwind CSS class conflicts
7
+ */
8
+ export function cn(...inputs: ClassValue[]) {
9
+ return twMerge(clsx(inputs));
10
+ }
@@ -0,0 +1,116 @@
1
+ interface ErrorCallbackData {
2
+ type: 'error';
3
+ status: 'error';
4
+ flowCompleted: boolean;
5
+ errorCode?: string;
6
+ errorMessage: string;
7
+ errorType?: 'api' | 'auth' | 'network' | 'validation' | 'unknown';
8
+ statusCode?: number;
9
+ timestamp: string;
10
+ userAgent?: string;
11
+ url?: string;
12
+ stack?: string;
13
+ consentHandle?: string;
14
+ redirectUrl?: string;
15
+ }
16
+
17
+ /**
18
+ * Send error callback to parent window
19
+ */
20
+ export const sendErrorCallback = (
21
+ errorMessage: string,
22
+ options: {
23
+ errorCode?: string;
24
+ errorType?: 'api' | 'auth' | 'network' | 'validation' | 'unknown';
25
+ statusCode?: number;
26
+ stack?: string;
27
+ consentHandle?: string;
28
+ redirectUrl?: string;
29
+ flowCompleted?: boolean;
30
+ } = {}
31
+ ) => {
32
+ const errorData: ErrorCallbackData = {
33
+ type: 'error',
34
+ status: 'error',
35
+ flowCompleted: options.flowCompleted ?? false,
36
+ errorCode: options.errorCode,
37
+ errorMessage,
38
+ errorType: options.errorType || 'unknown',
39
+ statusCode: options.statusCode,
40
+ timestamp: new Date().toISOString(),
41
+ userAgent: navigator.userAgent,
42
+ url: window.location.href,
43
+ stack: options.stack,
44
+ consentHandle: options.consentHandle,
45
+ redirectUrl: options.redirectUrl
46
+ };
47
+
48
+ console.log('Sending error message to parent:', errorData);
49
+
50
+ try {
51
+ window.parent.postMessage(errorData, '*');
52
+ } catch (err) {
53
+ console.error('Failed to send error callback to parent:', err);
54
+ }
55
+ };
56
+
57
+ /**
58
+ * Handle API errors and send callback
59
+ */
60
+ export const handleApiErrorWithCallback = (
61
+ error: unknown,
62
+ customMessage?: string,
63
+ consentHandle?: string,
64
+ redirectUrl?: string
65
+ ) => {
66
+ let errorMessage = customMessage || 'An unexpected error occurred';
67
+ let errorCode: string | undefined;
68
+ let statusCode: number | undefined;
69
+ let errorType: 'api' | 'auth' | 'network' | 'validation' | 'unknown' = 'unknown';
70
+
71
+ // Handle Axios errors
72
+ if (error && typeof error === 'object' && 'response' in error) {
73
+ const errorResponse = error?.response as any;
74
+ statusCode = errorResponse?.status;
75
+ errorCode = errorResponse?.data?.errorCode;
76
+ errorType = 'api';
77
+
78
+ // Get specific error message from response
79
+ if (errorResponse?.data?.errorCode) {
80
+ errorMessage = errorResponse.data.errorCode;
81
+ } else if (errorResponse?.data?.errorMsg) {
82
+ errorMessage = errorResponse.data.errorMsg;
83
+ } else if (statusCode === 401) {
84
+ errorMessage = 'Authentication failed. Please log in again.';
85
+ errorType = 'auth';
86
+ } else if (statusCode === 403) {
87
+ errorMessage = "You don't have permission to perform this action.";
88
+ errorType = 'auth';
89
+ } else if (statusCode === 404) {
90
+ errorMessage = 'Resource not found.';
91
+ } else if (statusCode && statusCode >= 500) {
92
+ errorMessage = 'Server error. Please try again later.';
93
+ }
94
+ }
95
+ // Handle network errors
96
+ else if (error && typeof error === 'object' && 'code' in error && (error as any).code === 'NETWORK_ERROR') {
97
+ errorMessage = 'Network error. Please check your connection.';
98
+ errorType = 'network';
99
+ }
100
+ // Handle standard errors
101
+ else if (error && typeof error === 'object' && 'message' in error) {
102
+ errorMessage = (error as Error).message;
103
+ errorType = error.name === 'ValidationError' ? 'validation' : 'unknown';
104
+ }
105
+
106
+ // Send error callback
107
+ sendErrorCallback(errorMessage, {
108
+ errorCode,
109
+ errorType,
110
+ statusCode,
111
+ stack: error instanceof Error ? error.stack : undefined,
112
+ consentHandle,
113
+ redirectUrl,
114
+ flowCompleted: false
115
+ });
116
+ };
@@ -0,0 +1,9 @@
1
+ export const formatAccountNumber = (accountNumber: string) => {
2
+ return (
3
+ '****' +
4
+ accountNumber
5
+ .replace(/XX+/g, '')
6
+ .replace(/\s/g, '')
7
+ .replace(/(\d{4})(?=\d)/g, '$1')
8
+ )
9
+ }
@@ -0,0 +1,90 @@
1
+ interface Identifier {
2
+ type: 'PAN' | 'AADHAAR' | 'MOBILE';
3
+ value?: string;
4
+ }
5
+
6
+ interface Fip {
7
+ id: string;
8
+ Identifiers?: Identifier[];
9
+ }
10
+
11
+ interface DecodedInfo {
12
+ pan?: string;
13
+ aadhaar?: string;
14
+ }
15
+
16
+ interface SelectedFips {
17
+ [key: string]: string[];
18
+ }
19
+
20
+ interface NavigateState {
21
+ category: string;
22
+ selectedFips: string[];
23
+ }
24
+
25
+ interface NavigateOptions {
26
+ state?: NavigateState;
27
+ }
28
+
29
+ const handleIdentifiers = ({
30
+ selectedFips,
31
+ activeCategory,
32
+ decodedInfo,
33
+ selectedMobileNumber,
34
+ setIdentifiers,
35
+ fips,
36
+ navigate
37
+ }: {
38
+ selectedFips: SelectedFips;
39
+ activeCategory: string;
40
+ decodedInfo: DecodedInfo;
41
+ selectedMobileNumber: string;
42
+ setIdentifiers: (identifiers: Identifier[]) => void;
43
+ fips: Fip[];
44
+ navigate: (path: string, options?: NavigateOptions) => void;
45
+ }) => {
46
+ let redirect = false;
47
+ const identifiersList: Identifier[] = [];
48
+
49
+ fips
50
+ .filter(fip =>
51
+ selectedFips[activeCategory]?.includes(fip.id)
52
+ )
53
+ .forEach(fip => {
54
+ fip.Identifiers?.forEach(identifier => {
55
+ if (!identifiersList.find(i => i.type === identifier.type)) {
56
+ const value =
57
+ identifier.type === 'PAN'
58
+ ? decodedInfo?.pan
59
+ : identifier.type === 'AADHAAR'
60
+ ? decodedInfo?.aadhaar
61
+ : identifier.type === 'MOBILE'
62
+ ? selectedMobileNumber
63
+ : undefined;
64
+ identifiersList.push({ ...identifier, value });
65
+ }
66
+ });
67
+ });
68
+
69
+ setIdentifiers(identifiersList);
70
+ if (identifiersList?.length > 0 && Boolean(identifiersList.find(val => !val.value)?.type)) {
71
+ if (identifiersList.find(i => i.type === 'PAN')) {
72
+ redirect = true;
73
+ }
74
+ if (identifiersList.find(i => i.type === 'AADHAAR')) {
75
+ redirect = true;
76
+ }
77
+ }
78
+
79
+ if (redirect) {
80
+ navigate(`/link-accounts/discover-account`, {
81
+ state: {
82
+ category: activeCategory,
83
+ selectedFips: selectedFips[activeCategory]
84
+ }
85
+ });
86
+ return null;
87
+ }
88
+ };
89
+
90
+ export default handleIdentifiers;
@@ -0,0 +1,67 @@
1
+ import posthog from 'posthog-js';
2
+ import { EXTERNAL_URLS } from '@/config/urls';
3
+ import { useRedirectStore } from '@/store/redirect.store';
4
+
5
+ export const trackEvent = (eventName: string, properties?: Record<string, unknown>) => {
6
+ if (import.meta.env.VITE_MODE.toLowerCase() !== 'production') {
7
+ return;
8
+ }
9
+
10
+ // Get decoded info from the redirect store
11
+ const { decodedInfo } = useRedirectStore.getState();
12
+
13
+ // Prepare default properties from redirect store
14
+ const defaultProperties: Record<string, unknown> = {};
15
+
16
+ if (decodedInfo) {
17
+ defaultProperties.fiuId = decodedInfo.fiuId;
18
+ defaultProperties.fiuName = decodedInfo.fiuName;
19
+ defaultProperties.fipId = decodedInfo.fipId;
20
+ defaultProperties.platform = decodedInfo.platform;
21
+ defaultProperties.isNewUser = !decodedInfo.isUserPresent;
22
+ }
23
+
24
+ // Merge default properties with provided properties
25
+ const combinedProperties = { ...defaultProperties, ...properties };
26
+
27
+ const prefixedEventName = `AA_${eventName}`;
28
+ posthog.capture(prefixedEventName, combinedProperties);
29
+ };
30
+
31
+ // Event names as constants for type safety and reusability
32
+ export const EVENTS = {
33
+ INIT_OTP: 'INIT_OTP',
34
+ RESEND_OTP: 'RESEND_OTP',
35
+ SUBMIT_OTP: 'SUBMIT_OTP',
36
+ ADD_NEW_MOBILE: 'ADD_NEW_MOBILE',
37
+ VERIFY_NEW_MOBILE: 'VERIFY_NEW_MOBILE',
38
+ DISCOVER_ACCOUNT: 'DISCOVER_ACCOUNT',
39
+ DISCOVER_MORE: 'DISCOVER_MORE',
40
+ CHANGE_LANGUAGE: 'CHANGE_LANGUAGE',
41
+ USER_EXIT: 'USER_EXIT',
42
+ VIEW_TERMS: 'VIEW_TERMS',
43
+ SELECT_ALL_ACCOUNTS: 'SELECT_ALL_ACCOUNTS',
44
+ SKIP_LINKING: 'SKIP_LINKING',
45
+ LINK_ACCOUNT: 'LINK_ACCOUNT',
46
+ RETRY_FAILED_ACCOUNTS: 'RETRY_FAILED_ACCOUNTS',
47
+ EDIT_ACCOUNT: 'EDIT_ACCOUNT',
48
+ ADD_ACCOUNT: 'ADD_ACCOUNT',
49
+ APPROVE_CONSENT: 'APPROVE_CONSENT',
50
+ REJECT_CONSENT: 'REJECT_CONSENT',
51
+ VIEW_MORE_CONSENT: 'VIEW_MORE_CONSENT',
52
+ SUBMIT_RATING: 'SUBMIT_RATING',
53
+ SUBMIT_FEEDBACK: 'SUBMIT_FEEDBACK',
54
+ NOTIFY_UPDATES: 'NOTIFY_UPDATES',
55
+ SEARCH_BANKS: 'SEARCH_BANKS',
56
+ } as const;
57
+
58
+ // Initialize PostHog with configuration from environment
59
+ export const initPostHog = () => {
60
+ if (EXTERNAL_URLS.POSTHOG_KEY) {
61
+ posthog.init(EXTERNAL_URLS.POSTHOG_KEY, {
62
+ api_host: EXTERNAL_URLS.POSTHOG_HOST || 'https://app.posthog.com',
63
+ disable_session_recording: true,
64
+ capture_pageview: true,
65
+ });
66
+ }
67
+ };
@@ -0,0 +1,61 @@
1
+ import { toast } from 'sonner'
2
+
3
+ export const handleApiError = (
4
+ error: unknown,
5
+ customMessage: string = 'An error occurred'
6
+ ) => {
7
+ // Handle Axios errors
8
+ if (error && typeof error === 'object' && 'response' in error) {
9
+ const errorResponse = error?.response as any
10
+
11
+ // Display server error message if available
12
+ if (errorResponse?.data?.errorCode) {
13
+ toast.error('Error', {
14
+ description: errorResponse.data.errorCode
15
+ })
16
+ return
17
+ }
18
+
19
+ // Handle different status codes
20
+ if (errorResponse?.status === 401) {
21
+ toast.error('Unauthorized. Please log in again.')
22
+ return
23
+ }
24
+
25
+ if (errorResponse?.status === 403) {
26
+ toast.error("You don't have permission to perform this action.")
27
+ return
28
+ }
29
+
30
+ if (errorResponse?.status === 404) {
31
+ toast.error('Resource not found.')
32
+ return
33
+ }
34
+
35
+ if (errorResponse?.status >= 500) {
36
+ toast.error('Server error. Please try again later.!!!')
37
+ return
38
+ }
39
+ }
40
+
41
+ // For standard errors with message property
42
+ if (error && typeof error === 'object' && 'message' in error) {
43
+ toast.error(error.message as string)
44
+ return
45
+ }
46
+
47
+ // Default fallback
48
+ toast.error(customMessage)
49
+ }
50
+
51
+ export const showSuccessToast = (message: string) => {
52
+ toast.success(message)
53
+ }
54
+
55
+ export const showInfoToast = (message: string) => {
56
+ toast.info(message)
57
+ }
58
+
59
+ export const showWarningToast = (message: string) => {
60
+ toast.warning(message)
61
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
Binary file