@fadyshawky/react-native-magic 1.0.8 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/.vscode/settings.json +7 -0
  2. package/package.json +1 -1
  3. package/template/App.tsx +30 -9
  4. package/template/package-lock.json +181 -123
  5. package/template/package.json +2 -0
  6. package/template/src/common/ImageResources.g.ts +3 -5
  7. package/template/src/common/components/Background.tsx +66 -28
  8. package/template/src/common/components/Cards.tsx +116 -0
  9. package/template/src/common/components/Container.tsx +145 -0
  10. package/template/src/common/components/FlatListWrapper.tsx +1 -0
  11. package/template/src/common/components/ImageCropPickerButton.tsx +1 -1
  12. package/template/src/common/components/OTPInput.tsx +107 -0
  13. package/template/src/common/components/PhotoTakingButton.tsx +1 -4
  14. package/template/src/common/components/PrimaryButton.tsx +171 -157
  15. package/template/src/common/components/RTLAwareText.tsx +42 -0
  16. package/template/src/common/components/RTLAwareView.tsx +179 -0
  17. package/template/src/common/components/RadioButton.tsx +1 -3
  18. package/template/src/common/components/RadioIcon.tsx +1 -2
  19. package/template/src/common/components/SearchBar.tsx +179 -0
  20. package/template/src/common/components/Separator.tsx +7 -4
  21. package/template/src/common/components/TouchablePlatform.tsx +1 -3
  22. package/template/src/common/components/TryAgain.tsx +3 -3
  23. package/template/src/common/helpers/inAppReviewHelper.ts +0 -1
  24. package/template/src/common/helpers/stringsHelpers.ts +10 -0
  25. package/template/src/common/hooks/useFlatListActions.ts +1 -1
  26. package/template/src/common/localization/LocalizationProvider.tsx +152 -0
  27. package/template/src/common/localization/README.md +488 -0
  28. package/template/src/common/localization/localization.ts +12 -0
  29. package/template/src/common/localization/translations/profileLocalization.ts +24 -0
  30. package/template/src/common/validations/errorValidations.ts +1 -6
  31. package/template/src/common/validations/examples/TextInputWithValidation.tsx +229 -0
  32. package/template/src/common/validations/index.ts +28 -0
  33. package/template/src/common/validations/regex.js +83 -0
  34. package/template/src/common/validations/regexValidator.ts +240 -0
  35. package/template/src/common/validations/validationConstants.ts +2 -2
  36. package/template/src/core/api/errorHandler.ts +39 -0
  37. package/template/src/core/api/responseHandlers.ts +1 -26
  38. package/template/src/core/api/serverHeaders.ts +13 -23
  39. package/template/src/core/store/app/appSlice.ts +1 -2
  40. package/template/src/core/theme/ThemeProvider.tsx +63 -0
  41. package/template/src/core/theme/colors.ts +31 -42
  42. package/template/src/core/theme/commonConsts.ts +1 -1
  43. package/template/src/core/theme/commonStyles.ts +267 -210
  44. package/template/src/core/theme/fonts.ts +17 -1
  45. package/template/src/core/theme/scaling.ts +101 -0
  46. package/template/src/core/theme/themes.ts +214 -0
  47. package/template/src/core/theme/types.ts +51 -0
  48. package/template/src/navigation/AuthStack.tsx +25 -30
  49. package/template/src/navigation/HeaderComponents.tsx +18 -58
  50. package/template/src/navigation/MainNavigation.tsx +5 -6
  51. package/template/src/navigation/MainStack.tsx +3 -28
  52. package/template/src/navigation/RootNavigation.tsx +1 -7
  53. package/template/src/navigation/TabBar.tsx +2 -2
  54. package/template/src/navigation/TopTabBar.tsx +1 -1
  55. package/template/src/screens/Login/Login.tsx +3 -3
  56. package/template/src/screens/home/components/CarouselSection.tsx +7 -8
  57. package/template/src/screens/home/components/FeaturedCarousel.tsx +5 -6
  58. package/template/src/screens/registration/RegistrationScreen.tsx +2 -2
  59. package/template/src/screens/resetPassword/ForgotPasswordScreen.tsx +2 -2
  60. package/template/src/utils/stringBuilder.js +25 -0
