Commit 13c77bfa authored by Mahmoud Aglan's avatar Mahmoud Aglan

Bulk transfer wizard: move multiple participants between groups

Rebuilt the transfer wizard to support selecting multiple participants
from a source group and moving them all to a destination group at once.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 4a57a333
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
namespace App\Livewire\Enrollments; namespace App\Livewire\Enrollments;
use App\Domain\Financial\Models\Invoice;
use App\Domain\Financial\Services\InvoiceService; use App\Domain\Financial\Services\InvoiceService;
use App\Domain\Participant\Models\Participant; use App\Domain\Participant\Models\Participant;
use App\Domain\Pricing\Services\PricingService; use App\Domain\Pricing\Services\PricingService;
...@@ -16,38 +15,43 @@ ...@@ -16,38 +15,43 @@
use Livewire\Component; use Livewire\Component;
#[Layout('layouts.app')] #[Layout('layouts.app')]
#[Title('نقل مشترك')] #[Title('نقل مشتركين')]
class TransferParticipantWizard extends Component class TransferParticipantWizard extends Component
{ {
public int $currentStep = 1; public int $currentStep = 1;
public int $totalSteps = 4; public int $totalSteps = 4;
public bool $completed = false; public bool $completed = false;
// Step 1: Select Participant & Enrollment // Mode: 'single' or 'bulk'
public string $mode = 'bulk';
// Step 1: Source selection
public ?int $sourceGroupId = null;
public ?array $sourceGroupInfo = null;
public string $participantSearch = ''; public string $participantSearch = '';
public ?int $participantId = null;
public ?int $enrollmentId = null; // Selected participants (enrollment_id => participant info)
public array $selectedParticipants = [];
public bool $selectAll = false;
// Single mode
public ?int $singleParticipantId = null;
public ?int $singleEnrollmentId = null;
public ?array $singleParticipantInfo = null;
public array $singleEnrollments = [];
// Step 2: Destination // Step 2: Destination
public ?int $destinationProgramId = null; public ?int $destinationProgramId = null;
public ?int $destinationGroupId = null; public ?int $destinationGroupId = null;
public ?array $destinationGroupInfo = null;
// Step 3: Impact & Reason // Step 3: Reason
public string $reason = ''; public string $reason = '';
// Price difference // Step 4: Results
public int $sourceProgramPrice = 0; public array $transferResults = [];
public int $destinationProgramPrice = 0; public int $successCount = 0;
public int $priceDifference = 0; public int $failCount = 0;
public bool $requiresPayment = false;
// Cached data for display
public ?array $selectedParticipant = null;
public array $activeEnrollments = [];
public ?array $selectedEnrollment = null;
public ?array $sourceGroup = null;
public ?array $destinationGroup = null;
public ?string $createdInvoiceUuid = null;
public function mount(): void public function mount(): void
{ {
...@@ -57,137 +61,159 @@ public function mount(): void ...@@ -57,137 +61,159 @@ public function mount(): void
public function getStepLabels(): array public function getStepLabels(): array
{ {
return [ return [
1 => 'اختيار المشترك', 1 => 'اختيار المشتركين',
2 => 'الوجهة', 2 => 'الوجهة',
3 => 'التأثير', 3 => 'السبب',
4 => 'تأكيد النقل', 4 => 'تأكيد النقل',
]; ];
} }
public function selectParticipant(int $id): void // --- Mode ---
public function setMode(string $mode): void
{ {
$participant = Participant::with('person')->findOrFail($id); $this->mode = $mode;
$this->participantId = $id; $this->reset(['sourceGroupId', 'sourceGroupInfo', 'participantSearch', 'selectedParticipants', 'selectAll', 'singleParticipantId', 'singleEnrollmentId', 'singleParticipantInfo', 'singleEnrollments']);
$this->selectedParticipant = [ }
'id' => $participant->id,
'name' => $participant->person?->name_ar ?? $participant->person?->name ?? '-',
'participant_number' => $participant->participant_number,
];
$this->loadActiveEnrollments(); // --- Bulk Mode: Source Group ---
public function selectSourceGroup(int $id): void
{
$group = TrainingGroup::with('program')->findOrFail($id);
$this->sourceGroupId = $id;
$this->sourceGroupInfo = [
'id' => $group->id,
'name_ar' => $group->name_ar,
'program_name' => $group->program?->name_ar ?? '-',
'current_count' => $group->current_count,
'max_capacity' => $group->max_capacity,
'program_id' => $group->training_program_id,
];
$this->selectedParticipants = [];
$this->selectAll = false;
} }
public function updatedParticipantId(): void public function toggleParticipant(int $enrollmentId): void
{ {
if ($this->participantId) { if (isset($this->selectedParticipants[$enrollmentId])) {
$this->loadActiveEnrollments(); unset($this->selectedParticipants[$enrollmentId]);
$this->selectAll = false;
} else { } else {
$this->activeEnrollments = []; $enrollment = Enrollment::with('participant.person')->find($enrollmentId);
if ($enrollment) {
$this->selectedParticipants[$enrollmentId] = [
'enrollment_id' => $enrollmentId,
'participant_id' => $enrollment->participant_id,
'name' => $enrollment->participant?->person?->name_ar ?? $enrollment->participant?->person?->name ?? '-',
'participant_number' => $enrollment->participant?->participant_number,
];
}
} }
$this->enrollmentId = null;
$this->selectedEnrollment = null;
$this->sourceGroup = null;
} }
private function loadActiveEnrollments(): void public function toggleSelectAll(): void
{ {
$enrollments = Enrollment::where('participant_id', $this->participantId) if ($this->selectAll) {
$this->selectedParticipants = [];
$this->selectAll = false;
return;
}
$enrollments = Enrollment::where('training_group_id', $this->sourceGroupId)
->where('status', 'active') ->where('status', 'active')
->with(['group', 'program']) ->with('participant.person')
->get(); ->get();
$this->activeEnrollments = $enrollments->map(fn ($e) => [ $this->selectedParticipants = [];
'id' => $e->id, foreach ($enrollments as $enrollment) {
'group_name' => $e->group?->name_ar ?? '-', $this->selectedParticipants[$enrollment->id] = [
'program_name' => $e->program?->name_ar ?? '-', 'enrollment_id' => $enrollment->id,
'enrollment_date' => $e->enrollment_date?->format('Y-m-d'), 'participant_id' => $enrollment->participant_id,
'group_id' => $e->training_group_id, 'name' => $enrollment->participant?->person?->name_ar ?? $enrollment->participant?->person?->name ?? '-',
'program_id' => $e->training_program_id, 'participant_number' => $enrollment->participant?->participant_number,
])->toArray(); ];
}
$this->selectAll = true;
} }
public function selectEnrollment(int $id): void // --- Single Mode ---
public function selectSingleParticipant(int $id): void
{ {
$enrollment = Enrollment::with(['group', 'program'])->findOrFail($id); $participant = Participant::with('person')->findOrFail($id);
$this->enrollmentId = $id; $this->singleParticipantId = $id;
$this->selectedEnrollment = [ $this->singleParticipantInfo = [
'id' => $enrollment->id, 'id' => $participant->id,
'group_name' => $enrollment->group?->name_ar ?? '-', 'name' => $participant->person?->name_ar ?? $participant->person?->name ?? '-',
'program_name' => $enrollment->program?->name_ar ?? '-', 'participant_number' => $participant->participant_number,
'group_id' => $enrollment->training_group_id,
'program_id' => $enrollment->training_program_id,
]; ];
$this->sourceGroup = [
$this->singleEnrollments = Enrollment::where('participant_id', $id)
->where('status', 'active')
->with(['group', 'program'])
->get()
->map(fn ($e) => [
'id' => $e->id,
'group_name' => $e->group?->name_ar ?? '-',
'program_name' => $e->program?->name_ar ?? '-',
'group_id' => $e->training_group_id,
'program_id' => $e->training_program_id,
])->toArray();
}
public function selectSingleEnrollment(int $enrollmentId): void
{
$enrollment = Enrollment::with(['participant.person', 'group'])->findOrFail($enrollmentId);
$this->singleEnrollmentId = $enrollmentId;
$this->sourceGroupId = $enrollment->training_group_id;
$this->sourceGroupInfo = [
'id' => $enrollment->group?->id, 'id' => $enrollment->group?->id,
'name_ar' => $enrollment->group?->name_ar, 'name_ar' => $enrollment->group?->name_ar,
'program_name' => $enrollment->program?->name_ar ?? '-',
'current_count' => $enrollment->group?->current_count, 'current_count' => $enrollment->group?->current_count,
'max_capacity' => $enrollment->group?->max_capacity, 'max_capacity' => $enrollment->group?->max_capacity,
'program_id' => $enrollment->training_program_id,
];
$this->selectedParticipants = [
$enrollmentId => [
'enrollment_id' => $enrollmentId,
'participant_id' => $enrollment->participant_id,
'name' => $enrollment->participant?->person?->name_ar ?? $enrollment->participant?->person?->name ?? '-',
'participant_number' => $enrollment->participant?->participant_number,
],
]; ];
} }
public function clearSingleParticipant(): void
{
$this->reset(['singleParticipantId', 'singleParticipantInfo', 'singleEnrollments', 'singleEnrollmentId', 'selectedParticipants', 'sourceGroupId', 'sourceGroupInfo']);
}
// --- Destination ---
public function selectDestinationProgram(?int $id): void public function selectDestinationProgram(?int $id): void
{ {
$this->destinationProgramId = $id; $this->destinationProgramId = $id;
$this->destinationGroupId = null; $this->destinationGroupId = null;
$this->destinationGroup = null; $this->destinationGroupInfo = null;
$this->priceDifference = 0;
$this->requiresPayment = false;
} }
public function selectDestinationGroup(int $id): void public function selectDestinationGroup(int $id): void
{ {
$group = TrainingGroup::with('program')->findOrFail($id); $group = TrainingGroup::with('program')->findOrFail($id);
$this->destinationGroupId = $id; $this->destinationGroupId = $id;
$this->destinationGroup = [ $this->destinationGroupInfo = [
'id' => $group->id, 'id' => $group->id,
'name_ar' => $group->name_ar, 'name_ar' => $group->name_ar,
'program_name' => $group->program?->name_ar ?? '-', 'program_name' => $group->program?->name_ar ?? '-',
'current_count' => $group->current_count, 'current_count' => $group->current_count,
'max_capacity' => $group->max_capacity, 'max_capacity' => $group->max_capacity,
]; ];
$this->calculatePriceDifference($group);
} }
private function calculatePriceDifference(TrainingGroup $destGroup): void // --- Navigation ---
{
$this->priceDifference = 0;
$this->requiresPayment = false;
if (!$this->selectedEnrollment || !$this->participantId) {
return;
}
$participant = Participant::find($this->participantId);
$sourceProgram = TrainingProgram::find($this->selectedEnrollment['program_id']);
$destProgram = $destGroup->program;
if (!$sourceProgram || !$destProgram || !$participant) {
return;
}
$pricingService = app(PricingService::class);
try {
$sourceResult = $pricingService->calculate($sourceProgram, $participant);
$this->sourceProgramPrice = $sourceResult->finalAmount;
} catch (DomainException) {
$this->sourceProgramPrice = 0;
}
try {
$destResult = $pricingService->calculate($destProgram, $participant);
$this->destinationProgramPrice = $destResult->finalAmount;
} catch (DomainException) {
$this->destinationProgramPrice = 0;
}
$diff = $this->destinationProgramPrice - $this->sourceProgramPrice;
if ($diff > 0) {
$this->priceDifference = $diff;
$this->requiresPayment = true;
}
}
public function nextStep(): void public function nextStep(): void
{ {
...@@ -212,90 +238,144 @@ public function goToStep(int $step): void ...@@ -212,90 +238,144 @@ public function goToStep(int $step): void
} }
} }
// --- Confirm ---
public function confirm(): void public function confirm(): void
{ {
try { $enrollmentService = app(EnrollmentService::class);
$enrollment = Enrollment::findOrFail($this->enrollmentId); $toGroup = TrainingGroup::findOrFail($this->destinationGroupId);
$toGroup = TrainingGroup::findOrFail($this->destinationGroupId); $actor = auth()->user();
$newEnrollment = app(EnrollmentService::class)->transfer($enrollment, $toGroup, auth()->user()); $this->transferResults = [];
$this->successCount = 0;
// Create price difference invoice if destination is more expensive $this->failCount = 0;
if ($this->requiresPayment && $this->priceDifference > 0) {
$invoice = app(InvoiceService::class)->create([ foreach ($this->selectedParticipants as $enrollmentId => $info) {
'billable_type' => Participant::class, try {
'billable_id' => $this->participantId, $enrollment = Enrollment::findOrFail($enrollmentId);
'branch_id' => $toGroup->branch_id ?? auth()->user()->branch_id, $enrollmentService->transfer($enrollment, $toGroup, $actor);
'due_date' => now()->addDays(7)->toDateString(), $this->transferResults[] = [
'notes' => 'فاتورة فرق سعر نقل — اشتراك #' . $newEnrollment->id, 'name' => $info['name'],
], [[ 'status' => 'success',
'description' => 'فرق سعر نقل من ' . ($this->selectedEnrollment['program_name'] ?? '-') . ' إلى ' . ($this->destinationGroup['program_name'] ?? '-'), 'message' => __('تم النقل بنجاح'),
'quantity' => 1, ];
'unit_price' => $this->priceDifference, $this->successCount++;
]], auth()->user()); } catch (DomainException $e) {
$this->transferResults[] = [
$this->createdInvoiceUuid = $invoice->uuid; 'name' => $info['name'],
'status' => 'error',
'message' => $e->getMessage(),
];
$this->failCount++;
} catch (\Throwable $e) {
$this->transferResults[] = [
'name' => $info['name'],
'status' => 'error',
'message' => __('خطأ غير متوقع'),
];
$this->failCount++;
} }
$this->completed = true;
} catch (DomainException $e) {
session()->flash('error', $e->getMessage());
} }
$this->completed = true;
} }
private function rulesForStep(int $step): array private function rulesForStep(int $step): array
{ {
return match ($step) { return match ($step) {
1 => [ 1 => [
'participantId' => 'required|integer|exists:participants,id', 'selectedParticipants' => 'required|array|min:1',
'enrollmentId' => 'required|integer|exists:enrollments,id',
], ],
2 => [ 2 => [
'destinationGroupId' => 'required|integer|exists:training_groups,id', 'destinationGroupId' => 'required|integer|exists:training_groups,id',
], ],
3 => [ 3 => [
'reason' => 'required|string|min:5|max:500', 'reason' => 'required|string|min:3|max:500',
], ],
default => [], default => [],
}; };
} }
public function messages(): array
{
return [
'selectedParticipants.required' => 'يجب اختيار مشترك واحد على الأقل',
'selectedParticipants.min' => 'يجب اختيار مشترك واحد على الأقل',
'destinationGroupId.required' => 'يجب اختيار المجموعة الوجهة',
'reason.required' => 'يجب كتابة سبب النقل',
'reason.min' => 'سبب النقل قصير جدًا',
];
}
public function render() public function render()
{ {
// Search results for single mode
$searchResults = collect(); $searchResults = collect();
if (strlen($this->participantSearch) >= 2) { if ($this->mode === 'single' && strlen($this->participantSearch) >= 2 && !$this->singleParticipantInfo) {
$searchResults = Participant::with('person') $searchResults = Participant::with('person')
->whereHas('person', function ($q) { ->whereHas('person', function ($q) {
$q->where('name_ar', 'ilike', "%{$this->participantSearch}%") $q->where('name_ar', 'ilike', "%{$this->participantSearch}%")
->orWhere('name', 'ilike', "%{$this->participantSearch}%") ->orWhere('name', 'ilike', "%{$this->participantSearch}%")
->orWhere('phone', 'ilike', "%{$this->participantSearch}%") ->orWhere('phone', 'ilike', "%{$this->participantSearch}%");
->orWhere('national_id', 'ilike', "%{$this->participantSearch}%");
}) })
->orWhere('participant_number', 'ilike', "%{$this->participantSearch}%") ->orWhere('participant_number', 'ilike', "%{$this->participantSearch}%")
->where('status', 'active')
->limit(10) ->limit(10)
->get(); ->get();
} }
// Source groups for bulk mode
$sourceGroups = collect();
if ($this->mode === 'bulk' && !$this->sourceGroupId) {
$sourceGroups = TrainingGroup::whereIn('status', ['active', 'forming'])
->where('current_count', '>', 0)
->with('program')
->orderBy('name_ar')
->get();
}
// Source group enrollments (for bulk selection)
$sourceEnrollments = collect();
if ($this->mode === 'bulk' && $this->sourceGroupId) {
$query = Enrollment::where('training_group_id', $this->sourceGroupId)
->where('status', 'active')
->with('participant.person');
if (strlen($this->participantSearch) >= 2) {
$search = $this->participantSearch;
$query->whereHas('participant.person', function ($q) use ($search) {
$q->where('name_ar', 'ilike', "%{$search}%")
->orWhere('name', 'ilike', "%{$search}%")
->orWhere('phone', 'ilike', "%{$search}%");
});
}
$sourceEnrollments = $query->get();
}
// Destination options
$availablePrograms = collect(); $availablePrograms = collect();
$availableGroups = collect(); $availableGroups = collect();
if ($this->selectedEnrollment && $this->currentStep >= 2) { if ($this->currentStep >= 2 && $this->sourceGroupId) {
$availablePrograms = TrainingProgram::where('status', 'active') $availablePrograms = TrainingProgram::where('status', 'active')
->orderBy('name_ar') ->orderBy('name_ar')
->get(); ->get();
$targetProgramId = $this->destinationProgramId ?? $this->selectedEnrollment['program_id']; $targetProgramId = $this->destinationProgramId ?? ($this->sourceGroupInfo['program_id'] ?? null);
$availableGroups = TrainingGroup::where('training_program_id', $targetProgramId) if ($targetProgramId) {
->where('id', '!=', $this->selectedEnrollment['group_id']) $availableGroups = TrainingGroup::where('training_program_id', $targetProgramId)
->whereIn('status', ['forming', 'active']) ->where('id', '!=', $this->sourceGroupId)
->with('program') ->whereIn('status', ['forming', 'active'])
->orderBy('name_ar') ->with('program')
->get(); ->orderBy('name_ar')
->get();
}
} }
return view('livewire.enrollments.transfer-participant-wizard', [ return view('livewire.enrollments.transfer-participant-wizard', [
'searchResults' => $searchResults, 'searchResults' => $searchResults,
'sourceGroups' => $sourceGroups,
'sourceEnrollments' => $sourceEnrollments,
'availablePrograms' => $availablePrograms, 'availablePrograms' => $availablePrograms,
'availableGroups' => $availableGroups, 'availableGroups' => $availableGroups,
]); ]);
......
...@@ -2,9 +2,12 @@ ...@@ -2,9 +2,12 @@
{{-- Header --}} {{-- Header --}}
<div class="flex items-center justify-between mb-6"> <div class="flex items-center justify-between mb-6">
<div> <div>
<h1 class="text-xl sm:text-2xl font-bold text-gray-800">{{ __('نقل مشترك') }}</h1> <h1 class="text-xl sm:text-2xl font-bold text-gray-800">{{ __('نقل مشتركين') }}</h1>
<p class="text-sm text-gray-500 mt-1">{{ __('نقل مشترك من مجموعة إلى أخرى (نفس البرنامج أو برنامج آخر)') }}</p> <p class="text-sm text-gray-500 mt-1">{{ __('نقل مشترك أو مجموعة مشتركين من مجموعة إلى أخرى') }}</p>
</div> </div>
<a href="{{ route('enrollments.list') }}" wire:navigate class="text-sm text-gray-500 hover:text-gray-700">
{{ __('العودة') }}
</a>
</div> </div>
{{-- Flash Messages --}} {{-- Flash Messages --}}
...@@ -16,29 +19,63 @@ ...@@ -16,29 +19,63 @@
{{-- Success State --}} {{-- Success State --}}
@if($completed) @if($completed)
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-8 text-center"> <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 sm:p-8">
<div class="w-16 h-16 mx-auto mb-4 bg-green-100 rounded-full flex items-center justify-center"> <div class="text-center mb-6">
<svg class="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <div class="w-16 h-16 mx-auto mb-4 bg-green-100 rounded-full flex items-center justify-center">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/> <svg class="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
</svg> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
</svg>
</div>
<h2 class="text-xl font-bold text-gray-800 mb-2">{{ __('اكتملت عملية النقل') }}</h2>
<p class="text-gray-500">
{{ __('تم نقل') }} <span class="font-bold text-green-600">{{ $successCount }}</span>
@if($failCount > 0)
{{ __('— فشل') }} <span class="font-bold text-red-600">{{ $failCount }}</span>
@endif
</p>
</div> </div>
<h2 class="text-xl font-bold text-gray-800 mb-2">{{ __('تم النقل بنجاح') }}</h2>
<p class="text-gray-500 mb-4">{{ __('تم نقل المشترك إلى المجموعة الجديدة بنجاح') }}</p> {{-- Results Table --}}
@if($createdInvoiceUuid) <div class="max-h-96 overflow-y-auto border border-gray-200 rounded-xl mb-6">
<div class="mb-6 p-4 bg-amber-50 border border-amber-200 rounded-xl text-amber-800 text-sm"> <table class="w-full text-sm">
<p class="font-semibold mb-1">{{ __('تم إنشاء فاتورة فرق السعر') }}</p> <thead class="bg-gray-50 sticky top-0">
<p>{{ __('المبلغ:') }} {{ number_format($priceDifference / 100, 2) }} {{ __('ج.م') }}</p> <tr>
<th class="px-4 py-3 text-start text-xs font-medium text-gray-500">{{ __('المشترك') }}</th>
<th class="px-4 py-3 text-start text-xs font-medium text-gray-500">{{ __('الحالة') }}</th>
<th class="px-4 py-3 text-start text-xs font-medium text-gray-500">{{ __('التفاصيل') }}</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@foreach($transferResults as $result)
<tr>
<td class="px-4 py-3 font-medium text-gray-800">{{ $result['name'] }}</td>
<td class="px-4 py-3">
@if($result['status'] === 'success')
<span class="inline-flex items-center gap-1 px-2 py-1 text-xs rounded-full bg-green-100 text-green-700">
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
{{ __('نجح') }}
</span>
@else
<span class="inline-flex items-center gap-1 px-2 py-1 text-xs rounded-full bg-red-100 text-red-700">
<svg class="w-3 h-3" 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>
@endif
</td>
<td class="px-4 py-3 text-gray-500 text-xs">{{ $result['message'] }}</td>
</tr>
@endforeach
</tbody>
</table>
</div> </div>
@endif
<div class="flex items-center justify-center gap-4"> <div class="flex items-center justify-center gap-4">
@if($createdInvoiceUuid) <a href="{{ route('enrollments.transfer-wizard') }}" wire:navigate
<a href="{{ route('invoices.show', $createdInvoiceUuid) }}" wire:navigate class="inline-flex items-center gap-2 px-6 py-3 bg-emerald-600 text-white rounded-lg hover:bg-emerald-700 font-medium transition-colors">
class="inline-flex items-center gap-2 px-6 py-3 bg-amber-600 text-white rounded-lg hover:bg-amber-700 font-medium transition-colors"> {{ __('نقل آخر') }}
{{ __('عرض الفاتورة') }}
</a> </a>
@endif
<a href="{{ route('enrollments.list') }}" wire:navigate <a href="{{ route('enrollments.list') }}" wire:navigate
class="inline-flex items-center gap-2 px-6 py-3 bg-emerald-600 text-white rounded-lg hover:bg-emerald-700 font-medium transition-colors"> 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> </a>
</div> </div>
...@@ -83,49 +120,157 @@ class="flex items-center gap-2 {{ $num < $currentStep ? 'cursor-pointer' : 'curs ...@@ -83,49 +120,157 @@ class="flex items-center gap-2 {{ $num < $currentStep ? 'cursor-pointer' : 'curs
{{-- Step Content --}} {{-- Step Content --}}
<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">
{{-- Step 1: Select Participant & Enrollment --}} {{-- Step 1: Select Participants --}}
@if($currentStep === 1) @if($currentStep === 1)
<div> <div>
<h2 class="text-lg font-semibold text-gray-800 mb-6">{{ __('اختيار المشترك والتسجيل') }}</h2> {{-- Mode Toggle --}}
<div class="flex items-center gap-3 mb-6">
{{-- Search --}} <button wire:click="setMode('bulk')"
<div class="mb-6"> class="px-4 py-2 rounded-lg text-sm font-medium transition-colors
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __('بحث عن المشترك') }}</label> {{ $mode === 'bulk' ? 'bg-emerald-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200' }}">
<input type="text" wire:model.live.debounce.300ms="participantSearch" {{ __('نقل من مجموعة (Bulk)') }}
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 text-lg" </button>
placeholder="{{ __('بحث بالاسم، الهاتف، الرقم القومي، أو رقم المشترك...') }}"> <button wire:click="setMode('single')"
class="px-4 py-2 rounded-lg text-sm font-medium transition-colors
{{ $mode === 'single' ? 'bg-emerald-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200' }}">
{{ __('نقل فردي') }}
</button>
</div> </div>
{{-- Selected Participant --}} {{-- BULK MODE --}}
@if($selectedParticipant) @if($mode === 'bulk')
<div class="mb-4 p-4 bg-emerald-50 border border-emerald-200 rounded-xl flex items-center justify-between"> @if(!$sourceGroupId)
<div class="flex items-center gap-3"> {{-- Select Source Group --}}
<div class="w-10 h-10 rounded-full bg-emerald-200 flex items-center justify-center"> <h2 class="text-lg font-semibold text-gray-800 mb-4">{{ __('اختر المجموعة المصدر') }}</h2>
<svg class="w-5 h-5 text-emerald-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <div class="space-y-2 max-h-96 overflow-y-auto">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/> @foreach($sourceGroups as $group)
</svg> <button wire:click="selectSourceGroup({{ $group->id }})"
</div> class="w-full text-start p-4 border border-gray-200 rounded-xl hover:border-emerald-300 hover:bg-emerald-50 transition-colors">
<div class="flex items-center justify-between">
<div>
<div class="font-medium text-gray-800">{{ $group->name_ar }}</div>
<div class="text-sm text-gray-500">{{ $group->program?->name_ar ?? '-' }}</div>
</div>
<span class="px-3 py-1 text-sm rounded-full bg-gray-100 text-gray-700 font-medium">
{{ $group->current_count }} {{ __('مشترك') }}
</span>
</div>
</button>
@endforeach
</div>
@else
{{-- Source Group Selected — Show Participants --}}
<div class="mb-4 p-3 bg-emerald-50 border border-emerald-200 rounded-xl flex items-center justify-between">
<div> <div>
<span class="font-medium text-emerald-800">{{ $selectedParticipant['name'] }}</span> <span class="text-xs text-emerald-600">{{ __('المجموعة المصدر') }}</span>
@if($selectedParticipant['participant_number']) <div class="font-medium text-emerald-800">{{ $sourceGroupInfo['name_ar'] }}</div>
<span class="text-xs text-emerald-600 ms-2">#{{ $selectedParticipant['participant_number'] }}</span> <div class="text-xs text-emerald-600">{{ $sourceGroupInfo['program_name'] }}</div>
@endif
</div> </div>
<button wire:click="$set('sourceGroupId', null)" class="text-emerald-600 hover:text-emerald-800 text-sm">
{{ __('تغيير') }}
</button>
</div> </div>
<button wire:click="$set('participantId', null)" class="text-emerald-600 hover:text-emerald-800 text-sm">
{{ __('تغيير') }}
</button>
</div>
{{-- Active Enrollments --}} {{-- Search within group --}}
@if(count($activeEnrollments) > 0) <div class="mb-4">
<div class="mb-4"> <input type="text" wire:model.live.debounce.300ms="participantSearch"
<h3 class="text-sm font-medium text-gray-700 mb-3">{{ __('التسجيلات النشطة') }}</h3> class="w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500"
placeholder="{{ __('فلتر بالاسم...') }}">
</div>
{{-- Select All + Counter --}}
<div class="flex items-center justify-between mb-3">
<label class="flex items-center gap-2 cursor-pointer">
<input type="checkbox" wire:click="toggleSelectAll" {{ $selectAll ? 'checked' : '' }}
class="w-4 h-4 rounded border-gray-300 text-emerald-600 focus:ring-emerald-500">
<span class="text-sm font-medium text-gray-700">{{ __('تحديد الكل') }}</span>
</label>
<span class="text-sm font-medium {{ count($selectedParticipants) > 0 ? 'text-emerald-600' : 'text-gray-400' }}">
{{ __('محدد:') }} {{ count($selectedParticipants) }}
</span>
</div>
{{-- Participants List --}}
<div class="space-y-1 max-h-80 overflow-y-auto border border-gray-200 rounded-xl p-2">
@forelse($sourceEnrollments as $enrollment)
<label class="flex items-center gap-3 p-3 rounded-lg cursor-pointer hover:bg-gray-50 transition-colors
{{ isset($selectedParticipants[$enrollment->id]) ? 'bg-emerald-50 border border-emerald-200' : '' }}">
<input type="checkbox" wire:click="toggleParticipant({{ $enrollment->id }})"
{{ isset($selectedParticipants[$enrollment->id]) ? 'checked' : '' }}
class="w-4 h-4 rounded border-gray-300 text-emerald-600 focus:ring-emerald-500">
<div class="flex-1 min-w-0">
<div class="font-medium text-gray-800 text-sm truncate">
{{ $enrollment->participant?->person?->name_ar ?? $enrollment->participant?->person?->name ?? '-' }}
</div>
@if($enrollment->participant?->participant_number)
<div class="text-xs text-gray-400">#{{ $enrollment->participant->participant_number }}</div>
@endif
</div>
</label>
@empty
<div class="p-4 text-center text-gray-500 text-sm">
{{ __('لا توجد تسجيلات نشطة') }}
</div>
@endforelse
</div>
@endif
@endif
{{-- SINGLE MODE --}}
@if($mode === 'single')
@if(!$singleParticipantInfo)
<h2 class="text-lg font-semibold text-gray-800 mb-4">{{ __('بحث عن المشترك') }}</h2>
<input type="text" wire:model.live.debounce.300ms="participantSearch"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 text-lg mb-4"
placeholder="{{ __('بحث بالاسم أو الهاتف أو رقم المشترك...') }}">
@if(strlen($participantSearch) >= 2)
<div class="space-y-2"> <div class="space-y-2">
@foreach($activeEnrollments as $enrollment) @forelse($searchResults as $participant)
<button wire:click="selectSingleParticipant({{ $participant->id }})"
class="w-full text-start p-4 border border-gray-200 rounded-xl hover:border-emerald-300 hover:bg-emerald-50 transition-colors">
<div class="flex items-center justify-between">
<div>
<div class="font-medium text-gray-800">{{ $participant->person?->name_ar ?? $participant->person?->name ?? '-' }}</div>
<div class="text-sm text-gray-500">{{ $participant->participant_number }}</div>
</div>
<span class="px-2 py-1 text-xs rounded-full bg-green-100 text-green-700">{{ __('نشط') }}</span>
</div>
</button>
@empty
<div class="p-4 text-center text-gray-500 text-sm">{{ __('لا توجد نتائج') }}</div>
@endforelse
</div>
@endif
@else
{{-- Participant selected --}}
<div class="mb-4 p-4 bg-emerald-50 border border-emerald-200 rounded-xl flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-full bg-emerald-200 flex items-center justify-center">
<svg class="w-5 h-5 text-emerald-700" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/>
</svg>
</div>
<div>
<span class="font-medium text-emerald-800">{{ $singleParticipantInfo['name'] }}</span>
@if($singleParticipantInfo['participant_number'])
<span class="text-xs text-emerald-600 ms-2">#{{ $singleParticipantInfo['participant_number'] }}</span>
@endif
</div>
</div>
<button wire:click="clearSingleParticipant" class="text-emerald-600 hover:text-emerald-800 text-sm">
{{ __('تغيير') }}
</button>
</div>
{{-- Active Enrollments --}}
@if(count($singleEnrollments) > 0)
<h3 class="text-sm font-medium text-gray-700 mb-3">{{ __('اختر التسجيل المراد نقله') }}</h3>
<div class="space-y-2">
@foreach($singleEnrollments as $enrollment)
<label class="relative cursor-pointer block"> <label class="relative cursor-pointer block">
<input type="radio" wire:click="selectEnrollment({{ $enrollment['id'] }})" name="enrollment" <input type="radio" wire:click="selectSingleEnrollment({{ $enrollment['id'] }})" name="enrollment"
{{ $enrollmentId === $enrollment['id'] ? 'checked' : '' }} class="peer sr-only"> {{ $singleEnrollmentId === $enrollment['id'] ? 'checked' : '' }} class="peer sr-only">
<div class="p-4 border-2 rounded-xl transition-all <div class="p-4 border-2 rounded-xl transition-all
peer-checked:border-emerald-500 peer-checked:bg-emerald-50 peer-checked:border-emerald-500 peer-checked:bg-emerald-50
border-gray-200 hover:border-gray-300"> border-gray-200 hover:border-gray-300">
...@@ -134,47 +279,20 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin ...@@ -134,47 +279,20 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin
<div class="font-medium text-gray-800">{{ $enrollment['group_name'] }}</div> <div class="font-medium text-gray-800">{{ $enrollment['group_name'] }}</div>
<div class="text-sm text-gray-500">{{ $enrollment['program_name'] }}</div> <div class="text-sm text-gray-500">{{ $enrollment['program_name'] }}</div>
</div> </div>
<div class="text-xs text-gray-400">{{ $enrollment['enrollment_date'] }}</div>
</div> </div>
</div> </div>
</label> </label>
@endforeach @endforeach
</div> </div>
</div> @else
@else <div class="p-4 bg-amber-50 border border-amber-200 rounded-xl text-amber-700 text-sm">
<div class="p-4 bg-amber-50 border border-amber-200 rounded-xl text-amber-700 text-sm"> {{ __('لا توجد تسجيلات نشطة لهذا المشترك') }}
{{ __('لا توجد تسجيلات نشطة لهذا المشترك') }}
</div>
@endif
@endif
{{-- Search Results --}}
@if(strlen($participantSearch) >= 2 && !$selectedParticipant)
<div class="space-y-2">
@forelse($searchResults as $participant)
<button wire:click="selectParticipant({{ $participant->id }})"
class="w-full text-start p-4 border border-gray-200 rounded-xl hover:border-emerald-300 hover:bg-emerald-50 transition-colors">
<div class="flex items-center justify-between">
<div>
<div class="font-medium text-gray-800">{{ $participant->person?->name_ar ?? $participant->person?->name ?? '-' }}</div>
<div class="text-sm text-gray-500">{{ $participant->participant_number }}</div>
</div>
<span class="px-2 py-1 text-xs rounded-full
{{ $participant->status->value === 'active' ? 'bg-green-100 text-green-700' : 'bg-gray-100 text-gray-600' }}">
{{ $participant->status->value === 'active' ? __('نشط') : $participant->status->value }}
</span>
</div>
</button>
@empty
<div class="p-4 text-center text-gray-500 text-sm">
{{ __('لا توجد نتائج') }}
</div> </div>
@endforelse @endif
</div> @endif
@endif @endif
@error('participantId') <p class="text-red-500 text-xs mt-2">{{ $message }}</p> @enderror @error('selectedParticipants') <p class="text-red-500 text-xs mt-3">{{ $message }}</p> @enderror
@error('enrollmentId') <p class="text-red-500 text-xs mt-2">{{ $message }}</p> @enderror
</div> </div>
@endif @endif
...@@ -182,7 +300,9 @@ class="w-full text-start p-4 border border-gray-200 rounded-xl hover:border-emer ...@@ -182,7 +300,9 @@ class="w-full text-start p-4 border border-gray-200 rounded-xl hover:border-emer
@if($currentStep === 2) @if($currentStep === 2)
<div> <div>
<h2 class="text-lg font-semibold text-gray-800 mb-2">{{ __('اختيار الوجهة') }}</h2> <h2 class="text-lg font-semibold text-gray-800 mb-2">{{ __('اختيار الوجهة') }}</h2>
<p class="text-sm text-gray-500 mb-6">{{ __('اختر البرنامج والمجموعة — يمكنك النقل لبرنامج آخر') }}</p> <p class="text-sm text-gray-500 mb-6">
{{ __('نقل') }} <span class="font-bold text-emerald-600">{{ count($selectedParticipants) }}</span> {{ __('مشترك — اختر البرنامج والمجموعة') }}
</p>
{{-- Program Selector --}} {{-- Program Selector --}}
<div class="mb-5"> <div class="mb-5">
...@@ -190,9 +310,9 @@ class="w-full text-start p-4 border border-gray-200 rounded-xl hover:border-emer ...@@ -190,9 +310,9 @@ class="w-full text-start p-4 border border-gray-200 rounded-xl hover:border-emer
<select wire:change="selectDestinationProgram($event.target.value)" <select wire:change="selectDestinationProgram($event.target.value)"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-emerald-500 text-sm"> class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-emerald-500 text-sm">
@foreach($availablePrograms as $program) @foreach($availablePrograms as $program)
<option value="{{ $program->id }}" {{ ($destinationProgramId ?? $selectedEnrollment['program_id'] ?? '') == $program->id ? 'selected' : '' }}> <option value="{{ $program->id }}" {{ ($destinationProgramId ?? ($sourceGroupInfo['program_id'] ?? '')) == $program->id ? 'selected' : '' }}>
{{ $program->name_ar }} {{ $program->name_ar }}
@if($program->id == ($selectedEnrollment['program_id'] ?? null)) @if($program->id == ($sourceGroupInfo['program_id'] ?? null))
({{ __('البرنامج الحالي') }}) ({{ __('البرنامج الحالي') }})
@endif @endif
</option> </option>
...@@ -204,14 +324,16 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin ...@@ -204,14 +324,16 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin
@if($availableGroups->count() > 0) @if($availableGroups->count() > 0)
<div class="space-y-3"> <div class="space-y-3">
@foreach($availableGroups as $group) @foreach($availableGroups as $group)
<label class="relative cursor-pointer block {{ $group->isFull() ? 'pointer-events-none' : '' }}"> @php
$spotsAfter = $group->max_capacity - $group->current_count - count($selectedParticipants);
$canFitAll = $spotsAfter >= 0;
@endphp
<label class="relative cursor-pointer block">
<input type="radio" wire:click="selectDestinationGroup({{ $group->id }})" name="destination" <input type="radio" wire:click="selectDestinationGroup({{ $group->id }})" name="destination"
{{ $destinationGroupId === $group->id ? 'checked' : '' }} class="peer sr-only" {{ $destinationGroupId === $group->id ? 'checked' : '' }} class="peer sr-only">
{{ $group->isFull() ? 'disabled' : '' }}>
<div class="p-4 border-2 rounded-xl transition-all <div class="p-4 border-2 rounded-xl transition-all
peer-checked:border-emerald-500 peer-checked:bg-emerald-50 peer-checked:border-emerald-500 peer-checked:bg-emerald-50
border-gray-200 hover:border-gray-300 border-gray-200 hover:border-gray-300">
{{ $group->isFull() ? 'opacity-50' : '' }}">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div> <div>
<div class="font-medium text-gray-800">{{ $group->name_ar }}</div> <div class="font-medium text-gray-800">{{ $group->name_ar }}</div>
...@@ -219,13 +341,13 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin ...@@ -219,13 +341,13 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin
</div> </div>
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<span class="px-3 py-1 text-sm rounded-full font-medium <span class="px-3 py-1 text-sm rounded-full font-medium
{{ $group->hasAvailableSpots() ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700' }}"> {{ $canFitAll ? 'bg-green-100 text-green-700' : 'bg-amber-100 text-amber-700' }}">
{{ $group->current_count }}/{{ $group->max_capacity }} {{ $group->current_count }}/{{ $group->max_capacity }}
</span> </span>
@if($group->hasAvailableSpots()) @if($canFitAll)
<span class="text-xs text-green-600">{{ __('متاح') }}: {{ $group->max_capacity - $group->current_count }}</span> <span class="text-xs text-green-600">{{ __('متاح') }}: {{ $group->max_capacity - $group->current_count }}</span>
@else @else
<span class="text-xs text-red-600">{{ __('ممتلئة') }}</span> <span class="text-xs text-amber-600">{{ __('سعة غير كافية') }}</span>
@endif @endif
</div> </div>
</div> </div>
...@@ -242,20 +364,17 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin ...@@ -242,20 +364,17 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin
</div> </div>
@endif @endif
{{-- Price Difference Notice --}} {{-- Capacity Warning --}}
@if($requiresPayment && $priceDifference > 0) @if($destinationGroupInfo && count($selectedParticipants) > ($destinationGroupInfo['max_capacity'] - $destinationGroupInfo['current_count']))
<div class="mt-4 p-4 bg-blue-50 border border-blue-200 rounded-xl"> <div class="mt-4 p-4 bg-amber-50 border border-amber-200 rounded-xl">
<div class="flex items-start gap-3"> <div class="flex items-start gap-3">
<svg class="w-5 h-5 text-blue-600 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-5 h-5 text-amber-600 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="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/> <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 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"/>
</svg> </svg>
<div> <div class="text-sm text-amber-700">
<p class="text-sm font-semibold text-blue-800">{{ __('يوجد فرق سعر') }}</p> <p class="font-semibold">{{ __('تحذير: السعة غير كافية') }}</p>
<p class="text-sm text-blue-700 mt-1"> <p>{{ __('المجموعة المختارة بها') }} {{ $destinationGroupInfo['max_capacity'] - $destinationGroupInfo['current_count'] }} {{ __('مكان متاح فقط، وتحاول نقل') }} {{ count($selectedParticipants) }} {{ __('مشترك.') }}</p>
{{ __('البرنامج الجديد أغلى بمبلغ') }} <p class="mt-1">{{ __('سيتم نقل من يمكن نقله فقط وسيفشل الباقي.') }}</p>
<span class="font-bold" dir="ltr">{{ number_format($priceDifference / 100, 2) }}</span>
{{ __('ج.م — سيتم إنشاء فاتورة بالفرق عند التأكيد.') }}
</p>
</div> </div>
</div> </div>
</div> </div>
...@@ -265,22 +384,17 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin ...@@ -265,22 +384,17 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin
</div> </div>
@endif @endif
{{-- Step 3: Impact --}} {{-- Step 3: Reason --}}
@if($currentStep === 3) @if($currentStep === 3)
<div> <div>
<h2 class="text-lg font-semibold text-gray-800 mb-6">{{ __('تأثير النقل') }}</h2> <h2 class="text-lg font-semibold text-gray-800 mb-6">{{ __('سبب النقل') }}</h2>
{{-- Transfer Visual --}} {{-- Transfer Visual --}}
<div class="flex items-center justify-center gap-4 mb-6 p-6 bg-gray-50 rounded-xl"> <div class="flex items-center justify-center gap-4 mb-6 p-6 bg-gray-50 rounded-xl">
<div class="text-center p-4 bg-white rounded-lg border border-gray-200 flex-1"> <div class="text-center p-4 bg-white rounded-lg border border-gray-200 flex-1">
<div class="text-xs text-gray-500 mb-1">{{ __('من') }}</div> <div class="text-xs text-gray-500 mb-1">{{ __('من') }}</div>
<div class="font-medium text-gray-800">{{ $sourceGroup['name_ar'] ?? '-' }}</div> <div class="font-medium text-gray-800">{{ $sourceGroupInfo['name_ar'] ?? '-' }}</div>
<div class="text-sm text-gray-500 mt-1" dir="ltr"> <div class="text-sm text-gray-500 mt-1">{{ count($selectedParticipants) }} {{ __('مشترك') }}</div>
{{ ($sourceGroup['current_count'] ?? 0) }}/{{ $sourceGroup['max_capacity'] ?? 0 }}
</div>
<div class="text-xs text-green-600 mt-1">
{{ __('بعد النقل:') }} {{ (($sourceGroup['current_count'] ?? 0) - 1) }}/{{ $sourceGroup['max_capacity'] ?? 0 }}
</div>
</div> </div>
<div class="text-gray-400"> <div class="text-gray-400">
<svg class="w-8 h-8 rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-8 h-8 rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24">
...@@ -289,13 +403,8 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin ...@@ -289,13 +403,8 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin
</div> </div>
<div class="text-center p-4 bg-white rounded-lg border border-gray-200 flex-1"> <div class="text-center p-4 bg-white rounded-lg border border-gray-200 flex-1">
<div class="text-xs text-gray-500 mb-1">{{ __('إلى') }}</div> <div class="text-xs text-gray-500 mb-1">{{ __('إلى') }}</div>
<div class="font-medium text-gray-800">{{ $destinationGroup['name_ar'] ?? '-' }}</div> <div class="font-medium text-gray-800">{{ $destinationGroupInfo['name_ar'] ?? '-' }}</div>
<div class="text-sm text-gray-500 mt-1" dir="ltr"> <div class="text-sm text-gray-500 mt-1">{{ $destinationGroupInfo['program_name'] ?? '' }}</div>
{{ ($destinationGroup['current_count'] ?? 0) }}/{{ $destinationGroup['max_capacity'] ?? 0 }}
</div>
<div class="text-xs text-amber-600 mt-1">
{{ __('بعد النقل:') }} {{ (($destinationGroup['current_count'] ?? 0) + 1) }}/{{ $destinationGroup['max_capacity'] ?? 0 }}
</div>
</div> </div>
</div> </div>
...@@ -304,7 +413,7 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin ...@@ -304,7 +413,7 @@ class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:rin
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __('سبب النقل') }} <span class="text-red-500">*</span></label> <label class="block text-sm font-medium text-gray-700 mb-1">{{ __('سبب النقل') }} <span class="text-red-500">*</span></label>
<textarea wire:model="reason" rows="3" <textarea wire:model="reason" rows="3"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500"
placeholder="{{ __('اذكر سبب نقل المشترك...') }}"></textarea> placeholder="{{ __('مثال: تغيير المستوى، تغيير الوقت، طلب ولي الأمر...') }}"></textarea>
@error('reason') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror @error('reason') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
</div> </div>
</div> </div>
...@@ -316,22 +425,15 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:rin ...@@ -316,22 +425,15 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:rin
<h2 class="text-lg font-semibold text-gray-800 mb-6">{{ __('تأكيد النقل') }}</h2> <h2 class="text-lg font-semibold text-gray-800 mb-6">{{ __('تأكيد النقل') }}</h2>
<div class="space-y-4"> <div class="space-y-4">
{{-- Summary --}}
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4"> <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="p-4 bg-gray-50 rounded-lg"> <div class="p-4 bg-gray-50 rounded-lg">
<div class="text-xs text-gray-500 mb-1">{{ __('المشترك') }}</div> <div class="text-xs text-gray-500 mb-1">{{ __('من مجموعة') }}</div>
<div class="font-medium text-gray-800">{{ $selectedParticipant['name'] ?? '-' }}</div> <div class="font-medium text-gray-800">{{ $sourceGroupInfo['name_ar'] ?? '-' }}</div>
</div>
<div class="p-4 bg-gray-50 rounded-lg">
<div class="text-xs text-gray-500 mb-1">{{ __('البرنامج') }}</div>
<div class="font-medium text-gray-800">{{ $selectedEnrollment['program_name'] ?? '-' }}</div>
</div>
<div class="p-4 bg-gray-50 rounded-lg">
<div class="text-xs text-gray-500 mb-1">{{ __('المجموعة الحالية') }}</div>
<div class="font-medium text-gray-800">{{ $sourceGroup['name_ar'] ?? '-' }}</div>
</div> </div>
<div class="p-4 bg-gray-50 rounded-lg"> <div class="p-4 bg-gray-50 rounded-lg">
<div class="text-xs text-gray-500 mb-1">{{ __('المجموعة الجديدة') }}</div> <div class="text-xs text-gray-500 mb-1">{{ __('إلى مجموعة') }}</div>
<div class="font-medium text-gray-800">{{ $destinationGroup['name_ar'] ?? '-' }}</div> <div class="font-medium text-gray-800">{{ $destinationGroupInfo['name_ar'] ?? '-' }}</div>
</div> </div>
</div> </div>
...@@ -340,27 +442,24 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:rin ...@@ -340,27 +442,24 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:rin
<div class="text-gray-800">{{ $reason }}</div> <div class="text-gray-800">{{ $reason }}</div>
</div> </div>
@if($requiresPayment && $priceDifference > 0) {{-- Participants List --}}
<div class="p-4 bg-blue-50 border border-blue-200 rounded-lg"> <div class="p-4 bg-gray-50 rounded-lg">
<div class="flex items-start gap-2"> <div class="text-xs text-gray-500 mb-2">{{ __('المشتركون') }} ({{ count($selectedParticipants) }})</div>
<svg class="w-5 h-5 text-blue-600 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <div class="flex flex-wrap gap-2">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/> @foreach($selectedParticipants as $p)
</svg> <span class="px-3 py-1 bg-emerald-100 text-emerald-800 text-sm rounded-full">{{ $p['name'] }}</span>
<div class="text-sm text-blue-700"> @endforeach
{{ __('سيتم إنشاء فاتورة بفرق السعر:') }}
<span class="font-bold" dir="ltr">{{ number_format($priceDifference / 100, 2) }} {{ __('ج.م') }}</span>
</div>
</div> </div>
</div> </div>
@endif
{{-- Warning --}}
<div class="p-4 bg-amber-50 border border-amber-200 rounded-lg"> <div class="p-4 bg-amber-50 border border-amber-200 rounded-lg">
<div class="flex items-start gap-2"> <div class="flex items-start gap-2">
<svg class="w-5 h-5 text-amber-600 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-5 h-5 text-amber-600 mt-0.5 flex-shrink-0" 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 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"/> <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 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"/>
</svg> </svg>
<div class="text-sm text-amber-700"> <div class="text-sm text-amber-700">
{{ __('سيتم إلغاء التسجيل الحالي وإنشاء تسجيل جديد في المجموعة الوجهة. هذا الإجراء لا يمكن التراجع عنه.') }} {{ __('سيتم إلغاء التسجيلات الحالية وإنشاء تسجيلات جديدة. سيتم نقل الحضور المستقبلي تلقائيًا.') }}
</div> </div>
</div> </div>
</div> </div>
...@@ -388,7 +487,7 @@ class="px-6 py-2.5 bg-emerald-600 text-white rounded-lg hover:bg-emerald-700 fon ...@@ -388,7 +487,7 @@ class="px-6 py-2.5 bg-emerald-600 text-white rounded-lg hover:bg-emerald-700 fon
@else @else
<button wire:click="confirm" wire:loading.attr="disabled" wire:target="confirm" <button wire:click="confirm" wire:loading.attr="disabled" wire:target="confirm"
class="px-6 py-3 bg-emerald-600 text-white rounded-lg hover:bg-emerald-700 font-medium disabled:opacity-50"> class="px-6 py-3 bg-emerald-600 text-white rounded-lg hover:bg-emerald-700 font-medium disabled:opacity-50">
<span wire:loading.remove wire:target="confirm">{{ __('تأكيد النقل') }}</span> <span wire:loading.remove wire:target="confirm">{{ __('تأكيد نقل') }} {{ count($selectedParticipants) }} {{ __('مشترك') }}</span>
<span wire:loading wire:target="confirm">{{ __('جارٍ النقل...') }}</span> <span wire:loading wire:target="confirm">{{ __('جارٍ النقل...') }}</span>
</button> </button>
@endif @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