@truworth/twc-auth 1.2.2 → 1.2.3

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 (72) hide show
  1. package/build/src/components/PasswordCriteria/hooks/usePasswordCriteria.js +1 -8
  2. package/build/src/components/PasswordCriteria/index.native.js +3 -3
  3. package/build/src/components/{EmailOTPVerify → VerifyEmailOTP}/index.js +2 -2
  4. package/build/src/components/{EmailOTPVerify → VerifyEmailOTP}/index.native.js +3 -10
  5. package/build/src/components/{VerifyOTP → VerifyMobileOTP}/index.js +2 -2
  6. package/build/src/components/{VerifyOTP → VerifyMobileOTP}/index.native.js +2 -2
  7. package/build/src/constants/defaultPolicy.js +9 -0
  8. package/build/src/helpers/Network.js +1 -1
  9. package/build/src/helpers/show-message/index.js +2 -2
  10. package/build/src/helpers/show-message/index.native.js +1 -1
  11. package/build/src/hooks/useRequest.js +1 -1
  12. package/build/src/navigator/index.native.js +4 -1
  13. package/build/src/screens/CreatePassword/hooks/internal/useCreatePassword.js +2 -15
  14. package/build/src/screens/EnterEmail/hooks/internal/useEnterEmail.js +2 -2
  15. package/build/src/screens/EnterPassword/index.js +16 -9
  16. package/build/src/screens/EnterPassword/index.native.js +28 -16
  17. package/build/src/screens/Login/components/LoginWebComponent/index.js +19 -3
  18. package/build/src/screens/Login/components/LoginWebComponent/types.js +1 -0
  19. package/build/src/screens/LoginWithEmailOTP/hooks/internal/useLoginWithEmailOTP.js +72 -0
  20. package/build/src/screens/LoginWithEmailOTP/index.js +8 -0
  21. package/build/src/screens/LoginWithEmailOTP/index.native.js +10 -0
  22. package/build/src/screens/ResetPassword/hooks/internal/useResetPassword.js +55 -0
  23. package/build/src/screens/ResetPassword/index.js +46 -0
  24. package/build/src/screens/ResetPassword/index.native.js +26 -0
  25. package/build/src/screens/ResetPassword/types.js +1 -0
  26. package/build/src/screens/SignUp/components/SignUpForm/index.js +1 -1
  27. package/build/src/screens/SignUp/hooks/internal/useSignUp.js +3 -3
  28. package/build/src/screens/UserConsent/hooks/internal/useConsent.js +1 -1
  29. package/build/src/screens/VerifyEmail/hooks/internal/useVerifyEmail.js +3 -3
  30. package/build/src/screens/VerifyEmail/index.js +2 -2
  31. package/build/src/screens/VerifyEmail/index.native.js +2 -2
  32. package/build/src/screens/VerifyMobile/hooks/internal/useVerifyMobile.js +5 -5
  33. package/build/src/screens/VerifyMobile/index.js +2 -2
  34. package/build/src/screens/VerifyMobile/index.native.js +2 -2
  35. package/build/src/screens/VerifyResetPasswordOTP/hooks/internal/useVerifyResetPasswordOTP.js +74 -0
  36. package/build/src/screens/VerifyResetPasswordOTP/index.js +8 -0
  37. package/build/src/screens/VerifyResetPasswordOTP/index.native.js +14 -0
  38. package/build/src/types/types.js +1 -0
  39. package/build/types/components/PasswordCriteria/hooks/usePasswordCriteria.d.ts +2 -7
  40. package/build/types/components/VerifyEmailOTP/index.d.ts +3 -0
  41. package/build/types/components/VerifyEmailOTP/index.native.d.ts +3 -0
  42. package/build/types/components/{EmailOTPVerify/type.d.ts → VerifyEmailOTP/types.d.ts} +2 -2
  43. package/build/types/components/VerifyMobileOTP/index.d.ts +3 -0
  44. package/build/types/components/VerifyMobileOTP/index.native.d.ts +3 -0
  45. package/build/types/constants/defaultPolicy.d.ts +9 -0
  46. package/build/types/helpers/show-message/index.d.ts +2 -2
  47. package/build/types/helpers/show-message/index.native.d.ts +2 -2
  48. package/build/types/helpers/show-message/types.d.ts +6 -2
  49. package/build/types/navigator/index.native.d.ts +10 -0
  50. package/build/types/screens/EnterPassword/index.d.ts +1 -1
  51. package/build/types/screens/EnterPassword/index.native.d.ts +1 -1
  52. package/build/types/screens/EnterPassword/types.d.ts +13 -1
  53. package/build/types/screens/Login/components/LoginWebComponent/types.d.ts +6 -0
  54. package/build/types/screens/LoginWithEmailOTP/hooks/internal/useLoginWithEmailOTP.d.ts +14 -0
  55. package/build/types/screens/LoginWithEmailOTP/index.d.ts +4 -0
  56. package/build/types/screens/LoginWithEmailOTP/index.native.d.ts +4 -0
  57. package/build/types/screens/ResetPassword/hooks/internal/useResetPassword.d.ts +28 -0
  58. package/build/types/screens/ResetPassword/index.d.ts +3 -0
  59. package/build/types/screens/ResetPassword/index.native.d.ts +4 -0
  60. package/build/types/screens/ResetPassword/types.d.ts +11 -0
  61. package/build/types/screens/VerifyResetPasswordOTP/hooks/internal/useVerifyResetPasswordOTP.d.ts +19 -0
  62. package/build/types/screens/VerifyResetPasswordOTP/index.d.ts +7 -0
  63. package/build/types/screens/VerifyResetPasswordOTP/index.native.d.ts +4 -0
  64. package/build/types/types/types.d.ts +9 -1
  65. package/package.json +2 -2
  66. package/build/types/components/EmailOTPVerify/index.d.ts +0 -3
  67. package/build/types/components/EmailOTPVerify/index.native.d.ts +0 -3
  68. package/build/types/components/VerifyOTP/index.d.ts +0 -3
  69. package/build/types/components/VerifyOTP/index.native.d.ts +0 -3
  70. /package/build/src/components/{VerifyOTP → VerifyEmailOTP}/types.js +0 -0
  71. /package/build/src/components/{EmailOTPVerify/type.js → VerifyMobileOTP/types.js} +0 -0
  72. /package/build/types/components/{VerifyOTP → VerifyMobileOTP}/types.d.ts +0 -0
