Commit c8ba2296 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Add pre-flight checks to registration wizard — block if critical data missing

Wizard now checks on mount: active academy, branch, activities, programs
with groups, and base prices. Shows a clear Arabic error screen instead of
silently failing at confirm(). Also blocks step 3→4 if no price exists
for the selected program.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 28713729
......@@ -4,6 +4,7 @@
use App\Domain\Financial\Services\InvoiceService;
use App\Domain\Financial\Services\PaymentService;
use App\Domain\Identity\Models\Branch;
use App\Domain\Identity\Models\Guardian;
use App\Domain\Identity\Models\Person;
use App\Domain\Identity\Services\PersonService;
......@@ -71,10 +72,50 @@ class NewRegistrationWizard extends Component
public ?string $invoice_number = null;
public bool $payment_recorded = false;
// Pre-flight errors — blocks wizard from starting
public array $systemErrors = [];
public function mount(): void
{
$this->authorize('participants.create');
$this->branchId = $this->getActiveBranchIdOrFail();
$this->branchId = $this->getActiveBranchId() ?? Branch::where('is_active', true)->first()?->id;
$this->runPreflightChecks();
}
private function runPreflightChecks(): void
{
$errors = [];
if (!app('current_academy')) {
$errors[] = 'لا توجد أكاديمية مفعّلة — تواصل مع المسؤول';
}
if (!$this->branchId) {
$errors[] = 'لا يوجد فرع محدد — اختر فرعاً من الإعدادات';
}
$hasActivities = Activity::where('is_active', true)->exists();
if (!$hasActivities) {
$errors[] = 'لا توجد أنشطة مفعّلة — يجب إنشاء نشاط واحد على الأقل';
}
$hasPrograms = TrainingProgram::where('status', 'active')
->whereHas('groups', fn ($q) => $q->where('branch_id', $this->branchId)->whereIn('status', ['active', 'forming']))
->exists();
if (!$hasPrograms) {
$errors[] = 'لا توجد برامج متاحة بها مجموعات نشطة في هذا الفرع';
}
$hasPrices = BasePrice::where('is_active', true)
->where('priceable_type', TrainingProgram::class)
->where('effective_from', '<=', now())
->where(fn ($q) => $q->whereNull('effective_to')->orWhere('effective_to', '>=', now()))
->exists();
if (!$hasPrices) {
$errors[] = 'لا توجد أسعار محددة لأي برنامج — يجب تحديد سعر أساسي أولاً';
}
$this->systemErrors = $errors;
}
public function nextStep(): void
......@@ -88,10 +129,18 @@ public function nextStep(): void
// Duplicate check on step 1 — if duplicates found, stay on step 1
if ($this->currentStep === 1 && !$this->duplicateCheckDone) {
$this->checkDuplicates();
// checkDuplicates will advance step if no duplicates
return;
}
// Price guard on step 3 — block if no base price for selected program
if ($this->currentStep === 3 && $this->selected_program_id) {
$program = TrainingProgram::find($this->selected_program_id);
if ($program && $this->resolveProgramFee($program) === 0) {
$this->addError('selected_program_id', 'لا يوجد سعر محدد لهذا البرنامج — تواصل مع المسؤول');
return;
}
}
$this->currentStep = min($this->currentStep + 1, $this->totalSteps);
}
......
......@@ -21,6 +21,35 @@ class="inline-flex items-center gap-2 px-4 py-2 text-gray-600 bg-gray-100 rounde
</div>
@endif
{{-- System Pre-flight Errors — Block Wizard --}}
@if(!empty($systemErrors))
<div class="bg-white rounded-xl shadow-sm border border-red-300 p-8 text-center">
<div class="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg class="w-8 h-8 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"/>
</svg>
</div>
<h2 class="text-xl font-bold text-gray-800 mb-2">{{ __('لا يمكن بدء التسجيل') }}</h2>
<p class="text-gray-500 text-sm mb-6">{{ __('يجب إصلاح المشاكل التالية قبل استخدام معالج التسجيل:') }}</p>
<ul class="text-start text-sm space-y-3 max-w-md mx-auto">
@foreach($systemErrors as $error)
<li class="flex items-start gap-3 p-3 bg-red-50 rounded-lg border border-red-100">
<svg class="w-5 h-5 text-red-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
<span class="text-red-700">{{ $error }}</span>
</li>
@endforeach
</ul>
<div class="mt-8">
<a href="{{ route('receptionist.dashboard') }}" wire:navigate
class="inline-flex items-center gap-2 px-6 py-3 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 font-medium transition-colors">
{{ __('العودة للاستقبال') }}
</a>
</div>
</div>
@else
{{-- Step Indicator --}}
@if($currentStep < 6)
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6">
......@@ -686,4 +715,6 @@ class="inline-flex items-center gap-2 px-6 py-3 min-h-16 bg-blue-600 text-white
</div>
@endif
</div>
@endif {{-- end systemErrors else block --}}
</div>
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