291 lines
8.3 KiB
TypeScript
291 lines
8.3 KiB
TypeScript
// Validation utility functions with Arabic error messages
|
|
|
|
export interface ValidationRule {
|
|
required?: boolean;
|
|
minLength?: number;
|
|
maxLength?: number;
|
|
min?: number;
|
|
max?: number;
|
|
pattern?: RegExp;
|
|
email?: boolean;
|
|
phone?: boolean;
|
|
url?: boolean;
|
|
custom?: (value: any) => string | null;
|
|
}
|
|
|
|
export interface ValidationResult {
|
|
isValid: boolean;
|
|
error?: string;
|
|
}
|
|
|
|
// Arabic error messages
|
|
export const ERROR_MESSAGES = {
|
|
required: 'هذا الحقل مطلوب',
|
|
email: 'البريد الإلكتروني غير صحيح',
|
|
phone: 'رقم الهاتف غير صحيح',
|
|
url: 'الرابط غير صحيح',
|
|
minLength: (min: number) => `يجب أن يكون على الأقل ${min} أحرف`,
|
|
maxLength: (max: number) => `يجب أن يكون أقل من ${max} حرف`,
|
|
min: (min: number) => `يجب أن يكون على الأقل ${min}`,
|
|
max: (max: number) => `يجب أن يكون أقل من ${max}`,
|
|
pattern: 'التنسيق غير صحيح',
|
|
number: 'يجب أن يكون رقم صحيح',
|
|
integer: 'يجب أن يكون رقم صحيح',
|
|
positive: 'يجب أن يكون رقم موجب',
|
|
negative: 'يجب أن يكون رقم سالب',
|
|
date: 'التاريخ غير صحيح',
|
|
time: 'الوقت غير صحيح',
|
|
datetime: 'التاريخ والوقت غير صحيح',
|
|
};
|
|
|
|
// Regular expressions for validation
|
|
export const PATTERNS = {
|
|
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
phone: /^[\+]?[0-9\s\-\(\)]+$/,
|
|
url: /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/,
|
|
arabicText: /^[\u0600-\u06FF\s\d\p{P}]+$/u,
|
|
englishText: /^[a-zA-Z\s\d\p{P}]+$/u,
|
|
alphanumeric: /^[a-zA-Z0-9]+$/,
|
|
numeric: /^\d+$/,
|
|
decimal: /^\d+(\.\d+)?$/,
|
|
plateNumber: /^[A-Z0-9\u0600-\u06FF\s\-]+$/u,
|
|
username: /^[a-zA-Z0-9_]+$/,
|
|
};
|
|
|
|
// Validate a single field
|
|
export function validateField(value: any, rules: ValidationRule): ValidationResult {
|
|
// Convert value to string for most validations
|
|
const stringValue = value != null ? String(value).trim() : '';
|
|
|
|
// Required validation
|
|
if (rules.required && (!value || stringValue === '')) {
|
|
return { isValid: false, error: ERROR_MESSAGES.required };
|
|
}
|
|
|
|
// Skip other validations if value is empty and not required
|
|
if (!rules.required && (!value || stringValue === '')) {
|
|
return { isValid: true };
|
|
}
|
|
|
|
// Email validation
|
|
if (rules.email && !PATTERNS.email.test(stringValue)) {
|
|
return { isValid: false, error: ERROR_MESSAGES.email };
|
|
}
|
|
|
|
// Phone validation
|
|
if (rules.phone && !PATTERNS.phone.test(stringValue)) {
|
|
return { isValid: false, error: ERROR_MESSAGES.phone };
|
|
}
|
|
|
|
// URL validation
|
|
if (rules.url && !PATTERNS.url.test(stringValue)) {
|
|
return { isValid: false, error: ERROR_MESSAGES.url };
|
|
}
|
|
|
|
// Pattern validation
|
|
if (rules.pattern && !rules.pattern.test(stringValue)) {
|
|
return { isValid: false, error: ERROR_MESSAGES.pattern };
|
|
}
|
|
|
|
// Length validations
|
|
if (rules.minLength && stringValue.length < rules.minLength) {
|
|
return { isValid: false, error: ERROR_MESSAGES.minLength(rules.minLength) };
|
|
}
|
|
|
|
if (rules.maxLength && stringValue.length > rules.maxLength) {
|
|
return { isValid: false, error: ERROR_MESSAGES.maxLength(rules.maxLength) };
|
|
}
|
|
|
|
// Numeric validations
|
|
if (rules.min !== undefined || rules.max !== undefined) {
|
|
const numValue = Number(value);
|
|
if (isNaN(numValue)) {
|
|
return { isValid: false, error: ERROR_MESSAGES.number };
|
|
}
|
|
|
|
if (rules.min !== undefined && numValue < rules.min) {
|
|
return { isValid: false, error: ERROR_MESSAGES.min(rules.min) };
|
|
}
|
|
|
|
if (rules.max !== undefined && numValue > rules.max) {
|
|
return { isValid: false, error: ERROR_MESSAGES.max(rules.max) };
|
|
}
|
|
}
|
|
|
|
// Custom validation
|
|
if (rules.custom) {
|
|
const customError = rules.custom(value);
|
|
if (customError) {
|
|
return { isValid: false, error: customError };
|
|
}
|
|
}
|
|
|
|
return { isValid: true };
|
|
}
|
|
|
|
// Validate multiple fields
|
|
export function validateFields(data: Record<string, any>, rules: Record<string, ValidationRule>): {
|
|
isValid: boolean;
|
|
errors: Record<string, string>;
|
|
} {
|
|
const errors: Record<string, string> = {};
|
|
|
|
Object.entries(rules).forEach(([fieldName, fieldRules]) => {
|
|
const result = validateField(data[fieldName], fieldRules);
|
|
if (!result.isValid && result.error) {
|
|
errors[fieldName] = result.error;
|
|
}
|
|
});
|
|
|
|
return {
|
|
isValid: Object.keys(errors).length === 0,
|
|
errors,
|
|
};
|
|
}
|
|
|
|
// Specific validation functions
|
|
export function validateEmail(email: string): ValidationResult {
|
|
return validateField(email, { required: true, email: true });
|
|
}
|
|
|
|
export function validatePhone(phone: string): ValidationResult {
|
|
return validateField(phone, { phone: true });
|
|
}
|
|
|
|
export function validatePassword(password: string, minLength = 8): ValidationResult {
|
|
return validateField(password, { required: true, minLength });
|
|
}
|
|
|
|
export function validateUsername(username: string): ValidationResult {
|
|
return validateField(username, {
|
|
required: true,
|
|
minLength: 3,
|
|
pattern: PATTERNS.username
|
|
});
|
|
}
|
|
|
|
export function validatePlateNumber(plateNumber: string): ValidationResult {
|
|
return validateField(plateNumber, {
|
|
required: true,
|
|
pattern: PATTERNS.plateNumber
|
|
});
|
|
}
|
|
|
|
export function validateYear(year: number, minYear = 1900, maxYear = new Date().getFullYear() + 1): ValidationResult {
|
|
return validateField(year, {
|
|
required: true,
|
|
min: minYear,
|
|
max: maxYear
|
|
});
|
|
}
|
|
|
|
export function validateCurrency(amount: number, min = 0, max = 999999999): ValidationResult {
|
|
return validateField(amount, {
|
|
required: true,
|
|
min,
|
|
max
|
|
});
|
|
}
|
|
|
|
// Form data sanitization
|
|
export function sanitizeString(value: string): string {
|
|
return value.trim().replace(/\s+/g, ' ');
|
|
}
|
|
|
|
export function sanitizeNumber(value: string | number): number | null {
|
|
const num = Number(value);
|
|
return isNaN(num) ? null : num;
|
|
}
|
|
|
|
export function sanitizeInteger(value: string | number): number | null {
|
|
const num = parseInt(String(value));
|
|
return isNaN(num) ? null : num;
|
|
}
|
|
|
|
export function sanitizeFormData(data: Record<string, any>): Record<string, any> {
|
|
const sanitized: Record<string, any> = {};
|
|
|
|
Object.entries(data).forEach(([key, value]) => {
|
|
if (typeof value === 'string') {
|
|
sanitized[key] = sanitizeString(value);
|
|
} else {
|
|
sanitized[key] = value;
|
|
}
|
|
});
|
|
|
|
return sanitized;
|
|
}
|
|
|
|
// Date validation helpers
|
|
export function isValidDate(date: any): boolean {
|
|
return date instanceof Date && !isNaN(date.getTime());
|
|
}
|
|
|
|
export function validateDateRange(startDate: Date, endDate: Date): ValidationResult {
|
|
if (!isValidDate(startDate) || !isValidDate(endDate)) {
|
|
return { isValid: false, error: ERROR_MESSAGES.date };
|
|
}
|
|
|
|
if (startDate >= endDate) {
|
|
return { isValid: false, error: 'تاريخ البداية يجب أن يكون قبل تاريخ النهاية' };
|
|
}
|
|
|
|
return { isValid: true };
|
|
}
|
|
|
|
// File validation helpers
|
|
export function validateFileSize(file: File, maxSizeInMB: number): ValidationResult {
|
|
const maxSizeInBytes = maxSizeInMB * 1024 * 1024;
|
|
|
|
if (file.size > maxSizeInBytes) {
|
|
return {
|
|
isValid: false,
|
|
error: `حجم الملف يجب أن يكون أقل من ${maxSizeInMB} ميجابايت`
|
|
};
|
|
}
|
|
|
|
return { isValid: true };
|
|
}
|
|
|
|
export function validateFileType(file: File, allowedTypes: string[]): ValidationResult {
|
|
if (!allowedTypes.includes(file.type)) {
|
|
return {
|
|
isValid: false,
|
|
error: `نوع الملف غير مدعوم. الأنواع المدعومة: ${allowedTypes.join(', ')}`
|
|
};
|
|
}
|
|
|
|
return { isValid: true };
|
|
}
|
|
|
|
// Array validation helpers
|
|
export function validateArrayLength(array: any[], min?: number, max?: number): ValidationResult {
|
|
if (min !== undefined && array.length < min) {
|
|
return {
|
|
isValid: false,
|
|
error: `يجب اختيار ${min} عنصر على الأقل`
|
|
};
|
|
}
|
|
|
|
if (max !== undefined && array.length > max) {
|
|
return {
|
|
isValid: false,
|
|
error: `يجب اختيار ${max} عنصر كحد أقصى`
|
|
};
|
|
}
|
|
|
|
return { isValid: true };
|
|
}
|
|
|
|
// Conditional validation
|
|
export function validateConditional(
|
|
value: any,
|
|
condition: boolean,
|
|
rules: ValidationRule
|
|
): ValidationResult {
|
|
if (!condition) {
|
|
return { isValid: true };
|
|
}
|
|
|
|
return validateField(value, rules);
|
|
} |