@umituz/react-native-validation 1.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.
- package/LICENSE +22 -0
- package/README.md +181 -0
- package/lib/domain/entities/ValidationResult.d.ts +8 -0
- package/lib/domain/entities/ValidationResult.js +6 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +36 -0
- package/lib/infrastructure/utils/validators.d.ts +114 -0
- package/lib/infrastructure/utils/validators.js +290 -0
- package/package.json +52 -0
- package/src/domain/entities/ValidationResult.ts +10 -0
- package/src/index.ts +39 -0
- package/src/infrastructure/utils/validators.ts +368 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Ümit UZ
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# @umituz/react-native-validation
|
|
2
|
+
|
|
3
|
+
Comprehensive validation utilities for React Native forms. Provides reusable validation functions for common input types.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @umituz/react-native-validation
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Peer Dependencies
|
|
12
|
+
|
|
13
|
+
- `react` >= 18.2.0
|
|
14
|
+
- `react-native` >= 0.74.0
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- ✅ Email validation
|
|
19
|
+
- ✅ Password validation (with strength requirements)
|
|
20
|
+
- ✅ Phone number validation (E.164 format)
|
|
21
|
+
- ✅ Required field validation
|
|
22
|
+
- ✅ Name validation
|
|
23
|
+
- ✅ Number range validation
|
|
24
|
+
- ✅ Min/Max length validation
|
|
25
|
+
- ✅ Pattern (regex) validation
|
|
26
|
+
- ✅ Date of birth validation
|
|
27
|
+
- ✅ Health & fitness validators (weight, height, age, calories, macros, etc.)
|
|
28
|
+
- ✅ Batch validation
|
|
29
|
+
- ✅ TypeScript type safety
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
### Basic Example
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { validateEmail, validatePassword, validateRequired } from '@umituz/react-native-validation';
|
|
37
|
+
|
|
38
|
+
// Validate email
|
|
39
|
+
const emailResult = validateEmail('user@example.com');
|
|
40
|
+
if (!emailResult.isValid) {
|
|
41
|
+
console.error(emailResult.error);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Validate password
|
|
45
|
+
const passwordResult = validatePassword('MySecure123', {
|
|
46
|
+
minLength: 8,
|
|
47
|
+
requireUppercase: true,
|
|
48
|
+
requireLowercase: true,
|
|
49
|
+
requireNumber: true,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Validate required field
|
|
53
|
+
const requiredResult = validateRequired('value', 'Field Name');
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Form Validation Example
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import {
|
|
60
|
+
validateEmail,
|
|
61
|
+
validatePassword,
|
|
62
|
+
validatePasswordConfirmation,
|
|
63
|
+
validateRequired,
|
|
64
|
+
batchValidate,
|
|
65
|
+
} from '@umituz/react-native-validation';
|
|
66
|
+
|
|
67
|
+
const validateForm = (email: string, password: string, confirmPassword: string) => {
|
|
68
|
+
return batchValidate([
|
|
69
|
+
{ field: 'email', validator: () => validateEmail(email) },
|
|
70
|
+
{ field: 'password', validator: () => validatePassword(password) },
|
|
71
|
+
{
|
|
72
|
+
field: 'confirmPassword',
|
|
73
|
+
validator: () => validatePasswordConfirmation(password, confirmPassword),
|
|
74
|
+
},
|
|
75
|
+
]);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const result = validateForm('user@example.com', 'MySecure123', 'MySecure123');
|
|
79
|
+
if (!result.isValid) {
|
|
80
|
+
console.log('Validation errors:', result.errors);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Health & Fitness Validators
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import {
|
|
88
|
+
validateWeight,
|
|
89
|
+
validateHeight,
|
|
90
|
+
validateAge,
|
|
91
|
+
validateCalories,
|
|
92
|
+
validateWorkoutDuration,
|
|
93
|
+
} from '@umituz/react-native-validation';
|
|
94
|
+
|
|
95
|
+
const weightResult = validateWeight(75); // kg
|
|
96
|
+
const heightResult = validateHeight(175); // cm
|
|
97
|
+
const ageResult = validateAge(25);
|
|
98
|
+
const caloriesResult = validateCalories(2000);
|
|
99
|
+
const durationResult = validateWorkoutDuration(45); // minutes
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Custom Validation
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { validatePattern, validateNumberRange } from '@umituz/react-native-validation';
|
|
106
|
+
|
|
107
|
+
// Validate with custom pattern
|
|
108
|
+
const patternResult = validatePattern(
|
|
109
|
+
'ABC123',
|
|
110
|
+
/^[A-Z]{3}\d{3}$/,
|
|
111
|
+
'Code',
|
|
112
|
+
'Code must be 3 uppercase letters followed by 3 numbers'
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Validate number range
|
|
116
|
+
const rangeResult = validateNumberRange(50, 0, 100, 'Percentage');
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## API Reference
|
|
120
|
+
|
|
121
|
+
### Core Validators
|
|
122
|
+
|
|
123
|
+
- `validateEmail(email: string): ValidationResult`
|
|
124
|
+
- `validatePassword(password: string, options?: PasswordOptions): ValidationResult`
|
|
125
|
+
- `validatePasswordConfirmation(password: string, confirmPassword: string): ValidationResult`
|
|
126
|
+
- `validateRequired(value: string, fieldName?: string): ValidationResult`
|
|
127
|
+
- `validateName(name: string, fieldName?: string, minLength?: number): ValidationResult`
|
|
128
|
+
- `validatePhone(phone: string): ValidationResult`
|
|
129
|
+
|
|
130
|
+
### Number Validators
|
|
131
|
+
|
|
132
|
+
- `validateNumberRange(value: number, min: number, max: number, fieldName?: string): ValidationResult`
|
|
133
|
+
- `validatePositiveNumber(value: number, fieldName?: string): ValidationResult`
|
|
134
|
+
|
|
135
|
+
### String Validators
|
|
136
|
+
|
|
137
|
+
- `validateMinLength(value: string, minLength: number, fieldName?: string): ValidationResult`
|
|
138
|
+
- `validateMaxLength(value: string, maxLength: number, fieldName?: string): ValidationResult`
|
|
139
|
+
- `validatePattern(value: string, pattern: RegExp, fieldName?: string, errorMessage?: string): ValidationResult`
|
|
140
|
+
|
|
141
|
+
### Date Validators
|
|
142
|
+
|
|
143
|
+
- `validateDateOfBirth(date: Date): ValidationResult`
|
|
144
|
+
- `validateAge(age: number): ValidationResult`
|
|
145
|
+
|
|
146
|
+
### Health & Fitness Validators
|
|
147
|
+
|
|
148
|
+
- `validateWeight(weight: number): ValidationResult` (20-300 kg)
|
|
149
|
+
- `validateHeight(height: number): ValidationResult` (50-250 cm)
|
|
150
|
+
- `validateCalories(calories: number): ValidationResult` (0-10000)
|
|
151
|
+
- `validateMacros(grams: number, macroType: string): ValidationResult` (0-1000 g)
|
|
152
|
+
- `validateWaterIntake(glasses: number): ValidationResult` (0-50)
|
|
153
|
+
- `validateWorkoutDuration(minutes: number): ValidationResult` (1-600 min)
|
|
154
|
+
- `validateReps(reps: number): ValidationResult` (1-1000)
|
|
155
|
+
- `validateSets(sets: number): ValidationResult` (1-100)
|
|
156
|
+
- `validateExerciseWeight(weight: number): ValidationResult` (0-1000 kg)
|
|
157
|
+
|
|
158
|
+
### Batch Validation
|
|
159
|
+
|
|
160
|
+
- `batchValidate(validations: Array<{ field: string; validator: () => ValidationResult }>): { isValid: boolean; errors: Record<string, string> }`
|
|
161
|
+
|
|
162
|
+
## Types
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
interface ValidationResult {
|
|
166
|
+
isValid: boolean;
|
|
167
|
+
error?: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
interface PasswordOptions {
|
|
171
|
+
minLength?: number;
|
|
172
|
+
requireUppercase?: boolean;
|
|
173
|
+
requireLowercase?: boolean;
|
|
174
|
+
requireNumber?: boolean;
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT
|
|
181
|
+
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @umituz/react-native-validation
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive validation utilities for React Native forms.
|
|
5
|
+
* Provides reusable validation functions for common input types.
|
|
6
|
+
*
|
|
7
|
+
* @package react-native-validation
|
|
8
|
+
*/
|
|
9
|
+
export type { ValidationResult } from './domain/entities/ValidationResult';
|
|
10
|
+
export { validateEmail, validatePassword, validatePasswordConfirmation, validateRequired, validateName, validatePhone, validateNumberRange, validatePositiveNumber, validateMinLength, validateMaxLength, validatePattern, validateDateOfBirth, validateAge, validateWeight, validateHeight, validateCalories, validateMacros, validateWaterIntake, validateWorkoutDuration, validateReps, validateSets, validateExerciseWeight, batchValidate, } from './infrastructure/utils/validators';
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @umituz/react-native-validation
|
|
4
|
+
*
|
|
5
|
+
* Comprehensive validation utilities for React Native forms.
|
|
6
|
+
* Provides reusable validation functions for common input types.
|
|
7
|
+
*
|
|
8
|
+
* @package react-native-validation
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.batchValidate = exports.validateExerciseWeight = exports.validateSets = exports.validateReps = exports.validateWorkoutDuration = exports.validateWaterIntake = exports.validateMacros = exports.validateCalories = exports.validateHeight = exports.validateWeight = exports.validateAge = exports.validateDateOfBirth = exports.validatePattern = exports.validateMaxLength = exports.validateMinLength = exports.validatePositiveNumber = exports.validateNumberRange = exports.validatePhone = exports.validateName = exports.validateRequired = exports.validatePasswordConfirmation = exports.validatePassword = exports.validateEmail = void 0;
|
|
12
|
+
// Infrastructure Layer - Validators
|
|
13
|
+
var validators_1 = require("./infrastructure/utils/validators");
|
|
14
|
+
Object.defineProperty(exports, "validateEmail", { enumerable: true, get: function () { return validators_1.validateEmail; } });
|
|
15
|
+
Object.defineProperty(exports, "validatePassword", { enumerable: true, get: function () { return validators_1.validatePassword; } });
|
|
16
|
+
Object.defineProperty(exports, "validatePasswordConfirmation", { enumerable: true, get: function () { return validators_1.validatePasswordConfirmation; } });
|
|
17
|
+
Object.defineProperty(exports, "validateRequired", { enumerable: true, get: function () { return validators_1.validateRequired; } });
|
|
18
|
+
Object.defineProperty(exports, "validateName", { enumerable: true, get: function () { return validators_1.validateName; } });
|
|
19
|
+
Object.defineProperty(exports, "validatePhone", { enumerable: true, get: function () { return validators_1.validatePhone; } });
|
|
20
|
+
Object.defineProperty(exports, "validateNumberRange", { enumerable: true, get: function () { return validators_1.validateNumberRange; } });
|
|
21
|
+
Object.defineProperty(exports, "validatePositiveNumber", { enumerable: true, get: function () { return validators_1.validatePositiveNumber; } });
|
|
22
|
+
Object.defineProperty(exports, "validateMinLength", { enumerable: true, get: function () { return validators_1.validateMinLength; } });
|
|
23
|
+
Object.defineProperty(exports, "validateMaxLength", { enumerable: true, get: function () { return validators_1.validateMaxLength; } });
|
|
24
|
+
Object.defineProperty(exports, "validatePattern", { enumerable: true, get: function () { return validators_1.validatePattern; } });
|
|
25
|
+
Object.defineProperty(exports, "validateDateOfBirth", { enumerable: true, get: function () { return validators_1.validateDateOfBirth; } });
|
|
26
|
+
Object.defineProperty(exports, "validateAge", { enumerable: true, get: function () { return validators_1.validateAge; } });
|
|
27
|
+
Object.defineProperty(exports, "validateWeight", { enumerable: true, get: function () { return validators_1.validateWeight; } });
|
|
28
|
+
Object.defineProperty(exports, "validateHeight", { enumerable: true, get: function () { return validators_1.validateHeight; } });
|
|
29
|
+
Object.defineProperty(exports, "validateCalories", { enumerable: true, get: function () { return validators_1.validateCalories; } });
|
|
30
|
+
Object.defineProperty(exports, "validateMacros", { enumerable: true, get: function () { return validators_1.validateMacros; } });
|
|
31
|
+
Object.defineProperty(exports, "validateWaterIntake", { enumerable: true, get: function () { return validators_1.validateWaterIntake; } });
|
|
32
|
+
Object.defineProperty(exports, "validateWorkoutDuration", { enumerable: true, get: function () { return validators_1.validateWorkoutDuration; } });
|
|
33
|
+
Object.defineProperty(exports, "validateReps", { enumerable: true, get: function () { return validators_1.validateReps; } });
|
|
34
|
+
Object.defineProperty(exports, "validateSets", { enumerable: true, get: function () { return validators_1.validateSets; } });
|
|
35
|
+
Object.defineProperty(exports, "validateExerciseWeight", { enumerable: true, get: function () { return validators_1.validateExerciseWeight; } });
|
|
36
|
+
Object.defineProperty(exports, "batchValidate", { enumerable: true, get: function () { return validators_1.batchValidate; } });
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Utilities
|
|
3
|
+
* Comprehensive validation functions for React Native forms
|
|
4
|
+
*/
|
|
5
|
+
import type { ValidationResult } from '../../domain/entities/ValidationResult';
|
|
6
|
+
/**
|
|
7
|
+
* Validate email format
|
|
8
|
+
*/
|
|
9
|
+
export declare const validateEmail: (email: string) => ValidationResult;
|
|
10
|
+
/**
|
|
11
|
+
* Validate password strength
|
|
12
|
+
* @param password - Password to validate
|
|
13
|
+
* @param minLength - Minimum password length (default: 8)
|
|
14
|
+
* @param requireUppercase - Require uppercase letter (default: true)
|
|
15
|
+
* @param requireLowercase - Require lowercase letter (default: true)
|
|
16
|
+
* @param requireNumber - Require number (default: true)
|
|
17
|
+
*/
|
|
18
|
+
export declare const validatePassword: (password: string, options?: {
|
|
19
|
+
minLength?: number;
|
|
20
|
+
requireUppercase?: boolean;
|
|
21
|
+
requireLowercase?: boolean;
|
|
22
|
+
requireNumber?: boolean;
|
|
23
|
+
}) => ValidationResult;
|
|
24
|
+
/**
|
|
25
|
+
* Validate password confirmation
|
|
26
|
+
*/
|
|
27
|
+
export declare const validatePasswordConfirmation: (password: string, confirmPassword: string) => ValidationResult;
|
|
28
|
+
/**
|
|
29
|
+
* Validate required field
|
|
30
|
+
*/
|
|
31
|
+
export declare const validateRequired: (value: string, fieldName?: string) => ValidationResult;
|
|
32
|
+
/**
|
|
33
|
+
* Validate name
|
|
34
|
+
*/
|
|
35
|
+
export declare const validateName: (name: string, fieldName?: string, minLength?: number) => ValidationResult;
|
|
36
|
+
/**
|
|
37
|
+
* Validate phone number (E.164 format)
|
|
38
|
+
*/
|
|
39
|
+
export declare const validatePhone: (phone: string) => ValidationResult;
|
|
40
|
+
/**
|
|
41
|
+
* Validate number range
|
|
42
|
+
*/
|
|
43
|
+
export declare const validateNumberRange: (value: number, min: number, max: number, fieldName?: string) => ValidationResult;
|
|
44
|
+
/**
|
|
45
|
+
* Validate positive number
|
|
46
|
+
*/
|
|
47
|
+
export declare const validatePositiveNumber: (value: number, fieldName?: string) => ValidationResult;
|
|
48
|
+
/**
|
|
49
|
+
* Validate min length
|
|
50
|
+
*/
|
|
51
|
+
export declare const validateMinLength: (value: string, minLength: number, fieldName?: string) => ValidationResult;
|
|
52
|
+
/**
|
|
53
|
+
* Validate max length
|
|
54
|
+
*/
|
|
55
|
+
export declare const validateMaxLength: (value: string, maxLength: number, fieldName?: string) => ValidationResult;
|
|
56
|
+
/**
|
|
57
|
+
* Validate pattern (regex)
|
|
58
|
+
*/
|
|
59
|
+
export declare const validatePattern: (value: string, pattern: RegExp, fieldName?: string, errorMessage?: string) => ValidationResult;
|
|
60
|
+
/**
|
|
61
|
+
* Validate date of birth
|
|
62
|
+
*/
|
|
63
|
+
export declare const validateDateOfBirth: (date: Date) => ValidationResult;
|
|
64
|
+
/**
|
|
65
|
+
* Validate age
|
|
66
|
+
*/
|
|
67
|
+
export declare const validateAge: (age: number) => ValidationResult;
|
|
68
|
+
/**
|
|
69
|
+
* Validate weight (kg)
|
|
70
|
+
*/
|
|
71
|
+
export declare const validateWeight: (weight: number) => ValidationResult;
|
|
72
|
+
/**
|
|
73
|
+
* Validate height (cm)
|
|
74
|
+
*/
|
|
75
|
+
export declare const validateHeight: (height: number) => ValidationResult;
|
|
76
|
+
/**
|
|
77
|
+
* Validate calories
|
|
78
|
+
*/
|
|
79
|
+
export declare const validateCalories: (calories: number) => ValidationResult;
|
|
80
|
+
/**
|
|
81
|
+
* Validate macros (protein, carbs, fat in grams)
|
|
82
|
+
*/
|
|
83
|
+
export declare const validateMacros: (grams: number, macroType: string) => ValidationResult;
|
|
84
|
+
/**
|
|
85
|
+
* Validate water intake (glasses)
|
|
86
|
+
*/
|
|
87
|
+
export declare const validateWaterIntake: (glasses: number) => ValidationResult;
|
|
88
|
+
/**
|
|
89
|
+
* Validate workout duration (minutes)
|
|
90
|
+
*/
|
|
91
|
+
export declare const validateWorkoutDuration: (minutes: number) => ValidationResult;
|
|
92
|
+
/**
|
|
93
|
+
* Validate reps
|
|
94
|
+
*/
|
|
95
|
+
export declare const validateReps: (reps: number) => ValidationResult;
|
|
96
|
+
/**
|
|
97
|
+
* Validate sets
|
|
98
|
+
*/
|
|
99
|
+
export declare const validateSets: (sets: number) => ValidationResult;
|
|
100
|
+
/**
|
|
101
|
+
* Validate exercise weight (kg)
|
|
102
|
+
*/
|
|
103
|
+
export declare const validateExerciseWeight: (weight: number) => ValidationResult;
|
|
104
|
+
/**
|
|
105
|
+
* Batch validation
|
|
106
|
+
* Validates multiple fields and returns all errors
|
|
107
|
+
*/
|
|
108
|
+
export declare const batchValidate: (validations: Array<{
|
|
109
|
+
field: string;
|
|
110
|
+
validator: () => ValidationResult;
|
|
111
|
+
}>) => {
|
|
112
|
+
isValid: boolean;
|
|
113
|
+
errors: Record<string, string>;
|
|
114
|
+
};
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Validation Utilities
|
|
4
|
+
* Comprehensive validation functions for React Native forms
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.batchValidate = exports.validateExerciseWeight = exports.validateSets = exports.validateReps = exports.validateWorkoutDuration = exports.validateWaterIntake = exports.validateMacros = exports.validateCalories = exports.validateHeight = exports.validateWeight = exports.validateAge = exports.validateDateOfBirth = exports.validatePattern = exports.validateMaxLength = exports.validateMinLength = exports.validatePositiveNumber = exports.validateNumberRange = exports.validatePhone = exports.validateName = exports.validateRequired = exports.validatePasswordConfirmation = exports.validatePassword = exports.validateEmail = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Validate email format
|
|
10
|
+
*/
|
|
11
|
+
const validateEmail = (email) => {
|
|
12
|
+
if (!email || email.trim() === '') {
|
|
13
|
+
return { isValid: false, error: 'Email is required' };
|
|
14
|
+
}
|
|
15
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
16
|
+
if (!emailRegex.test(email)) {
|
|
17
|
+
return { isValid: false, error: 'Please enter a valid email address' };
|
|
18
|
+
}
|
|
19
|
+
return { isValid: true };
|
|
20
|
+
};
|
|
21
|
+
exports.validateEmail = validateEmail;
|
|
22
|
+
/**
|
|
23
|
+
* Validate password strength
|
|
24
|
+
* @param password - Password to validate
|
|
25
|
+
* @param minLength - Minimum password length (default: 8)
|
|
26
|
+
* @param requireUppercase - Require uppercase letter (default: true)
|
|
27
|
+
* @param requireLowercase - Require lowercase letter (default: true)
|
|
28
|
+
* @param requireNumber - Require number (default: true)
|
|
29
|
+
*/
|
|
30
|
+
const validatePassword = (password, options) => {
|
|
31
|
+
const { minLength = 8, requireUppercase = true, requireLowercase = true, requireNumber = true, } = options || {};
|
|
32
|
+
if (!password || password.trim() === '') {
|
|
33
|
+
return { isValid: false, error: 'Password is required' };
|
|
34
|
+
}
|
|
35
|
+
if (password.length < minLength) {
|
|
36
|
+
return {
|
|
37
|
+
isValid: false,
|
|
38
|
+
error: `Password must be at least ${minLength} characters`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (requireUppercase && !/[A-Z]/.test(password)) {
|
|
42
|
+
return {
|
|
43
|
+
isValid: false,
|
|
44
|
+
error: 'Password must contain at least one uppercase letter',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (requireLowercase && !/[a-z]/.test(password)) {
|
|
48
|
+
return {
|
|
49
|
+
isValid: false,
|
|
50
|
+
error: 'Password must contain at least one lowercase letter',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (requireNumber && !/[0-9]/.test(password)) {
|
|
54
|
+
return {
|
|
55
|
+
isValid: false,
|
|
56
|
+
error: 'Password must contain at least one number',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return { isValid: true };
|
|
60
|
+
};
|
|
61
|
+
exports.validatePassword = validatePassword;
|
|
62
|
+
/**
|
|
63
|
+
* Validate password confirmation
|
|
64
|
+
*/
|
|
65
|
+
const validatePasswordConfirmation = (password, confirmPassword) => {
|
|
66
|
+
if (!confirmPassword) {
|
|
67
|
+
return { isValid: false, error: 'Please confirm your password' };
|
|
68
|
+
}
|
|
69
|
+
if (password !== confirmPassword) {
|
|
70
|
+
return { isValid: false, error: 'Passwords do not match' };
|
|
71
|
+
}
|
|
72
|
+
return { isValid: true };
|
|
73
|
+
};
|
|
74
|
+
exports.validatePasswordConfirmation = validatePasswordConfirmation;
|
|
75
|
+
/**
|
|
76
|
+
* Validate required field
|
|
77
|
+
*/
|
|
78
|
+
const validateRequired = (value, fieldName = 'This field') => {
|
|
79
|
+
if (!value || value.trim() === '') {
|
|
80
|
+
return { isValid: false, error: `${fieldName} is required` };
|
|
81
|
+
}
|
|
82
|
+
return { isValid: true };
|
|
83
|
+
};
|
|
84
|
+
exports.validateRequired = validateRequired;
|
|
85
|
+
/**
|
|
86
|
+
* Validate name
|
|
87
|
+
*/
|
|
88
|
+
const validateName = (name, fieldName = 'Name', minLength = 2) => {
|
|
89
|
+
if (!name || name.trim() === '') {
|
|
90
|
+
return { isValid: false, error: `${fieldName} is required` };
|
|
91
|
+
}
|
|
92
|
+
if (name.trim().length < minLength) {
|
|
93
|
+
return {
|
|
94
|
+
isValid: false,
|
|
95
|
+
error: `${fieldName} must be at least ${minLength} characters`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return { isValid: true };
|
|
99
|
+
};
|
|
100
|
+
exports.validateName = validateName;
|
|
101
|
+
/**
|
|
102
|
+
* Validate phone number (E.164 format)
|
|
103
|
+
*/
|
|
104
|
+
const validatePhone = (phone) => {
|
|
105
|
+
if (!phone || phone.trim() === '') {
|
|
106
|
+
return { isValid: false, error: 'Phone number is required' };
|
|
107
|
+
}
|
|
108
|
+
const phoneRegex = /^\+[1-9]\d{1,14}$/;
|
|
109
|
+
if (!phoneRegex.test(phone)) {
|
|
110
|
+
return { isValid: false, error: 'Please enter a valid phone number' };
|
|
111
|
+
}
|
|
112
|
+
return { isValid: true };
|
|
113
|
+
};
|
|
114
|
+
exports.validatePhone = validatePhone;
|
|
115
|
+
/**
|
|
116
|
+
* Validate number range
|
|
117
|
+
*/
|
|
118
|
+
const validateNumberRange = (value, min, max, fieldName = 'Value') => {
|
|
119
|
+
if (isNaN(value)) {
|
|
120
|
+
return { isValid: false, error: `${fieldName} must be a number` };
|
|
121
|
+
}
|
|
122
|
+
if (value < min || value > max) {
|
|
123
|
+
return {
|
|
124
|
+
isValid: false,
|
|
125
|
+
error: `${fieldName} must be between ${min} and ${max}`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return { isValid: true };
|
|
129
|
+
};
|
|
130
|
+
exports.validateNumberRange = validateNumberRange;
|
|
131
|
+
/**
|
|
132
|
+
* Validate positive number
|
|
133
|
+
*/
|
|
134
|
+
const validatePositiveNumber = (value, fieldName = 'Value') => {
|
|
135
|
+
if (isNaN(value)) {
|
|
136
|
+
return { isValid: false, error: `${fieldName} must be a number` };
|
|
137
|
+
}
|
|
138
|
+
if (value <= 0) {
|
|
139
|
+
return { isValid: false, error: `${fieldName} must be greater than 0` };
|
|
140
|
+
}
|
|
141
|
+
return { isValid: true };
|
|
142
|
+
};
|
|
143
|
+
exports.validatePositiveNumber = validatePositiveNumber;
|
|
144
|
+
/**
|
|
145
|
+
* Validate min length
|
|
146
|
+
*/
|
|
147
|
+
const validateMinLength = (value, minLength, fieldName = 'Field') => {
|
|
148
|
+
if (!value || value.trim().length < minLength) {
|
|
149
|
+
return {
|
|
150
|
+
isValid: false,
|
|
151
|
+
error: `${fieldName} must be at least ${minLength} characters`,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return { isValid: true };
|
|
155
|
+
};
|
|
156
|
+
exports.validateMinLength = validateMinLength;
|
|
157
|
+
/**
|
|
158
|
+
* Validate max length
|
|
159
|
+
*/
|
|
160
|
+
const validateMaxLength = (value, maxLength, fieldName = 'Field') => {
|
|
161
|
+
if (value && value.trim().length > maxLength) {
|
|
162
|
+
return {
|
|
163
|
+
isValid: false,
|
|
164
|
+
error: `${fieldName} must be at most ${maxLength} characters`,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return { isValid: true };
|
|
168
|
+
};
|
|
169
|
+
exports.validateMaxLength = validateMaxLength;
|
|
170
|
+
/**
|
|
171
|
+
* Validate pattern (regex)
|
|
172
|
+
*/
|
|
173
|
+
const validatePattern = (value, pattern, fieldName = 'Field', errorMessage) => {
|
|
174
|
+
if (!value) {
|
|
175
|
+
return { isValid: false, error: `${fieldName} is required` };
|
|
176
|
+
}
|
|
177
|
+
if (!pattern.test(value)) {
|
|
178
|
+
return {
|
|
179
|
+
isValid: false,
|
|
180
|
+
error: errorMessage || `${fieldName} format is invalid`,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return { isValid: true };
|
|
184
|
+
};
|
|
185
|
+
exports.validatePattern = validatePattern;
|
|
186
|
+
/**
|
|
187
|
+
* Validate date of birth
|
|
188
|
+
*/
|
|
189
|
+
const validateDateOfBirth = (date) => {
|
|
190
|
+
if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
|
|
191
|
+
return { isValid: false, error: 'Please enter a valid date' };
|
|
192
|
+
}
|
|
193
|
+
const today = new Date();
|
|
194
|
+
const age = today.getFullYear() - date.getFullYear();
|
|
195
|
+
if (age < 13) {
|
|
196
|
+
return { isValid: false, error: 'You must be at least 13 years old' };
|
|
197
|
+
}
|
|
198
|
+
if (age > 120) {
|
|
199
|
+
return { isValid: false, error: 'Please enter a valid date of birth' };
|
|
200
|
+
}
|
|
201
|
+
return { isValid: true };
|
|
202
|
+
};
|
|
203
|
+
exports.validateDateOfBirth = validateDateOfBirth;
|
|
204
|
+
/**
|
|
205
|
+
* Validate age
|
|
206
|
+
*/
|
|
207
|
+
const validateAge = (age) => {
|
|
208
|
+
return (0, exports.validateNumberRange)(age, 13, 120, 'Age');
|
|
209
|
+
};
|
|
210
|
+
exports.validateAge = validateAge;
|
|
211
|
+
/**
|
|
212
|
+
* Validate weight (kg)
|
|
213
|
+
*/
|
|
214
|
+
const validateWeight = (weight) => {
|
|
215
|
+
return (0, exports.validateNumberRange)(weight, 20, 300, 'Weight');
|
|
216
|
+
};
|
|
217
|
+
exports.validateWeight = validateWeight;
|
|
218
|
+
/**
|
|
219
|
+
* Validate height (cm)
|
|
220
|
+
*/
|
|
221
|
+
const validateHeight = (height) => {
|
|
222
|
+
return (0, exports.validateNumberRange)(height, 50, 250, 'Height');
|
|
223
|
+
};
|
|
224
|
+
exports.validateHeight = validateHeight;
|
|
225
|
+
/**
|
|
226
|
+
* Validate calories
|
|
227
|
+
*/
|
|
228
|
+
const validateCalories = (calories) => {
|
|
229
|
+
return (0, exports.validateNumberRange)(calories, 0, 10000, 'Calories');
|
|
230
|
+
};
|
|
231
|
+
exports.validateCalories = validateCalories;
|
|
232
|
+
/**
|
|
233
|
+
* Validate macros (protein, carbs, fat in grams)
|
|
234
|
+
*/
|
|
235
|
+
const validateMacros = (grams, macroType) => {
|
|
236
|
+
return (0, exports.validateNumberRange)(grams, 0, 1000, macroType);
|
|
237
|
+
};
|
|
238
|
+
exports.validateMacros = validateMacros;
|
|
239
|
+
/**
|
|
240
|
+
* Validate water intake (glasses)
|
|
241
|
+
*/
|
|
242
|
+
const validateWaterIntake = (glasses) => {
|
|
243
|
+
return (0, exports.validateNumberRange)(glasses, 0, 50, 'Water intake');
|
|
244
|
+
};
|
|
245
|
+
exports.validateWaterIntake = validateWaterIntake;
|
|
246
|
+
/**
|
|
247
|
+
* Validate workout duration (minutes)
|
|
248
|
+
*/
|
|
249
|
+
const validateWorkoutDuration = (minutes) => {
|
|
250
|
+
return (0, exports.validateNumberRange)(minutes, 1, 600, 'Workout duration');
|
|
251
|
+
};
|
|
252
|
+
exports.validateWorkoutDuration = validateWorkoutDuration;
|
|
253
|
+
/**
|
|
254
|
+
* Validate reps
|
|
255
|
+
*/
|
|
256
|
+
const validateReps = (reps) => {
|
|
257
|
+
return (0, exports.validateNumberRange)(reps, 1, 1000, 'Reps');
|
|
258
|
+
};
|
|
259
|
+
exports.validateReps = validateReps;
|
|
260
|
+
/**
|
|
261
|
+
* Validate sets
|
|
262
|
+
*/
|
|
263
|
+
const validateSets = (sets) => {
|
|
264
|
+
return (0, exports.validateNumberRange)(sets, 1, 100, 'Sets');
|
|
265
|
+
};
|
|
266
|
+
exports.validateSets = validateSets;
|
|
267
|
+
/**
|
|
268
|
+
* Validate exercise weight (kg)
|
|
269
|
+
*/
|
|
270
|
+
const validateExerciseWeight = (weight) => {
|
|
271
|
+
return (0, exports.validateNumberRange)(weight, 0, 1000, 'Weight');
|
|
272
|
+
};
|
|
273
|
+
exports.validateExerciseWeight = validateExerciseWeight;
|
|
274
|
+
/**
|
|
275
|
+
* Batch validation
|
|
276
|
+
* Validates multiple fields and returns all errors
|
|
277
|
+
*/
|
|
278
|
+
const batchValidate = (validations) => {
|
|
279
|
+
const errors = {};
|
|
280
|
+
let isValid = true;
|
|
281
|
+
validations.forEach(({ field, validator }) => {
|
|
282
|
+
const result = validator();
|
|
283
|
+
if (!result.isValid && result.error) {
|
|
284
|
+
errors[field] = result.error;
|
|
285
|
+
isValid = false;
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
return { isValid, errors };
|
|
289
|
+
};
|
|
290
|
+
exports.batchValidate = batchValidate;
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@umituz/react-native-validation",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Comprehensive validation utilities for React Native forms",
|
|
5
|
+
"main": "./lib/index.js",
|
|
6
|
+
"types": "./lib/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"typecheck": "tsc --noEmit",
|
|
10
|
+
"lint": "tsc --noEmit",
|
|
11
|
+
"clean": "rm -rf lib",
|
|
12
|
+
"prebuild": "npm run clean",
|
|
13
|
+
"prepublishOnly": "npm run build",
|
|
14
|
+
"version:patch": "npm version patch -m 'chore: release v%s'",
|
|
15
|
+
"version:minor": "npm version minor -m 'chore: release v%s'",
|
|
16
|
+
"version:major": "npm version major -m 'chore: release v%s'"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"react-native",
|
|
20
|
+
"validation",
|
|
21
|
+
"forms",
|
|
22
|
+
"validators",
|
|
23
|
+
"form-validation"
|
|
24
|
+
],
|
|
25
|
+
"author": "Ümit UZ <umit@umituz.com>",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/umituz/react-native-validation"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"react": ">=18.2.0",
|
|
33
|
+
"react-native": ">=0.74.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/react": "^18.2.45",
|
|
37
|
+
"@types/react-native": "^0.73.0",
|
|
38
|
+
"react": "^18.2.0",
|
|
39
|
+
"react-native": "^0.74.0",
|
|
40
|
+
"typescript": "^5.3.3"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"lib",
|
|
47
|
+
"src",
|
|
48
|
+
"README.md",
|
|
49
|
+
"LICENSE"
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @umituz/react-native-validation
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive validation utilities for React Native forms.
|
|
5
|
+
* Provides reusable validation functions for common input types.
|
|
6
|
+
*
|
|
7
|
+
* @package react-native-validation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Domain Layer - Entities
|
|
11
|
+
export type { ValidationResult } from './domain/entities/ValidationResult';
|
|
12
|
+
|
|
13
|
+
// Infrastructure Layer - Validators
|
|
14
|
+
export {
|
|
15
|
+
validateEmail,
|
|
16
|
+
validatePassword,
|
|
17
|
+
validatePasswordConfirmation,
|
|
18
|
+
validateRequired,
|
|
19
|
+
validateName,
|
|
20
|
+
validatePhone,
|
|
21
|
+
validateNumberRange,
|
|
22
|
+
validatePositiveNumber,
|
|
23
|
+
validateMinLength,
|
|
24
|
+
validateMaxLength,
|
|
25
|
+
validatePattern,
|
|
26
|
+
validateDateOfBirth,
|
|
27
|
+
validateAge,
|
|
28
|
+
validateWeight,
|
|
29
|
+
validateHeight,
|
|
30
|
+
validateCalories,
|
|
31
|
+
validateMacros,
|
|
32
|
+
validateWaterIntake,
|
|
33
|
+
validateWorkoutDuration,
|
|
34
|
+
validateReps,
|
|
35
|
+
validateSets,
|
|
36
|
+
validateExerciseWeight,
|
|
37
|
+
batchValidate,
|
|
38
|
+
} from './infrastructure/utils/validators';
|
|
39
|
+
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Utilities
|
|
3
|
+
* Comprehensive validation functions for React Native forms
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ValidationResult } from '../../domain/entities/ValidationResult';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Validate email format
|
|
10
|
+
*/
|
|
11
|
+
export const validateEmail = (email: string): ValidationResult => {
|
|
12
|
+
if (!email || email.trim() === '') {
|
|
13
|
+
return { isValid: false, error: 'Email is required' };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
17
|
+
if (!emailRegex.test(email)) {
|
|
18
|
+
return { isValid: false, error: 'Please enter a valid email address' };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return { isValid: true };
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Validate password strength
|
|
26
|
+
* @param password - Password to validate
|
|
27
|
+
* @param minLength - Minimum password length (default: 8)
|
|
28
|
+
* @param requireUppercase - Require uppercase letter (default: true)
|
|
29
|
+
* @param requireLowercase - Require lowercase letter (default: true)
|
|
30
|
+
* @param requireNumber - Require number (default: true)
|
|
31
|
+
*/
|
|
32
|
+
export const validatePassword = (
|
|
33
|
+
password: string,
|
|
34
|
+
options?: {
|
|
35
|
+
minLength?: number;
|
|
36
|
+
requireUppercase?: boolean;
|
|
37
|
+
requireLowercase?: boolean;
|
|
38
|
+
requireNumber?: boolean;
|
|
39
|
+
}
|
|
40
|
+
): ValidationResult => {
|
|
41
|
+
const {
|
|
42
|
+
minLength = 8,
|
|
43
|
+
requireUppercase = true,
|
|
44
|
+
requireLowercase = true,
|
|
45
|
+
requireNumber = true,
|
|
46
|
+
} = options || {};
|
|
47
|
+
|
|
48
|
+
if (!password || password.trim() === '') {
|
|
49
|
+
return { isValid: false, error: 'Password is required' };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (password.length < minLength) {
|
|
53
|
+
return {
|
|
54
|
+
isValid: false,
|
|
55
|
+
error: `Password must be at least ${minLength} characters`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (requireUppercase && !/[A-Z]/.test(password)) {
|
|
60
|
+
return {
|
|
61
|
+
isValid: false,
|
|
62
|
+
error: 'Password must contain at least one uppercase letter',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (requireLowercase && !/[a-z]/.test(password)) {
|
|
67
|
+
return {
|
|
68
|
+
isValid: false,
|
|
69
|
+
error: 'Password must contain at least one lowercase letter',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (requireNumber && !/[0-9]/.test(password)) {
|
|
74
|
+
return {
|
|
75
|
+
isValid: false,
|
|
76
|
+
error: 'Password must contain at least one number',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { isValid: true };
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Validate password confirmation
|
|
85
|
+
*/
|
|
86
|
+
export const validatePasswordConfirmation = (
|
|
87
|
+
password: string,
|
|
88
|
+
confirmPassword: string
|
|
89
|
+
): ValidationResult => {
|
|
90
|
+
if (!confirmPassword) {
|
|
91
|
+
return { isValid: false, error: 'Please confirm your password' };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (password !== confirmPassword) {
|
|
95
|
+
return { isValid: false, error: 'Passwords do not match' };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { isValid: true };
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Validate required field
|
|
103
|
+
*/
|
|
104
|
+
export const validateRequired = (
|
|
105
|
+
value: string,
|
|
106
|
+
fieldName: string = 'This field'
|
|
107
|
+
): ValidationResult => {
|
|
108
|
+
if (!value || value.trim() === '') {
|
|
109
|
+
return { isValid: false, error: `${fieldName} is required` };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return { isValid: true };
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Validate name
|
|
117
|
+
*/
|
|
118
|
+
export const validateName = (
|
|
119
|
+
name: string,
|
|
120
|
+
fieldName: string = 'Name',
|
|
121
|
+
minLength: number = 2
|
|
122
|
+
): ValidationResult => {
|
|
123
|
+
if (!name || name.trim() === '') {
|
|
124
|
+
return { isValid: false, error: `${fieldName} is required` };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (name.trim().length < minLength) {
|
|
128
|
+
return {
|
|
129
|
+
isValid: false,
|
|
130
|
+
error: `${fieldName} must be at least ${minLength} characters`,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return { isValid: true };
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Validate phone number (E.164 format)
|
|
139
|
+
*/
|
|
140
|
+
export const validatePhone = (phone: string): ValidationResult => {
|
|
141
|
+
if (!phone || phone.trim() === '') {
|
|
142
|
+
return { isValid: false, error: 'Phone number is required' };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const phoneRegex = /^\+[1-9]\d{1,14}$/;
|
|
146
|
+
if (!phoneRegex.test(phone)) {
|
|
147
|
+
return { isValid: false, error: 'Please enter a valid phone number' };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return { isValid: true };
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Validate number range
|
|
155
|
+
*/
|
|
156
|
+
export const validateNumberRange = (
|
|
157
|
+
value: number,
|
|
158
|
+
min: number,
|
|
159
|
+
max: number,
|
|
160
|
+
fieldName: string = 'Value'
|
|
161
|
+
): ValidationResult => {
|
|
162
|
+
if (isNaN(value)) {
|
|
163
|
+
return { isValid: false, error: `${fieldName} must be a number` };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (value < min || value > max) {
|
|
167
|
+
return {
|
|
168
|
+
isValid: false,
|
|
169
|
+
error: `${fieldName} must be between ${min} and ${max}`,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return { isValid: true };
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Validate positive number
|
|
178
|
+
*/
|
|
179
|
+
export const validatePositiveNumber = (
|
|
180
|
+
value: number,
|
|
181
|
+
fieldName: string = 'Value'
|
|
182
|
+
): ValidationResult => {
|
|
183
|
+
if (isNaN(value)) {
|
|
184
|
+
return { isValid: false, error: `${fieldName} must be a number` };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (value <= 0) {
|
|
188
|
+
return { isValid: false, error: `${fieldName} must be greater than 0` };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return { isValid: true };
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Validate min length
|
|
196
|
+
*/
|
|
197
|
+
export const validateMinLength = (
|
|
198
|
+
value: string,
|
|
199
|
+
minLength: number,
|
|
200
|
+
fieldName: string = 'Field'
|
|
201
|
+
): ValidationResult => {
|
|
202
|
+
if (!value || value.trim().length < minLength) {
|
|
203
|
+
return {
|
|
204
|
+
isValid: false,
|
|
205
|
+
error: `${fieldName} must be at least ${minLength} characters`,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return { isValid: true };
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Validate max length
|
|
214
|
+
*/
|
|
215
|
+
export const validateMaxLength = (
|
|
216
|
+
value: string,
|
|
217
|
+
maxLength: number,
|
|
218
|
+
fieldName: string = 'Field'
|
|
219
|
+
): ValidationResult => {
|
|
220
|
+
if (value && value.trim().length > maxLength) {
|
|
221
|
+
return {
|
|
222
|
+
isValid: false,
|
|
223
|
+
error: `${fieldName} must be at most ${maxLength} characters`,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return { isValid: true };
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Validate pattern (regex)
|
|
232
|
+
*/
|
|
233
|
+
export const validatePattern = (
|
|
234
|
+
value: string,
|
|
235
|
+
pattern: RegExp,
|
|
236
|
+
fieldName: string = 'Field',
|
|
237
|
+
errorMessage?: string
|
|
238
|
+
): ValidationResult => {
|
|
239
|
+
if (!value) {
|
|
240
|
+
return { isValid: false, error: `${fieldName} is required` };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!pattern.test(value)) {
|
|
244
|
+
return {
|
|
245
|
+
isValid: false,
|
|
246
|
+
error: errorMessage || `${fieldName} format is invalid`,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return { isValid: true };
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Validate date of birth
|
|
255
|
+
*/
|
|
256
|
+
export const validateDateOfBirth = (date: Date): ValidationResult => {
|
|
257
|
+
if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
|
|
258
|
+
return { isValid: false, error: 'Please enter a valid date' };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const today = new Date();
|
|
262
|
+
const age = today.getFullYear() - date.getFullYear();
|
|
263
|
+
|
|
264
|
+
if (age < 13) {
|
|
265
|
+
return { isValid: false, error: 'You must be at least 13 years old' };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (age > 120) {
|
|
269
|
+
return { isValid: false, error: 'Please enter a valid date of birth' };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return { isValid: true };
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Validate age
|
|
277
|
+
*/
|
|
278
|
+
export const validateAge = (age: number): ValidationResult => {
|
|
279
|
+
return validateNumberRange(age, 13, 120, 'Age');
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Validate weight (kg)
|
|
284
|
+
*/
|
|
285
|
+
export const validateWeight = (weight: number): ValidationResult => {
|
|
286
|
+
return validateNumberRange(weight, 20, 300, 'Weight');
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Validate height (cm)
|
|
291
|
+
*/
|
|
292
|
+
export const validateHeight = (height: number): ValidationResult => {
|
|
293
|
+
return validateNumberRange(height, 50, 250, 'Height');
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Validate calories
|
|
298
|
+
*/
|
|
299
|
+
export const validateCalories = (calories: number): ValidationResult => {
|
|
300
|
+
return validateNumberRange(calories, 0, 10000, 'Calories');
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Validate macros (protein, carbs, fat in grams)
|
|
305
|
+
*/
|
|
306
|
+
export const validateMacros = (
|
|
307
|
+
grams: number,
|
|
308
|
+
macroType: string
|
|
309
|
+
): ValidationResult => {
|
|
310
|
+
return validateNumberRange(grams, 0, 1000, macroType);
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Validate water intake (glasses)
|
|
315
|
+
*/
|
|
316
|
+
export const validateWaterIntake = (glasses: number): ValidationResult => {
|
|
317
|
+
return validateNumberRange(glasses, 0, 50, 'Water intake');
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Validate workout duration (minutes)
|
|
322
|
+
*/
|
|
323
|
+
export const validateWorkoutDuration = (minutes: number): ValidationResult => {
|
|
324
|
+
return validateNumberRange(minutes, 1, 600, 'Workout duration');
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Validate reps
|
|
329
|
+
*/
|
|
330
|
+
export const validateReps = (reps: number): ValidationResult => {
|
|
331
|
+
return validateNumberRange(reps, 1, 1000, 'Reps');
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Validate sets
|
|
336
|
+
*/
|
|
337
|
+
export const validateSets = (sets: number): ValidationResult => {
|
|
338
|
+
return validateNumberRange(sets, 1, 100, 'Sets');
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Validate exercise weight (kg)
|
|
343
|
+
*/
|
|
344
|
+
export const validateExerciseWeight = (weight: number): ValidationResult => {
|
|
345
|
+
return validateNumberRange(weight, 0, 1000, 'Weight');
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Batch validation
|
|
350
|
+
* Validates multiple fields and returns all errors
|
|
351
|
+
*/
|
|
352
|
+
export const batchValidate = (
|
|
353
|
+
validations: Array<{ field: string; validator: () => ValidationResult }>
|
|
354
|
+
): { isValid: boolean; errors: Record<string, string> } => {
|
|
355
|
+
const errors: Record<string, string> = {};
|
|
356
|
+
let isValid = true;
|
|
357
|
+
|
|
358
|
+
validations.forEach(({ field, validator }) => {
|
|
359
|
+
const result = validator();
|
|
360
|
+
if (!result.isValid && result.error) {
|
|
361
|
+
errors[field] = result.error;
|
|
362
|
+
isValid = false;
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
return { isValid, errors };
|
|
367
|
+
};
|
|
368
|
+
|