Commit e1f1837a authored by Mahmoud Aglan's avatar Mahmoud Aglan

Overhaul coach create form: full validation, NID auto-parse, edge cases

Frontend:
- Client-side NID parser (no API call) — extracts DOB, gender, age, governorate
- Shows green badge with parsed info (governorate, gender, age)
- Red highlight + Arabic message on every invalid field
- Validates: code, name, employment_type, payment_model (non-academy),
  academy_id (academy), NID length, email format, phone format
- Hides payment/rate fields for academy coaches (not needed)
- Shows note explaining academy coaches follow salary system
- Gender/DOB auto-locked when NID is valid, manual otherwise
- Age displayed under DOB field

Backend:
- Strips non-digits from NID input
- Uppercase code automatically
- Checks NID uniqueness against existing coaches
- Validates email with filter_var
- Academy coach requires academy_id selected
- payment_model defaults to 'salary' for academy, 'per_session' fallback
- max_groups minimum 1
- Success message includes coach name
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 646d11e6
......@@ -99,10 +99,10 @@ class CoachController extends Controller
{
$db = App::getInstance()->db();
$code = trim((string) $request->post('code', ''));
$code = strtoupper(trim((string) $request->post('code', '')));
$fullNameAr = trim((string) $request->post('full_name_ar', ''));
$fullNameEn = trim((string) $request->post('full_name_en', ''));
$nationalId = trim((string) $request->post('national_id', ''));
$nationalId = preg_replace('/\D/', '', trim((string) $request->post('national_id', '')));
$phone = trim((string) $request->post('phone', ''));
$email = trim((string) $request->post('email', ''));
$dateOfBirth = trim((string) $request->post('date_of_birth', ''));
......@@ -114,6 +114,11 @@ class CoachController extends Controller
$monthlyRate = trim((string) $request->post('monthly_rate', ''));
$maxGroups = trim((string) $request->post('max_groups', ''));
$bioAr = trim((string) $request->post('bio_ar', ''));
$coachType = trim((string) $request->post('coach_type', 'independent'));
if (!array_key_exists($coachType, Coach::getCoachTypeOptions())) {
$coachType = 'independent';
}
// Validation
$errors = [];
......@@ -121,6 +126,8 @@ class CoachController extends Controller
$errors[] = 'كود المدرب مطلوب';
} elseif (mb_strlen($code) > 30) {
$errors[] = 'كود المدرب يجب ألا يتجاوز 30 حرف';
} elseif (Coach::findByCode($code) !== null) {
$errors[] = 'كود المدرب "' . $code . '" مستخدم بالفعل — اختر كود آخر';
}
if ($fullNameAr === '') {
$errors[] = 'الاسم بالعربي مطلوب';
......@@ -130,15 +137,23 @@ class CoachController extends Controller
if ($employmentType === '') {
$errors[] = 'نوع التوظيف مطلوب';
}
$coachType = trim((string) $request->post('coach_type', 'independent'));
if (!array_key_exists($coachType, Coach::getCoachTypeOptions())) {
$coachType = 'independent';
}
if ($coachType !== 'academy' && $paymentModel === '') {
$errors[] = 'نموذج الدفع مطلوب';
$errors[] = 'نموذج الدفع مطلوب للمدربين غير التابعين لأكاديمية';
}
if ($coachType === 'academy' && !(int) $request->post('academy_id', 0)) {
$errors[] = 'يجب اختيار الأكاديمية لمدرب أكاديمية';
}
if ($nationalId !== '' && strlen($nationalId) !== 14) {
$errors[] = 'الرقم القومي يجب أن يكون 14 رقم بالضبط';
}
if ($nationalId !== '' && strlen($nationalId) === 14) {
$existingCoach = $db->selectOne("SELECT id, full_name_ar FROM sa_coaches WHERE national_id = ? AND is_archived = 0", [$nationalId]);
if ($existingCoach) {
$errors[] = 'الرقم القومي مسجل بالفعل للمدرب: ' . ($existingCoach['full_name_ar'] ?? '');
}
if ($code !== '' && Coach::findByCode($code) !== null) {
$errors[] = 'كود المدرب مستخدم بالفعل';
}
if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'صيغة البريد الإلكتروني غير صحيحة';
}
if (!empty($errors)) {
......@@ -149,11 +164,12 @@ class CoachController extends Controller
return $this->redirect('/sa/coaches/create');
}
// Parse NID for DOB/gender
if ($nationalId !== '' && strlen($nationalId) === 14) {
$parsed = NationalIdParser::parse($nationalId);
if ($parsed['is_valid']) {
$dateOfBirth = $parsed['dob'];
$gender = $parsed['gender'];
if ($parsed['dob']) $dateOfBirth = $parsed['dob'];
if ($parsed['gender']) $gender = $parsed['gender'];
}
}
......@@ -175,16 +191,15 @@ class CoachController extends Controller
'hourly_rate' => $coachType === 'academy' ? null : ($hourlyRate !== '' ? (float) $hourlyRate : null),
'session_rate' => $coachType === 'academy' ? null : ($sessionRate !== '' ? (float) $sessionRate : null),
'monthly_rate' => $coachType === 'academy' ? null : ($monthlyRate !== '' ? (float) $monthlyRate : null),
'max_groups' => $maxGroups !== '' ? (int) $maxGroups : 10,
'max_groups' => $maxGroups !== '' ? max(1, (int) $maxGroups) : 10,
'bio_ar' => $bioAr ?: null,
'is_active' => 1,
'branch_id' => App::getInstance()->session()->get('branch_id', 1),
]);
// Sync disciplines
$this->syncDisciplines($db, (int) $coach->id, $request);
return $this->redirect('/sa/coaches')->withSuccess('تم إضافة المدرب بنجاح');
return $this->redirect('/sa/coaches')->withSuccess('تم إضافة المدرب "' . $fullNameAr . '" بنجاح');
}
public function show(Request $request, string $id): Response
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment