@fadyshawky/react-native-magic 2.0.3 → 2.0.5

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 (110) hide show
  1. package/package.json +1 -1
  2. package/template/src/common/ImageResources.g.ts +33 -1
  3. package/template/src/common/components/Background.tsx +3 -1
  4. package/template/src/common/components/Container.tsx +1 -1
  5. package/template/src/common/components/OTPInput.tsx +3 -2
  6. package/template/src/common/components/PrimaryButton.tsx +23 -23
  7. package/template/src/common/components/PrimaryTextInput.tsx +189 -199
  8. package/template/src/common/components/RadioIcon.tsx +4 -4
  9. package/template/src/common/components/SafeText.tsx +41 -0
  10. package/template/src/common/components/SearchBar.tsx +19 -17
  11. package/template/src/common/components/TryAgain.tsx +3 -3
  12. package/template/src/common/localization/localization.ts +10 -0
  13. package/template/src/common/localization/translations/commonLocalization.ts +97 -0
  14. package/template/src/common/localization/translations/homeLocalization.ts +24 -0
  15. package/template/src/common/localization/translations/loginLocalization.ts +28 -2
  16. package/template/src/common/localization/translations/mainNavigationLocalization.ts +30 -0
  17. package/template/src/common/localization/translations/navigationLocalization.ts +48 -0
  18. package/template/src/common/localization/translations/otpLocalization.ts +28 -0
  19. package/template/src/common/localization/translations/passwordLocalization.ts +54 -0
  20. package/template/src/common/localization/translations/posLocalization.ts +196 -0
  21. package/template/src/common/utils/FeesCaalculation.tsx +37 -0
  22. package/template/src/common/utils/index.tsx +11 -0
  23. package/template/src/common/utils/printData.tsx +161 -0
  24. package/template/src/common/validations/errorValidations.ts +3 -2
  25. package/template/src/components/PrinterExample.js +226 -0
  26. package/template/src/core/api/serverHeaders.ts +62 -1
  27. package/template/src/core/store/Categories/categoryActions.ts +33 -0
  28. package/template/src/core/store/Categories/categorySlice.ts +75 -0
  29. package/template/src/core/store/Categories/categoryState.ts +41 -0
  30. package/template/src/core/store/Providers/providersActions.ts +102 -0
  31. package/template/src/core/store/Providers/providersSlice.ts +136 -0
  32. package/template/src/core/store/Providers/providersState.ts +37 -0
  33. package/template/src/core/store/Services/servicesActions.ts +191 -0
  34. package/template/src/core/store/Services/servicesSlice.ts +205 -0
  35. package/template/src/core/store/Services/servicesState.ts +466 -0
  36. package/template/src/core/store/app/appSlice.ts +13 -5
  37. package/template/src/core/store/app/appState.ts +10 -2
  38. package/template/src/core/store/rootReducer.ts +6 -1
  39. package/template/src/core/store/store.tsx +55 -2
  40. package/template/src/core/store/user/userActions.ts +164 -26
  41. package/template/src/core/store/user/userSlice.ts +193 -21
  42. package/template/src/core/store/user/userState.ts +148 -25
  43. package/template/src/core/theme/colors.ts +70 -94
  44. package/template/src/core/theme/commonConsts.ts +1 -1
  45. package/template/src/core/theme/commonSizes.ts +94 -119
  46. package/template/src/core/theme/commonStyles.ts +22 -22
  47. package/template/src/core/theme/fonts.ts +14 -13
  48. package/template/src/core/theme/themes.ts +75 -386
  49. package/template/src/core/theme/types.ts +15 -201
  50. package/template/src/core/utils/stringUtils.ts +114 -0
  51. package/template/src/modules/SunmiCard.js +212 -0
  52. package/template/src/modules/SunmiPrepaid.ts +122 -0
  53. package/template/src/navigation/AuthStack.tsx +8 -0
  54. package/template/src/navigation/HeaderComponents.tsx +76 -1
  55. package/template/src/navigation/MainNavigation.tsx +3 -1
  56. package/template/src/navigation/MainStack.tsx +130 -56
  57. package/template/src/navigation/TabBar.tsx +111 -59
  58. package/template/src/navigation/types.ts +24 -0
  59. package/template/src/screens/Categories/Categories.tsx +141 -0
  60. package/template/src/screens/Categories/hooks/useCategoriesData.ts +33 -0
  61. package/template/src/screens/Categories/types.ts +7 -0
  62. package/template/src/screens/Favorites/Favorites.tsx +130 -0
  63. package/template/src/screens/ForceChangePassword/ForceChangePasswordScreen.tsx +155 -0
  64. package/template/src/screens/History/History.tsx +430 -0
  65. package/template/src/screens/History/hooks/useHistoryData.ts +49 -0
  66. package/template/src/screens/History/types.ts +7 -0
  67. package/template/src/screens/InquiredBill/InquiredBill.tsx +443 -0
  68. package/template/src/screens/InquiredBill/hooks/useInquiredData.ts +91 -0
  69. package/template/src/screens/Login/Login.tsx +85 -85
  70. package/template/src/screens/OTP/OTPScreen.tsx +170 -0
  71. package/template/src/screens/PaymentConfirmation/PaymentConfirmation.tsx +326 -0
  72. package/template/src/screens/Providers/Providers.tsx +166 -0
  73. package/template/src/screens/Providers/hooks/useProvidersData.ts +33 -0
  74. package/template/src/screens/Providers/types.ts +7 -0
  75. package/template/src/screens/ReceiptScreen/ReceiptScreen.tsx +181 -0
  76. package/template/src/screens/ReceiptScreen/hooks/useReceiptData.ts +46 -0
  77. package/template/src/screens/ReceiptScreen/utils/utils.tsx +156 -0
  78. package/template/src/screens/Services/Services.tsx +144 -0
  79. package/template/src/screens/Services/hooks/useServicesData.ts +41 -0
  80. package/template/src/screens/SingleService/Components/FawryInputs.tsx +446 -0
  81. package/template/src/screens/SingleService/SingleService.tsx +229 -0
  82. package/template/src/screens/SingleService/hooks/useServiceData.ts +164 -0
  83. package/template/src/screens/home/Components/PayByCode.tsx +129 -0
  84. package/template/src/screens/home/HomeScreen.tsx +268 -77
  85. package/template/src/screens/home/hooks/useHomeData.ts +32 -38
  86. package/template/src/screens/index.tsx +24 -0
  87. package/template/src/screens/profile/Profile.tsx +290 -2
  88. package/template/src/services/SunmiPrinterInternal.js +268 -0
  89. package/template/src/types/sunmiPrepaid.d.ts +20 -0
  90. package/template/src/utils/SunmiPrinter.ts +442 -0
  91. package/template/src/utils/feesCalculator.ts +92 -0
  92. package/template/src/common/components/Stepper.tsx +0 -153
  93. package/template/src/common/components/Svg.tsx +0 -25
  94. package/template/src/common/hooks/useDebounce.ts +0 -17
  95. package/template/src/common/hooks/usePrevious.ts +0 -11
  96. package/template/src/common/localization/intlFormatter.ts +0 -37
  97. package/template/src/common/urls/emailUrl.ts +0 -20
  98. package/template/src/common/urls/mapUrl.ts +0 -22
  99. package/template/src/common/utils/listHandlers.ts +0 -30
  100. package/template/src/common/utils/serializeQueryParams.ts +0 -10
  101. package/template/src/common/validations/hooks/useDatesError.ts +0 -40
  102. package/template/src/common/validations/profileValidations.ts +0 -30
  103. package/template/src/core/theme/shadows.ts +0 -135
  104. package/template/src/navigation/TopTabBar.tsx +0 -77
  105. package/template/src/screens/Settings/Settings.tsx +0 -5
  106. package/template/src/screens/home/components/CarouselSection.tsx +0 -79
  107. package/template/src/screens/home/components/FeaturedCarousel.tsx +0 -128
  108. package/template/src/screens/main/Main.tsx +0 -5
  109. package/template/src/screens/registration/RegistrationScreen.tsx +0 -198
  110. package/template/src/screens/resetPassword/ForgotPasswordScreen.tsx +0 -129