@@ -0,0 +1,229 @@
1
+ import React, {useState} from 'react';
2
+ import {View, TextInput, Text, StyleSheet} from 'react-native';
3
+ import {
4
+ regexValidation,
5
+ patterns,
6
+ ValidationResult,
7
+ requiredValidation,
8
+ runValidations,
9
+ } from '../regexValidator';
10
+
11
+ interface TextInputWithValidationProps {
12
+ label: string;
13
+ placeholder?: string;
14
+ value: string;
15
+ onChangeText: (text: string) => void;
16
+ secureTextEntry?: boolean;
17
+ validationType?:
18
+ | 'email'
19
+ | 'password'
20
+ | 'phone'
21
+ | 'name'
22
+ | 'username'
23
+ | 'numeric'
24
+ | 'price'
25
+ | 'custom';
26
+ customRegex?: RegExp;
27
+ customErrorMessage?: string;
28
+ required?: boolean;
29
+ minLength?: number;
30
+ }
31
+
32
+ /**
33
+ * A reusable text input component with built-in validation
34
+ */
35
+ const TextInputWithValidation: React.FC<TextInputWithValidationProps> = ({
36
+ label,
37
+ placeholder,
38
+ value,
39
+ onChangeText,
40
+ secureTextEntry = false,
41
+ validationType = 'custom',
42
+ customRegex,
43
+ customErrorMessage,
44
+ required = false,
45
+ minLength,
46
+ }) => {
47
+ const [touched, setTouched] = useState(false);
48
+ const [validationResult, setValidationResult] = useState<ValidationResult>({
49
+ isValid: true,
50
+ message: '',
51
+ });
52
+
53
+ // Get the appropriate regex based on validation type
54
+ const getRegexForType = (): RegExp => {
55
+ switch (validationType) {
56
+ case 'email':
57
+ return patterns.EMAIL;
58
+ case 'password':
59
+ return patterns.PASSWORD;
60
+ case 'phone':
61
+ return patterns.PHONE;
62
+ case 'name':
63
+ return patterns.NAME;
64
+ case 'username':
65
+ return patterns.USERNAME;
66
+ case 'numeric':
67
+ return patterns.NUMERIC;
68
+ case 'price':
69
+ return patterns.PRICE;
70
+ case 'custom':
71
+ return customRegex || /^.*$/; // Allow anything if no custom regex
72
+ default:
73
+ return /^.*$/;
74
+ }
75
+ };
76
+
77
+ // Get the default error message based on validation type
78
+ const getDefaultErrorMessage = (): string => {
79
+ switch (validationType) {
80
+ case 'email':
81
+ return 'Please enter a valid email address';
82
+ case 'password':
83
+ return 'Password must be at least 8 characters with uppercase, lowercase, and number';
84
+ case 'phone':
85
+ return 'Please enter a valid phone number';
86
+ case 'name':
87
+ return 'Please enter a valid name';
88
+ case 'username':
89
+ return 'Username must be 3-20 characters (letters, numbers, underscores)';
90
+ case 'numeric':
91
+ return 'Please enter numbers only';
92
+ case 'price':
93
+ return 'Please enter a valid price (e.g., 10.99)';
94
+ default:
95
+ return customErrorMessage || 'Invalid input';
96
+ }
97
+ };
98
+
99
+ // Validate the input when it loses focus
100
+ const handleBlur = () => {
101
+ setTouched(true);
102
+
103
+ // Build validations array
104
+ const validations = [];
105
+
106
+ // Add required validation if needed
107
+ if (required) {
108
+ validations.push((val: string) =>
109
+ requiredValidation(val, 'This field is required'),
110
+ );
111
+ }
112
+
113
+ // Add pattern validation if it's not empty
114
+ validations.push((val: string) => {
115
+ // Skip empty validation if not required
116
+ if (!required && (!val || val.trim() === '')) {
117
+ return {isValid: true, message: ''};
118
+ }
119
+ return regexValidation(val, getRegexForType(), getDefaultErrorMessage());
120
+ });
121
+
122
+ // Run all validations
123
+ const result = runValidations(value, validations);
124
+ setValidationResult(result);
125
+ };
126
+
127
+ return (
128
+ <View style={styles.container}>
129
+ <Text style={styles.label}>{label}</Text>
130
+ <TextInput
131
+ style={[
132
+ styles.input,
133
+ touched && !validationResult.isValid && styles.inputError,
134
+ ]}
135
+ placeholder={placeholder}
136
+ value={value}
137
+ onChangeText={onChangeText}
138
+ onBlur={handleBlur}
139
+ secureTextEntry={secureTextEntry}
140
+ />
141
+ {touched && !validationResult.isValid && (
142
+ <Text style={styles.errorText}>{validationResult.message}</Text>
143
+ )}
144
+ </View>
145
+ );
146
+ };
147
+
148
+ const styles = StyleSheet.create({
149
+ container: {
150
+ marginBottom: 16,
151
+ },
152
+ label: {
153
+ fontSize: 16,
154
+ marginBottom: 8,
155
+ fontWeight: '500',
156
+ },
157
+ input: {
158
+ borderWidth: 1,
159
+ borderColor: '#ccc',
160
+ borderRadius: 4,
161
+ paddingHorizontal: 12,
162
+ paddingVertical: 8,
163
+ fontSize: 16,
164
+ },
165
+ inputError: {
166
+ borderColor: '#ff3b30',
167
+ },
168
+ errorText: {
169
+ color: '#ff3b30',
170
+ fontSize: 14,
171
+ marginTop: 4,
172
+ },
173
+ });
174
+
175
+ export default TextInputWithValidation;
176
+
177
+ // Usage Example:
178
+ /*
179
+ import { TextInputWithValidation } from 'src/common/validations/examples';
180
+
181
+ const MyForm = () => {
182
+ const [email, setEmail] = useState('');
183
+ const [password, setPassword] = useState('');
184
+ const [phone, setPhone] = useState('');
185
+ const [customField, setCustomField] = useState('');
186
+
187
+ return (
188
+ <View>
189
+ <TextInputWithValidation
190
+ label="Email"
191
+ placeholder="Enter your email"
192
+ value={email}
193
+ onChangeText={setEmail}
194
+ validationType="email"
195
+ required
196
+ />
197
+
198
+ <TextInputWithValidation
199
+ label="Password"
200
+ placeholder="Enter your password"
201
+ value={password}
202
+ onChangeText={setPassword}
203
+ validationType="password"
204
+ required
205
+ secureTextEntry
206
+ />
207
+
208
+ <TextInputWithValidation
209
+ label="Phone Number"
210
+ placeholder="Enter your phone number"
211
+ value={phone}
212
+ onChangeText={setPhone}
213
+ validationType="phone"
214
+ />
215
+
216
+ <TextInputWithValidation
217
+ label="Product ID"
218
+ placeholder="Enter product ID"
219
+ value={customField}
220
+ onChangeText={setCustomField}
221
+ validationType="custom"
222
+ customRegex={/^PRD-[0-9]{6}$/}
223
+ customErrorMessage="Product ID must be in format PRD-123456"
224
+ required
225
+ />
226
+ </View>
227
+ );
228
+ };
229
+ */
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Validations Module
3
+ *
4
+ * Central export point for all validation utilities
5
+ */
6
+
7
+ export * from './regexValidator';
8
+
9
+ // Additional regex patterns for specific use cases
10
+ export const REGEX_PATTERNS = {
11
+ // Standard patterns (exported also from regexValidator)
12
+ ...require('./regexValidator').patterns,
13
+
14
+ // Additional patterns
15
+ STRONG_PASSWORD:
16
+ /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
17
+ TIME_24H: /^([01]\d|2[0-3]):([0-5]\d)$/,
18
+ TIME_12H: /^(0?[1-9]|1[0-2]):([0-5]\d)\s?(AM|PM|am|pm)$/,
19
+ HEX_COLOR: /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,
20
+ POSTAL_CODE: /^[A-Za-z]\d[A-Za-z][ -]?\d[A-Za-z]\d$|^\d{5}(-\d{4})?$/,
21
+ CURRENCY: /^[$€£¥]?([0-9]{1,3},([0-9]{3},)*[0-9]{3}|[0-9]+)(\.[0-9]{1,2})?$/,
22
+ MAC_ADDRESS: /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/,
23
+
24
+ // Form input validation
25
+ PRODUCT_SKU: /^[A-Za-z0-9-]{6,20}$/,
26
+ QUANTITY: /^[1-9]\d*$/,
27
+ DISCOUNT_PERCENTAGE: /^([0-9]|[1-9][0-9]|100)$/,
28
+ };
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Regex Validation Constants
3
+ *
4
+ * A collection of regex patterns for common input validations.
5
+ */
6
+
7
+ // Email validation regex
8
+ export const EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
9
+
10
+ // Phone number validation regex (international format)
11
+ export const PHONE_REGEX =
12
+ /^(\+\d{1,3}[-\s]?)?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})$/;
13
+
14
+ // Password validation regex (min 8 chars, one uppercase, one lowercase, one number)
15
+ export const PASSWORD_REGEX = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;
16
+
17
+ // Username validation regex (alphanumeric, 3-20 chars, underscores allowed)
18
+ export const USERNAME_REGEX = /^[a-zA-Z0-9_]{3,20}$/;
19
+
20
+ // Name validation regex (letters, spaces, apostrophes, hyphens)
21
+ export const NAME_REGEX = /^[a-zA-Z\s'-]{2,50}$/;
22
+
23
+ // URL validation regex
24
+ export const URL_REGEX =
25
+ /^(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)$/;
26
+
27
+ // Postal/ZIP code validation regex (US and Canada)
28
+ export const POSTAL_CODE_REGEX =
29
+ /^[A-Za-z]\d[A-Za-z][ -]?\d[A-Za-z]\d$|^\d{5}(-\d{4})?$/;
30
+
31
+ // Credit card validation regex (major card types)
32
+ export const CREDIT_CARD_REGEX =
33
+ /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|6(?:011|5[0-9][0-9])[0-9]{12})$/;
34
+
35
+ // Date validation regex (YYYY-MM-DD format)
36
+ export const DATE_REGEX = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;
37
+
38
+ // IP Address validation regex
39
+ export const IP_ADDRESS_REGEX =
40
+ /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
41
+
42
+ // Numeric value validation regex (integers only)
43
+ export const NUMERIC_REGEX = /^[0-9]+$/;
44
+
45
+ // Decimal value validation regex (positive or negative)
46
+ export const DECIMAL_REGEX = /^-?\d+(\.\d+)?$/;
47
+
48
+ // Price validation regex (positive decimal with up to 2 decimal places)
49
+ export const PRICE_REGEX = /^\d+(\.\d{1,2})?$/;
50
+
51
+ // Alphanumeric validation regex (letters and numbers only)
52
+ export const ALPHANUMERIC_REGEX = /^[a-zA-Z0-9]+$/;
53
+
54
+ // Letters only validation regex
55
+ export const LETTERS_ONLY_REGEX = /^[a-zA-Z]+$/;
56
+
57
+ // Strong password validation (8+ chars, upper, lower, number, special char)
58
+ export const STRONG_PASSWORD_REGEX =
59
+ /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
60
+
61
+ // Time validation regex (HH:MM format, 24-hour)
62
+ export const TIME_24H_REGEX = /^([01]\d|2[0-3]):([0-5]\d)$/;
63
+
64
+ // Time validation regex (HH:MM format, 12-hour with AM/PM)
65
+ export const TIME_12H_REGEX = /^(0?[1-9]|1[0-2]):([0-5]\d)\s?(AM|PM|am|pm)$/;
66
+
67
+ // Hex color code validation regex
68
+ export const HEX_COLOR_REGEX = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
69
+
70
+ // Social security number validation regex (US format)
71
+ export const SSN_REGEX =
72
+ /^(?!000|666|9\d{2})([0-8]\d{2})(?!00)(\d{2})(?!0000)(\d{4})$/;
73
+
74
+ // ISBN validation regex (ISBN-10 or ISBN-13)
75
+ export const ISBN_REGEX =
76
+ /^(?:ISBN(?:-1[03])?:?\s)?(?=[0-9X]{10}$|(?=(?:[0-9]+[-\s]){3})[-\s0-9X]{13}$|97[89][0-9]{10}$|(?=(?:[0-9]+[-\s]){4})[-\s0-9]{17}$)(?:97[89][-\s]?)?[0-9]{1,5}[-\s]?[0-9]+[-\s]?[0-9]+[-\s]?[0-9X]$/;
77
+
78
+ // Currency validation regex (with symbol and thousands separators)
79
+ export const CURRENCY_REGEX =
80
+ /^[$€£¥]?([0-9]{1,3},([0-9]{3},)*[0-9]{3}|[0-9]+)(\.[0-9]{1,2})?$/;
81
+
82
+ // MAC Address validation regex
83
+ export const MAC_ADDRESS_REGEX = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Regex Validation Utility
3
+ *
4
+ * TypeScript utility for regex-based validation with custom error messages.
5
+ */
6
+
7
+ // Types for validation result
8
+ export interface ValidationResult {
9
+ isValid: boolean;
10
+ message: string;
11
+ }
12
+
13
+ /**
14
+ * Generic regex validation function
15
+ * @param value - The string value to validate
16
+ * @param regex - The regex pattern to validate against
17
+ * @param errorMessage - Optional custom error message
18
+ * @returns ValidationResult with isValid flag and message
19
+ */
20
+ export const regexValidation = (
21
+ value: string,
22
+ regex: RegExp,
23
+ errorMessage: string = 'Invalid format',
24
+ ): ValidationResult => {
25
+ const isValid = regex?.test(value);
26
+ return {
27
+ isValid,
28
+ message: isValid ? '' : errorMessage,
29
+ };
30
+ };
31
+
32
+ /**
33
+ * Validate if a field is empty
34
+ * @param value - The string value to check
35
+ * @param errorMessage - Optional custom error message
36
+ * @returns ValidationResult
37
+ */
38
+ export const requiredValidation = (
39
+ value: string,
40
+ errorMessage: string = 'This field is required',
41
+ ): ValidationResult => {
42
+ const isValid = value !== undefined && value !== null && value.trim() !== '';
43
+ return {
44
+ isValid,
45
+ message: isValid ? '' : errorMessage,
46
+ };
47
+ };
48
+
49
+ /**
50
+ * Validate minimum length
51
+ * @param value - The string value to check
52
+ * @param minLength - Minimum required length
53
+ * @param errorMessage - Optional custom error message
54
+ * @returns ValidationResult
55
+ */
56
+ export const minLengthValidation = (
57
+ value: string,
58
+ minLength: number,
59
+ errorMessage?: string,
60
+ ): ValidationResult => {
61
+ const isValid =
62
+ value !== undefined && value !== null && value.length >= minLength;
63
+ return {
64
+ isValid,
65
+ message: isValid
66
+ ? ''
67
+ : errorMessage || `Input must be at least ${minLength} characters`,
68
+ };
69
+ };
70
+
71
+ /**
72
+ * Validate maximum length
73
+ * @param value - The string value to check
74
+ * @param maxLength - Maximum allowed length
75
+ * @param errorMessage - Optional custom error message
76
+ * @returns ValidationResult
77
+ */
78
+ export const maxLengthValidation = (
79
+ value: string,
80
+ maxLength: number,
81
+ errorMessage?: string,
82
+ ): ValidationResult => {
83
+ const isValid = !value || value.length <= maxLength;
84
+ return {
85
+ isValid,
86
+ message: isValid
87
+ ? ''
88
+ : errorMessage || `Input cannot exceed ${maxLength} characters`,
89
+ };
90
+ };
91
+
92
+ /**
93
+ * Run multiple validations in sequence
94
+ * @param value - The string value to validate
95
+ * @param validations - Array of validation functions to run
96
+ * @returns First failed ValidationResult or success result
97
+ */
98
+ export const runValidations = (
99
+ value: string,
100
+ validations: ((value: string) => ValidationResult)[],
101
+ ): ValidationResult => {
102
+ for (const validationFn of validations) {
103
+ const result = validationFn(value);
104
+ if (!result.isValid) {
105
+ return result;
106
+ }
107
+ }
108
+ return {isValid: true, message: ''};
109
+ };
110
+
111
+ /**
112
+ * Common validation patterns for easy access
113
+ */
114
+ export const patterns = {
115
+ // Email validation regex
116
+ EMAIL: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
117
+
118
+ // Phone number validation regex (international format)
119
+ PHONE: /^(\+\d{1,3}[-\s]?)?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})$/,
120
+
121
+ // Password validation regex (min 8 chars, one uppercase, one lowercase, one number)
122
+ PASSWORD: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/,
123
+
124
+ // Username validation regex (alphanumeric, 3-20 chars, underscores allowed)
125
+ USERNAME: /^[a-zA-Z0-9_]{3,20}$/,
126
+
127
+ // Name validation regex (letters, spaces, apostrophes, hyphens)
128
+ NAME: /^[a-zA-Z\s'-]{2,50}$/,
129
+
130
+ // URL validation regex
131
+ URL: /^(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)$/,
132
+
133
+ // Numeric value validation regex (integers only)
134
+ NUMERIC: /^[0-9]+$/,
135
+
136
+ // Decimal value validation regex (positive or negative)
137
+ DECIMAL: /^-?\d+(\.\d+)?$/,
138
+
139
+ // Price validation regex (positive decimal with up to 2 decimal places)
140
+ PRICE: /^\d+(\.\d{1,2})?$/,
141
+
142
+ // Alphanumeric validation regex (letters and numbers only)
143
+ ALPHANUMERIC: /^[a-zA-Z0-9]+$/,
144
+
145
+ // Letters only validation regex
146
+ LETTERS_ONLY: /^[a-zA-Z]+$/,
147
+ };
148
+
149
+ // Export convenience validation functions
150
+ export const validateEmail = (
151
+ email: string,
152
+ errorMessage?: string,
153
+ ): ValidationResult =>
154
+ regexValidation(
155
+ email,
156
+ patterns.EMAIL,
157
+ errorMessage || 'Please enter a valid email address',
158
+ );
159
+
160
+ export const validatePhone = (
161
+ phone: string,
162
+ errorMessage?: string,
163
+ ): ValidationResult =>
164
+ regexValidation(
165
+ phone,
166
+ patterns.PHONE,
167
+ errorMessage || 'Please enter a valid phone number',
168
+ );
169
+
170
+ export const validatePassword = (
171
+ password: string,
172
+ errorMessage?: string,
173
+ ): ValidationResult =>
174
+ regexValidation(
175
+ password,
176
+ patterns.PASSWORD,
177
+ errorMessage ||
178
+ 'Password must be at least 8 characters with uppercase, lowercase, and number',
179
+ );
180
+
181
+ export const validateUsername = (
182
+ username: string,
183
+ errorMessage?: string,
184
+ ): ValidationResult =>
185
+ regexValidation(
186
+ username,
187
+ patterns.USERNAME,
188
+ errorMessage ||
189
+ 'Username must be 3-20 characters (letters, numbers, underscores)',
190
+ );
191
+
192
+ export const validateName = (
193
+ name: string,
194
+ errorMessage?: string,
195
+ ): ValidationResult =>
196
+ regexValidation(
197
+ name,
198
+ patterns.NAME,
199
+ errorMessage || 'Please enter a valid name',
200
+ );
201
+
202
+ export const validateUrl = (
203
+ url: string,
204
+ errorMessage?: string,
205
+ ): ValidationResult =>
206
+ regexValidation(
207
+ url,
208
+ patterns.URL,
209
+ errorMessage || 'Please enter a valid URL',
210
+ );
211
+
212
+ export const validateNumeric = (
213
+ value: string,
214
+ errorMessage?: string,
215
+ ): ValidationResult =>
216
+ regexValidation(
217
+ value,
218
+ patterns.NUMERIC,
219
+ errorMessage || 'Please enter numbers only',
220
+ );
221
+
222
+ export const validateDecimal = (
223
+ value: string,
224
+ errorMessage?: string,
225
+ ): ValidationResult =>
226
+ regexValidation(
227
+ value,
228
+ patterns.DECIMAL,
229
+ errorMessage || 'Please enter a valid decimal number',
230
+ );
231
+
232
+ export const validatePrice = (
233
+ price: string,
234
+ errorMessage?: string,
235
+ ): ValidationResult =>
236
+ regexValidation(
237
+ price,
238
+ patterns.PRICE,
239
+ errorMessage || 'Please enter a valid price (e.g., 10.99)',
240
+ );
@@ -1,7 +1,7 @@
1
1
  export const validationConstants = {
2
2
  phone: {
3
- minLength: 8,
4
- maxLength: 18,
3
+ minLength: 11,
4
+ maxLength: 11,
5
5
  },
6
6
  fullName: {
7
7
  minLength: 3,
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Extracts meaningful error information from Axios error responses
3
+ * and ensures the message is always a string
4
+ * @param error The error caught from an API request
5
+ * @returns A structured error object with message (as string) and details
6
+ */
7
+ import {ensureString} from '../utils/stringUtils';
8
+
9
+ export const extractServerError = (error: any) => {
10
+ let errorObj = {
11
+ message: 'Unknown error occurred',
12
+ status: 500,
13
+ originalError: error,
14
+ };
15
+
16
+ try {
17
+ // Extract error information from Axios response
18
+ if (error?.response?.data) {
19
+ const data = error.response.data;
20
+
21
+ // Handle different error formats
22
+ if (data.error) {
23
+ errorObj.message = ensureString(data.error);
24
+ } else {
25
+ errorObj.message = ensureString(data);
26
+ }
27
+
28
+ errorObj.status = error.response.status;
29
+ errorObj.originalError = data;
30
+ } else if (error?.message) {
31
+ errorObj.message = ensureString(error.message);
32
+ }
33
+ } catch (e) {}
34
+
35
+ // Final safety check
36
+ errorObj.message = ensureString(errorObj.message);
37
+
38
+ return errorObj;
39
+ };
@@ -1,35 +1,12 @@
1
1
  import {AxiosResponse} from 'axios';
2
2
  import Snackbar from 'react-native-snackbar';
3
- import {Colors} from '../theme/colors';
4
3
  import {setLogout} from '../store/user/userSlice';
5
4
  import {store} from '../store/store';
6
5
 
7
- export const handleFetchJsonResponse = async (
6
+ export const handleFetchJsonResponse = (
8
7
  response: AxiosResponse,
9
8
  showSuccessMessage?: boolean,
10
9
  ) => {
11
- if (response.status === 401) {
12
- store.dispatch(setLogout());
13
- } else if (
14
- !response.status ||
15
- response.status < 200 ||
16
- (response.status >= 300 && response.status !== 401)
17
- ) {
18
- return Snackbar.show({
19
- text: response?.data?.message,
20
- duration: Snackbar.LENGTH_SHORT,
21
- textColor: Colors.white,
22
- backgroundColor: Colors.red,
23
- });
24
- } else if (showSuccessMessage) {
25
- Snackbar.show({
26
- text: 'Success',
27
- duration: Snackbar.LENGTH_SHORT,
28
- textColor: Colors.white,
29
- backgroundColor: Colors.green,
30
- });
31
- }
32
-
33
10
  return response.data;
34
11
  };
35
12
 
@@ -37,7 +14,5 @@ export const handleErrorResponse = async (message: string) => {
37
14
  return Snackbar.show({
38
15
  text: message,
39
16
  duration: Snackbar.LENGTH_SHORT,
40
- textColor: Colors.white,
41
- backgroundColor: Colors.red,
42
17
  });
43
18
  };