@@ -1,13 +1,6 @@
1
1
  import { useEffect, useState } from "react";
2
2
  import { axiosClient } from "../../../api/axiosClient";
3
- const defaultPolicy = {
4
- minLength: 6,
5
- maxLength: 30,
6
- numbers: true,
7
- uppercase: true,
8
- lowercase: true,
9
- specialCharacters: true,
10
- };
3
+ import { defaultPolicy } from "../../../constants/defaultPolicy";
11
4
  const usePasswordCriteria = ({ email, password, onCriteriaChange, onMaxLengthChange }) => {
12
5
  const { maxLength: defaultMaxLength, ...restDefaultPolicy } = defaultPolicy;
13
6
  const [loading, setLoading] = useState(false);
@@ -4,7 +4,7 @@ import { Text, View } from 'react-native';
4
4
  import { usePasswordCriteria } from './hooks/usePasswordCriteria';
5
5
  const { gray, utility } = Colors;
6
6
  const PasswordCriteria = ({ email, password, criteria, onCriteriaChange, onMaxLengthChange }) => {
7
- const { passwordPolicy, loading, renderLabel, } = usePasswordCriteria({ email, password, onCriteriaChange, onMaxLengthChange });
7
+ const { loading, passwordPolicy, renderLabel, } = usePasswordCriteria({ email, password, onCriteriaChange, onMaxLengthChange });
8
8
  const getCriteriaColor = (isValid) => (isValid ? utility.success_main : gray.gray_600);
9
9
  const getCriteriaBackground = (isValid) => (isValid ? utility.success_bg : gray.gray_100);
10
10
  return (_jsx(View, { style: { flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', alignContent: 'space-between', marginTop: 8 }, children: Object.keys(passwordPolicy).map((item, index) => (loading
@@ -13,10 +13,10 @@ const PasswordCriteria = ({ email, password, criteria, onCriteriaChange, onMaxLe
13
13
  _jsx(View, { style: {
14
14
  marginRight: 8, marginTop: 8, borderRadius: 32,
15
15
  paddingVertical: 4, paddingHorizontal: 12,
16
- backgroundColor: getCriteriaBackground(criteria[item]),
16
+ backgroundColor: getCriteriaBackground(Boolean(criteria[item])),
17
17
  }, children: _jsx(Text, { style: {
18
18
  fontSize: 12, fontWeight: '500',
19
- color: getCriteriaColor(criteria[item]),
19
+ color: getCriteriaColor(Boolean(criteria[item])),
20
20
  }, children: renderLabel(item) }) }, index))) }));
21
21
  };
22
22
  export { PasswordCriteria };
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Button, Flex, OTPInput, Typography } from "@truworth/twc-web-design";
3
3
  import { useEffect, useState } from "react";
4
- const EmailOTPVerify = ({ validateOTP, resendOTP, resendOTPCounter, email, status }) => {
4
+ const VerifyEmailOTP = ({ validateOTP, resendOTP, resendOTPCounter, email, status }) => {
5
5
  const [otp, setOtp] = useState('');
6
6
  const [otpError, setOtpError] = useState(false);
7
7
  useEffect(() => {
@@ -17,4 +17,4 @@ const EmailOTPVerify = ({ validateOTP, resendOTP, resendOTPCounter, email, statu
17
17
  _jsx(Typography, { type: "utility", size: "medium", className: "mt-1", color: "text-utility-danger-main", children: "Please enter the correct OTP." }), (resendOTPCounter < 3) &&
18
18
  _jsxs(Flex, { align: "center", justify: "between", className: 'mt-7', children: [_jsx(Typography, { type: "body", size: "medium", className: "font-semibold", color: "text-gray-400", children: "Didn't receive OTP?" }), _jsx(Button, { variant: 'link', size: 'small', className: "p-0 h-auto", onClick: resendOTP, children: "Resend Code" })] }), _jsx(Button, { label: "Continue", isFullWidth: true, variant: 'primary', className: "mt-7", onClick: () => validateOTP?.(otp), disabled: (otp?.length ?? 0) < 6 })] }));
19
19
  };
20
- export { EmailOTPVerify };
20
+ export { VerifyEmailOTP };
@@ -1,18 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useState } from "react";
3
2
  import { Dimensions, Platform, ScrollView, Text, TouchableOpacity, View } from "react-native";
4
3
  import { Layout } from "@ui-kitten/components";
5
4
  import { Header, Colors, CustomKeyboardAvoidingView } from "@truworth/twc-rn-common";
6
5
  import { ScreenLayout } from "../ScreenLayout";
7
6
  import { OTPStatusLabel } from "../OTPStatusLabel/index.native";
8
7
  import OTPInputView from "@twotalltotems/react-native-otp-input";
9
- const EmailOTPVerify = ({ status, validateOTP, resendOTP, resendOTPCounter, email }) => {
10
- const [otp, setOTP] = useState('');
11
- useEffect(() => {
12
- if (otp && otp.length === 6) {
13
- validateOTP?.(otp);
14
- }
15
- }, [otp]);
8
+ const VerifyEmailOTP = ({ email, status, validateOTP, resendOTP, resendOTPCounter }) => {
16
9
  return (_jsxs(Layout, { style: { flex: 1, backgroundColor: '#fff' }, children: [_jsx(Header, {}), _jsxs(CustomKeyboardAvoidingView, { behavior: Platform.OS === 'ios' ? 'padding' : undefined, keyboardVerticalOffset: Platform.OS === 'ios' ? 100 : 0, children: [_jsx(ScrollView, { showsVerticalScrollIndicator: false, children: _jsxs(ScreenLayout, { hideHeader: true, title: _jsxs(_Fragment, { children: ["OTP sent to ", '\n', _jsx(Text, { style: { fontSize: 20 }, children: email })] }), children: [_jsx(View, { style: { justifyContent: 'center', flexDirection: 'row', marginBottom: 30, position: 'relative' }, children: _jsx(OTPInputView, { pinCount: 6, autoFocusOnLoad: false, codeInputFieldStyle: {
17
10
  fontSize: 20, color: 'black',
18
11
  height: 45, width: 30,
@@ -20,7 +13,7 @@ const EmailOTPVerify = ({ status, validateOTP, resendOTP, resendOTPCounter, emai
20
13
  }, codeInputHighlightStyle: { borderColor: '#03DAC6' }, style: {
21
14
  height: 50,
22
15
  width: Dimensions.get('window').width - 100,
23
- }, onCodeFilled: code => setOTP(code) }) }), resendOTPCounter < 3 &&
16
+ }, onCodeFilled: code => validateOTP(code) }) }), resendOTPCounter < 3 &&
24
17
  _jsxs(View, { style: { flexDirection: 'row', alignItems: 'center' }, children: [_jsxs(Text, { style: { fontSize: 14, fontWeight: '500', color: Colors.gray.gray_400, lineHeight: 20 }, children: ["Did not receive OTP?", ' '] }), _jsx(TouchableOpacity, { activeOpacity: 0.8, onPress: resendOTP, children: _jsx(Text, { style: { fontSize: 14, fontWeight: '500', color: '#2cbaa4', lineHeight: 20 }, children: "Re-send OTP" }) })] })] }) }), _jsx(View, { style: { alignSelf: 'flex-end', width: '100%' }, children: _jsx(OTPStatusLabel, { status: status }) })] })] }));
