@eka-care/abha 0.0.6 → 0.0.8

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 (85) hide show
  1. package/{index.html → dist/index.html} +2 -1
  2. package/dist/sdk/abha/css/abha.css +1 -0
  3. package/dist/sdk/abha/js/abha.js +136 -0
  4. package/package.json +1 -1
  5. package/.eslintignore +0 -3
  6. package/.eslintrc +0 -29
  7. package/.prettierrc +0 -7
  8. package/dev-abha-ios-android.zip +0 -0
  9. package/postcss.config.js +0 -6
  10. package/prod-abha-mixpanel.zip +0 -0
  11. package/scripts/build-purged-css.cjs +0 -70
  12. package/src/App.css +0 -0
  13. package/src/App.tsx +0 -32
  14. package/src/api-queries/use-get-profiles-patient.ts +0 -12
  15. package/src/api-queries/use-get-profiles-phr-user.ts +0 -27
  16. package/src/api-queries/use-post-abdm-login-verify-v1.ts +0 -25
  17. package/src/api-queries/use-post-auth-verify-v2.ts +0 -74
  18. package/src/api-queries/use-post-profile-switch.ts +0 -57
  19. package/src/api-queries/use-post-register-mobile-create-phr.ts +0 -39
  20. package/src/api-queries/user-post-abdm-profile-login-phr.ts +0 -25
  21. package/src/assets/react.svg +0 -1
  22. package/src/atoms/button/custom-button.tsx +0 -32
  23. package/src/atoms/button/index.tsx +0 -40
  24. package/src/atoms/button/types.d.ts +0 -31
  25. package/src/atoms/header.tsx +0 -25
  26. package/src/atoms/input-field/index.tsx +0 -62
  27. package/src/atoms/input-field/patient-input-field.tsx +0 -16
  28. package/src/atoms/input-field/types.ts +0 -24
  29. package/src/atoms/pds2-otp-input/index.tsx +0 -35
  30. package/src/atoms/pds2-otp-input/types.d.ts +0 -3
  31. package/src/atoms/single-input-chip/index.tsx +0 -32
  32. package/src/atoms/single-input-chip/types.ts +0 -6
  33. package/src/atoms/spinner.tsx +0 -33
  34. package/src/atoms/text-separator.tsx +0 -11
  35. package/src/index.css +0 -118
  36. package/src/main.tsx +0 -313
  37. package/src/molecules/abha/bottom-sheet/bottom-sheet-wrapper.tsx +0 -40
  38. package/src/molecules/abha/bottom-sheet/index.tsx +0 -66
  39. package/src/molecules/abha/spaced-input-component.tsx +0 -150
  40. package/src/molecules/copyright-year.tsx +0 -16
  41. package/src/molecules/exit-popup/index.tsx +0 -99
  42. package/src/molecules/pds2-otp-component/index.tsx +0 -148
  43. package/src/organisms/abha/abha-header.tsx +0 -25
  44. package/src/organisms/abha/error-bottom-sheet.tsx +0 -27
  45. package/src/organisms/abha/otp-card.tsx +0 -99
  46. package/src/organisms/abha/verification-status.tsx +0 -40
  47. package/src/organisms/choose-language/choose-language.tsx +0 -53
  48. package/src/organisms/choose-language/types.ts +0 -10
  49. package/src/organisms/screen-switcher/screen-switcher.tsx +0 -80
  50. package/src/routes/abha-aadhaar-verification-status-screen.tsx +0 -209
  51. package/src/routes/abha-created-screen.tsx +0 -45
  52. package/src/routes/abha-login-otp-verify-screen.tsx +0 -523
  53. package/src/routes/abha-mobile-linking-status-screen.tsx +0 -267
  54. package/src/routes/abha-otp-and-mobile-screen.tsx +0 -429
  55. package/src/routes/abha-phone-number-verification-screen.tsx +0 -373
  56. package/src/routes/create-abha-address-screen.tsx +0 -928
  57. package/src/routes/create-abha-with-aadhaar-screen.tsx +0 -984
  58. package/src/routes/create-eka-profile-screen.tsx +0 -777
  59. package/src/routes/get-all-profiles-screen.tsx +0 -161
  60. package/src/routes/login-or-create-abha-address-screen.tsx +0 -953
  61. package/src/routes/login-with-abha-screen.tsx +0 -448
  62. package/src/routes/select-abha-from-list-screen.tsx +0 -718
  63. package/src/routes/select-eka-profile-screen.tsx +0 -444
  64. package/src/routes/utils/trackAbhaEvent.ts +0 -41
  65. package/src/stores/auth-abha-store/index.ts +0 -138
  66. package/src/stores/auth-abha-store/types.ts +0 -204
  67. package/src/utils/mock-auth-response.ts +0 -31
  68. package/src/vite-env.d.ts +0 -1
  69. package/tailwind.config.ts +0 -9
  70. package/tsconfig.app.json +0 -26
  71. package/tsconfig.json +0 -25
  72. package/tsconfig.node.json +0 -10
  73. package/tsconfig.node.tsbuildinfo +0 -1
  74. package/tsconfig.tsbuildinfo +0 -1
  75. package/vite.config.d.ts +0 -2
  76. package/vite.config.js +0 -30
  77. package/vite.config.ts +0 -35
  78. /package/{public → dist}/images/adhaar.webp +0 -0
  79. /package/{public → dist}/images/at-the-rate.webp +0 -0
  80. /package/{public → dist}/images/avatar.webp +0 -0
  81. /package/{public → dist}/images/ayushman-bharat.webp +0 -0
  82. /package/{public → dist}/images/circle-checkmark.webp +0 -0
  83. /package/{public → dist}/images/link-abha.webp +0 -0
  84. /package/{public → dist}/images/national-authority.webp +0 -0
  85. /package/{public → dist}/images/three-dots.webp +0 -0