@@ -1,39 +1,46 @@
1
1
  import {useNavigation} from '@react-navigation/native';
2
2
  import type {NativeStackNavigationProp} from '@react-navigation/native-stack';
3
- import {toLower} from 'lodash';
4
3
  import React, {useRef, useState} from 'react';
5
- import {
6
- findNodeHandle,
7
- NativeSyntheticEvent,
8
- StyleSheet,
9
- Text,
10
- TextInputFocusEventData,
11
- } from 'react-native';
4
+ import {Dimensions, StyleSheet} from 'react-native';
12
5
  import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
13
6
  import {ButtonType} from '../../../types';
7
+ import {Container} from '../../common/components/Container';
14
8
  import {PrimaryButton} from '../../common/components/PrimaryButton';
15
9
  import {PrimaryTextInput} from '../../common/components/PrimaryTextInput';
16
- import {localization} from '../../common/localization/localization';
10
+ import {RTLAwareText} from '../../common/components/RTLAwareText';
11
+ import {RTLAwareView} from '../../common/components/RTLAwareView';
12
+ import {ImageResources} from '../../common/ImageResources.g';
13
+ import {
14
+ useTranslation,
15
+ useRTL,
16
+ } from '../../common/localization/LocalizationProvider';
17
+ import {phoneValidations} from '../../common/validations/authValidations';
17
18
  import {emptyValidation} from '../../common/validations/commonValidations';