25
18
  };
26
- export { EmailOTPVerify };
19
+ export { VerifyEmailOTP };
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useState, useEffect } from "react";
3
3
  import { Button, Flex, OTPInput, Typography } from "@truworth/twc-web-design";
4
- const OTPVerify = ({ validateOTP, timer, resendOTP, phone, status, resendTextClassName }) => {
4
+ const VerifyMobileOTP = ({ validateOTP, timer, resendOTP, phone, status, resendTextClassName }) => {
5
5
  const [otp, setOtp] = useState('');
6
6
  const [otpError, setOtpError] = useState(false);
7
7
  useEffect(() => {
@@ -20,4 +20,4 @@ const OTPVerify = ({ validateOTP, timer, resendOTP, phone, status, resendTextCla
20
20
  :
21
21
  _jsxs(Typography, { type: 'utility', size: 'medium', className: `mt-3 ${resendTextClassName}`, children: ["Resend Code in ", timer > 0 ? `${timer} seconds` : 'OTP expired'] }), _jsx(Button, { label: "Continue", isFullWidth: true, onClick: () => validateOTP?.(otp), disabled: otp ? otp?.length < 6 : true, className: "mt-6" })] }));
22
22
  };
23
- export { OTPVerify };
23
+ export { VerifyMobileOTP };
@@ -7,7 +7,7 @@ import { useAuthPackageContext } from '../../hooks/internal/useAuthPackageContex
7
7
  import { OTPStatusLabel } from '../OTPStatusLabel/index.native';
8
8
  import OTPInputView from '@twotalltotems/react-native-otp-input';
9
9
  const { gray } = Colors;
10
- const OTPVerify = ({ validateOTP, timer, status, resendOTP, phone }) => {
10
+ const VerifyMobileOTP = ({ validateOTP, timer, status, resendOTP, phone }) => {
11
11
  const [otp, setOTP] = useState('');
12
12
  const { otpValue } = useAuthPackageContext();
13
13
  useEffect(() => {
@@ -35,4 +35,4 @@ const OTPVerify = ({ validateOTP, timer, status, resendOTP, phone }) => {
35
35
  resendOTP();
36
36
  }, children: _jsx(Text, { style: { fontSize: 14, color: '#2cbaa4', fontWeight: '400', opacity: 1 }, children: "Re-send OTP" }) })] })] }), _jsx(View, { style: { width: '100%' }, children: _jsx(OTPStatusLabel, { status: status, timer: timer }) })] })] }));
37
37
  };