@@ -1,984 +0,0 @@
1
- import {
2
- ArrowLeftRegularIcon,
3
- CheckSquareSolidIcon,
4
- ChevronDownRegularIcon,
5
- CircleInfoSolidIcon,
6
- PhoneSolidIcon,
7
- SquareRegularIcon,
8
- XMarkRegularIcon,
9
- } from '@elixir/icons';
10
-
11
- import {
12
- ABHA_AUTH_FLOW_METHOD,
13
- AUTH_METHOD,
14
- LOADING_STATE,
15
- REGISTRATION_TYPE,
16
- } from '@elixir/types';
17
- import {
18
- postAuthInitV2,
19
- postRegisterAadhaarInit,
20
- postRegisterMobileInit,
21
- postAuthLogoutV2,
22
- } from 'apis';
23
- import { useEffect, useRef, useState } from 'react';
24
- import Pds2Button from '../atoms/button';
25
- import Pds2CustomButton from '../atoms/button/custom-button';
26
- import Pds2Header from '../atoms/header';
27
- import PatientPds2InputField from '../atoms/input-field/patient-input-field';
28
- import Pds2Spinner from '../atoms/spinner';
29
- import TextSeparator from '../atoms/text-separator';
30
- import BottomSheetWrapper from '../molecules/abha/bottom-sheet/bottom-sheet-wrapper';
31
- import SpacedInputComponent from '../molecules/abha/spaced-input-component';
32
- import AbhaErrorBottomSheet from '../organisms/abha/error-bottom-sheet';
33
- import useAuthAbhaStore from '../stores/auth-abha-store';
34
- import { SCREEN_NAMES } from '../stores/auth-abha-store/types';
35
- import { unMount } from '../main';
36
- import { handleSendEvent } from '@elixir/utils';
37
-
38
- type InitMethod = 'aadhaar' | 'mobile';
39
-
40
- const CreateAbhaWithAadhaarScreen = () => {
41
- const [isBottomSheetOpen, setIsBottomSheetOpen] = useState(false);
42
- const setScreen = useAuthAbhaStore((state) => state.setScreen);
43
- const goBackLoginScreen = useAuthAbhaStore((state) => state.goBackLoginScreen);
44
- const setAbhaAuthFlowMethod = useAuthAbhaStore((state) => state.setAbhaAuthFlowMethod);
45
- const initAbhaAppMethod = useAuthAbhaStore((state) => state.initAbhaAppMethod);
46
- const clientId = useAuthAbhaStore((state) => state.clientId);
47
-
48
- useEffect(() => {
49
- handleSendEvent({
50
- eventName: 'page_view',
51
- eventData: {
52
- page_view: 'abha_landing_screen',
53
- platform: clientId,
54
- },
55
- });
56
-
57
- window.curio?.pushToMixpanel?.('page_view', {
58
- platform: clientId,
59
- page_view: 'abha_landing_screen',
60
- });
61
-
62
- if (window.EkaAbha?.changeStatusBarColor) {
63
- window.EkaAbha.changeStatusBarColor('#E4E1FA');
64
- return () => {
65
- if (window.EkaAbha?.changeStatusBarColor) window?.EkaAbha?.changeStatusBarColor('#FFFFFF');
66
- };
67
- }
68
-
69
- // Default create flow will be mobile flow.
70
- setAbhaAuthFlowMethod(ABHA_AUTH_FLOW_METHOD.MOBILE);
71
- }, []);
72
-
73
- const handleBackButtonClick = () => {
74
- window.EkaAbha?.onAbhaClose();
75
- unMount();
76
- };
77
-
78
- return (
79
- <div className="pds2-bg-bg-brand-03 pds2-min-h-screen">
80
- {/* * If clientId is 'eka-web', do not show back button, but preserve space */}
81
- {clientId !== 'eka-web' ? (
82
- <Pds2Header
83
- prefixIcon={
84
- <button
85
- className="pds2-w-24 pds2-bg-white pds2-h-24 pds2-rounded-full pds2-flex pds2-items-center pds2-justify-center ripple"
86
- onClick={handleBackButtonClick}
87
- >
88
- <ArrowLeftRegularIcon />
89
- </button>
90
- }
91
- title=""
92
- className="pds2-bg-white"
93
- />
94
- ) : (
95
- <Pds2Header
96
- prefixIcon={<div className="pds2-w-24 pds2-h-24" aria-hidden="true" />}
97
- title=""
98
- className="pds2-bg-white"
99
- />
100
- )}
101
-
102
- <LoginWithAadhaar openInfoDialog={() => setIsBottomSheetOpen(true)} />
103
- <OtherLoginOptions />
104
-
105
- <BottomSheetWrapper
106
- isOpen={isBottomSheetOpen}
107
- setIsOpen={setIsBottomSheetOpen}
108
- shouldCloseOnClickOutside={true}
109
- >
110
- <BottomSheetContent onClose={() => setIsBottomSheetOpen(false)} />
111
- </BottomSheetWrapper>
112
- </div>
113
- );
114
- };
115
-
116
- export default CreateAbhaWithAadhaarScreen;
117
-
118
- const BottomSheetContent = ({ onClose }: { onClose: () => void }) => {
119
- return (
120
- <div className="pds2-flex pds2-flex-col pds2-p-16 pds2-text-center">
121
- <div className="pds2-flex pds2-justify-end pds2-items-center">
122
- <button
123
- onClick={onClose}
124
- className="pds2-w-24 pds2-h-24 pds2-rounded-full pds2-flex pds2-items-center pds2-justify-center ripple"
125
- >
126
- <XMarkRegularIcon className="pds2-w-18 pds2-h-18 pds2-text-text-03" />
127
- </button>
128
- </div>
129
- <div className="pds2-space-y-12 pds2-flex pds2-flex-col pds2-items-center pds2-justify-center">
130
- <img
131
- src="https://cdn.eka.care/vagus/cm5m4sx4a000c0tg9a5fo1gkz.webp"
132
- alt="national health authority"
133
- className="pds2-h-52"
134
- />
135
- <div className="Heading4Semibold pds2-text-text-black">Terms & Conditions</div>
136
- </div>
137
- <div className="pds2-space-y-8">
138
- <div className="Body2Regular pds2-text-text-03 pds2-py-16">
139
- I, hereby declare that I am voluntarily sharing my identity information with National
140
- Health Authority (NHA) for the sole purpose of creation of ABHA number. I understand that
141
- my ABHA number can be used and shared for purposes as may be notified by ABDM (Ayushman
142
- Bharat Digital Mission) from time to time including provision of healthcare services.
143
- Further, I am aware that my personal identifiable information (Name, Address, Age, Date of
144
- Birth, Gender and Photograph) may be made available to the entities working in the
145
- National Digital Health Ecosystem (NDHE) which inter alia includes stakeholders and
146
- entities such as healthcare professionals (e.g. doctors), facilities
147
- </div>
148
- <Pds2Button
149
- title="Okay, Got it!"
150
- onClick={onClose}
151
- state={'enabled'}
152
- className="pds2-w-full"
153
- />
154
- </div>
155
- </div>
156
- );
157
- };
158
-
159
- const loginOptions = [
160
- {
161
- textSeparatorTitle: 'Other options to create ABHA',
162
- loginCTA: [
163
- {
164
- method: ABHA_AUTH_FLOW_METHOD.AADHAR_NUMBER,
165
- title: 'Use Aadhaar to create ABHA',
166
- prefixIcon: (
167
- <img
168
- src="https://cdn.eka.care/vagus/cm5mcdm3h00070tfs9fjkepff.webp"
169
- alt="Aadhaar Icon"
170
- className="pds2-h-24 pds2-w-24"
171
- loading="lazy"
172
- />
173
- ),
174
- },
175
- {
176
- method: ABHA_AUTH_FLOW_METHOD.MOBILE,
177
- title: 'Use mobile to create ABHA',
178
- prefixIcon: <PhoneSolidIcon className="pds2-w-20 pds2-h-20 pds2-text-icon-success-02" />,
179
- },
180
- ],
181
- },
182
- {
183
- textSeparatorTitle: 'Login with ABHA',
184
- loginCTA: [
185
- {
186
- method: ABHA_AUTH_FLOW_METHOD.ABHA_NUMBER,
187
- title: 'ABHA',
188
- prefixIcon: (
189
- <img
190
- src="https://cdn.eka.care/vagus/cm4ml1lwu00000tfs1okl7hs9.webp"
191
- alt="ABHA Address Icon"
192
- className="pds2-h-24 pds2-w-24"
193
- loading="lazy"
194
- />
195
- ),
196
- },
197
- ],
198
- },
199
- ];
200
-
201
- const OtherLoginOptions = () => {
202
- const setAbhaAuthFlowMethod = useAuthAbhaStore((state) => state.setAbhaAuthFlowMethod);
203
- const setScreen = useAuthAbhaStore((state) => state.setScreen);
204
- const selectedMethod = useAuthAbhaStore((state) => state.abhaAuthFlowMethod);
205
- // android is faster to load the state, so it gets the correct value before rendering.
206
- // iOS is a bit slower, so it shows 'MOBILE' first before the correct state kicks in —
207
- // causing both Aadhaar form and Aadhaar button to show.
208
- const [selectedFlow, setSelectedFlow] = useState<string>(
209
- selectedMethod ?? ABHA_AUTH_FLOW_METHOD.MOBILE
210
- );
211
- const initAbhaAppMethod = useAuthAbhaStore((state) => state.initAbhaAppMethod);
212
- const clientId = useAuthAbhaStore((state) => state.clientId);
213
- const isNewLoginOrCreateFlow = useAuthAbhaStore((state) => state.isNewLoginOrCreateFlow);
214
-
215
- useEffect(() => {
216
- if (selectedMethod && selectedMethod !== selectedFlow) {
217
- setSelectedFlow(selectedMethod);
218
- }
219
- }, [selectedMethod]);
220
-
221
- return (
222
- <div className="pds2-p-16 pds2-space-y-16">
223
- {loginOptions.map((option) => (
224
- <>
225
- <TextSeparator title={option.textSeparatorTitle} />
226
- {option.loginCTA
227
- .filter((CTA) => {
228
- // Login with 'ABHA' button is always displayed(not in web)
229
- if (CTA.title === 'ABHA') return true;
230
-
231
- // Show only Aadhaar when Mobile is selected and vice versa
232
- if (selectedFlow === ABHA_AUTH_FLOW_METHOD.MOBILE)
233
- return CTA.method === ABHA_AUTH_FLOW_METHOD.AADHAR_NUMBER;
234
- if (selectedFlow === ABHA_AUTH_FLOW_METHOD.AADHAR_NUMBER)
235
- return CTA.method === ABHA_AUTH_FLOW_METHOD.MOBILE;
236
-
237
- return true;
238
- })
239
- .map((CTA) => (
240
- <Pds2CustomButton
241
- key={CTA.method}
242
- prefixIcon={CTA.prefixIcon}
243
- title={CTA.title}
244
- suffixIcon={
245
- <ChevronDownRegularIcon
246
- className="pds2-ml-auto pds2-h-16 pds2-w-16 pds2-text-text-04 -pds2-rotate-90"
247
- aria-hidden="true"
248
- />
249
- }
250
- onClick={() => {
251
- window.curio?.pushToMixpanel?.('abha_landing_screen_clicks', {
252
- platform: clientId,
253
- type:
254
- CTA.method === ABHA_AUTH_FLOW_METHOD.AADHAR_NUMBER
255
- ? 'use Aadhaar'
256
- : 'use Mobile',
257
- });
258
- if (CTA.title === 'ABHA') {
259
- if (isNewLoginOrCreateFlow) {
260
- setScreen(SCREEN_NAMES.LOGIN_OR_CREATE_ABHA);
261
- } else {
262
- setScreen(SCREEN_NAMES.LOGIN_WITH_ABHA);
263
- }
264
- return;
265
- }
266
- if (CTA.method === ABHA_AUTH_FLOW_METHOD.AADHAR_NUMBER) {
267
- setSelectedFlow(ABHA_AUTH_FLOW_METHOD.AADHAR_NUMBER);
268
- } else if (CTA.method === ABHA_AUTH_FLOW_METHOD.MOBILE) {
269
- setSelectedFlow(ABHA_AUTH_FLOW_METHOD.MOBILE);
270
- }
271
-
272
- setAbhaAuthFlowMethod(CTA.method);
273
- }}
274
- />
275
- ))}
276
- </>
277
- ))}
278
- </div>
279
- );
280
- };
281
-
282
- const LoginWithAadhaar = ({ openInfoDialog }: { openInfoDialog: () => void }) => {
283
- const setScreen = useAuthAbhaStore((state) => state.setScreen);
284
- const selectedMethod = useAuthAbhaStore((state) => state.abhaAuthFlowMethod);
285
- const setRegistrationType = useAuthAbhaStore((state) => state.setRegistrationType);
286
-
287
- const setAadhaarInitResponse = useAuthAbhaStore((state) => state.setAadhaarInitResponse);
288
- const setMobileInitResponse = useAuthAbhaStore((state) => state.setMobileInitResponse);
289
- const setBottomsheetErrorInfo = useAuthAbhaStore((state) => state.setBottomsheetErrorInfo);
290
- const clearAbhaAuthStore = useAuthAbhaStore((state) => state.clearAbhaAuthStore);
291
- const initAbhaAppMethod = useAuthAbhaStore((state) => state.initAbhaAppMethod);
292
- const initAbhaAppMobileNumber = useAuthAbhaStore((state) => state.initAbhaAppMobileNumber);
293
- const isLoggedInFromRedirect = useAuthAbhaStore((state) => state.isLoggedInFromRedirect);
294
-
295
- const [aadhaarNo, setAadhaarNo] = useState<string>('');
296
- const [mobileNumber, setMobileNumber] = useState('');
297
- const [isValidAadhaar, setIsValidAadhaar] = useState<boolean>(false);
298
- const [isValidMobileNumber, setIsValidMobileNumber] = useState(false);
299
-
300
- const [error, setError] = useState<string | null>(null);
301
-
302
- const [isNhaConsent, setIsNhaConsent] = useState<boolean>(true);
303
- const [isErrorNhaConsent, setIsErrorNhaConsent] = useState<boolean>(false);
304
-
305
- const [initApiStatus, setInitApiStatus] = useState<Record<InitMethod, LOADING_STATE>>({
306
- aadhaar: LOADING_STATE.IDLE,
307
- mobile: LOADING_STATE.IDLE,
308
- });
309
-
310
- const isMobileNonEditable = !!initAbhaAppMobileNumber;
311
- const clientId = useAuthAbhaStore((state) => state.clientId);
312
- const isEkaAppLogin = useAuthAbhaStore((state) => state.isEkaAppLogin);
313
- const isDocAppRequest = useAuthAbhaStore((state) => state.isDocAppRequest);
314
- const setIsApiTriggered = useAuthAbhaStore((state) => state.setIsApiTriggered);
315
- const isApiTriggered = useAuthAbhaStore((state) => state.isApiTriggered);
316
- const setSelectedAbhaFlow = useAuthAbhaStore((state) => state.setSelectedAbhaFlow);
317
- const isNewLoginOrCreateFlow = useAuthAbhaStore((state) => state.isNewLoginOrCreateFlow);
318
-
319
- // 1. First useEffect: Initialize the mobile number (runs only once)
320
- useEffect(() => {
321
- if (initAbhaAppMobileNumber) {
322
- setMobileNumber(initAbhaAppMobileNumber);
323
- setIsValidMobileNumber(initAbhaAppMobileNumber.length === 10);
324
- }
325
- }, [initAbhaAppMobileNumber]);
326
-
327
- // 2. Second useEffect: Trigger handleRegister (fixed to prevent multiple calls)
328
- useEffect(() => {
329
- if (isApiTriggered) {
330
- console.log('Api is already triggered: ', isApiTriggered);
331
- return;
332
- }
333
-
334
- const shouldTriggerAadhaar = aadhaarNo && aadhaarNo.length === 12 && isNhaConsent;
335
- const shouldTriggerMobile = mobileNumber && mobileNumber.length === 10 && isNhaConsent;
336
-
337
- console.log(
338
- 'Conditions check:',
339
- 'isLoggedInFromRedirect:',
340
- isLoggedInFromRedirect,
341
- 'shouldTriggerAadhaar:',
342
- shouldTriggerAadhaar,
343
- 'shouldTriggerMobile:',
344
- shouldTriggerMobile
345
- );
346
-
347
- if (isLoggedInFromRedirect || shouldTriggerAadhaar || shouldTriggerMobile) {
348
- // Set the flag BEFORE any async operations to prevent race conditions
349
- setIsApiTriggered(true);
350
- console.log('Setting hasTriggered to true and proceeding with registration');
351
-
352
- // Track events before API call
353
- let eventName = '';
354
- if (shouldTriggerAadhaar) {
355
- eventName = 'abha_aadhaar_otp_verify_clicks';
356
- } else if (shouldTriggerMobile) {
357
- eventName = 'abha_mobile_otp_verify_clicks';
358
- }
359
-
360
- if (eventName) {
361
- window.curio?.pushToMixpanel?.(eventName, {
362
- platform: clientId,
363
- type: 'next',
364
- });
365
- }
366
-
367
- // Now call the memoized handler
368
- handleRegister();
369
- }
370
- }, [isLoggedInFromRedirect, aadhaarNo, mobileNumber, isNhaConsent, isApiTriggered]);
371
-
372
- // Create a helper function to update status
373
- const updateInitStatus = (method: InitMethod, status: LOADING_STATE) => {
374
- setInitApiStatus((prev) => ({
375
- ...prev,
376
- [method]: status,
377
- }));
378
- };
379
-
380
- // Mobile number handler
381
- const handleMobileNumberChange = (e: React.ChangeEvent<HTMLInputElement>) => {
382
- const value = e.target.value;
383
- if (value.length <= 10) {
384
- setMobileNumber(value);
385
- setIsValidMobileNumber(value.length === 10);
386
- setError(null);
387
- }
388
- };
389
-
390
- const handleOnAadhaarInputComplete = ({ inputVal: newAadhaarNo }: { inputVal: string }) => {
391
- if (error) setError(null);
392
- if (newAadhaarNo.length === 12) {
393
- setAadhaarNo(newAadhaarNo);
394
- }
395
- };
396
-
397
- const handleRegister = async () => {
398
- setError(null);
399
-
400
- const method: InitMethod =
401
- selectedMethod === ABHA_AUTH_FLOW_METHOD.AADHAR_NUMBER ? 'aadhaar' : 'mobile';
402
- updateInitStatus(method, LOADING_STATE.LOADING);
403
-
404
- let authTxnId = '';
405
- // if the trigger is not from the eka app, then get the auth txn id
406
- if (!isEkaAppLogin) {
407
- // Get auth transaction ID first
408
- handleSendEvent({
409
- eventName: 'abha_create_auth_api_trigger',
410
- eventData: {
411
- type: 'api_trigger',
412
- platform: clientId,
413
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
414
- },
415
- });
416
-
417
- window.curio?.pushToMixpanel?.('abha_create_auth_api_trigger', {
418
- platform: clientId,
419
- type: 'api_trigger',
420
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
421
- });
422
- const { txn_id: txnId, error: authInitError } = await postAuthInitV2({
423
- method: AUTH_METHOD.ABHA,
424
- });
425
- if (!txnId) {
426
- handleSendEvent({
427
- eventName: 'abha_create_auth_txnId_not_found',
428
- eventData: {
429
- type: 'api_trigger',
430
- platform: clientId,
431
- transactionId: txnId || '',
432
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
433
- },
434
- });
435
-
436
- window.curio?.pushToMixpanel?.('abha_create_auth_txnId_not_found', {
437
- platform: clientId,
438
- type: 'api_trigger',
439
- transactionId: txnId || '',
440
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
441
- });
442
- setError(authInitError?.message || 'Something went wrong');
443
- updateInitStatus(method, LOADING_STATE.REJECTED);
444
- return;
445
- }
446
- authTxnId = txnId;
447
- handleSendEvent({
448
- eventName: 'abha_create_auth_api_trigger_success',
449
- eventData: {
450
- type: 'api_trigger',
451
- platform: clientId,
452
- authTransactionId: authTxnId,
453
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
454
- },
455
- });
456
-
457
- window.curio?.pushToMixpanel?.('abha_create_auth_api_trigger_success', {
458
- platform: clientId,
459
- type: 'api_trigger',
460
- authTransactionId: authTxnId,
461
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
462
- });
463
- // call the auth logout api
464
- await postAuthLogoutV2();
465
- }
466
-
467
- // depending on aadhaar flow or mobile flow, take the action
468
- // aadhaar flow
469
- if (selectedMethod === ABHA_AUTH_FLOW_METHOD.AADHAR_NUMBER) {
470
- handleSendEvent({
471
- eventName: 'abha_aadhaar_init_api_trigger_initiate',
472
- eventData: {
473
- type: 'api_trigger',
474
- platform: clientId,
475
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
476
- },
477
- });
478
-
479
- window.curio?.pushToMixpanel?.('abha_aadhaar_init_api_trigger_initiate', {
480
- platform: clientId,
481
- type: 'api_trigger',
482
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
483
- });
484
- // set the initial status to loading
485
- updateInitStatus('aadhaar', LOADING_STATE.LOADING);
486
- // check if aadhaar number is valid
487
- if (aadhaarNo.length !== 12) {
488
- setError('Please enter valid 12 digit Aadhaar number');
489
- updateInitStatus('aadhaar', LOADING_STATE.REJECTED);
490
- return;
491
- }
492
- // check if aadhaar consent is given
493
- if (!isNhaConsent) {
494
- setError('Please provide consent to create ABHA');
495
- updateInitStatus('aadhaar', LOADING_STATE.REJECTED);
496
- return;
497
- }
498
- // trigger aadhaar init api
499
- const {
500
- txn_id,
501
- hint,
502
- error: aadhaarError,
503
- action: errorAction,
504
- } = await postRegisterAadhaarInit({
505
- aadhaar_number: aadhaarNo,
506
- });
507
- if (txn_id && hint) {
508
- setAadhaarInitResponse({
509
- authTxnId,
510
- txnId: txn_id,
511
- aadhaarOtpSentToHint: hint,
512
- });
513
- handleSendEvent({
514
- eventName: 'abha_aadhaar_init_api_trigger_success',
515
- eventData: {
516
- type: 'api_trigger',
517
- platform: clientId,
518
- transactionId: txn_id,
519
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
520
- },
521
- });
522
-
523
- window.curio?.pushToMixpanel?.('abha_aadhaar_init_api_trigger_success', {
524
- platform: clientId,
525
- type: 'api_trigger',
526
- transactionId: txn_id,
527
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
528
- });
529
- updateInitStatus('aadhaar', LOADING_STATE.RESOLVED);
530
- setRegistrationType(REGISTRATION_TYPE.CREATE);
531
- setScreen(SCREEN_NAMES.ABHA_OTP_AND_MOBILE);
532
- return;
533
- }
534
-
535
- handleSendEvent({
536
- eventName: 'abha_aadhaar_init_api_trigger_failure',
537
- eventData: {
538
- type: 'api_trigger',
539
- platform: clientId,
540
- transactionId: txn_id || '',
541
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
542
- },
543
- });
544
-
545
- window.curio?.pushToMixpanel?.('abha_aadhaar_init_api_trigger_failure', {
546
- platform: clientId,
547
- type: 'api_trigger',
548
- transactionId: txn_id || '',
549
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
550
- });
551
-
552
- updateInitStatus('aadhaar', LOADING_STATE.REJECTED);
553
- if (errorAction) {
554
- setBottomsheetErrorInfo(errorAction);
555
- } else {
556
- setError(aadhaarError || 'Something went wrong');
557
- }
558
- } else {
559
- // mobile flow
560
- // set initial status to loading
561
- updateInitStatus('mobile', LOADING_STATE.LOADING);
562
- const mobileNo = initAbhaAppMobileNumber || mobileNumber;
563
- // check if mobile number is valid
564
- if (!mobileNo || mobileNo.length !== 10) {
565
- handleSendEvent({
566
- eventName: 'abha_mobile_number_length_error',
567
- eventData: {
568
- type: 'verify',
569
- platform: clientId,
570
- mobileNumber: mobileNo,
571
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
572
- },
573
- });
574
-
575
- window.curio?.pushToMixpanel?.('abha_mobile_number_length_error', {
576
- platform: clientId,
577
- type: 'verify',
578
- mobileNumber: mobileNo,
579
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
580
- });
581
- setError('Please enter valid 10 digit mobile number');
582
- updateInitStatus('mobile', LOADING_STATE.REJECTED);
583
- return false;
584
- }
585
-
586
- handleSendEvent({
587
- eventName: 'abha_mobile_init_api_trigger_initiate',
588
- eventData: {
589
- type: 'api_trigger',
590
- platform: clientId,
591
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
592
- },
593
- });
594
-
595
- window.curio?.pushToMixpanel?.('abha_mobile_init_api_trigger_initiate', {
596
- platform: clientId,
597
- type: 'api_trigger',
598
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
599
- });
600
-
601
- // trigger mobile init api
602
- const {
603
- txn_id,
604
- hint,
605
- error: mobileError,
606
- action: errorAction,
607
- } = await postRegisterMobileInit({
608
- mobile_number: mobileNo,
609
- });
610
- if (txn_id && hint) {
611
- setMobileInitResponse({
612
- authTxnId,
613
- txnId: txn_id,
614
- mobileOtpSentToHint: hint,
615
- });
616
- handleSendEvent({
617
- eventName: 'abha_mobile_init_api_trigger_success',
618
- eventData: {
619
- type: 'api_trigger',
620
- platform: clientId,
621
- transactionId: txn_id,
622
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
623
- authTransactionId: authTxnId,
624
- },
625
- });
626
-
627
- window.curio?.pushToMixpanel?.('abha_mobile_init_api_trigger_success', {
628
- platform: clientId,
629
- type: 'api_trigger',
630
- transactionId: txn_id,
631
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
632
- authTransactionId: authTxnId,
633
- });
634
- setSelectedAbhaFlow('mobile');
635
- updateInitStatus('mobile', LOADING_STATE.RESOLVED);
636
- setRegistrationType(REGISTRATION_TYPE.CREATE);
637
- setSelectedAbhaFlow('mobile');
638
- console.log('update the selected abha flow to mobile');
639
- setScreen(SCREEN_NAMES.ABHA_PHONE_NUMBER_VERIFICATION);
640
- return;
641
- }
642
-
643
- handleSendEvent({
644
- eventName: 'abha_mobile_init_api_trigger_failure',
645
- eventData: {
646
- type: 'api_trigger',
647
- platform: clientId,
648
- transactionId: txn_id || '',
649
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
650
- authTransactionId: authTxnId,
651
- },
652
- });
653
-
654
- window.curio?.pushToMixpanel?.('abha_mobile_init_api_trigger_failure', {
655
- platform: clientId,
656
- type: 'api_trigger',
657
- transactionId: txn_id || '',
658
- isEkaAppLogin: isEkaAppLogin ? 'true' : 'false',
659
- authTransactionId: authTxnId,
660
- });
661
-
662
- updateInitStatus('mobile', LOADING_STATE.REJECTED);
663
- if (errorAction) {
664
- setBottomsheetErrorInfo(errorAction);
665
- } else {
666
- setError(mobileError || 'Something went wrong');
667
- }
668
- }
669
- return;
670
- };
671
-
672
- const handleSubmitClick: React.MouseEventHandler<HTMLButtonElement> = (e) => {
673
- e.stopPropagation();
674
- // Send event for 'Next' click'
675
- console.log('selectedMethod : ', selectedMethod);
676
- const eventName =
677
- selectedMethod === 'aadhaar_number'
678
- ? 'abha_aadhaar_otp_verify_clicks'
679
- : 'abha_mobile_otp_verify_clicks';
680
-
681
- handleSendEvent({
682
- eventName: eventName,
683
- eventData: {
684
- type: 'verify',
685
- platform: clientId,
686
- },
687
- });
688
-
689
- window.curio?.pushToMixpanel?.(eventName, {
690
- platform: clientId,
691
- type: 'next',
692
- });
693
-
694
- if (error) setError(null);
695
- handleRegister();
696
- };
697
-
698
- const handleErrorBottomsheetButtonClick = () => {
699
- const mobileNumber = useAuthAbhaStore.getState().initAbhaAppMobileNumber;
700
- const abhaAppMethod = useAuthAbhaStore.getState().initAbhaAppMethod;
701
- const isAppLogin = useAuthAbhaStore.getState().isEkaAppLogin;
702
- const isTriggered = useAuthAbhaStore.getState().isApiTriggered;
703
- const client = useAuthAbhaStore.getState().clientId;
704
- setScreen(SCREEN_NAMES.CREATE_ABHA_WITH_AADHAAR);
705
- clearAbhaAuthStore();
706
- useAuthAbhaStore.setState({ initAbhaAppMobileNumber: mobileNumber });
707
- useAuthAbhaStore.setState({
708
- screen: [SCREEN_NAMES.LOGIN_WITH_ABHA],
709
- });
710
- useAuthAbhaStore.setState({ isEkaAppLogin: isAppLogin });
711
- useAuthAbhaStore.setState({ initAbhaAppMethod: abhaAppMethod });
712
- useAuthAbhaStore.setState({ isApiTriggered: isTriggered });
713
- useAuthAbhaStore.setState({ clientId: client });
714
- setBottomsheetErrorInfo(null);
715
- };
716
-
717
- return (
718
- <>
719
- <div className="pds2-space-y-16 pds2-p-16 pds2-text-center">
720
- <div className="pds2-flex pds2-justify-center pds2-items-center">
721
- <img
722
- src="https://cdn.eka.care/vagus/cm4ml1lwu00000tfs1okl7hs9.webp"
723
- alt="adaar"
724
- className="pds2-w-64 pds2-h-64 pds2-rounded-16"
725
- />
726
- </div>
727
- <div className="pds2-space-y-16">
728
- <div className="pds2-space-y-4">
729
- <div className="pds2-text-text-black Heading3Semibold">
730
- {isDocAppRequest ? 'Create ABHA' : 'Create your ABHA'}
731
- </div>
732
- <div className="pds2-text-text-03 Body2Regular">
733
- {selectedMethod === ABHA_AUTH_FLOW_METHOD.AADHAR_NUMBER
734
- ? 'Enter 12 digit Aadhaar number'
735
- : 'Enter 10 digit mobile number'}
736
- </div>
737
- </div>
738
- {selectedMethod === ABHA_AUTH_FLOW_METHOD.AADHAR_NUMBER ? (
739
- <SpacedInputComponent
740
- numberOfInputs={3}
741
- length={4}
742
- placeholder="XXXX"
743
- onSubmit={handleOnAadhaarInputComplete}
744
- setIsValid={setIsValidAadhaar}
745
- error={error}
746
- />
747
- ) : (
748
- <div className="pds2-flex pds2-flex-col pds2-justify-center pds2-items-center pds2-gap-y-16 pds2-px-09">
749
- <PatientPds2InputField
750
- value={mobileNumber}
751
- onChange={handleMobileNumberChange}
752
- disabled={isMobileNonEditable}
753
- type="tel"
754
- placeholder="Mobile Number"
755
- PrefixComponent={
756
- <div className="pds2-flex pds2-items-center pds2-gap-x-8 pds2-border-r pds2-border-border-brand-02 pds2-pr-24">
757
- <img
758
- src="https://cdn.eka.care/vagus/cm4mlct2900000tg98p8u967s.webp"
759
- alt="India Flag"
760
- className="pds2-w-24 pds2-h-24"
761
- />
762
- <span className="pds2-text-text-black">+91</span>
763
- </div>
764
- }
765
- />
766
- </div>
767
- )}
768
- </div>
769
- <CustomCheckBox
770
- id="aadhaar-number"
771
- label="I give consent to NHA to share my identity info"
772
- openInfoDialog={openInfoDialog}
773
- className="pds2-justify-between"
774
- isCheckboxChecked={isNhaConsent}
775
- onCheckboxChange={(checkedStatus) => {
776
- if (error) setError(null);
777
- setIsErrorNhaConsent(false);
778
- setIsNhaConsent(checkedStatus);
779
- }}
780
- isErrored={isErrorNhaConsent}
781
- />
782
- <div
783
- onClick={() => {
784
- if (!isNhaConsent) {
785
- setIsErrorNhaConsent(true);
786
- }
787
- }}
788
- >
789
- <Pds2Button
790
- title={
791
- initApiStatus[
792
- selectedMethod === ABHA_AUTH_FLOW_METHOD.AADHAR_NUMBER ? 'aadhaar' : 'mobile'
793
- ] === LOADING_STATE.LOADING
794
- ? 'Verifying'
795
- : 'Next'
796
- }
797
- onClick={handleSubmitClick}
798
- IconComponent={
799
- initApiStatus[
800
- selectedMethod === ABHA_AUTH_FLOW_METHOD.AADHAR_NUMBER ? 'aadhaar' : 'mobile'
801
- ] === LOADING_STATE.LOADING
802
- ? () => <Pds2Spinner className="pds2-ml-8" size={16} />
803
- : undefined
804
- }
805
- state={
806
- selectedMethod === ABHA_AUTH_FLOW_METHOD.AADHAR_NUMBER
807
- ? isValidAadhaar && isNhaConsent
808
- ? 'enabled'
809
- : 'disabled'
810
- : isValidMobileNumber && isNhaConsent
811
- ? 'enabled'
812
- : 'disabled'
813
- }
814
- className="pds2-w-full"
815
- />
816
- </div>
817
- {error && <div className="pds2-text-text-error Body3Semibold">{error}</div>}
818
- </div>
819
-
820
- <AbhaErrorBottomSheet onSubmitClick={handleErrorBottomsheetButtonClick} />
821
- </>
822
- );
823
- };
824
-
825
- export const CustomCheckBox = ({
826
- id,
827
- label,
828
- onCheckboxChange,
829
- openInfoDialog,
830
- className = '',
831
- isCheckboxChecked = false,
832
- isErrored = false,
833
- }: {
834
- id: string;
835
- label: string;
836
- onCheckboxChange: (isChecked: boolean) => void;
837
- openInfoDialog?: () => void;
838
- className?: string;
839
- isCheckboxChecked?: boolean;
840
- isErrored?: boolean;
841
- }) => {
842
- return (
843
- <div
844
- className={`pds2-flex pds2-items-center pds2-space-x-8 ${className} ${isErrored ? 'pds2-text-text-error' : 'pds2-text-text-03'}`}
845
- >
846
- <label htmlFor={id} className="Body2Regular pds2-flex pds2-items-center pds2-space-x-8">
847
- <input
848
- type="checkbox"
849
- checked={isCheckboxChecked}
850
- onChange={(e) => onCheckboxChange(e.target.checked)}
851
- id={id}
852
- className="pds2-hidden"
853
- />
854
- {isCheckboxChecked ? (
855
- <CheckSquareSolidIcon className="pds2-w-14 pds2-h-14 pds2-text-icon-brand" />
856
- ) : (
857
- <SquareRegularIcon className="pds2-w-14 pds2-h-14" />
858
- )}
859
-
860
- <span>{label}</span>
861
- </label>
862
- {openInfoDialog && (
863
- <button
864
- onClick={openInfoDialog}
865
- className="pds2-w-24 pds2-h-24 pds2-rounded-full pds2-flex pds2-items-center pds2-justify-center ripple"
866
- >
867
- <CircleInfoSolidIcon className="pds2-w-14 pds2-h-14 pds2-text-text-04" />
868
- </button>
869
- )}
870
- </div>
871
- );
872
- };
873
-
874
- export const OTPInput = ({
875
- numberOfInputs,
876
- length = 1,
877
- placeholder = '',
878
- setIsValid,
879
- onSubmit,
880
- }: {
881
- numberOfInputs: number;
882
- length?: number;
883
- placeholder?: string;
884
- setIsValid?: (val: boolean) => void;
885
- onSubmit: ({ inputVal }: { inputVal: string }) => void;
886
- }) => {
887
- const inputRefs = useRef<HTMLInputElement[]>([]);
888
- const [inputValues, setInputValues] = useState<string[]>(Array(numberOfInputs).fill(''));
889
- const handleInput = (e: React.FormEvent<HTMLInputElement>, index: number) => {
890
- const value = (e.target as HTMLInputElement).value;
891
-
892
- const isValueNan = isNaN(Number(value));
893
- if (isValueNan) return;
894
-
895
- // Ensure total length doesn't exceed 12 digits
896
- const currentValues = [...inputValues];
897
- currentValues[index] = value;
898
- const totalLength = currentValues.join('').length;
899
- if (totalLength > numberOfInputs * length) return;
900
-
901
- // Update current input value
902
- setInputValues(currentValues);
903
-
904
- if (totalLength === numberOfInputs * length) {
905
- setIsValid?.(true);
906
- onSubmit({ inputVal: currentValues.join('') });
907
- } else {
908
- setIsValid?.(false);
909
- }
910
-
911
- // Move to next input if current is full
912
- if (value.length === length && index < numberOfInputs - 1) {
913
- inputRefs.current[index + 1]?.focus();
914
- }
915
- };
916
-
917
- const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, index: number) => {
918
- if (e.key === 'Backspace' && !inputValues[index] && index > 0) {
919
- inputRefs.current[index - 1]?.focus();
920
- setInputValues((prev) => {
921
- const newValues = [...prev];
922
- newValues[index - 1] = '';
923
- return newValues;
924
- });
925
- }
926
- };
927
-
928
- const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>, index: number) => {
929
- e.preventDefault();
930
- const pastedData = e.clipboardData.getData('text').replace(/\D/g, ''); // Only keep digits
931
-
932
- if (!pastedData) return;
933
-
934
- const maxLength = numberOfInputs * length;
935
- const validPastedData = pastedData.slice(0, maxLength);
936
-
937
- // Split pasted data into chunks based on input length
938
- const chunks = validPastedData.match(new RegExp(`.{1,${length}}`, 'g')) || [];
939
-
940
- const newValues = [...inputValues];
941
- chunks.forEach((chunk, i) => {
942
- if (index + i < numberOfInputs) {
943
- newValues[index + i] = chunk;
944
- }
945
- });
946
-
947
- setInputValues(newValues);
948
-
949
- // Focus the next empty input or the last input
950
- const nextEmptyIndex = newValues.findIndex((val, i) => i >= index && !val);
951
- if (nextEmptyIndex !== -1) {
952
- inputRefs.current[nextEmptyIndex]?.focus();
953
- } else {
954
- inputRefs.current[numberOfInputs - 1]?.focus();
955
- }
956
- };
957
- return (
958
- <div className="pds2-flex pds2-justify-center pds2-items-center pds2-space-x-8">
959
- {Array.from({ length: numberOfInputs }).map((_, index) => (
960
- <label
961
- key={index}
962
- className="focus-within:pds2-border-border-brand-01 focus-within:pds2-border pds2-border-border-03 pds2-rounded-16"
963
- >
964
- <input
965
- placeholder={placeholder}
966
- value={inputValues[index]}
967
- key={index}
968
- type="text"
969
- inputMode="numeric"
970
- pattern="[0-9]*"
971
- maxLength={length}
972
- onPaste={(e) => handlePaste(e, index)}
973
- ref={(el) => {
974
- if (el) inputRefs.current[index] = el;
975
- }}
976
- onInput={(e) => handleInput(e, index)}
977
- onKeyDown={(e) => handleKeyDown(e, index)}
978
- className={`pds2-w-full pds2-text-center pds2-rounded-16 pds2-p-16 pds2-outline-none`}
979
- />
980
- </label>
981
- ))}
982
- </div>
983
- );
984
- };