import { VALIDATION, AUTH_LEVELS, USER_STATUS, TRANSMISSION_TYPES, FUEL_TYPES, USE_TYPES, PAYMENT_STATUS } from './constants'; // User validation export function validateUser(data: { name?: string; username?: string; email?: string; password?: string; authLevel?: number; status?: string; }) { const errors: Record = {}; if (data.name !== undefined) { if (!data.name || data.name.trim().length === 0) { errors.name = 'الاسم مطلوب'; } else if (data.name.length > VALIDATION.MAX_NAME_LENGTH) { errors.name = `الاسم يجب أن يكون أقل من ${VALIDATION.MAX_NAME_LENGTH} حرف`; } } if (data.username !== undefined) { if (!data.username || data.username.trim().length === 0) { errors.username = 'اسم المستخدم مطلوب'; } else if (data.username.length < 3) { errors.username = 'اسم المستخدم يجب أن يكون على الأقل 3 أحرف'; } else if (!/^[a-zA-Z0-9_]+$/.test(data.username)) { errors.username = 'اسم المستخدم يجب أن يحتوي على أحرف وأرقام فقط'; } } if (data.email !== undefined) { if (!data.email || data.email.trim().length === 0) { errors.email = 'البريد الإلكتروني مطلوب'; } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) { errors.email = 'البريد الإلكتروني غير صحيح'; } } if (data.password !== undefined) { if (!data.password || data.password.length === 0) { errors.password = 'كلمة المرور مطلوبة'; } else if (data.password.length < VALIDATION.MIN_PASSWORD_LENGTH) { errors.password = `كلمة المرور يجب أن تكون على الأقل ${VALIDATION.MIN_PASSWORD_LENGTH} أحرف`; } } if (data.authLevel !== undefined) { if (!Object.values(AUTH_LEVELS).includes(data.authLevel as any)) { errors.authLevel = 'مستوى الصلاحية غير صحيح'; } } if (data.status !== undefined) { if (!Object.values(USER_STATUS).includes(data.status as any)) { errors.status = 'حالة المستخدم غير صحيحة'; } } return { isValid: Object.keys(errors).length === 0, errors, }; } // Customer validation export function validateCustomer(data: { name?: string; phone?: string; email?: string; address?: string; }) { const errors: Record = {}; if (data.name !== undefined) { if (!data.name || data.name.trim().length === 0) { errors.name = 'اسم العميل مطلوب'; } else if (data.name.length > VALIDATION.MAX_NAME_LENGTH) { errors.name = `الاسم يجب أن يكون أقل من ${VALIDATION.MAX_NAME_LENGTH} حرف`; } } if (data.phone && data.phone.trim().length > 0) { if (!/^[\+]?[0-9\s\-\(\)]+$/.test(data.phone)) { errors.phone = 'رقم الهاتف غير صحيح'; } } if (data.email && data.email.trim().length > 0) { if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) { errors.email = 'البريد الإلكتروني غير صحيح'; } } return { isValid: Object.keys(errors).length === 0, errors, }; } // Vehicle validation export function validateVehicle(data: { plateNumber?: string; bodyType?: string; manufacturer?: string; model?: string; year?: number; transmission?: string; fuel?: string; cylinders?: number; engineDisplacement?: number; useType?: string; ownerId?: number; }) { const errors: Record = {}; if (data.plateNumber !== undefined) { if (!data.plateNumber || data.plateNumber.trim().length === 0) { errors.plateNumber = 'رقم اللوحة مطلوب'; } } if (data.bodyType !== undefined) { if (!data.bodyType || data.bodyType.trim().length === 0) { errors.bodyType = 'نوع الهيكل مطلوب'; } } if (data.manufacturer !== undefined) { if (!data.manufacturer || data.manufacturer.trim().length === 0) { errors.manufacturer = 'الشركة المصنعة مطلوبة'; } } if (data.model !== undefined) { if (!data.model || data.model.trim().length === 0) { errors.model = 'الموديل مطلوب'; } } if (data.year !== undefined) { if (!data.year || data.year < VALIDATION.MIN_YEAR || data.year > VALIDATION.MAX_YEAR) { errors.year = `السنة يجب أن تكون بين ${VALIDATION.MIN_YEAR} و ${VALIDATION.MAX_YEAR}`; } } if (data.transmission !== undefined) { const validTransmissions = TRANSMISSION_TYPES.map(t => t.value); if (!validTransmissions.includes(data.transmission as any)) { errors.transmission = 'نوع ناقل الحركة غير صحيح'; } } if (data.fuel !== undefined) { const validFuels = FUEL_TYPES.map(f => f.value); if (!validFuels.includes(data.fuel as any)) { errors.fuel = 'نوع الوقود غير صحيح'; } } if (data.cylinders !== undefined && data.cylinders !== null) { if (data.cylinders < 1 || data.cylinders > VALIDATION.MAX_CYLINDERS) { errors.cylinders = `عدد الأسطوانات يجب أن يكون بين 1 و ${VALIDATION.MAX_CYLINDERS}`; } } if (data.engineDisplacement !== undefined && data.engineDisplacement !== null) { if (data.engineDisplacement <= 0 || data.engineDisplacement > VALIDATION.MAX_ENGINE_DISPLACEMENT) { errors.engineDisplacement = `سعة المحرك يجب أن تكون بين 0.1 و ${VALIDATION.MAX_ENGINE_DISPLACEMENT}`; } } if (data.useType !== undefined) { const validUseTypes = USE_TYPES.map(u => u.value); if (!validUseTypes.includes(data.useType as any)) { errors.useType = 'نوع الاستخدام غير صحيح'; } } if (data.ownerId !== undefined) { if (!data.ownerId || data.ownerId <= 0) { errors.ownerId = 'مالك المركبة مطلوب'; } } return { isValid: Object.keys(errors).length === 0, errors, }; } // Maintenance visit validation export function validateMaintenanceVisit(data: { vehicleId?: number; customerId?: number; maintenanceJobs?: Array<{typeId: number; job: string; notes?: string}>; description?: string; cost?: number; paymentStatus?: string; kilometers?: number; nextVisitDelay?: number; }) { const errors: Record = {}; if (data.vehicleId !== undefined) { if (!data.vehicleId || data.vehicleId <= 0) { errors.vehicleId = 'المركبة مطلوبة'; } } if (data.customerId !== undefined) { if (!data.customerId || data.customerId <= 0) { errors.customerId = 'العميل مطلوب'; } } if (data.maintenanceJobs !== undefined) { if (!data.maintenanceJobs || data.maintenanceJobs.length === 0) { errors.maintenanceJobs = 'يجب إضافة عمل صيانة واحد على الأقل'; } else { // Validate each maintenance job const invalidJobs = data.maintenanceJobs.filter(job => !job.typeId || job.typeId <= 0 || !job.job || job.job.trim().length === 0 ); if (invalidJobs.length > 0) { errors.maintenanceJobs = 'جميع أعمال الصيانة يجب أن تحتوي على نوع ووصف صحيح'; } } } if (data.description !== undefined) { if (!data.description || data.description.trim().length === 0) { errors.description = 'وصف الصيانة مطلوب'; } else if (data.description.length > VALIDATION.MAX_DESCRIPTION_LENGTH) { errors.description = `الوصف يجب أن يكون أقل من ${VALIDATION.MAX_DESCRIPTION_LENGTH} حرف`; } } if (data.cost !== undefined) { if (data.cost === null || data.cost < VALIDATION.MIN_COST || data.cost > VALIDATION.MAX_COST) { errors.cost = `التكلفة يجب أن تكون بين ${VALIDATION.MIN_COST} و ${VALIDATION.MAX_COST}`; } } if (data.paymentStatus !== undefined) { if (!Object.values(PAYMENT_STATUS).includes(data.paymentStatus as any)) { errors.paymentStatus = 'حالة الدفع غير صحيحة'; } } if (data.kilometers !== undefined) { if (data.kilometers === null || data.kilometers < 0) { errors.kilometers = 'عدد الكيلومترات يجب أن يكون رقم موجب'; } } if (data.nextVisitDelay !== undefined) { if (!data.nextVisitDelay || ![1, 2, 3, 4].includes(data.nextVisitDelay)) { errors.nextVisitDelay = 'فترة الزيارة التالية يجب أن تكون 1، 2، 3، أو 4 أشهر'; } } return { isValid: Object.keys(errors).length === 0, errors, }; } // Expense validation export function validateExpense(data: { description?: string; category?: string; amount?: number; }) { const errors: Record = {}; if (data.description !== undefined) { if (!data.description || data.description.trim().length === 0) { errors.description = 'وصف المصروف مطلوب'; } else if (data.description.length > VALIDATION.MAX_DESCRIPTION_LENGTH) { errors.description = `الوصف يجب أن يكون أقل من ${VALIDATION.MAX_DESCRIPTION_LENGTH} حرف`; } } if (data.category !== undefined) { if (!data.category || data.category.trim().length === 0) { errors.category = 'فئة المصروف مطلوبة'; } } if (data.amount !== undefined) { if (data.amount === null || data.amount <= VALIDATION.MIN_COST || data.amount > VALIDATION.MAX_COST) { errors.amount = `المبلغ يجب أن يكون بين ${VALIDATION.MIN_COST + 0.01} و ${VALIDATION.MAX_COST}`; } } return { isValid: Object.keys(errors).length === 0, errors, }; } // Generic validation helpers export function isValidDate(date: any): date is Date { return date instanceof Date && !isNaN(date.getTime()); } export function isValidNumber(value: any): value is number { return typeof value === 'number' && !isNaN(value) && isFinite(value); } export function isValidString(value: any, minLength = 1): value is string { return typeof value === 'string' && value.trim().length >= minLength; } export function sanitizeString(value: string): string { return value.trim().replace(/\s+/g, ' '); } export function formatCurrency(amount: number): string { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'JOD', }).format(amount); } export function formatDate(date: Date, format: string = 'dd/MM/yyyy'): string { const day = date.getDate().toString().padStart(2, '0'); const month = (date.getMonth() + 1).toString().padStart(2, '0'); const year = date.getFullYear(); const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); switch (format) { case 'dd/MM/yyyy': return `${day}/${month}/${year}`; case 'dd/MM/yyyy HH:mm': return `${day}/${month}/${year} ${hours}:${minutes}`; default: return date.toLocaleDateString('ar-SA'); } }