Commit aec2c2be authored by Mahmoud Aglan's avatar Mahmoud Aglan

Fix guardian handling + wizard name derivation + enrollment start date

1. Participant edit: guardian section now allows creating a new guardian
   (name + phone) OR selecting existing + editing phone. Checkbox toggles
   between modes.

2. Receptionist wizard: guardian name auto-fill now takes ALL names after
   the first (was only taking 2). "محمود أحمد عبد الفتاح علي" → guardian
   gets "أحمد عبد الفتاح علي" instead of just "أحمد عبد الفتاح".

3. Receptionist wizard step 5: added enrollment_start_date field so the
   user can specify when the player actually started (for accurate
   proration). Defaults to today if left empty.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 0ac860ad
...@@ -48,8 +48,11 @@ class ParticipantForm extends Component ...@@ -48,8 +48,11 @@ class ParticipantForm extends Component
public ?string $weight_kg = null; public ?string $weight_kg = null;
public string $notes = ''; public string $notes = '';
// Guardian phone (editable in edit mode) // Guardian (editable in edit mode)
public string $guardian_phone = ''; public string $guardian_phone = '';
public string $new_guardian_name = '';
public string $new_guardian_phone = '';
public bool $createNewGuardian = false;
public function mount(?Participant $participant = null): void public function mount(?Participant $participant = null): void
{ {
...@@ -125,6 +128,10 @@ public function rules(): array ...@@ -125,6 +128,10 @@ public function rules(): array
$rules['national_id'] = 'nullable|string|max:14'; $rules['national_id'] = 'nullable|string|max:14';
$rules['date_of_birth'] = 'nullable|date|before:today'; $rules['date_of_birth'] = 'nullable|date|before:today';
$rules['guardian_phone'] = 'nullable|string|max:20'; $rules['guardian_phone'] = 'nullable|string|max:20';
if ($this->createNewGuardian) {
$rules['new_guardian_name'] = 'required|string|max:255';
$rules['new_guardian_phone'] = 'required|string|max:20';
}
} elseif (!$this->person_id) { } elseif (!$this->person_id) {
$rules['name_ar'] = 'required|string|max:255'; $rules['name_ar'] = 'required|string|max:255';
$rules['name'] = 'required|string|max:255'; $rules['name'] = 'required|string|max:255';
...@@ -162,6 +169,8 @@ public function messages(): array ...@@ -162,6 +169,8 @@ public function messages(): array
'medical_clearance_expires.date' => 'تاريخ انتهاء الموافقة الطبية غير صالح', 'medical_clearance_expires.date' => 'تاريخ انتهاء الموافقة الطبية غير صالح',
'date_of_birth.before' => 'تاريخ الميلاد يجب أن يكون في الماضي', 'date_of_birth.before' => 'تاريخ الميلاد يجب أن يكون في الماضي',
'email.email' => 'البريد الإلكتروني غير صالح', 'email.email' => 'البريد الإلكتروني غير صالح',
'new_guardian_name.required' => 'اسم ولي الأمر الجديد مطلوب',
'new_guardian_phone.required' => 'رقم هاتف ولي الأمر الجديد مطلوب',
]; ];
} }
...@@ -196,8 +205,24 @@ public function save(ParticipantService $service): void ...@@ -196,8 +205,24 @@ public function save(ParticipantService $service): void
'date_of_birth' => $this->date_of_birth ?: null, 'date_of_birth' => $this->date_of_birth ?: null,
]); ]);
// Update guardian phone if guardian exists // Handle guardian: create new or update existing phone
if ($this->participant->primaryGuardian?->person && $this->guardian_phone !== '') { if ($this->createNewGuardian && $this->new_guardian_name) {
$guardianPerson = Person::create([
'academy_id' => $this->participant->academy_id,
'name_ar' => $this->new_guardian_name,
'name' => $this->new_guardian_name,
'phone' => $this->new_guardian_phone ?: null,
]);
$newGuardian = Guardian::create([
'academy_id' => $this->participant->academy_id,
'person_id' => $guardianPerson->id,
'relationship_type' => 'guardian',
'is_emergency_contact' => true,
'is_financial_responsible' => true,
'can_pickup' => true,
]);
$this->primary_guardian_id = $newGuardian->id;
} elseif ($this->participant->primaryGuardian?->person && $this->guardian_phone !== '') {
$this->participant->primaryGuardian->person->update([ $this->participant->primaryGuardian->person->update([
'phone' => $this->guardian_phone ?: null, 'phone' => $this->guardian_phone ?: null,
]); ]);
......
...@@ -80,6 +80,9 @@ class NewRegistrationWizard extends Component ...@@ -80,6 +80,9 @@ class NewRegistrationWizard extends Component
public ?int $selected_activity_id = null; public ?int $selected_activity_id = null;
public ?int $selected_program_id = null; public ?int $selected_program_id = null;
// Step 5: Enrollment options
public ?string $enrollment_start_date = null;
// Step 6: Payment // Step 6: Payment
public bool $pay_now = false; public bool $pay_now = false;
public string $payment_method = 'cash'; public string $payment_method = 'cash';
...@@ -232,11 +235,8 @@ private function autoFillGuardianName(): void ...@@ -232,11 +235,8 @@ private function autoFillGuardianName(): void
$parts = array_values(array_filter(explode(' ', trim($this->participant_name_ar)))); $parts = array_values(array_filter(explode(' ', trim($this->participant_name_ar))));
// Father name = words[1] + words[2] (the two words after the player's first name) if (count($parts) >= 2) {
if (count($parts) >= 3) { $this->guardian_name_ar = implode(' ', array_slice($parts, 1));
$this->guardian_name_ar = implode(' ', array_slice($parts, 1, 2));
} elseif (count($parts) === 2) {
$this->guardian_name_ar = $parts[1];
} }
} }
...@@ -755,7 +755,8 @@ public function proratedProgramFee(): ProrationResult ...@@ -755,7 +755,8 @@ public function proratedProgramFee(): ProrationResult
description: '', description: '',
); );
} }
return $service->calculate($baseFee); $startDate = $this->enrollment_start_date ? \Carbon\Carbon::parse($this->enrollment_start_date) : null;
return $service->calculate($baseFee, $startDate);
} }
#[Computed] #[Computed]
......
...@@ -291,10 +291,37 @@ class="w-full px-4 py-2.5 text-sm border border-gray-300 rounded-lg focus:ring-2 ...@@ -291,10 +291,37 @@ class="w-full px-4 py-2.5 text-sm border border-gray-300 rounded-lg focus:ring-2
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6"> <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6">
<h2 class="text-base sm:text-lg font-semibold text-gray-800 mb-4 border-b border-gray-100 pb-2">{{ __('ولي الأمر') }}</h2> <h2 class="text-base sm:text-lg font-semibold text-gray-800 mb-4 border-b border-gray-100 pb-2">{{ __('ولي الأمر') }}</h2>
@if($editing)
<div class="mb-4">
<label class="inline-flex items-center gap-2 cursor-pointer">
<input type="checkbox" wire:model.live="createNewGuardian"
class="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500">
<span class="text-sm font-medium text-gray-700">{{ __('تسجيل ولي أمر جديد') }}</span>
</label>
</div>
@if($createNewGuardian)
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4 p-4 bg-blue-50 rounded-lg border border-blue-100">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __('اسم ولي الأمر') }} <span class="text-red-500">*</span></label>
<input type="text" wire:model="new_guardian_name"
placeholder="{{ __('الاسم بالكامل') }}"
class="w-full px-4 py-2.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 @error('new_guardian_name') border-red-500 @enderror">
@error('new_guardian_name') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __('هاتف ولي الأمر') }} <span class="text-red-500">*</span></label>
<input type="text" wire:model="new_guardian_phone" dir="ltr"
placeholder="{{ __('رقم الهاتف') }}"
class="w-full px-4 py-2.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 @error('new_guardian_phone') border-red-500 @enderror">
@error('new_guardian_phone') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
</div>
</div>
@else
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4"> <div class="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div> <div>
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __('ولي الأمر الأساسي') }}</label> <label class="block text-sm font-medium text-gray-700 mb-1">{{ __('اختر ولي أمر') }}</label>
<select wire:model="primary_guardian_id" <select wire:model.live="primary_guardian_id"
class="w-full px-4 py-2.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> class="w-full px-4 py-2.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="">{{ __('— بدون ولي أمر —') }}</option> <option value="">{{ __('— بدون ولي أمر —') }}</option>
@foreach($guardians as $guardian) @foreach($guardians as $guardian)
...@@ -305,7 +332,7 @@ class="w-full px-4 py-2.5 text-sm border border-gray-300 rounded-lg focus:ring-2 ...@@ -305,7 +332,7 @@ class="w-full px-4 py-2.5 text-sm border border-gray-300 rounded-lg focus:ring-2
@endforeach @endforeach
</select> </select>
</div> </div>
@if($editing && $primary_guardian_id) @if($primary_guardian_id)
<div> <div>
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __('هاتف ولي الأمر') }}</label> <label class="block text-sm font-medium text-gray-700 mb-1">{{ __('هاتف ولي الأمر') }}</label>
<input type="text" wire:model="guardian_phone" dir="ltr" <input type="text" wire:model="guardian_phone" dir="ltr"
...@@ -315,6 +342,24 @@ class="w-full px-4 py-2.5 text-sm border border-gray-300 rounded-lg focus:ring-2 ...@@ -315,6 +342,24 @@ class="w-full px-4 py-2.5 text-sm border border-gray-300 rounded-lg focus:ring-2
</div> </div>
@endif @endif
</div> </div>
@endif
@else
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __('ولي الأمر الأساسي') }}</label>
<select wire:model="primary_guardian_id"
class="w-full px-4 py-2.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="">{{ __('— بدون ولي أمر —') }}</option>
@foreach($guardians as $guardian)
<option value="{{ $guardian->id }}">
{{ $guardian->name_ar }}
@if($guardian->phone) — {{ $guardian->phone }} @endif
</option>
@endforeach
</select>
</div>
</div>
@endif
</div> </div>
{{-- Section 6: Notes --}} {{-- Section 6: Notes --}}
......
...@@ -679,6 +679,15 @@ class="flex-1 sm:flex-none inline-flex items-center justify-center gap-2 px-6 py ...@@ -679,6 +679,15 @@ class="flex-1 sm:flex-none inline-flex items-center justify-center gap-2 px-6 py
@if($this->selectedProgram->activity) @if($this->selectedProgram->activity)
<p class="text-xs text-gray-500 mt-1">{{ $this->selectedProgram->activity->name_ar }}</p> <p class="text-xs text-gray-500 mt-1">{{ $this->selectedProgram->activity->name_ar }}</p>
@endif @endif
{{-- Enrollment Start Date --}}
<div class="mt-3 pt-3 border-t border-gray-200">
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __('تاريخ بداية اللعب') }}</label>
<input type="date" wire:model.live="enrollment_start_date" dir="ltr"
class="w-full sm:w-auto px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<p class="text-xs text-gray-500 mt-1">{{ __('اتركه فارغاً لاستخدام تاريخ اليوم. حدد التاريخ إذا بدأ اللاعب قبل اليوم لحساب الأيام المتبقية بدقة.') }}</p>
</div>
@if($this->selectedProgramFee > 0) @if($this->selectedProgramFee > 0)
<div class="mt-3 space-y-1 text-sm border-t border-gray-200 pt-3"> <div class="mt-3 space-y-1 text-sm border-t border-gray-200 pt-3">
@if($this->proratedProgramFee->applied) @if($this->proratedProgramFee->applied)
......
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