38
- export { OTPVerify };
38
+ export { VerifyMobileOTP };
@@ -0,0 +1,9 @@
1
+ const defaultPolicy = {
2
+ minLength: 6,
3
+ maxLength: 30,
4
+ numbers: true,
5
+ uppercase: true,
6
+ lowercase: true,
7
+ specialCharacters: true,
8
+ };
9
+ export { defaultPolicy };
@@ -77,7 +77,7 @@ export const createHttpClient = ({ token, onRefreshSession, onLogout }) => {
77
77
  const handleInvalidSession = async () => {
78
78
  try {
79
79
  await onLogout?.();
80
- showMessage('Your session has expired. Please login again.');
80
+ showMessage({ message: 'Your session has expired. Please login again.' });
81
81
  }
82
82
  catch (err) {
83
83
  console.error('Error logging out:', err);
@@ -1,5 +1,5 @@
1
1
  import { showNotification } from "@truworth/twc-web-design";
2
- const showMessage = (message) => {
2
+ const showMessage = ({ message, type }) => {
3
3
  if (message == null)
4
4
  return;
5
5
  const text = typeof message === 'string'
@@ -10,7 +10,7 @@ const showMessage = (message) => {
10
10
  ? String(message.message)
11
11
  : String(message);
12
12
  showNotification({
13
- type: 'error',
13
+ type: type ?? 'error',
14
14
  title: text,
15
15
  showIcon: false,
16
16
  duration: 3000,
@@ -1,5 +1,5 @@
1
1
  import { Snackbar } from "@truworth/twc-rn-common";
2
- const showMessage = (message, leftIcon = 'warning') => {
2
+ const showMessage = ({ message, leftIcon = 'warning' }) => {
3
3
  if (message == null)
4
4
  return;
5
5
  const text = typeof message === 'string'
@@ -32,7 +32,7 @@ const useRequest = () => {
32
32
  onFailure(err);
33
33
  }
34
34
  if (err?.response?.status >= 500) {
35
- showMessage("Something went wrong");
35
+ showMessage({ message: "Something went wrong" });
36
36
  }
37
37
  }
38
38
  };
@@ -9,8 +9,11 @@ import CreatePassword from '../screens/CreatePassword/index.native';
9
9
  import UserConsent from '../screens/UserConsent/index.native';
10
10
  import VerifyMobile from '../screens/VerifyMobile/index.native';
11
11
  import VerifyEmail from '../screens/VerifyEmail/index.native';
12
+ import LoginWithEmailOTP from '../screens/LoginWithEmailOTP/index.native';
13
+ import VerifyResetPasswordOTP from '../screens/VerifyResetPasswordOTP/index.native';
14
+ import ResetPassword from '../screens/ResetPassword/index.native';
12
15
  const { Navigator, Screen } = createNativeStackNavigator();
13
16
  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 })] }));
17
+ 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 }), _jsx(Screen, { name: "LoginWithEmailOTP", component: LoginWithEmailOTP }), _jsx(Screen, { name: "VerifyResetPasswordOTP", component: VerifyResetPasswordOTP }), _jsx(Screen, { name: "ResetPassword", component: ResetPassword })] }));
15
18
  };
16
19
  export { AuthNavigator };
@@ -1,4 +1,5 @@
1
1
  import { useState } from "react";
