@truworth/twc-auth 1.1.0 → 1.2.1

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 (202) hide show
  1. package/README.md +239 -306
  2. package/build/assets/animation/redirect-home.json +1101 -0
  3. package/build/assets/cross_icon copy.svg +5 -0
  4. package/build/assets/cross_icon.svg +5 -0
  5. package/build/assets/gender-diversity.svg +22 -0
  6. package/build/assets/logo.svg +55 -0
  7. package/build/assets/okay_icon copy.svg +3 -0
  8. package/build/assets/okay_icon.svg +3 -0
  9. package/build/src/api/axiosClient/index.js +5 -0
  10. package/build/src/api/axiosClient/index.native.js +5 -0
  11. package/build/src/components/AdvancedTransitionWrapper/index.js +380 -0
  12. package/build/src/components/AdvancedTransitionWrapper/index.native.js +10 -0
  13. package/build/src/components/AdvancedTransitionWrapper/types.js +1 -0
  14. package/build/src/components/ConfirmationModal/index.js +11 -0
  15. package/build/src/components/ConfirmationModal/index.native.js +15 -0
  16. package/build/src/components/ConfirmationModal/types.js +1 -0
  17. package/build/src/components/EmailOTPVerify/index.js +20 -0
  18. package/build/src/components/EmailOTPVerify/index.native.js +26 -0
  19. package/build/src/components/EmailOTPVerify/type.js +1 -0
  20. package/build/src/components/IonIcon/index.js +11 -0
  21. package/build/src/components/IonIcon/index.native.js +9 -0
  22. package/build/src/components/IonIcon/types.js +1 -0
  23. package/build/src/components/OTPStatusLabel/index.js +10 -0
  24. package/build/src/components/OTPStatusLabel/index.native.js +21 -0
  25. package/build/src/components/OTPStatusLabel/types.js +1 -0
  26. package/build/src/components/PasswordCriteria/hooks/usePasswordCriteria.js +78 -0
  27. package/build/src/components/PasswordCriteria/index.js +47 -0
  28. package/build/src/components/PasswordCriteria/index.native.js +19 -0
  29. package/build/src/components/PasswordCriteria/types.js +1 -0
  30. package/build/src/components/ScreenLayout/index.js +12 -0
  31. package/build/src/components/ScreenLayout/index.native.js +18 -0
  32. package/build/src/components/ScreenLayout/types.js +1 -0
  33. package/build/src/components/SupportDetails/index.js +9 -0
  34. package/build/src/components/SupportDetails/index.native.js +6 -0
  35. package/build/src/components/VerifyOTP/index.js +23 -0
  36. package/build/src/components/VerifyOTP/index.native.js +38 -0
  37. package/build/src/components/VerifyOTP/types.js +1 -0
  38. package/build/src/constants/cdn-url/index.js +1 -0
  39. package/build/src/constants/cdn-url/index.native.js +5 -0
  40. package/build/src/contexts/AuthContext.js +184 -0
  41. package/build/src/contexts/type.js +1 -0
  42. package/build/src/helpers/Network.js +93 -0
  43. package/build/src/helpers/Validation.js +12 -0
  44. package/build/src/helpers/show-message/index.js +19 -0
  45. package/build/src/helpers/show-message/index.native.js +14 -0
  46. package/build/src/helpers/show-message/types.js +1 -0
  47. package/build/src/helpers/types.js +1 -0
  48. package/build/src/hooks/internal/useAuthPackageContext.js +9 -0
  49. package/build/src/hooks/internal/useTimer.js +40 -0
  50. package/build/src/hooks/useAuthContext.js +10 -0
  51. package/build/src/hooks/useRequest.js +38 -0
  52. package/build/src/index.js +12 -0
  53. package/build/src/navigator/index.js +10 -0
  54. package/build/src/navigator/index.native.js +16 -0
  55. package/build/src/screens/CountryCode/components/CountryCodeDropdown/index.js +38 -0
  56. package/build/src/screens/CountryCode/components/CountryCodeDropdown/index.native.js +6 -0
  57. package/build/src/screens/CountryCode/components/CountryCodeDropdown/types.js +1 -0
  58. package/build/src/screens/CountryCode/hooks/internal/useCountryCode.js +38 -0
  59. package/build/src/screens/CountryCode/index.js +10 -0
  60. package/build/src/screens/CountryCode/index.native.js +37 -0
  61. package/build/src/screens/CountryCode/type.js +1 -0
  62. package/build/src/screens/CreatePassword/hooks/internal/useCreatePassword.js +52 -0
  63. package/build/src/screens/CreatePassword/index.js +43 -0
  64. package/build/src/screens/CreatePassword/index.native.js +32 -0
  65. package/build/src/screens/CreatePassword/type.js +1 -0
  66. package/build/src/screens/EnterEmail/hooks/internal/useEnterEmail.js +86 -0
  67. package/build/src/screens/EnterEmail/index.js +30 -0
  68. package/build/src/screens/EnterEmail/index.native.js +34 -0
  69. package/build/src/screens/EnterPassword/hooks/internal/useEnterPassword.js +47 -0
  70. package/build/src/screens/EnterPassword/index.js +22 -0
  71. package/build/src/screens/EnterPassword/index.native.js +23 -0
  72. package/build/src/screens/EnterPassword/types.js +1 -0
  73. package/build/src/screens/Login/components/LoginWebComponent/index.js +44 -0
  74. package/build/src/screens/Login/components/LoginWebComponent/index.native.js +6 -0
  75. package/build/src/screens/Login/index.js +10 -0
  76. package/build/src/screens/Login/index.native.js +6 -0
  77. package/build/src/screens/SignUp/components/SignUpForm/index.js +181 -0
  78. package/build/src/screens/SignUp/components/SignUpForm/index.native.js +6 -0
  79. package/build/src/screens/SignUp/components/SignUpForm/type.js +1 -0
  80. package/build/src/screens/SignUp/components/SignUpWebComponent/index.js +82 -0
  81. package/build/src/screens/SignUp/components/SignUpWebComponent/index.native.js +6 -0
  82. package/build/src/screens/SignUp/components/SignUpWebComponent/types.js +1 -0
  83. package/build/src/screens/SignUp/hooks/internal/useSignUp.js +138 -0
  84. package/build/src/screens/SignUp/index.js +6 -0
  85. package/build/src/screens/SignUp/index.native.js +185 -0
  86. package/build/src/screens/SignUp/types.js +1 -0
  87. package/build/src/screens/UserConsent/hooks/internal/useConsent.js +48 -0
  88. package/build/src/screens/UserConsent/index.js +69 -0
  89. package/build/src/screens/UserConsent/index.native.js +46 -0
  90. package/build/src/screens/UserConsent/types.js +1 -0
  91. package/build/src/screens/VerifyEmail/hooks/internal/useVerifyEmail.js +69 -0
  92. package/build/src/screens/VerifyEmail/index.js +8 -0
  93. package/build/src/screens/VerifyEmail/index.native.js +14 -0
  94. package/build/src/screens/VerifyEmail/types.js +1 -0
  95. package/build/src/screens/VerifyMobile/hooks/internal/useVerifyMobile.js +73 -0
  96. package/build/src/screens/VerifyMobile/index.js +8 -0
  97. package/build/src/screens/VerifyMobile/index.native.js +9 -0
  98. package/build/src/screens/VerifyMobile/types.js +1 -0
  99. package/build/src/screens/Welcome/hooks/internal/useWelcome.js +21 -0
  100. package/build/src/screens/Welcome/index.js +28 -0
  101. package/build/src/screens/Welcome/index.native.js +41 -0
  102. package/build/src/types/types.js +1 -0
  103. package/build/types/api/axiosClient/index.d.ts +1 -0
  104. package/build/types/api/axiosClient/index.native.d.ts +1 -0
  105. package/build/types/components/AdvancedTransitionWrapper/index.d.ts +3 -0
  106. package/build/types/components/AdvancedTransitionWrapper/index.native.d.ts +5 -0
  107. package/build/types/components/AdvancedTransitionWrapper/types.d.ts +25 -0
  108. package/build/types/components/ConfirmationModal/index.d.ts +3 -0
  109. package/build/types/components/ConfirmationModal/index.native.d.ts +3 -0
  110. package/build/types/components/ConfirmationModal/types.d.ts +13 -0
  111. package/build/types/components/EmailOTPVerify/index.d.ts +3 -0
  112. package/build/types/components/EmailOTPVerify/index.native.d.ts +3 -0
  113. package/build/types/components/EmailOTPVerify/type.d.ts +9 -0
  114. package/build/types/components/IonIcon/index.d.ts +3 -0
  115. package/build/types/components/IonIcon/index.native.d.ts +4 -0
  116. package/build/types/components/IonIcon/types.d.ts +6 -0
  117. package/build/types/components/OTPStatusLabel/index.d.ts +2 -0
  118. package/build/types/components/OTPStatusLabel/index.native.d.ts +3 -0
  119. package/build/types/components/OTPStatusLabel/types.d.ts +6 -0
  120. package/build/types/components/PasswordCriteria/hooks/usePasswordCriteria.d.ts +13 -0
  121. package/build/types/components/PasswordCriteria/index.d.ts +3 -0
  122. package/build/types/components/PasswordCriteria/index.native.d.ts +3 -0
  123. package/build/types/components/PasswordCriteria/types.d.ts +15 -0
  124. package/build/types/components/ScreenLayout/index.d.ts +3 -0
  125. package/build/types/components/ScreenLayout/index.native.d.ts +3 -0
  126. package/build/types/components/ScreenLayout/types.d.ts +12 -0
  127. package/build/types/components/SupportDetails/index.d.ts +2 -0
  128. package/build/types/components/SupportDetails/index.native.d.ts +2 -0
  129. package/build/types/components/VerifyOTP/index.d.ts +3 -0
  130. package/build/types/components/VerifyOTP/index.native.d.ts +3 -0
  131. package/build/types/components/VerifyOTP/types.d.ts +10 -0
  132. package/build/types/constants/cdn-url/index.d.ts +1 -0
  133. package/build/types/constants/cdn-url/index.native.d.ts +3 -0
  134. package/build/types/contexts/AuthContext.d.ts +36 -0
  135. package/build/types/contexts/type.d.ts +58 -0
  136. package/build/types/helpers/Network.d.ts +5 -0
  137. package/build/types/helpers/Validation.d.ts +2 -0
  138. package/build/types/helpers/show-message/index.d.ts +3 -0
  139. package/build/types/helpers/show-message/index.native.d.ts +3 -0
  140. package/build/types/helpers/show-message/types.d.ts +2 -0
  141. package/build/types/helpers/types.d.ts +13 -0
  142. package/build/types/hooks/internal/useAuthPackageContext.d.ts +1 -0
  143. package/build/types/hooks/internal/useTimer.d.ts +3 -0
  144. package/build/types/hooks/useAuthContext.d.ts +2 -0
  145. package/build/types/hooks/useRequest.d.ts +5 -0
  146. package/build/types/index.d.ts +8 -0
  147. package/build/types/navigator/index.d.ts +2 -0
  148. package/build/types/navigator/index.native.d.ts +36 -0
  149. package/build/types/screens/CountryCode/components/CountryCodeDropdown/index.d.ts +3 -0
  150. package/build/types/screens/CountryCode/components/CountryCodeDropdown/index.native.d.ts +2 -0
  151. package/build/types/screens/CountryCode/components/CountryCodeDropdown/types.d.ts +17 -0
  152. package/build/types/screens/CountryCode/hooks/internal/useCountryCode.d.ts +12 -0
  153. package/build/types/screens/CountryCode/index.d.ts +2 -0
  154. package/build/types/screens/CountryCode/index.native.d.ts +2 -0
  155. package/build/types/screens/CountryCode/type.d.ts +11 -0
  156. package/build/types/screens/CreatePassword/hooks/internal/useCreatePassword.d.ts +26 -0
  157. package/build/types/screens/CreatePassword/index.d.ts +3 -0
  158. package/build/types/screens/CreatePassword/index.native.d.ts +4 -0
  159. package/build/types/screens/CreatePassword/type.d.ts +43 -0
  160. package/build/types/screens/EnterEmail/hooks/internal/useEnterEmail.d.ts +24 -0
  161. package/build/types/screens/EnterEmail/index.d.ts +7 -0
  162. package/build/types/screens/EnterEmail/index.native.d.ts +4 -0
  163. package/build/types/screens/EnterPassword/hooks/internal/useEnterPassword.d.ts +20 -0
  164. package/build/types/screens/EnterPassword/index.d.ts +3 -0
  165. package/build/types/screens/EnterPassword/index.native.d.ts +4 -0
  166. package/build/types/screens/EnterPassword/types.d.ts +5 -0
  167. package/build/types/screens/Login/components/LoginWebComponent/index.d.ts +2 -0
  168. package/build/types/screens/Login/components/LoginWebComponent/index.native.d.ts +2 -0
  169. package/build/types/screens/Login/index.d.ts +2 -0
  170. package/build/types/screens/Login/index.native.d.ts +2 -0
  171. package/build/types/screens/SignUp/components/SignUpForm/index.d.ts +3 -0
  172. package/build/types/screens/SignUp/components/SignUpForm/index.native.d.ts +2 -0
  173. package/build/types/screens/SignUp/components/SignUpForm/type.d.ts +9 -0
  174. package/build/types/screens/SignUp/components/SignUpWebComponent/index.d.ts +2 -0
  175. package/build/types/screens/SignUp/components/SignUpWebComponent/index.native.d.ts +2 -0
  176. package/build/types/screens/SignUp/components/SignUpWebComponent/types.d.ts +24 -0
  177. package/build/types/screens/SignUp/hooks/internal/useSignUp.d.ts +50 -0
  178. package/build/types/screens/SignUp/index.d.ts +2 -0
  179. package/build/types/screens/SignUp/index.native.d.ts +4 -0
  180. package/build/types/screens/SignUp/types.d.ts +18 -0
  181. package/build/types/screens/UserConsent/hooks/internal/useConsent.d.ts +10 -0
  182. package/build/types/screens/UserConsent/index.d.ts +3 -0
  183. package/build/types/screens/UserConsent/index.native.d.ts +4 -0
  184. package/build/types/screens/UserConsent/types.d.ts +27 -0
  185. package/build/types/screens/VerifyEmail/hooks/internal/useVerifyEmail.d.ts +19 -0
  186. package/build/types/screens/VerifyEmail/index.d.ts +3 -0
  187. package/build/types/screens/VerifyEmail/index.native.d.ts +4 -0
  188. package/build/types/screens/VerifyEmail/types.d.ts +8 -0
  189. package/build/types/screens/VerifyMobile/hooks/internal/useVerifyMobile.d.ts +14 -0
  190. package/build/types/screens/VerifyMobile/index.d.ts +3 -0
  191. package/build/types/screens/VerifyMobile/index.native.d.ts +4 -0
  192. package/build/types/screens/VerifyMobile/types.d.ts +5 -0
  193. package/build/types/screens/Welcome/hooks/internal/useWelcome.d.ts +14 -0
  194. package/build/types/screens/Welcome/index.d.ts +4 -0
  195. package/build/types/screens/Welcome/index.native.d.ts +4 -0
  196. package/build/types/types/types.d.ts +90 -0
  197. package/get-metro-config.js +40 -0
  198. package/get-next-config.js +26 -0
  199. package/package.json +86 -38
  200. package/dist/index.esm.js +0 -2028
  201. package/dist/index.umd.js +0 -70
  202. package/dist/vite.svg +0 -1
@@ -0,0 +1,93 @@
1
+ import { axiosClient } from '../api/axiosClient';
2
+ import { showMessage } from '../helpers/show-message';
3
+ import {} from 'axios';
4
+ export const createHttpClient = ({ token, onRefreshSession, onLogout }) => {
5
+ let currentToken = token;
6
+ const request = ({ url, method, data, ...axiosOptions }) => {
7
+ const upper = method?.toUpperCase();
8
+ const payload = {
9
+ method: upper,
10
+ url,
11
+ data,
12
+ headers: {
13
+ ...(axiosOptions.headers ?? {}),
14
+ ...(currentToken ? { 'x-access-token': currentToken } : {}),
15
+ },
16
+ ...axiosOptions,
17
+ };
18
+ return new Promise((resolve, reject) => {
19
+ axiosClient(payload)
20
+ .then((res) => resolve(res))
21
+ .catch(async (err) => {
22
+ console.log(err?.response?.data);
23
+ // TokenExpiredError → refresh & retry
24
+ if (isAxios401(err, 'TokenExpiredError') && token) {
25
+ try {
26
+ const newToken = await refreshToken();
27
+ if (newToken) {
28
+ currentToken = newToken;
29
+ await onRefreshSession?.(newToken);
30
+ const retryResponse = await axiosClient({
31
+ ...payload,
32
+ headers: {
33
+ ...(payload.headers ?? {}),
34
+ 'x-access-token': newToken,
35
+ },
36
+ });
37
+ resolve(retryResponse);
38
+ }
39
+ }
40
+ catch (refreshErr) {
41
+ if (isAxios401(refreshErr, 'InvalidSessionError')) {
42
+ await handleInvalidSession();
43
+ resolve(null);
44
+ return;
45
+ }
46
+ reject(refreshErr);
47
+ }
48
+ }
49
+ // InvalidSessionError → logout
50
+ if (isAxios401(err, 'InvalidSessionError')) {
51
+ await handleInvalidSession();
52
+ resolve(null);
53
+ return;
54
+ }
55
+ reject(err);
56
+ });
57
+ });
58
+ };
59
+ let refreshPromise = null;
60
+ const refreshToken = async () => {
61
+ if (refreshPromise)
62
+ return refreshPromise;
63
+ if (!currentToken)
64
+ return undefined;
65
+ const tokenRequest = {
66
+ method: 'GET',
67
+ url: '/auth/refresh-token',
68
+ headers: {
69
+ 'x-access-token': currentToken,
70
+ },
71
+ };
72
+ refreshPromise = axiosClient(tokenRequest)
73
+ .then(res => res.data?.token)
74
+ .finally(() => { refreshPromise = null; });
75
+ return refreshPromise;
76
+ };
77
+ const handleInvalidSession = async () => {
78
+ try {
79
+ await onLogout?.();
80
+ showMessage('Your session has expired. Please login again.');
81
+ }
82
+ catch (err) {
83
+ console.error('Error logging out:', err);
84
+ }
85
+ };
86
+ return { request };
87
+ };
88
+ const isAxios401 = (err, name) => {
89
+ const e = err;
90
+ const status = e?.response?.status;
91
+ const n = e?.response?.data?.errors?.[0]?.name;
92
+ return status === 401 && (!name || n === name);
93
+ };
@@ -0,0 +1,12 @@
1
+ export const validatePhoneNo = (text) => {
2
+ // Normalize common formatting then enforce E.164 constraints
3
+ const normalized = text.replace(/[\s()-]/g, '');
4
+ if (!/^\+?\d+$/.test(normalized))
5
+ return false; // digits (optional leading +)
6
+ return /^\+?[1-9]\d{6,14}$/.test(normalized); // 7–15 digits, no leading zero country code
7
+ };
8
+ export const validateEmojiRegex = (text) => {
9
+ const regexpEmoji = /[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}]/ug;
10
+ text = text.replace(regexpEmoji, '');
11
+ return text;
12
+ };
@@ -0,0 +1,19 @@
1
+ import { showNotification } from "@truworth/twc-web-design";
2
+ const showMessage = (message) => {
3
+ if (message == null)
4
+ return;
5
+ const text = typeof message === 'string'
6
+ ? message
7
+ : message instanceof Error
8
+ ? message.message
9
+ : (typeof message === 'object' && 'message' in message)
10
+ ? String(message.message)
11
+ : String(message);
12
+ showNotification({
13
+ type: 'error',
14
+ title: text,
15
+ showIcon: false,
16
+ duration: 3000,
17
+ });
18
+ };
19
+ export { showMessage };
@@ -0,0 +1,14 @@
1
+ import { Snackbar } from "@truworth/twc-rn-common";
2
+ const showMessage = (message, leftIcon = 'warning') => {
3
+ if (message == null)
4
+ return;
5
+ const text = typeof message === 'string'
6
+ ? message
7
+ : message instanceof Error
8
+ ? message.message
9
+ : (typeof message === 'object' && 'message' in message)
10
+ ? String(message.message)
11
+ : String(message);
12
+ Snackbar.show({ text, leftIcon });
13
+ };
14
+ export { showMessage };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import { AuthPackageContext } from "../../contexts/AuthContext";
3
+ export const useAuthPackageContext = () => {
4
+ const context = React.useContext(AuthPackageContext);
5
+ if (!context) {
6
+ throw new Error('useAuthPackageContext must be used within a LoginProvider');
7
+ }
8
+ return context;
9
+ };
@@ -0,0 +1,40 @@
1
+ import { useEffect, useState, useRef } from 'react';
2
+ const useTimer = (duration) => {
3
+ const [timer, setTimer] = useState(duration);
4
+ useEffect(() => {
5
+ // Reset timer whenever duration changes
6
+ setTimer(duration);
7
+ // Strongly type the interval ID
8
+ const id = setInterval(() => {
9
+ setTimer(prev => {
10
+ if (prev <= 1) {
11
+ clearInterval(id);
12
+ return 0; // Clamp at zero
13
+ }
14
+ return prev - 1;
15
+ });
16
+ }, 1000);
17
+ return () => clearInterval(id);
18
+ }, [duration]);
19
+ return timer;
20
+ };
21
+ const useInterval = (callback, delay) => {
22
+ const savedCallback = useRef();
23
+ // Remember the latest function.
24
+ useEffect(() => {
25
+ savedCallback.current = callback;
26
+ }, [callback]);
27
+ // Set up the interval.
28
+ useEffect(() => {
29
+ function tick() {
30
+ savedCallback.current?.();
31
+ }
32
+ if (delay !== null) {
33
+ const id = setInterval(tick, delay);
34
+ return () => {
35
+ clearInterval(id);
36
+ };
37
+ }
38
+ }, [delay]);
39
+ };
40
+ export { useTimer, useInterval };
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import AuthContext from "../contexts/AuthContext";
3
+ const useAuthContext = () => {
4
+ const context = React.useContext(AuthContext);
5
+ if (!context) {
6
+ throw new Error('useAuthContext must be used within a AuthProvider');
7
+ }
8
+ return context;
9
+ };
10
+ export { useAuthContext };
@@ -0,0 +1,38 @@
1
+ import { useEffect, useMemo, useState } from 'react';
2
+ import { showMessage } from '../helpers/show-message';
3
+ import { useAuthPackageContext } from './internal/useAuthPackageContext';
4
+ import { createHttpClient } from '../helpers/Network';
5
+ const useRequest = () => {
6
+ const { token: sessionToken, logout, refreshSession } = useAuthPackageContext();
7
+ const [token, setToken] = useState(sessionToken);
8
+ useEffect(() => {
9
+ setToken(sessionToken);
10
+ }, [sessionToken]);
11
+ const { request } = useMemo(() => createHttpClient({
12
+ token,
13
+ onRefreshSession: async (token) => refreshSession?.({ token }),
14
+ onLogout: async () => logout(),
15
+ }), [token, logout, refreshSession]);
16
+ const makeRequest = async ({ url, method, body, onSuccess, onFailure, ...axiosOptions }) => {
17
+ try {
18
+ const response = await request({
19
+ url,
20
+ method,
21
+ data: body,
22
+ ...axiosOptions,
23
+ });
24
+ onSuccess?.(response.data, response.headers);
25
+ return response.data;
26
+ }
27
+ catch (err) {
28
+ if (onFailure) {
29
+ onFailure(err);
30
+ }
31
+ if (err?.response?.status >= 500) {
32
+ showMessage("Something went wrong");
33
+ }
34
+ }
35
+ };
36
+ return { makeRequest };
37
+ };
38
+ export { useRequest };
@@ -0,0 +1,12 @@
1
+ // export contexts
2
+ export * from './contexts/AuthContext';
3
+ export { default as AuthContext } from './contexts/AuthContext';
4
+ export * from './contexts/type';
5
+ // export hooks
6
+ export * from './hooks/useAuthContext';
7
+ export * from './hooks/useRequest';
8
+ // export navigator/router
9
+ export * from './navigator';
10
+ // export components
11
+ export * from './screens/Login/components/LoginWebComponent';
12
+ export * from './screens/SignUp/components/SignUpWebComponent';
@@ -0,0 +1,10 @@
1
+ 'use client';
2
+ const AuthNavigator = () => {
3
+ if (process.env.NODE_ENV !== 'production') {
4
+ if (typeof window !== 'undefined') {
5
+ console.warn("[@truworth/twc-auth] We use only AuthNavigator for app navigation");
6
+ }
7
+ }
8
+ return null;
9
+ };
10
+ export { AuthNavigator };
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
3
+ import Welcome from '../screens/Welcome';
4
+ import EnterEmail from '../screens/EnterEmail/index.native';
5
+ import EnterPassword from '../screens/EnterPassword/index.native';
6
+ import SignUp from '../screens/SignUp/index.native';
7
+ import CountryCode from '../screens/CountryCode/index.native';
8
+ import CreatePassword from '../screens/CreatePassword/index.native';
9
+ import UserConsent from '../screens/UserConsent/index.native';
10
+ import VerifyMobile from '../screens/VerifyMobile/index.native';
11
+ import VerifyEmail from '../screens/VerifyEmail/index.native';
12
+ const { Navigator, Screen } = createNativeStackNavigator();
13
+ const AuthNavigator = () => {
14
+ return (_jsxs(Navigator, { screenOptions: { headerShown: false }, children: [_jsx(Screen, { name: "Welcome", component: Welcome }), _jsx(Screen, { name: "EnterEmail", component: EnterEmail }), _jsx(Screen, { name: "EnterPassword", component: EnterPassword }), _jsx(Screen, { name: "SignUp", component: SignUp }), _jsx(Screen, { name: "CountryCode", component: CountryCode }), _jsx(Screen, { name: "CreatePassword", component: CreatePassword }), _jsx(Screen, { name: "UserConsent", component: UserConsent }), _jsx(Screen, { name: "VerifyMobile", component: VerifyMobile }), _jsx(Screen, { name: "VerifyEmail", component: VerifyEmail })] }));
15
+ };
16
+ export { AuthNavigator };
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import Image from 'next/image';
3
+ import React, { useMemo } from "react";
4
+ import { Typography, SelectField, Flex } from '@truworth/twc-web-design';
5
+ import { useCountryCode } from '../../hooks/internal/useCountryCode';
6
+ export const CountryCodeDropdown = React.memo(({ selectedCountry, handleSelect }) => {
7
+ const { countryCodes: countries } = useCountryCode();
8
+ const isValidCountryCode = (code) => /^[a-z]{2}$/i.test(code); // only 2-letter codes
9
+ const allCountries = useMemo(() => {
10
+ const seen = new Set();
11
+ const uniqueCountries = countries.filter((item) => {
12
+ const rawCode = typeof item.countryCode === 'string' ? item.countryCode : '';
13
+ const code = rawCode.toLowerCase();
14
+ if (!isValidCountryCode(code))
15
+ return false; // filter invalid ones
16
+ if (seen.has(code))
17
+ return false;
18
+ seen.add(code);
19
+ return true;
20
+ });
21
+ return uniqueCountries.map((item) => {
22
+ const code = item.countryCode.toLowerCase();
23
+ const flagUrl = `https://cdn.thewellnesscorner.com/country-flags/${code}.png`;
24
+ return {
25
+ leftIcon: (_jsx(_Fragment, { children: isValidCountryCode(code) && (_jsx(Image, { src: flagUrl, width: 20, height: 20, className: "h-auto shrink-0", alt: `${item.name} flag`, loading: "lazy" })) })),
26
+ label: (_jsxs(Typography, { type: "body", size: "small", children: [item.name, " (+", item.phoneCode, ")"] })),
27
+ value: code,
28
+ selectedField: (_jsxs(Flex, { align: "center", className: "gap-2 pl-2 pr-2 cursor-pointer", children: [isValidCountryCode(code) && (_jsx(Image, { src: flagUrl, width: 20, height: 20, className: "h-auto shrink-0", alt: `${item.name} flag`, loading: "lazy" })), _jsx(Typography, { type: "body", size: "medium", className: "countryCode cursor-pointer", children: `+${item.phoneCode}` })] })),
29
+ };
30
+ });
31
+ }, [countries]);
32
+ return (_jsx(SelectField, { selectedValues: selectedCountry.countryCode, options: allCountries, className: "border-0 w-100 h-1", onSelect: (value) => {
33
+ const currSelectedCountry = countries.find((country) => country.countryCode.toLowerCase() === value);
34
+ if (currSelectedCountry) {
35
+ handleSelect(currSelectedCountry);
36
+ }
37
+ }, fullWidth: true, selectContentClassName: "h-32 pl-2 min-h-[30vh]", maxHeight: "400px", contentOffset: 10 }));
38
+ });
@@ -0,0 +1,6 @@
1
+ const CountryCodeDropdown = () => {
2
+ if (__DEV__)
3
+ console.warn("[@truworth/twc-auth] CountryCodeDropdown is web-only");
4
+ return null;
5
+ };
6
+ export { CountryCodeDropdown };
@@ -0,0 +1,38 @@
1
+ import { useEffect, useState } from "react";
2
+ import { axiosClient } from "../../../../api/axiosClient";
3
+ /**
4
+ * @internal
5
+ * Internal hook for managing CountryCode screen state and auth context integration.
6
+ * Not exposed to package consumers.
7
+ */
8
+ const useCountryCode = () => {
9
+ const [countryCodes, setCountryCodes] = useState([]);
10
+ const [searchCountry, setSearchCountry] = useState([]);
11
+ useEffect(() => {
12
+ if (countryCodes.length === 0) {
13
+ getCountryCodes();
14
+ }
15
+ }, []);
16
+ const getCountryCodes = () => {
17
+ axiosClient({
18
+ url: '/auth/country-codes',
19
+ method: 'GET',
20
+ }).then((res) => {
21
+ setCountryCodes(res.data);
22
+ }).catch((err) => {
23
+ console.log(err);
24
+ });
25
+ };
26
+ const onSearch = (text) => {
27
+ let query = text.toLowerCase();
28
+ let filtered = countryCodes.filter((item) => item.name.toLowerCase().includes(query));
29
+ if (!query || query === '') {
30
+ setSearchCountry([]);
31
+ }
32
+ else {
33
+ setSearchCountry(filtered);
34
+ }
35
+ };
36
+ return { countryCodes, searchCountry, onSearch };
37
+ };
38
+ export { useCountryCode };
@@ -0,0 +1,10 @@
1
+ 'use client';
2
+ const CountryCode = () => {
3
+ if (process.env.NODE_ENV !== 'production') {
4
+ if (typeof window !== 'undefined') {
5
+ console.warn("[@truworth/twc-auth] We use only CountryCode for app");
6
+ }
7
+ }
8
+ return null;
9
+ };
10
+ export default CountryCode;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { TextInput, Text, View, Image, FlatList, TouchableOpacity } from 'react-native';
3
+ import { Icon, Layout } from '@ui-kitten/components';
4
+ import { Colors, withPreventDoubleTap } from '@truworth/twc-rn-common';
5
+ import { useNavigation, useRoute } from '@react-navigation/native';
6
+ import { useCountryCode } from './hooks/internal/useCountryCode';
7
+ import { useState } from 'react';
8
+ const TouchableCard = withPreventDoubleTap(TouchableOpacity);
9
+ const { primary, gray } = Colors;
10
+ const CountryCode = () => {
11
+ const [isSearching, setIsSearching] = useState(false);
12
+ const route = useRoute();
13
+ const navigation = useNavigation();
14
+ const { countryCodes, searchCountry, onSearch } = useCountryCode();
15
+ const onSelect = (countryCode, iconCode) => {
16
+ navigation.navigate(route.params?.prevRoute, {
17
+ ...route.params,
18
+ countryCode,
19
+ iconCode,
20
+ });
21
+ };
22
+ return (_jsxs(Layout, { style: { flex: 1, backgroundColor: primary.white }, children: [_jsxs(View, { style: { flexDirection: 'row' }, children: [_jsx(TouchableCard, { style: { flexDirection: 'row', paddingHorizontal: 15, alignSelf: 'center' }, activeOpacity: 0.8, onPress: () => navigation.goBack(), children: _jsx(Icon, { name: "arrow-back", fill: "#313131", style: { width: 25, height: 25 } }) }), _jsxs(View, { style: {
23
+ flex: 1, backgroundColor: '#eeeef0', height: 45,
24
+ flexDirection: 'row', alignItems: 'center', borderRadius: 10,
25
+ marginVertical: 15, paddingHorizontal: 10, marginRight: 15,
26
+ }, children: [_jsx(Icon, { fill: "#8F9BB3", style: { height: 28, width: 28 }, name: "search-outline" }), _jsx(TextInput, { style: { fontSize: 16, flex: 1, color: primary.black }, placeholder: "Search Country", placeholderTextColor: gray.gray_400, onChangeText: text => {
27
+ onSearch(text);
28
+ setIsSearching(text.trim().length > 0);
29
+ } })] })] }), _jsx(View, { style: { borderBottomWidth: 1, borderBottomColor: '#ddd' } }), _jsx(FlatList, { data: isSearching ? searchCountry : countryCodes, keyExtractor: (item) => item.id.toString(), renderItem: ({ item }) => {
30
+ const sanitizedCountryCode = (item.countryCode ?? '').replace(/[^a-zA-Z]/g, '').toLowerCase();
31
+ return (_jsxs(TouchableOpacity, { style: { flexDirection: 'row', paddingHorizontal: 15, paddingTop: 20, alignItems: 'center' }, onPress: () => onSelect(item.phoneCode, item.countryCode), children: [_jsx(Image, { source: { uri: `https://cdn.thewellnesscorner.com/country-flags/${sanitizedCountryCode}.png` }, style: { height: 25, width: 40 }, resizeMode: "cover" }), _jsx(Text, { style: {
32
+ flex: 1, textTransform: 'uppercase',
33
+ fontSize: 18, color: primary.black, marginLeft: 15,
34
+ }, children: `${item.name} (+${item.phoneCode})` })] }));
35
+ } })] }));
36
+ };
37
+ export default CountryCode;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ import { useState } from "react";
2
+ /**
3
+ * @internal
4
+ * Internal hook for managing CreatePassword screen state and auth context integration.
5
+ * Not exposed to package consumers.
6
+ */
7
+ const useCreatePassword = () => {
8
+ const [passwordVisible, setPasswordVisible] = useState(false);
9
+ const [confirmPasswordVisible, setConfirmPasswordVisible] = useState(false);
10
+ const [password, setPassword] = useState('');
11
+ const [confirmPassword, setConfirmPassword] = useState('');
12
+ const [maxLength, setMaxLength] = useState(defaultPolicy.maxLength);
13
+ const [criteria, setCriteria] = useState({
14
+ numbers: false,
15
+ minLength: false,
16
+ uppercase: false,
17
+ lowercase: false,
18
+ specialCharacters: false,
19
+ });
20
+ const handlePassword = (text) => {
21
+ setPassword(text.replace(/\s/g, ''));
22
+ };
23
+ const handleConfirmPassword = (text) => {
24
+ setConfirmPassword(text.replace(/\s/g, ''));
25
+ };
26
+ const handleSkip = ({ onResult }) => {
27
+ setPassword('');
28
+ setConfirmPassword('');
29
+ onResult();
30
+ };
31
+ const disabled = !Object.keys(criteria).every(c => criteria[c]) || (password !== confirmPassword);
32
+ return {
33
+ password, setPassword,
34
+ confirmPassword, setConfirmPassword,
35
+ passwordVisible, setPasswordVisible,
36
+ confirmPasswordVisible, setConfirmPasswordVisible,
37
+ maxLength, setMaxLength,
38
+ criteria, setCriteria,
39
+ handlePassword, handleConfirmPassword,
40
+ handleSkip,
41
+ disabled
42
+ };
43
+ };
44
+ export { useCreatePassword };
45
+ const defaultPolicy = {
46
+ minLength: 6,
47
+ maxLength: 30,
48
+ numbers: true,
49
+ uppercase: true,
50
+ lowercase: true,
51
+ specialCharacters: true,
52
+ };
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Flex, Form, PasswordInput, Typography, useForm } from "@truworth/twc-web-design";
3
+ import { useCreatePassword } from "./hooks/internal/useCreatePassword";
4
+ import { ScreenLayout } from "../../components/ScreenLayout";
5
+ import { PasswordCriteria } from "../../components/PasswordCriteria";
6
+ const CreatePassword = ({ userDetails, handleBack, onContinue }) => {
7
+ const { countryCode, email } = userDetails;
8
+ const form = useForm({
9
+ defaultValues: {
10
+ password: userDetails.password || '',
11
+ confirmPassword: userDetails.confirmPassword || '',
12
+ }
13
+ });
14
+ const { password, confirmPassword } = form.watch();
15
+ const { criteria, setCriteria, maxLength, setMaxLength, handleSkip, } = useCreatePassword();
16
+ return (_jsx(ScreenLayout, { title: _jsxs(Flex, { justify: "between", align: "center", children: [_jsx(Typography, { type: "heading", size: "h5", children: "Create your Password" }), countryCode == '91' &&
17
+ _jsx(Button, { label: "Skip Now", variant: "link", onClick: () => handleSkip({ onResult: onContinue }) })] }), subTitle: "Use a password that's easy to remember and fulfills all the requirements listed below.", buttonProps: {
18
+ label: 'Continue',
19
+ onClick: () => onContinue(password, confirmPassword),
20
+ disabled: !password || !confirmPassword || (password !== confirmPassword)
21
+ }, onPressBack: handleBack, children: _jsxs(Form, { form: form, className: 'w-full', children: [_jsx(Form.Item, { name: "password", label: "Enter Password", normalize: (value) => value.replace(/\s/g, ''), rules: [
22
+ {
23
+ required: true,
24
+ message: 'Please enter your password'
25
+ }
26
+ ], children: _jsx(PasswordInput, { placeholder: "Enter password", value: password, maxLength: maxLength, showStrengthIndicator: false }) }), _jsx(PasswordCriteria, { email: email, password: password, criteria: criteria, onCriteriaChange: setCriteria, onMaxLengthChange: setMaxLength }), _jsx(Form.Item, { name: "confirmPassword", label: "Confirm Password", className: "mt-6", normalize: (value) => value.replace(/\s/g, ''), rules: [
27
+ {
28
+ required: true,
29
+ message: 'Please confirm your password',
30
+ },
31
+ {
32
+ validator: async (_, value) => {
33
+ const password = form.getValues('password');
34
+ if (!value)
35
+ return;
36
+ if (!value || password !== value) {
37
+ throw new Error('Passwords don’t match');
38
+ }
39
+ },
40
+ },
41
+ ], children: _jsx(PasswordInput, { className: "mb-6", value: confirmPassword, placeholder: "Re-enter password", maxLength: maxLength, showStrengthIndicator: false }) })] }) }));
42
+ };
43
+ export default CreatePassword;
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Keyboard, Text, TouchableOpacity } from 'react-native';
3
+ import { Colors, TextInputField } from '@truworth/twc-rn-common';
4
+ import { Layout } from '@ui-kitten/components';
5
+ import { ScreenLayout } from '../../components/ScreenLayout';
6
+ import { useCreatePassword } from './hooks/internal/useCreatePassword';
7
+ import { PasswordCriteria } from '../../components/PasswordCriteria/index.native';
8
+ const { primary } = Colors;
9
+ const CreatePassword = ({ navigation, route }) => {
10
+ const { countryCode, email } = route.params;
11
+ const { password, confirmPassword, passwordVisible, setPasswordVisible, confirmPasswordVisible, setConfirmPasswordVisible, maxLength, setMaxLength, criteria, setCriteria, handlePassword, handleConfirmPassword, handleSkip, disabled } = useCreatePassword();
12
+ const onContinue = () => {
13
+ navigation.navigate('UserConsent', { ...route.params, password });
14
+ };
15
+ return (_jsx(Layout, { style: { flex: 1, backgroundColor: primary.white }, children: _jsxs(ScreenLayout, { title: "Create your Password", subTitle: "Use a password that\u2019s easy to remember and fulfills all the requirement listed below.", buttonProps: {
16
+ label: 'Continue',
17
+ onPress: onContinue,
18
+ disabled: disabled,
19
+ }, accessoryRight: countryCode == '91'
20
+ ? _jsx(TouchableOpacity, { activeOpacity: 0.8, onPress: () => {
21
+ Keyboard.dismiss();
22
+ handleSkip({
23
+ onResult: () => {
24
+ navigation.navigate('UserConsent', { ...route.params });
25
+ }
26
+ });
27
+ }, children: _jsx(Text, { style: { fontSize: 16, fontWeight: '600', color: primary.primary_main }, children: "Skip" }) })
28
+ : _jsx(_Fragment, {}), children: [_jsx(TextInputField, { placeholder: "Enter password...", onChangeValue: handlePassword, secureTextEntry: !passwordVisible, value: password, rightIcon2: passwordVisible
29
+ ? 'eye-outline'
30
+ : 'eye-off-outline', onPressRightIcon2: () => setPasswordVisible(!passwordVisible), autoFocus: true, maxLength: maxLength }), _jsx(PasswordCriteria, { email: email, password: password, criteria: criteria, onCriteriaChange: setCriteria, onMaxLengthChange: setMaxLength }), _jsx(TextInputField, { placeholder: "Confirm password...", onChangeValue: handleConfirmPassword, secureTextEntry: !confirmPasswordVisible, value: confirmPassword, rightIcon2: confirmPasswordVisible ? 'eye-outline' : 'eye-off-outline', onPressRightIcon2: () => setConfirmPasswordVisible(!confirmPasswordVisible), maxLength: maxLength, containerStyle: { marginTop: 16 } })] }) }));
31
+ };
32
+ export default CreatePassword;
@@ -0,0 +1 @@
1
+ export {};