Commit 933b4f06 authored by Fares's avatar Fares

feat(swimming): improve registration form with live membership lookup and NID auto-fill

- Membership number field with live search that auto-fills all player data
- National ID field auto-extracts DOB, gender, and age (Egyptian 14-digit NID parsing)
- NID also checks membership status automatically
- Added date_of_birth, gender, age, and address fields
- Matches the UX pattern of the existing SA registration wizard
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent daec5951
...@@ -250,110 +250,291 @@ ...@@ -250,110 +250,291 @@
</div> </div>
<?php else: ?> <?php else: ?>
<!-- Lookup Form --> <!-- Registration Form -->
<div class="card"> <div class="card" style="margin-bottom:16px;">
<div style="padding:14px 16px;border-bottom:1px solid #E5E7EB;"> <div style="padding:16px 20px;border-bottom:1px solid #E5E7EB;">
<span style="font-weight:700;font-size:15px;">تسجيل لاعب سباحة جديد</span> <h3 style="margin:0;font-size:16px;font-weight:600;"><i data-lucide="user-plus" style="width:18px;height:18px;vertical-align:middle;margin-left:6px;"></i> تسجيل لاعب سباحة جديد</h3>
</div> </div>
<form id="lookupForm" style="padding:16px;"> <div style="padding:20px;">
<?= csrf_field() ?> <form id="lookupForm">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px;"> <?= csrf_field() ?>
<div> <div style="display:grid;grid-template-columns:1fr;gap:14px;">
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">رقم العضوية</label>
<input type="text" name="membership_number" class="form-control" placeholder="اختياري" style="width:100%;padding:8px 12px;border:1px solid #D1D5DB;border-radius:6px;font-size:13px;">
</div>
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">الرقم القومي <span style="color:#DC2626;">*</span></label>
<input type="text" name="national_id" class="form-control" maxlength="14" placeholder="14 رقم" style="width:100%;padding:8px 12px;border:1px solid #D1D5DB;border-radius:6px;font-size:13px;direction:ltr;">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px;">
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<input type="text" name="full_name_ar" class="form-control" required style="width:100%;padding:8px 12px;border:1px solid #D1D5DB;border-radius:6px;font-size:13px;">
</div>
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">الاسم بالإنجليزي</label>
<input type="text" name="full_name_en" class="form-control" style="width:100%;padding:8px 12px;border:1px solid #D1D5DB;border-radius:6px;font-size:13px;direction:ltr;">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px;">
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">الهاتف</label>
<input type="text" name="phone" class="form-control" style="width:100%;padding:8px 12px;border:1px solid #D1D5DB;border-radius:6px;font-size:13px;direction:ltr;">
</div>
<div></div>
</div>
<!-- Guardian Section --> <!-- Member Quick Lookup -->
<div style="border-top:1px solid #E5E7EB;padding-top:16px;margin-top:8px;margin-bottom:16px;"> <div>
<span style="font-size:13px;font-weight:700;color:#374151;">بيانات ولي الأمر (إذا كان اللاعب أقل من 18 سنة)</span> <label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">رقم العضوية <span style="font-weight:400;color:#6B7280;font-size:12px;">(للأعضاء — يملأ كل البيانات تلقائياً)</span></label>
</div> <input type="text" name="membership_number" id="regMembershipNo" class="form-control" inputmode="numeric" placeholder="أدخل رقم العضوية"
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px;"> style="width:100%;padding:14px;font-size:18px;border:1px solid #D1D5DB;border-radius:10px;direction:ltr;text-align:right;letter-spacing:1px;">
<div> <div id="regMemberLookup" style="display:none;margin-top:8px;padding:10px 12px;border-radius:8px;font-size:13px;"></div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">اسم ولي الأمر</label> </div>
<input type="text" name="guardian_name" class="form-control" style="width:100%;padding:8px 12px;border:1px solid #D1D5DB;border-radius:6px;font-size:13px;">
</div> <div style="text-align:center;color:#9CA3AF;font-size:12px;position:relative;">
<div> <span style="background:white;padding:0 12px;position:relative;z-index:1;">أو أدخل الرقم القومي</span>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">هاتف ولي الأمر</label> <div style="position:absolute;top:50%;left:0;right:0;height:1px;background:#E5E7EB;z-index:0;"></div>
<input type="text" name="guardian_phone" class="form-control" style="width:100%;padding:8px 12px;border:1px solid #D1D5DB;border-radius:6px;font-size:13px;direction:ltr;"> </div>
</div>
</div> <!-- National ID -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px;"> <div>
<div> <label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">الرقم القومي <span style="color:#DC2626;">*</span> <span style="font-weight:400;color:#6B7280;font-size:11px;">(يستخرج تاريخ الميلاد والنوع تلقائياً)</span></label>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">الرقم القومي لولي الأمر</label> <input type="text" name="national_id" id="regNid" class="form-control" maxlength="14" inputmode="numeric" pattern="[0-9]*" placeholder="أدخل 14 رقم"
<input type="text" name="guardian_national_id" class="form-control" maxlength="14" style="width:100%;padding:8px 12px;border:1px solid #D1D5DB;border-radius:6px;font-size:13px;direction:ltr;"> style="width:100%;padding:14px;font-size:18px;border:1px solid #D1D5DB;border-radius:10px;direction:ltr;text-align:right;letter-spacing:1px;">
</div> <div id="regNidInfo" style="display:none;margin-top:8px;padding:10px 12px;border-radius:8px;font-size:13px;"></div>
<div> </div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">صلة القرابة</label>
<select name="guardian_relationship" class="form-control" style="width:100%;padding:8px 12px;border:1px solid #D1D5DB;border-radius:6px;font-size:13px;"> <!-- Name -->
<option value=""></option> <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<option value="father">أب</option> <div>
<option value="mother">أم</option> <label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<option value="brother">أخ</option> <input type="text" name="full_name_ar" id="regNameAr" class="form-control" required style="width:100%;padding:10px 12px;border:1px solid #D1D5DB;border-radius:8px;font-size:14px;">
<option value="sister">أخت</option> </div>
<option value="uncle">عم / خال</option> <div>
<option value="other">أخرى</option> <label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">الاسم بالإنجليزي</label>
</select> <input type="text" name="full_name_en" id="regNameEn" class="form-control" style="width:100%;padding:10px 12px;border:1px solid #D1D5DB;border-radius:8px;font-size:14px;direction:ltr;">
</div>
</div>
<!-- Auto-filled from NID -->
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;">
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">تاريخ الميلاد</label>
<input type="date" name="date_of_birth" id="regDob" class="form-control" style="width:100%;padding:10px 12px;border:1px solid #D1D5DB;border-radius:8px;font-size:13px;">
</div>
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">النوع</label>
<select name="gender" id="regGender" class="form-control" style="width:100%;padding:10px 12px;border:1px solid #D1D5DB;border-radius:8px;font-size:13px;">
<option value=""></option>
<option value="male">ذكر</option>
<option value="female">أنثى</option>
</select>
</div>
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">العمر</label>
<input type="text" id="regAge" class="form-control" disabled style="width:100%;padding:10px 12px;border:1px solid #D1D5DB;border-radius:8px;font-size:13px;background:#F9FAFB;" placeholder="—">
</div>
</div>
<!-- Phone -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">الهاتف</label>
<input type="text" name="phone" id="regPhone" class="form-control" inputmode="tel" style="width:100%;padding:10px 12px;border:1px solid #D1D5DB;border-radius:8px;font-size:14px;direction:ltr;">
</div>
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">العنوان</label>
<input type="text" name="address" id="regAddress" class="form-control" style="width:100%;padding:10px 12px;border:1px solid #D1D5DB;border-radius:8px;font-size:14px;">
</div>
</div>
<!-- Guardian Section -->
<div style="border-top:1px solid #E5E7EB;padding-top:16px;margin-top:4px;">
<span style="font-size:13px;font-weight:700;color:#374151;">بيانات ولي الأمر <span style="font-weight:400;color:#6B7280;font-size:11px;">(مطلوب إذا كان اللاعب أقل من 18 سنة)</span></span>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">اسم ولي الأمر</label>
<input type="text" name="guardian_name" id="regGuardianName" class="form-control" style="width:100%;padding:10px 12px;border:1px solid #D1D5DB;border-radius:8px;font-size:14px;">
</div>
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">هاتف ولي الأمر</label>
<input type="text" name="guardian_phone" id="regGuardianPhone" class="form-control" inputmode="tel" style="width:100%;padding:10px 12px;border:1px solid #D1D5DB;border-radius:8px;font-size:14px;direction:ltr;">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">الرقم القومي لولي الأمر</label>
<input type="text" name="guardian_national_id" class="form-control" maxlength="14" style="width:100%;padding:10px 12px;border:1px solid #D1D5DB;border-radius:8px;font-size:14px;direction:ltr;">
</div>
<div>
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">صلة القرابة</label>
<select name="guardian_relationship" class="form-control" style="width:100%;padding:10px 12px;border:1px solid #D1D5DB;border-radius:8px;font-size:13px;">
<option value=""></option>
<option value="father">أب</option>
<option value="mother">أم</option>
<option value="brother">أخ</option>
<option value="sister">أخت</option>
<option value="uncle">عم / خال</option>
<option value="other">أخرى</option>
</select>
</div>
</div>
</div> </div>
</div>
<button type="submit" class="btn btn-primary" style="width:100%;padding:10px;font-size:14px;"> <button type="submit" id="btnStartReg" class="btn btn-primary" style="width:100%;margin-top:20px;padding:14px;font-size:15px;font-weight:700;border-radius:10px;">
<i data-lucide="search" style="width:15px;height:15px;vertical-align:middle;margin-left:6px;"></i>بحث وتسجيل <i data-lucide="play" style="width:16px;height:16px;vertical-align:middle;margin-left:6px;"></i> بدء التسجيل
</button> </button>
<div id="lookupError" style="display:none;margin-top:10px;padding:8px 12px;background:#FEF2F2;color:#DC2626;border-radius:6px;font-size:12px;"></div> <div id="lookupError" style="display:none;margin-top:12px;padding:10px 12px;background:#FEF2F2;border-radius:8px;color:#DC2626;font-size:13px;"></div>
</form> </form>
</div>
</div> </div>
<script> <script>
document.getElementById('lookupForm').addEventListener('submit', function(e) { document.addEventListener('DOMContentLoaded', function() {
e.preventDefault(); if (typeof lucide !== 'undefined') lucide.createIcons();
const form = this;
const btn = form.querySelector('button[type=submit]'); var csrfToken = document.querySelector('meta[name="csrf-token"]') ? document.querySelector('meta[name="csrf-token"]').content : '';
const errDiv = document.getElementById('lookupError'); var csrfInput = document.querySelector('input[name="_csrf_token"]');
errDiv.style.display = 'none'; if (csrfInput) csrfToken = csrfInput.value;
btn.disabled = true;
btn.innerHTML = 'جاري البحث...'; var regMembershipNo = document.getElementById('regMembershipNo');
var memberLookupBox = document.getElementById('regMemberLookup');
const fd = new FormData(form); var regNid = document.getElementById('regNid');
fetch('/sa/swimming/register/lookup', { method: 'POST', body: fd }) var nidInfoBox = document.getElementById('regNidInfo');
.then(r => r.json()) var regNameAr = document.getElementById('regNameAr');
.then(data => { var regNameEn = document.getElementById('regNameEn');
var regPhone = document.getElementById('regPhone');
var regDob = document.getElementById('regDob');
var regGender = document.getElementById('regGender');
var regAge = document.getElementById('regAge');
function fillMemberData(data) {
if (data.name && !regNameAr.value.trim()) regNameAr.value = data.name;
if (data.name_en && !regNameEn.value.trim()) regNameEn.value = data.name_en;
if (data.phone && !regPhone.value.trim()) regPhone.value = data.phone;
if (data.national_id && !regNid.value) regNid.value = data.national_id;
if (data.membership_number && !regMembershipNo.value) regMembershipNo.value = data.membership_number;
if (data.date_of_birth && !regDob.value) {
regDob.value = data.date_of_birth;
calculateAge(data.date_of_birth);
}
if (data.gender && !regGender.value) regGender.value = data.gender;
}
function parseNid(nid) {
if (nid.length !== 14) return null;
var century = nid[0] === '2' ? '19' : (nid[0] === '3' ? '20' : null);
if (!century) return null;
var year = century + nid.substring(1, 3);
var month = nid.substring(3, 5);
var day = nid.substring(5, 7);
var dob = year + '-' + month + '-' + day;
var genderDigit = parseInt(nid[12]);
var gender = (genderDigit % 2 === 1) ? 'male' : 'female';
return { dob: dob, gender: gender };
}
function calculateAge(dob) {
if (!dob) { regAge.value = ''; return; }
var birth = new Date(dob);
var today = new Date();
var age = today.getFullYear() - birth.getFullYear();
var m = today.getMonth() - birth.getMonth();
if (m < 0 || (m === 0 && today.getDate() < birth.getDate())) age--;
regAge.value = age + ' سنة';
}
// Membership number live lookup
var memberTimer = null;
regMembershipNo.addEventListener('input', function() {
var val = this.value.replace(/\D/g, '');
this.value = val;
if (val.length < 1) { memberLookupBox.style.display = 'none'; return; }
clearTimeout(memberTimer);
memberTimer = setTimeout(function() {
memberLookupBox.style.display = '';
memberLookupBox.style.background = '#F3F4F6';
memberLookupBox.style.color = '#6B7280';
memberLookupBox.textContent = 'جاري البحث...';
fetch('/api/sa/member-lookup', {
method: 'POST',
headers: {'Content-Type':'application/json','X-Requested-With':'XMLHttpRequest','X-CSRF-TOKEN': csrfToken},
body: JSON.stringify({membership_number: val, _csrf_token: csrfToken})
}).then(function(r){return r.json();}).then(function(data) {
if (data.success && data.member_id) {
memberLookupBox.style.background = '#ECFDF5';
memberLookupBox.style.color = '#059669';
memberLookupBox.innerHTML = '<strong>عضو</strong> — ' + (data.name || '') +
(data.is_member ? ' <span style="background:#ECFDF5;padding:1px 6px;border-radius:4px;font-size:11px;">فعال</span>' :
' <span style="color:#92400E;">(' + (data.reason || 'غير فعال') + ')</span>');
fillMemberData(data);
} else {
memberLookupBox.style.background = '#FEF2F2';
memberLookupBox.style.color = '#DC2626';
memberLookupBox.textContent = data.reason || 'رقم العضوية غير موجود';
}
}).catch(function() {
memberLookupBox.style.display = 'none';
});
}, 400);
});
// NID auto-parse + membership lookup
regNid.addEventListener('input', function() {
this.value = this.value.replace(/\D/g, '');
if (this.value.length === 14) {
var parsed = parseNid(this.value);
if (parsed) {
regDob.value = parsed.dob;
regGender.value = parsed.gender;
calculateAge(parsed.dob);
nidInfoBox.style.display = '';
nidInfoBox.style.background = '#EFF6FF';
nidInfoBox.style.color = '#1D4ED8';
nidInfoBox.innerHTML = '<i data-lucide="info" style="width:13px;height:13px;vertical-align:middle;margin-left:4px;"></i>' +
'تاريخ الميلاد: <strong>' + parsed.dob + '</strong> — النوع: <strong>' + (parsed.gender === 'male' ? 'ذكر' : 'أنثى') + '</strong>';
if (typeof lucide !== 'undefined') lucide.createIcons();
}
// Also check membership via NID
fetch('/api/sa/member-lookup', {
method: 'POST',
headers: {'Content-Type':'application/json','X-Requested-With':'XMLHttpRequest','X-CSRF-TOKEN': csrfToken},
body: JSON.stringify({national_id: this.value, _csrf_token: csrfToken})
}).then(function(r){return r.json();}).then(function(data) {
if (data.success && data.is_member) {
nidInfoBox.style.background = '#ECFDF5';
nidInfoBox.style.color = '#059669';
nidInfoBox.innerHTML = '<strong>عضو فعال</strong>' +
(data.membership_number ? ' — رقم العضوية: ' + data.membership_number : '') +
(data.name ? ' — ' + data.name : '');
fillMemberData(data);
} else if (data.success && data.reason) {
nidInfoBox.style.background = '#FEF3C7';
nidInfoBox.style.color = '#92400E';
nidInfoBox.innerHTML += '<br><strong>غير عضو</strong> — ' + data.reason;
if (data.name) fillMemberData(data);
}
});
} else {
nidInfoBox.style.display = 'none';
}
});
// DOB change -> recalculate age
regDob.addEventListener('change', function() { calculateAge(this.value); });
// Submit form
document.getElementById('lookupForm').addEventListener('submit', function(e) {
e.preventDefault();
var btn = document.getElementById('btnStartReg');
var errDiv = document.getElementById('lookupError');
errDiv.style.display = 'none';
btn.disabled = true;
btn.textContent = 'جاري التسجيل...';
var formData = new FormData(this);
var body = {};
formData.forEach(function(v, k) { body[k] = v; });
body._csrf_token = csrfToken;
fetch('/sa/swimming/register/lookup', {
method: 'POST',
headers: {'Content-Type':'application/json','X-Requested-With':'XMLHttpRequest','X-CSRF-TOKEN': csrfToken},
body: JSON.stringify(body)
}).then(function(r){return r.json();}).then(function(data) {
if (data.success && data.redirect) { if (data.success && data.redirect) {
window.location.href = data.redirect; window.location.href = data.redirect;
} else { } else {
errDiv.textContent = data.error || 'حدث خطأ'; errDiv.textContent = data.error || 'حدث خطأ';
errDiv.style.display = 'block'; errDiv.style.display = 'block';
btn.disabled = false; btn.disabled = false;
btn.innerHTML = '<i data-lucide="search" style="width:15px;height:15px;vertical-align:middle;margin-left:6px;"></i>بحث وتسجيل'; btn.textContent = 'بدء التسجيل';
} }
}) }).catch(function() {
.catch(() => {
errDiv.textContent = 'فشل الاتصال بالسيرفر'; errDiv.textContent = 'فشل الاتصال بالسيرفر';
errDiv.style.display = 'block'; errDiv.style.display = 'block';
btn.disabled = false; btn.disabled = false;
btn.innerHTML = '<i data-lucide="search" style="width:15px;height:15px;vertical-align:middle;margin-left:6px;"></i>بحث وتسجيل'; btn.textContent = 'بدء التسجيل';
}); });
});
}); });
</script> </script>
<?php endif; ?> <?php endif; ?>
......
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