2
+ import { defaultPolicy } from "../../../../constants/defaultPolicy";
2
3
  /**
3
4
  * @internal
4
5
  * Internal hook for managing CreatePassword screen state and auth context integration.
@@ -10,13 +11,7 @@ const useCreatePassword = () => {
10
11
  const [password, setPassword] = useState('');
11
12
  const [confirmPassword, setConfirmPassword] = useState('');
12
13
  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
- });
14
+ const [criteria, setCriteria] = useState({});
20
15
  const handlePassword = (text) => {
21
16
  setPassword(text.replace(/\s/g, ''));
22
17
  };
@@ -42,11 +37,3 @@ const useCreatePassword = () => {
42
37
  };
43
38
  };
44
39
  export { useCreatePassword };
45
- const defaultPolicy = {
46
- minLength: 6,
47
- maxLength: 30,
48
- numbers: true,
49
- uppercase: true,
50
- lowercase: true,
51
- specialCharacters: true,
52
- };
@@ -32,7 +32,7 @@ const useEnterEmail = () => {
32
32
  const code = Number(res.data.loginType);
33
33
  const isKnown = code === 1 || code === 2 || code === 3 || code === 4;
34
34
  if (!isKnown) {
35
- showMessage('Unsupported login method. Please try again later.');
35
+ showMessage({ message: 'Unsupported login method. Please try again later.' });
36
36
  return;
37
37
  }
38
38
  if (code !== 1) {
@@ -60,7 +60,7 @@ const useEnterEmail = () => {
60
60
  onResult({ email, emailExist: false });
61
61
  }
62
62
  if (err?.response?.status >= 500) {
63
- showMessage('Something went wrong');
63
+ showMessage({ message: 'Something went wrong' });
64
64
  }
65
65
  });
66
66
  };
@@ -1,22 +1,29 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Form, PasswordInput, Typography, useForm } from '@truworth/twc-web-design';
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Button, Flex, Form, PasswordInput, ResponsiveModal, Typography, useForm } from '@truworth/twc-web-design';
3
4
  import { ScreenLayout } from "../../components/ScreenLayout";
4
5
  import { useEnterPassword } from './hooks/internal/useEnterPassword';
5
6
  import { SupportDetails } from '../../components/SupportDetails';
6
- const EnterPassword = ({ email, onPressBack }) => {
7
+ import VerifyResetPasswordOTP from '../VerifyResetPasswordOTP';
8
+ const EnterPassword = ({ email, onPressBack, onSignInWithEmailOTP, onVerifiedResetPasswordOTP }) => {
9
+ const [showResetPasswordOTPModal, setShowResetPasswordOTPModal] = useState(false);
7
10
  const { loading, password, isInvalidPassword, handleSubmit, onPassword, appName } = useEnterPassword();
8
11
  const form = useForm({ defaultValues: { password } });
9
- return (_jsxs(_Fragment, { children: [_jsx(ScreenLayout, { title: `Hi, Welcome To ${appName}!`, subTitle: email, buttonProps: {
12
+ return (_jsxs(_Fragment, { children: [_jsxs(ScreenLayout, { title: `Hi, Welcome To ${appName}!`, subTitle: email, buttonProps: {
10
13
  loading,
11
14
  label: 'Continue',
12
15
  onClick: () => { if (email)
13
16
  handleSubmit({ email, source: 'web' }); },
14
17
  disabled: !password || !email || loading,
15
- }, onPressBack: onPressBack, children: _jsx(Form, { className: "w-full", form: form, onSubmit: () => { if (email)
16
- handleSubmit({ email, source: 'web' }); }, children: _jsx(Form.Item, { name: "password", label: "Password", description: isInvalidPassword &&
17
- _jsx(Typography, { type: "body", size: "small", className: "h-1", color: "text-utility-danger-main", children: "Email and password do not match!" }), children: _jsx(PasswordInput, { placeholder: "Enter your password", value: password, showStrengthIndicator: false, className: `border rounded-md px-3 py-2
18
+ }, onPressBack: onPressBack, children: [_jsx(Form, { className: "w-full", form: form, onSubmit: () => { if (email)
19
+ handleSubmit({ email, source: 'web' }); }, children: _jsx(Form.Item, { name: "password", label: "Password", description: isInvalidPassword &&
20
+ _jsx(Typography, { type: "body", size: "small", className: "h-1", color: "text-utility-danger-main", children: "Email and password do not match!" }), children: _jsx(PasswordInput, { placeholder: "Enter your password", value: password, showStrengthIndicator: false, className: `border rounded-md px-3 py-2
18
21
  ${isInvalidPassword ? "border-red-500" : "border-gray-300"}`, ...form.register('password', {
19
- onChange: (e) => onPassword(e.target.value)
20
- }) }) }) }) }), _jsx(SupportDetails, {})] }));
22
+ onChange: (e) => onPassword(e.target.value)
23
+ }) }) }) }), _jsx(Button, { type: "button", size: 'small', variant: 'link', label: "Forgot Password?", className: "px-0 h-auto ml-auto block mt-4", onClick: () => setShowResetPasswordOTPModal(true) }), showResetPasswordOTPModal &&
24
+ _jsx(VerifyResetPasswordOTPModal, { email: email, show: showResetPasswordOTPModal, hide: () => setShowResetPasswordOTPModal(false), onVerifiedOTP: onVerifiedResetPasswordOTP })] }), onSignInWithEmailOTP && (_jsxs(_Fragment, { children: [_jsxs(Flex, { align: "center", className: "mt-6 mb-6", children: [_jsx(Flex, { className: "flex-1 border-t border-gray-300" }), _jsx(Typography, { type: 'body', size: 'small', className: "text-gray-500 px-4", children: "OR" }), _jsx(Flex, { className: "flex-1 border-t border-gray-300" })] }), _jsx(Button, { label: 'Sign In With OTP', variant: 'secondary', className: 'flex-none', isFullWidth: true, type: "button", onClick: onSignInWithEmailOTP })] })), _jsx(SupportDetails, {})] }));
25
+ };
26
+ const VerifyResetPasswordOTPModal = ({ show, hide, email, onVerifiedOTP }) => {
27
+ return (_jsx(ResponsiveModal, { title: "Reset Your Password", open: show, onClose: hide, maskClosable: false, showCloseButton: false, size: 'sm', children: _jsx(VerifyResetPasswordOTP, { email: email, onVerifiedOTP: onVerifiedOTP }) }));
21
28
  };
22
29
  export default EnterPassword;
@@ -1,23 +1,35 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { View } from 'react-native';
3
- import { TextInputField } from '@truworth/twc-rn-common';
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { View, Text, Platform, TouchableOpacity } from 'react-native';
3
+ import { RoundedButton, TextInputField, Colors } from '@truworth/twc-rn-common';
4
4
  import { Layout } from '@ui-kitten/components';
5
5
  import { ScreenLayout } from '../../components/ScreenLayout/index.native';
6
6
  import { useEnterPassword } from './hooks/internal/useEnterPassword';
7
- const EnterPassword = ({ route }) => {
7
+ const { primary } = Colors;
8
+ const EnterPassword = ({ navigation, route }) => {
8
9
  const { email } = route.params;
9
10
  const { loading, password, isPasswordVisible, togglePasswordVisibility, isInvalidPassword, handleSubmit, onPassword } = useEnterPassword();
10
- return (_jsx(Layout, { style: { flex: 1, backgroundColor: '#FFFFFF' }, children: _jsx(ScreenLayout, { title: "Enter Your Password", buttonProps: {
11
- loading,
12
- label: 'Continue',
13
- onPress: () => { if (email)
14
- handleSubmit({ email, source: 'web' }); },
15
- disabled: password.length == 0 || isInvalidPassword || loading,
16
- }, children: _jsx(View, { style: { position: 'relative' }, children: _jsx(TextInputField, { placeholder: 'Enter password...', onChangeValue: onPassword, secureTextEntry: !isPasswordVisible, value: password, rightIcon2: isPasswordVisible
17
- ? 'eye-outline'
18
- : 'eye-off-outline', onPressRightIcon2: () => togglePasswordVisibility(), error: {
19
- show: isInvalidPassword,
20
- message: isInvalidPassword ? 'Email and password do not match!' : ''
21
- }, autoFocus: true }) }) }) }));
11
+ return (_jsxs(Layout, { style: { flex: 1, backgroundColor: '#FFFFFF' }, children: [_jsxs(ScreenLayout, { title: "Enter Your Password", buttonProps: {
12
+ loading,
13
+ label: 'Continue',
14
+ onPress: () => { if (email)
15
+ handleSubmit({ email, source: Platform.OS }); },
16
+ disabled: password.length == 0 || isInvalidPassword || loading,
17
+ }, children: [_jsx(View, { style: { position: 'relative' }, children: _jsx(TextInputField, { placeholder: 'Enter password...', onChangeValue: onPassword, secureTextEntry: !isPasswordVisible, value: password, rightIcon2: isPasswordVisible
18
+ ? 'eye-outline'
19
+ : 'eye-off-outline', onPressRightIcon2: () => togglePasswordVisibility(), error: {
20
+ show: isInvalidPassword,
21
+ message: isInvalidPassword ? 'Email and password do not match!' : ''
22
+ }, autoFocus: true }) }), _jsx(TouchableOpacity, { activeOpacity: 0.8, style: { marginTop: isInvalidPassword ? 0 : 10, alignSelf: 'flex-end' }, onPress: () => navigation.navigate('VerifyResetPasswordOTP', { email }), children: _jsx(Text, { style: {
23
+ fontSize: 12, fontWeight: '600',
24
+ color: primary.primary_main, lineHeight: 20,
25
+ }, children: "Forgot Password?" }) })] }), _jsxs(View, { style: { paddingHorizontal: 16 }, children: [_jsxs(View, { style: {
26
+ flexDirection: 'row', justifyContent: 'center',
27
+ alignItems: 'center', marginBottom: 14, marginTop: 14,
28
+ }, children: [_jsx(View, { style: { width: 12, height: 1, backgroundColor: '#7F7F7F' } }), _jsx(Text, { style: {
29
+ fontSize: 12, fontWeight: '600', color: '#7F7F7F',
30
+ lineHeight: 20, textAlign: 'center',
31
+ marginHorizontal: 6,
32
+ }, children: "OR" }), _jsx(View, { style: { width: 12, height: 1, backgroundColor: '#7F7F7F' } })] }), email &&
33
+ _jsx(RoundedButton, { type: "text", size: "medium", label: "Sign In With OTP", onPress: () => navigation.navigate('LoginWithEmailOTP', { email }) })] })] }));
22
34
  };
23
35
  export default EnterPassword;
@@ -1,14 +1,18 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { useState } from 'react';
3
- import { Flex } from '@truworth/twc-web-design';
3
+ import { Flex, ResponsiveModal, Typography } from '@truworth/twc-web-design';
4
4
  import { useRouter } from 'next/router';
5
5
  import { AdvancedTransitionWrapper } from '../../../../components/AdvancedTransitionWrapper';
6
6
  import { useAuthPackageContext } from '../../../../hooks/internal/useAuthPackageContext';
7
7
  import EnterEmail from '../../../EnterEmail/index';
8
8
  import EnterPassword from '../../../EnterPassword/index';
9
+ import LoginWithEmailOTP from '../../../LoginWithEmailOTP/index';
10
+ import ResetPassword from '../../../ResetPassword/index';
9
11
  const LoginWebComponent = () => {
10
12
  const [email, setEmail] = useState('');
11
13
  const [loginState, setLoginState] = useState('EnterEmail');
14
+ const [showLoginWithEmailOTPModal, setShowLoginWithEmailOTPModal] = useState(false);
15
+ const [sessionToken, setSessionToken] = useState('');
12
16
  const { LogoComponent } = useAuthPackageContext();
13
17
  const router = useRouter();
14
18
  const onRedirectToPassword = (email) => {
@@ -33,12 +37,24 @@ const LoginWebComponent = () => {
33
37
  }
34
38
  } }));
35
39
  case 'EnterPassword':
36
- return (_jsx(EnterPassword, { email: email, onPressBack: () => setLoginState('EnterEmail') }));
40
+ return (_jsx(EnterPassword, { email: email, onPressBack: () => setLoginState('EnterEmail'), onSignInWithEmailOTP: () => {
41
+ if (email) {
42
+ setShowLoginWithEmailOTPModal(true);
43
+ }
44
+ }, onVerifiedResetPasswordOTP: ({ sessionToken }) => {
45
+ setSessionToken(sessionToken);
46
+ setLoginState('ResetPassword');
47
+ } }));
48
+ case 'ResetPassword':
49
+ return (_jsx(ResetPassword, { email: email, sessionToken: sessionToken, onContinue: () => setLoginState('EnterEmail'), onPressBack: () => setLoginState('EnterPassword') }));
37
50
  default:
38
51
  return null;
39
52
  }
40
53
  };
41
54
  return (_jsx(AdvancedTransitionWrapper, { type: 'slide', duration: 0.5, children: _jsxs(Flex, { direction: 'column', justify: 'center', style: { width: '70%' }, className: `lg:w-[70%] lg:h-screen mx-auto h-full my-10`, children: [React.isValidElement(LogoComponent)
42
- && LogoComponent, renderStep()] }) }));
55
+ && LogoComponent, renderStep(), _jsx(LoginWithEmailOTPModal, { show: showLoginWithEmailOTPModal, hide: () => setShowLoginWithEmailOTPModal(false), email: email })] }) }));
43
56
  };
44
57
  export { LoginWebComponent };
58
+ const LoginWithEmailOTPModal = ({ show, hide, email }) => {
59
+ return (_jsx(ResponsiveModal, { open: show, onClose: hide, title: _jsx(Typography, { type: "heading", size: "h6", className: "text-center mb-0", children: "Verify Your Email" }), showCloseButton: false, maskClosable: false, size: 'sm', children: _jsx(LoginWithEmailOTP, { email: email }) }));
60
+ };
@@ -0,0 +1,72 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { showMessage } from "../../../../helpers/show-message";
3
+ import { useAuthPackageContext } from "../../../../hooks/internal/useAuthPackageContext";
4
+ import { axiosClient } from "../../../../api/axiosClient";
5
+ /**
6
+ * @internal
7
+ * Hook for handling Email OTP Login flow.
8
+ */
9
+ const useLoginWithEmailOTP = ({ email, source }) => {
10
+ const [resendOTPCounter, setResendOTPCounter] = useState(0);
11
+ const [status, setStatus] = useState("idle");
12
+ const sessionTokenRef = useRef("");
13
+ const { onLogin } = useAuthPackageContext();
14
+ /**
15
+ * Sends an OTP to the provided email.
16
+ */
17
+ const sendOtp = useCallback(() => {
18
+ setStatus("loading");
19
+ axiosClient({
20
+ url: '/auth/login/email',
21
+ method: 'POST',
22
+ data: { email, source },
23
+ }).then((res) => {
24
+ setResendOTPCounter(prevCounter => prevCounter + 1);
25
+ sessionTokenRef.current = res.data.sessionToken;
26
+ }).catch((error) => {
27
+ console.error('Failed to send OTP:', error);
28
+ const message = error?.response?.data?.errors?.[0]?.message || "Failed to send OTP. Please try again.";
29
+ showMessage({ message });
30
+ }).finally(() => setStatus("idle"));
31
+ }, [email, source]);
32
+ /**
33
+ * Verifies the entered OTP and logs the user in.
34
+ */
35
+ const verifyOtp = useCallback(async (otp) => {
36
+ setStatus("loading");
37
+ axiosClient({
38
+ url: '/auth/login/email/verify-otp',
39
+ method: 'POST',
40
+ data: { otp, sessionToken: sessionTokenRef.current },
41
+ }).then((res) => {
42
+ setStatus('valid');
43
+ const { token, member } = res.data;
44
+ onLogin({ token, member });
45
+ }).catch((error) => {
46
+ const message = error?.response?.data?.errors?.[0]?.message ||
47
+ (error?.response?.status === 400
48
+ ? "Invalid OTP. Please try again."
49
+ : "Verification failed. Please try again.");
50
+ showMessage({ message });
51
+ setStatus(error?.response?.status === 400 ? "invalid" : "idle");
52
+ if (error?.response?.status === 400) {
53
+ setTimeout(() => setStatus("idle"), 5000);
54
+ }
55
+ });
56
+ }, [sessionTokenRef.current]);
57
+ /**
58
+ * Automatically sends OTP when email and source are provided.
59
+ */
60
+ useEffect(() => {
61
+ if (email && source) {
62
+ sendOtp();
63
+ }
64
+ }, [email, source, sendOtp]);
65
+ return {
66
+ sendOtp,
67
+ verifyOtp,
68
+ resendOTPCounter,
69
+ status,
70
+ };
71
+ };
72
+ export { useLoginWithEmailOTP };
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useLoginWithEmailOTP } from "./hooks/internal/useLoginWithEmailOTP";
3
+ import { VerifyEmailOTP } from "../../components/VerifyEmailOTP";
4
+ const LoginWithEmailOTP = ({ email }) => {
5
+ const { resendOTPCounter, status, verifyOtp, sendOtp, } = useLoginWithEmailOTP({ email, source: 'web' });
6
+ return (_jsx(VerifyEmailOTP, { email: email, validateOTP: verifyOtp, resendOTP: sendOtp, status: status, resendOTPCounter: resendOTPCounter }));
7
+ };
8
+ export default LoginWithEmailOTP;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Platform } from 'react-native';
3
+ import { VerifyEmailOTP } from '../../components/VerifyEmailOTP';
4
+ import { useLoginWithEmailOTP } from './hooks/internal/useLoginWithEmailOTP';
5
+ const LoginWithEmailOTP = ({ route }) => {
6
+ const { email } = route.params;
7
+ const { resendOTPCounter, status, verifyOtp, sendOtp, } = useLoginWithEmailOTP({ email, source: Platform.OS });
8
+ return (_jsx(VerifyEmailOTP, { email: email, status: status, validateOTP: verifyOtp, resendOTP: sendOtp, resendOTPCounter: resendOTPCounter }));
9
+ };
10
+ export default LoginWithEmailOTP;
@@ -0,0 +1,55 @@
1
+ import { useState } from "react";
2
+ import { showMessage } from "../../../../helpers/show-message";
3
+ import { axiosClient } from "../../../../api/axiosClient";
4
+ import { defaultPolicy } from "../../../../constants/defaultPolicy";
5
+ /**
6
+ * @internal
7
+ * Hook for managing ResetPassword screen state and auth context integration.
8
+ * This hook is not exposed to package consumers.
9
+ */
10
+ const useResetPassword = ({ sessionToken }) => {
11
+ const [loading, setLoading] = useState(false);
12
+ const [passwordVisible, setPasswordVisible] = useState(false);
13
+ const [confirmPasswordVisible, setConfirmPasswordVisible] = useState(false);
14
+ const [password, setPassword] = useState('');
15
+ const [confirmPassword, setConfirmPassword] = useState('');
16
+ const [maxLength, setMaxLength] = useState(defaultPolicy.maxLength);
17
+ const [criteria, setCriteria] = useState({});
18
+ const handlePassword = (text) => {
19
+ setPassword(text.replace(/\s/g, ''));
20
+ };
21
+ const handleConfirmPassword = (text) => {
22
+ setConfirmPassword(text.replace(/\s/g, ''));
23
+ };
24
+ const handleContinue = ({ onResult }) => {
25
+ setLoading(true);
26
+ axiosClient({
27
+ url: '/auth/reset-password',
28
+ method: 'POST',
29
+ data: { sessionToken, password },
30
+ }).then(() => {
31
+ showMessage({ message: 'Password updated successfully. Please login with your new credentials.', type: 'success' });
32
+ onResult();
33
+ }).catch((err) => {
34
+ console.log(err);
35
+ if (err?.response?.status === 401) {
36
+ showMessage({ message: 'Your session expired. Please request a new OTP.' });
37
+ return;
38
+ }
39
+ showMessage({ message: err?.response?.data?.errors?.[0]?.message || 'An unexpected error occurred' });
40
+ }).finally(() => setLoading(false));
41
+ };
42
+ return {
43
+ loading,
44
+ password, setPassword,
45
+ confirmPassword, setConfirmPassword,
46
+ passwordVisible, setPasswordVisible,
47
+ confirmPasswordVisible, setConfirmPasswordVisible,
48
+ maxLength, setMaxLength,
49
+ criteria, setCriteria,
50
+ handlePassword,
51
+ handleContinue,
52
+ handleConfirmPassword,
53
+ };
54
+ };
55
+ export { useResetPassword };
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Flex, Form, PasswordInput, useForm } from "@truworth/twc-web-design";
3
+ import { useResetPassword } from "./hooks/internal/useResetPassword";
4
+ import { ScreenLayout } from "../../components/ScreenLayout";
5
+ import { PasswordCriteria } from "../../components/PasswordCriteria";
6
+ const ResetPassword = ({ email, sessionToken, onPressBack, onContinue }) => {
7
+ const form = useForm({
8
+ defaultValues: {
9
+ password: '',
10
+ confirmPassword: '',
11
+ }
12
+ });
13
+ const { password, confirmPassword } = form.watch();
14
+ const { loading, criteria, setCriteria, maxLength, setMaxLength, handlePassword, handleConfirmPassword, handleContinue, } = useResetPassword({ sessionToken });
15
+ return (_jsx(ScreenLayout, { title: "Reset your Password", subTitle: "Use a password that\u2019s easy to remember and fulfills all the requirements listed below.", buttonProps: {
16
+ loading,
17
+ label: 'Continue',
18
+ onClick: () => handleContinue({ onResult: onContinue }),
19
+ disabled: !password || !confirmPassword || (password !== confirmPassword)
20
+ }, onPressBack: onPressBack, children: _jsx(Flex, { direction: "column", children: _jsxs(Form, { form: form, className: "w-full", children: [_jsx(Form.Item, { label: "Enter Password", normalize: (value) => value.replace(/\s/g, ''), rules: [
21
+ {
22
+ required: true,
23
+ message: 'Please enter your password'
24
+ }
25
+ ], ...form.register('password', {
26
+ onChange: (e) => handlePassword(e.target.value)
27
+ }), 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, { label: "Confirm Password", className: "mt-6", normalize: (value) => value.replace(/\s/g, ''), rules: [
28
+ {
29
+ required: true,
30
+ message: 'Please confirm your password',
31
+ },
32
+ {
33
+ validator: async (_, value) => {
34
+ const password = form.getValues('password');
35
+ if (!value)
36
+ return;
37
+ if (password !== value) {
38
+ throw new Error('Passwords don’t match');
39
+ }
40
+ },
41
+ },
42
+ ], ...form.register('confirmPassword', {
43
+ onChange: (e) => handleConfirmPassword(e.target.value)
44
+ }), children: _jsx(PasswordInput, { className: "mb-6", value: confirmPassword, placeholder: "Re-enter password", maxLength: maxLength, showStrengthIndicator: false }) })] }) }) }));
45
+ };
46
+ export default ResetPassword;
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Colors, TextInputField } from '@truworth/twc-rn-common';
3
+ import { Layout } from '@ui-kitten/components';
4
+ import { ScreenLayout } from '../../components/ScreenLayout';
5
+ import { useResetPassword } from './hooks/internal/useResetPassword';
6
+ import { PasswordCriteria } from '../../components/PasswordCriteria/index.native';
7
+ const { primary } = Colors;
8
+ const ResetPassword = ({ navigation, route }) => {
9
+ const { sessionToken, email } = route.params || {};
10
+ const { loading, password, confirmPassword, passwordVisible, setPasswordVisible, confirmPasswordVisible, setConfirmPasswordVisible, maxLength, setMaxLength, criteria, setCriteria, handlePassword, handleContinue, handleConfirmPassword, } = useResetPassword({ sessionToken });
11
+ return (_jsx(Layout, { style: { flex: 1, backgroundColor: primary.white }, children: _jsxs(ScreenLayout, { title: "Reset your Password", subTitle: "Use a password that\u2019s easy to remember and fulfills all the requirements listed below.", buttonProps: {
12
+ loading,
13
+ label: 'Continue',
14
+ onPress: () => {
15
+ handleContinue({
16
+ onResult: () => navigation.navigate('EnterEmail')
17
+ });
18
+ },
19
+ disabled: !Object.keys(criteria).every(c => criteria[c]) || (password !== confirmPassword),
20
+ }, children: [_jsx(TextInputField, { placeholder: "Enter password...", onChangeValue: handlePassword, secureTextEntry: !passwordVisible, value: password, rightIcon2: passwordVisible
21
+ ? 'eye-outline'
22
+ : 'eye-off-outline', onPressRightIcon2: () => setPasswordVisible(!passwordVisible), autoFocus: true, maxLength: maxLength }), _jsx(PasswordCriteria, { email: email, criteria: criteria, onCriteriaChange: setCriteria, onMaxLengthChange: setMaxLength, password: password }), _jsx(TextInputField, { placeholder: "Confirm password...", onChangeValue: handleConfirmPassword, secureTextEntry: !confirmPasswordVisible, value: confirmPassword, rightIcon2: confirmPasswordVisible
23
+ ? 'eye-outline'
24
+ : 'eye-off-outline', onPressRightIcon2: () => setConfirmPasswordVisible(!confirmPasswordVisible), maxLength: maxLength, containerStyle: { marginTop: 16 } })] }) }));
25
+ };
26
+ export default ResetPassword;
@@ -0,0 +1 @@
1
+ export {};