// 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, rules: Record): { isValid: boolean; errors: Record; } { const errors: Record = {}; 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): Record { const sanitized: Record = {}; 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); }