18
19
  import {useInputError} from '../../common/validations/hooks/useInputError';
19
- import {emailValidations} from '../../common/validations/profileValidations';
20
20
  import {useAppDispatch} from '../../core/store/reduxHelpers';
21
21
  import {userLogin} from '../../core/store/user/userActions';
22
22
  import {CommonSizes} from '../../core/theme/commonSizes';
23
- import {CommonStyles} from '../../core/theme/commonStyles';
23
+ import {Fonts} from '../../core/theme/fonts';
24
+ import {useTheme} from '../../core/theme/ThemeProvider';
25
+ import {Header} from '../../navigation/HeaderComponents';
24
26
  import type {RootStackParamList} from '../../navigation/types';
27
+ import {scaleHeight} from '../../core/theme/scaling';
25
28
 
26
29
  export function Login(): JSX.Element {
27
30
  const dispatch = useAppDispatch();
28
- const [email, setEmail] = useState('');
31
+ const [phone, setPhone] = useState('');
29
32
  const [password, setPassword] = useState('');
30
33
  const [loading, setLoading] = useState(false);
31
34
  const navigation =
32
35
  useNavigation<NativeStackNavigationProp<RootStackParamList>>();
36
+ const scroll = useRef<KeyboardAwareScrollView>(null);
37
+ const {theme} = useTheme();
38
+ const t = useTranslation();
39
+ const isRTL = useRTL();
33
40
 
34
- const {error: emailError, recheckValue: recheckEmail} = useInputError(
35
- email,
36
- emailValidations,
41
+ const {error: phoneError, recheckValue: recheckPhone} = useInputError(
42
+ phone,
43
+ phoneValidations,
37
44
  );
38
45
  const {error: passwordError, recheckValue: recheckPassword} = useInputError(
39
46
  password,
@@ -41,111 +48,104 @@ export function Login(): JSX.Element {
41
48
  );
42
49
 
43
50
  async function loginUser() {
44
- const emailValid = recheckEmail() === null;
51
+ const phoneValid = recheckPhone() === null;
45
52
  const passwordValid = recheckPassword() === null;
46
53
 
47
- if (!emailValid || !passwordValid) {
54
+ if (!phoneValid || !passwordValid) {
48
55
  return;
49
56
  }
57
+ try {
58
+ setLoading(true);
59
+ const result = await dispatch(
60
+ userLogin({
61
+ phone: phone,
62
+ password,
63
+ }),
64
+ );
50
65
 
51
- setLoading(true);
52
- await dispatch(
53
- userLogin({
54
- email: toLower(email),
55
- password,
56
- }),
57
- );
58
- setLoading(false);
59
- }
60
- const scroll = useRef<KeyboardAwareScrollView>(null);
61
- function scrollToInput(reactNode: any) {
62
- // Add a 'scroll' ref to your ScrollView
63
-
64
- // setTimeout(() => {
65
- scroll.current?.scrollToFocusedInput(reactNode);
66
- // }, 500);
66
+ if (userLogin.fulfilled.match(result)) {
67
+ navigation.navigate('OTP', {phone: phone});
68
+ }
69
+ } catch (error) {
70
+ } finally {
71
+ setLoading(false);
72
+ }
67
73
  }
68
74
 
69
- const goToRegistration = () => {
70
- navigation.navigate('Registration');
71
- };
72
-
73
75
  const goToForgotPassword = () => {
74
76
  navigation.navigate('ForgotPassword');
75
77
  };
76
78
 
77
79
  return (
78
- <KeyboardAwareScrollView
80
+ <Container
79
81
  ref={scroll}
80
- resetScrollToCoords={{x: 0, y: 0}}
81
- scrollEnabled={true}
82
- enableOnAndroid={true}
83
- testID={'MainPageID'}
82
+ testID={'LoginScreenID'}
84
83
  contentContainerStyle={styles.contentContainer}
85
- contentInsetAdjustmentBehavior={'automatic'}
86
- style={styles.container}>
87
- <Text style={CommonStyles.h1_semiBold}>{localization.login.Login}</Text>
84
+ style={styles.container}
85
+ backgroundImage={ImageResources.background_2}
86
+ withoutPadding
87
+ extendedBackground
88
+ backgroundColor={theme.colors.background}>
89
+ <Header />
90
+ <RTLAwareText style={{...theme.text.header1, textAlign: 'center'}}>
91
+ {t('welcome', 'login')}
92
+ </RTLAwareText>
88
93
  <PrimaryTextInput
89
- onFocus={(event: NativeSyntheticEvent<TextInputFocusEventData>) => {
90
- scrollToInput(findNodeHandle(event.target));
91
- }}
92
- autoCapitalize="none"
93
- value={email}
94
- onChangeText={setEmail}
95
- containerStyle={CommonStyles.textInputContainer}
96
- label={localization.login.Email}
97
- placeholder={localization.login.EnterEmail}
98
- error={emailError}
94
+ value={phone}
95
+ onChangeText={setPhone}
96
+ error={phoneError}
97
+ keyboardType="numeric"
98
+ placeholder={t('EnterPhone', 'login')}
99
99
  />
100
100
  <PrimaryTextInput
101
- onFocus={(event: NativeSyntheticEvent<TextInputFocusEventData>) => {
102
- scrollToInput(findNodeHandle(event.target));
103
- }}
104
- clearButtonMode="never"
105
101
  value={password}
106
- secureTextEntry
107
102
  onChangeText={setPassword}
108
- containerStyle={CommonStyles.textInputContainer}
109
- label={localization.login.Password}
110
- placeholder={localization.login.EnterPassword}
111
103
  error={passwordError}
104
+ secureTextEntry={true}
105
+ keyboardType="numeric"
106
+ placeholder={t('EnterPassword', 'login')}
112
107
  />
108
+
109
+ <RTLAwareView
110
+ style={{width: '100%', alignItems: isRTL ? 'flex-start' : 'flex-end'}}>
111
+ <PrimaryButton
112
+ label={t('forgetPassword', 'login')}
113
+ onPressIn={goToForgotPassword}
114
+ type={ButtonType.borderless}
115
+ />
116
+ </RTLAwareView>
113
117
  <PrimaryButton
118
+ label={t('Login', 'login')}
119
+ onPressIn={loginUser}
114
120
  isLoading={loading}
115
- onPress={loginUser}
116
- label={localization.login.continue}
121
+ disabled={loading}
117
122
  type={ButtonType.solid}
118
123
  />
119
- </KeyboardAwareScrollView>
124
+ </Container>
120
125
  );
121
126
  }
122
127
 
123
128
  const styles = StyleSheet.create({
124
- imageContainer: {
125
- ...CommonStyles.flex1,
126
- justifyContent: 'flex-end',
127
- },
128
129
  container: {
129
130
  flexGrow: 1,
130
- borderTopRightRadius: CommonSizes.borderRadius.lg,
131
- borderTopLeftRadius: CommonSizes.borderRadius.lg,
131
+ borderTopRightRadius: CommonSizes.spacing.large,
132
+ borderTopLeftRadius: CommonSizes.spacing.large,
133
+ paddingHorizontal: CommonSizes.spacing.large,
134
+ gap: CommonSizes.spacing.xl,
135
+ justifyContent: 'flex-start',
132
136
  },
133
137
  contentContainer: {
134
- justifyContent: 'center',
135
- alignItems: 'center',
136
- paddingHorizontal: CommonSizes.spacing.lg,
137
- paddingVertical: 26,
138
- gap: 16,
138
+ flexGrow: 1,
139
139
  },
140
- forgotPassword: {
141
- ...CommonStyles.normalText,
142
- textAlign: 'right',
143
- marginTop: 8,
144
- marginBottom: 24,
140
+ formContainer: {
141
+ alignItems: 'center',
142
+ paddingHorizontal: CommonSizes.spacing.large,
143
+ gap: CommonSizes.spacing.large,
145
144
  },
146
- registerLink: {
147
- ...CommonStyles.normalText,
145
+ title: {
148
146
  textAlign: 'center',
149
- marginTop: 16,
147
+ fontSize: CommonSizes.fontSize.header1,
148
+ fontWeight: 'bold',
149
+ fontFamily: Fonts.regular,
150
150
  },
151
151
  });
@@ -0,0 +1,170 @@
1
+ import {useNavigation, useRoute} from '@react-navigation/native';
2
+ import type {NativeStackNavigationProp} from '@react-navigation/native-stack';
3
+ import React, {useRef, useState} from 'react';
4
+ import {StyleSheet, Text, View} from 'react-native';
5
+ import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
6
+ import {ButtonType} from '../../../types';
7
+ import {Container} from '../../common/components/Container';
8
+ import {PrimaryButton} from '../../common/components/PrimaryButton';
9
+ import {OTPInput} from '../../common/components/OTPInput';
10
+ import {useInputError} from '../../common/validations/hooks/useInputError';
11
+ import {useAppDispatch} from '../../core/store/reduxHelpers';
12
+ import {CommonSizes} from '../../core/theme/commonSizes';
13
+ import {useTheme} from '../../core/theme/ThemeProvider';
14
+ import {ImageResources} from '../../common/ImageResources.g';
15
+ import {Header, HeaderBack} from '../../navigation/HeaderComponents';
16
+ import type {RootStackParamList} from '../../navigation/types';
17
+ import {verifyOTP} from '../../core/store/user/userActions';
18
+ import {
19
+ useTranslation,
20
+ useRTL,
21
+ } from '../../common/localization/LocalizationProvider';
22
+ import {RTLAwareText} from '../../common/components/RTLAwareText';
23
+ import {RTLAwareView} from '../../common/components/RTLAwareView';
24
+
25
+ export function OTPScreen(): JSX.Element {
26
+ const dispatch = useAppDispatch();
27
+ const [otp, setOTP] = useState('');
28
+ const [loading, setLoading] = useState(false);
29
+ const [resendDisabled, setResendDisabled] = useState(false);
30
+ const [timer, setTimer] = useState(60);
31
+ const navigation =
32
+ useNavigation<NativeStackNavigationProp<RootStackParamList>>();
33
+ const route = useRoute();
34
+ const scroll = useRef<KeyboardAwareScrollView>(null);
35
+ const {theme} = useTheme();
36
+ const {phone} = route.params as {phone: string};
37
+ const t = useTranslation();
38
+ const isRTL = useRTL();
39
+
40
+ const {error: otpError, recheckValue: recheckOTP} = useInputError(
41
+ otp,
42
+ value => (value.length === 6 ? null : 'Please enter a valid OTP code'),
43
+ );
44
+
45
+ async function handleVerifyOTP() {
46
+ const otpValid = recheckOTP() === null;
47
+
48
+ if (!otpValid) {
49
+ return;
50
+ }
51
+
52
+ try {
53
+ setLoading(true);
54
+
55
+ const result = await dispatch(
56
+ verifyOTP({
57
+ verification_code: otp?.toString(),
58
+ mobile_number: phone,
59
+ device_token: undefined,
60
+ scheme_id: 1,
61
+ }),
62
+ );
63
+ } catch (error) {
64
+ } finally {
65
+ setLoading(false);
66
+ }
67
+ }
68
+
69
+ const handleResendOTP = () => {
70
+ if (resendDisabled) return;
71
+
72
+ setResendDisabled(true);
73
+ setTimer(60);
74
+
75
+ // Add your resend OTP logic here
76
+ // dispatch(resendOTP());
77
+
78
+ const interval = setInterval(() => {
79
+ setTimer(prevTimer => {
80
+ if (prevTimer <= 1) {
81
+ clearInterval(interval);
82
+ setResendDisabled(false);
83
+ return 0;
84
+ }
85
+ return prevTimer - 1;
86
+ });
87
+ }, 1000);
88
+ };
89
+
90
+ return (
91
+ <Container
92
+ ref={scroll}
93
+ testID={'OTPScreenID'}
94
+ contentContainerStyle={styles.contentContainer}
95
+ style={styles.container}
96
+ backgroundImage={ImageResources.background_2}
97
+ withoutPadding
98
+ extendedBackground
99
+ backgroundColor={theme.colors.background}>
100
+ <HeaderBack onPress={() => navigation.goBack()} />
101
+ <RTLAwareText style={{...theme.text.header1, textAlign: 'center'}}>
102
+ {t('title', 'otp')}
103
+ </RTLAwareText>
104
+ <RTLAwareText style={{...theme.text.body2, textAlign: 'center'}}>
105
+ {t('subtitle', 'otp')}
106
+ </RTLAwareText>
107
+ <View style={styles.inputContainer}>
108
+ <OTPInput value={otp} onChange={setOTP} error={otpError} />
109
+ {otpError && (
110
+ <RTLAwareText style={[theme.text.body2, styles.errorText]}>
111
+ {otpError}
112
+ </RTLAwareText>
113
+ )}
114
+ </View>
115
+ <RTLAwareView style={styles.resendContainer}>
116
+ <RTLAwareText style={{...theme.text.body2}}>
117
+ {resendDisabled
118
+ ? t('resendIn', 'otp').replace('{0}', timer?.toString())
119
+ : t('didntReceiveCode', 'otp')}
120
+ </RTLAwareText>
121
+ <PrimaryButton
122
+ label={t('resend', 'otp')}
123
+ onPressIn={handleResendOTP}
124
+ disabled={resendDisabled}
125
+ type={ButtonType.borderless}
126
+ style={styles.resendButton}
127
+ />
128
+ </RTLAwareView>
129
+ <PrimaryButton
130
+ label={t('verify', 'otp')}
131
+ onPressIn={handleVerifyOTP}
132
+ isLoading={loading}
133
+ disabled={loading || otp.length < 4}
134
+ type={ButtonType.solid}
135
+ />
136
+ </Container>
137
+ );
138
+ }
139
+
140
+ const styles = StyleSheet.create({
141
+ container: {
142
+ flexGrow: 1,
143
+ borderTopRightRadius: CommonSizes.spacing.large,
144
+ borderTopLeftRadius: CommonSizes.spacing.large,
145
+ paddingHorizontal: CommonSizes.spacing.medium,
146
+ gap: 8,
147
+ justifyContent: 'flex-start',
148
+ },
149
+ contentContainer: {
150
+ flexGrow: 1,
151
+ },
152
+ inputContainer: {
153
+ width: '100%',
154
+ marginTop: CommonSizes.spacing.large,
155
+ },
156
+ resendContainer: {
157
+ flexDirection: 'row',
158
+ alignItems: 'center',
159
+ justifyContent: 'center',
160
+ marginTop: CommonSizes.spacing.small,
161
+ },
162
+ resendButton: {
163
+ marginLeft: CommonSizes.spacing.small,
164
+ },
165
+ errorText: {
166
+ color: '#FF4444',
167
+ marginTop: CommonSizes.spacing.small,
168
+ textAlign: 'center',
169
+ },
170
+ });