Commit 9b7cfaa9 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Cross-program transfer with price diff invoice + smart collect payment

Transfer wizard:
- Can now transfer between different programs (not just same-program)
- Program dropdown in step 2 to pick destination program
- Calculates price difference via PricingService
- If new program costs more, shows the difference and creates an invoice
- Success screen links to the created invoice

Collect payment wizard:
- Shows upcoming installments (from payment plans) with exact due dates
- Shows upcoming monthly renewal dates from enrollments
- Sorted by due_date so the most urgent appears first
- Invoices now sorted ASC by due_date (urgent first)
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 9781062a
...@@ -2,10 +2,14 @@ ...@@ -2,10 +2,14 @@
namespace App\Livewire\Enrollments; namespace App\Livewire\Enrollments;
use App\Domain\Financial\Models\Invoice;
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\Shared\Exceptions\DomainException; use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Training\Models\Enrollment; use App\Domain\Training\Models\Enrollment;
use App\Domain\Training\Models\TrainingGroup; use App\Domain\Training\Models\TrainingGroup;
use App\Domain\Training\Models\TrainingProgram;
use App\Domain\Training\Services\EnrollmentService; use App\Domain\Training\Services\EnrollmentService;
use Livewire\Attributes\Layout; use Livewire\Attributes\Layout;
use Livewire\Attributes\Title; use Livewire\Attributes\Title;
...@@ -25,17 +29,25 @@ class TransferParticipantWizard extends Component ...@@ -25,17 +29,25 @@ class TransferParticipantWizard extends Component
public ?int $enrollmentId = null; public ?int $enrollmentId = null;
// Step 2: Destination // Step 2: Destination
public ?int $destinationProgramId = null;
public ?int $destinationGroupId = null; public ?int $destinationGroupId = null;
// Step 3: Impact & Reason // Step 3: Impact & Reason
public string $reason = ''; public string $reason = '';
// Price difference
public int $sourceProgramPrice = 0;
public int $destinationProgramPrice = 0;
public int $priceDifference = 0;
public bool $requiresPayment = false;
// Cached data for display // Cached data for display
public ?array $selectedParticipant = null; public ?array $selectedParticipant = null;
public array $activeEnrollments = []; public array $activeEnrollments = [];
public ?array $selectedEnrollment = null; public ?array $selectedEnrollment = null;
public ?array $sourceGroup = null; public ?array $sourceGroup = null;
public ?array $destinationGroup = null; public ?array $destinationGroup = null;
public ?string $createdInvoiceUuid = null;
public function mount(): void public function mount(): void
{ {
...@@ -113,16 +125,68 @@ public function selectEnrollment(int $id): void ...@@ -113,16 +125,68 @@ public function selectEnrollment(int $id): void
]; ];
} }
public function selectDestinationProgram(?int $id): void
{
$this->destinationProgramId = $id;
$this->destinationGroupId = null;
$this->destinationGroup = null;
$this->priceDifference = 0;
$this->requiresPayment = false;
}
public function selectDestinationGroup(int $id): void public function selectDestinationGroup(int $id): void
{ {
$group = TrainingGroup::findOrFail($id); $group = TrainingGroup::with('program')->findOrFail($id);
$this->destinationGroupId = $id; $this->destinationGroupId = $id;
$this->destinationGroup = [ $this->destinationGroup = [
'id' => $group->id, 'id' => $group->id,
'name_ar' => $group->name_ar, 'name_ar' => $group->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
{
$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
...@@ -154,7 +218,24 @@ public function confirm(): void ...@@ -154,7 +218,24 @@ public function confirm(): void
$enrollment = Enrollment::findOrFail($this->enrollmentId); $enrollment = Enrollment::findOrFail($this->enrollmentId);
$toGroup = TrainingGroup::findOrFail($this->destinationGroupId); $toGroup = TrainingGroup::findOrFail($this->destinationGroupId);
app(EnrollmentService::class)->transfer($enrollment, $toGroup, auth()->user()); $newEnrollment = app(EnrollmentService::class)->transfer($enrollment, $toGroup, auth()->user());
// Create price difference invoice if destination is more expensive
if ($this->requiresPayment && $this->priceDifference > 0) {
$invoice = app(InvoiceService::class)->create([
'billable_type' => Participant::class,
'billable_id' => $this->participantId,
'branch_id' => $toGroup->branch_id ?? auth()->user()->branch_id,
'due_date' => now()->addDays(7)->toDateString(),
'notes' => 'فاتورة فرق سعر نقل — اشتراك #' . $newEnrollment->id,
], [[
'description' => 'فرق سعر نقل من ' . ($this->selectedEnrollment['program_name'] ?? '-') . ' إلى ' . ($this->destinationGroup['program_name'] ?? '-'),
'quantity' => 1,
'unit_price' => $this->priceDifference,
]], auth()->user());
$this->createdInvoiceUuid = $invoice->uuid;
}
$this->completed = true; $this->completed = true;
} catch (DomainException $e) { } catch (DomainException $e) {
...@@ -196,17 +277,26 @@ public function render() ...@@ -196,17 +277,26 @@ public function render()
->get(); ->get();
} }
$availablePrograms = collect();
$availableGroups = collect(); $availableGroups = collect();
if ($this->selectedEnrollment && $this->currentStep >= 2) { if ($this->selectedEnrollment && $this->currentStep >= 2) {
$availableGroups = TrainingGroup::where('training_program_id', $this->selectedEnrollment['program_id']) $availablePrograms = TrainingProgram::where('is_active', true)
->orderBy('name_ar')
->get();
$targetProgramId = $this->destinationProgramId ?? $this->selectedEnrollment['program_id'];
$availableGroups = TrainingGroup::where('training_program_id', $targetProgramId)
->where('id', '!=', $this->selectedEnrollment['group_id']) ->where('id', '!=', $this->selectedEnrollment['group_id'])
->whereIn('status', ['forming', 'active']) ->whereIn('status', ['forming', 'active'])
->with('program')
->orderBy('name_ar') ->orderBy('name_ar')
->get(); ->get();
} }
return view('livewire.enrollments.transfer-participant-wizard', [ return view('livewire.enrollments.transfer-participant-wizard', [
'searchResults' => $searchResults, 'searchResults' => $searchResults,
'availablePrograms' => $availablePrograms,
'availableGroups' => $availableGroups, 'availableGroups' => $availableGroups,
]); ]);
} }
......
...@@ -3,11 +3,14 @@ ...@@ -3,11 +3,14 @@
namespace App\Livewire\Receptionist; namespace App\Livewire\Receptionist;
use App\Domain\Financial\Enums\InvoiceStatus; use App\Domain\Financial\Enums\InvoiceStatus;
use App\Domain\Financial\Models\Installment;
use App\Domain\Financial\Models\Invoice; use App\Domain\Financial\Models\Invoice;
use App\Domain\Financial\Models\PaymentPlan;
use App\Domain\Financial\Services\PaymentService; use App\Domain\Financial\Services\PaymentService;
use App\Domain\Participant\Models\Participant; use App\Domain\Participant\Models\Participant;
use App\Domain\Shared\Exceptions\DomainException; use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Traits\UsesBranchScope; use App\Domain\Shared\Traits\UsesBranchScope;
use App\Domain\Training\Models\Enrollment;
use Livewire\Attributes\Layout; use Livewire\Attributes\Layout;
use Livewire\Attributes\Title; use Livewire\Attributes\Title;
use Livewire\Component; use Livewire\Component;
...@@ -212,8 +215,9 @@ public function render() ...@@ -212,8 +215,9 @@ public function render()
// Outstanding invoices for selected participant // Outstanding invoices for selected participant
$invoices = collect(); $invoices = collect();
$upcomingPayments = collect();
if ($this->selected_participant_id) { if ($this->selected_participant_id) {
$invoices = Invoice::where('billable_type', \App\Domain\Participant\Models\Participant::class) $invoices = Invoice::where('billable_type', Participant::class)
->where('billable_id', $this->selected_participant_id) ->where('billable_id', $this->selected_participant_id)
->whereIn('status', [ ->whereIn('status', [
InvoiceStatus::Sent, InvoiceStatus::Sent,
...@@ -221,8 +225,46 @@ public function render() ...@@ -221,8 +225,46 @@ public function render()
InvoiceStatus::Overdue, InvoiceStatus::Overdue,
]) ])
->where('due_amount', '>', 0) ->where('due_amount', '>', 0)
->orderByDesc('due_date') ->orderBy('due_date')
->get(); ->get();
// Upcoming installments (from payment plans linked to this participant's invoices)
$participantInvoiceIds = Invoice::where('billable_type', Participant::class)
->where('billable_id', $this->selected_participant_id)
->pluck('id');
$nextInstallments = Installment::whereHas('paymentPlan', fn ($q) => $q->whereIn('invoice_id', $participantInvoiceIds))
->where('status', 'pending')
->orderBy('due_date')
->limit(3)
->get();
// Upcoming enrollment renewals (next_billing_date)
$upcomingRenewals = Enrollment::where('participant_id', $this->selected_participant_id)
->where('status', 'active')
->whereNotNull('next_billing_date')
->with('program')
->orderBy('next_billing_date')
->get();
// Merge into a unified upcoming list
foreach ($nextInstallments as $inst) {
$upcomingPayments->push([
'type' => 'installment',
'label' => 'قسط #' . $inst->sequence,
'amount' => $inst->amount,
'due_date' => $inst->due_date, // Carbon from model cast
]);
}
foreach ($upcomingRenewals as $enr) {
$upcomingPayments->push([
'type' => 'renewal',
'label' => 'تجديد: ' . ($enr->program?->name_ar ?? '-'),
'amount' => null,
'due_date' => $enr->next_billing_date, // Carbon from model cast
]);
}
$upcomingPayments = $upcomingPayments->sortBy(fn ($item) => $item['due_date']->timestamp)->values();
} }
$selectedInvoice = $this->selected_invoice_id $selectedInvoice = $this->selected_invoice_id
...@@ -232,6 +274,7 @@ public function render() ...@@ -232,6 +274,7 @@ public function render()
return view('livewire.receptionist.collect-payment-wizard', [ return view('livewire.receptionist.collect-payment-wizard', [
'searchResults' => $searchResults, 'searchResults' => $searchResults,
'invoices' => $invoices, 'invoices' => $invoices,
'upcomingPayments' => $upcomingPayments,
'selectedInvoice' => $selectedInvoice, 'selectedInvoice' => $selectedInvoice,
]); ]);
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<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>
</div> </div>
...@@ -23,8 +23,20 @@ ...@@ -23,8 +23,20 @@
</svg> </svg>
</div> </div>
<h2 class="text-xl font-bold text-gray-800 mb-2">{{ __('تم النقل بنجاح') }}</h2> <h2 class="text-xl font-bold text-gray-800 mb-2">{{ __('تم النقل بنجاح') }}</h2>
<p class="text-gray-500 mb-6">{{ __('تم نقل المشترك إلى المجموعة الجديدة بنجاح') }}</p> <p class="text-gray-500 mb-4">{{ __('تم نقل المشترك إلى المجموعة الجديدة بنجاح') }}</p>
@if($createdInvoiceUuid)
<div class="mb-6 p-4 bg-amber-50 border border-amber-200 rounded-xl text-amber-800 text-sm">
<p class="font-semibold mb-1">{{ __('تم إنشاء فاتورة فرق السعر') }}</p>
<p>{{ __('المبلغ:') }} {{ number_format($priceDifference / 100, 2) }} {{ __('ج.م') }}</p>
</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('invoices.show', $createdInvoiceUuid) }}" wire:navigate
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>
@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-emerald-600 text-white rounded-lg hover:bg-emerald-700 font-medium transition-colors">
{{ __('العودة للتسجيلات') }} {{ __('العودة للتسجيلات') }}
...@@ -169,15 +181,33 @@ class="w-full text-start p-4 border border-gray-200 rounded-xl hover:border-emer ...@@ -169,15 +181,33 @@ class="w-full text-start p-4 border border-gray-200 rounded-xl hover:border-emer
{{-- Step 2: Destination Group --}} {{-- Step 2: Destination Group --}}
@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">{{ __('اختر البرنامج والمجموعة — يمكنك النقل لبرنامج آخر') }}</p>
{{-- Program Selector --}}
<div class="mb-5">
<label class="block text-sm font-medium text-gray-700 mb-2">{{ __('البرنامج') }}</label>
<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">
@foreach($availablePrograms as $program)
<option value="{{ $program->id }}" {{ ($destinationProgramId ?? $selectedEnrollment['program_id'] ?? '') == $program->id ? 'selected' : '' }}>
{{ $program->name_ar }}
@if($program->id == ($selectedEnrollment['program_id'] ?? null))
({{ __('البرنامج الحالي') }})
@endif
</option>
@endforeach
</select>
</div>
{{-- Available Groups --}}
@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"> <label class="relative cursor-pointer block {{ $group->isFull() ? 'pointer-events-none' : '' }}">
<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
...@@ -185,9 +215,7 @@ class="w-full text-start p-4 border border-gray-200 rounded-xl hover:border-emer ...@@ -185,9 +215,7 @@ class="w-full text-start p-4 border border-gray-200 rounded-xl hover:border-emer
<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>
@if($group->name) <div class="text-xs text-gray-400">{{ $group->program?->name_ar ?? '' }}</div>
<div class="text-xs text-gray-400">{{ $group->name }}</div>
@endif
</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
...@@ -210,7 +238,26 @@ class="w-full text-start p-4 border border-gray-200 rounded-xl hover:border-emer ...@@ -210,7 +238,26 @@ class="w-full text-start p-4 border border-gray-200 rounded-xl hover:border-emer
<svg class="w-12 h-12 mx-auto mb-3 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-12 h-12 mx-auto mb-3 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"/>
</svg> </svg>
<p>{{ __('لا توجد مجموعات أخرى متاحة في نفس البرنامج') }}</p> <p>{{ __('لا توجد مجموعات متاحة في هذا البرنامج') }}</p>
</div>
@endif
{{-- Price Difference Notice --}}
@if($requiresPayment && $priceDifference > 0)
<div class="mt-4 p-4 bg-blue-50 border border-blue-200 rounded-xl">
<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">
<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"/>
</svg>
<div>
<p class="text-sm font-semibold text-blue-800">{{ __('يوجد فرق سعر') }}</p>
<p class="text-sm text-blue-700 mt-1">
{{ __('البرنامج الجديد أغلى بمبلغ') }}
<span class="font-bold" dir="ltr">{{ number_format($priceDifference / 100, 2) }}</span>
{{ __('ج.م — سيتم إنشاء فاتورة بالفرق عند التأكيد.') }}
</p>
</div>
</div>
</div> </div>
@endif @endif
...@@ -293,6 +340,20 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:rin ...@@ -293,6 +340,20 @@ 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)
<div class="p-4 bg-blue-50 border border-blue-200 rounded-lg">
<div class="flex items-start gap-2">
<svg class="w-5 h-5 text-blue-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 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"/>
</svg>
<div class="text-sm text-blue-700">
{{ __('سيتم إنشاء فاتورة بفرق السعر:') }}
<span class="font-bold" dir="ltr">{{ number_format($priceDifference / 100, 2) }} {{ __('ج.م') }}</span>
</div>
</div>
</div>
@endif
<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">
......
...@@ -220,6 +220,40 @@ class="inline-flex items-center gap-2 px-6 py-3 min-h-16 bg-amber-600 text-white ...@@ -220,6 +220,40 @@ class="inline-flex items-center gap-2 px-6 py-3 min-h-16 bg-amber-600 text-white
@error('selected_invoice_id') <p class="mt-2 text-sm text-red-600">{{ $message }}</p> @enderror @error('selected_invoice_id') <p class="mt-2 text-sm text-red-600">{{ $message }}</p> @enderror
{{-- Upcoming Installments & Renewals --}}
@if($upcomingPayments->isNotEmpty())
<div class="mt-6 p-4 bg-indigo-50 border border-indigo-200 rounded-xl">
<h3 class="text-sm font-semibold text-indigo-800 mb-3 flex items-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
</svg>
{{ __('مواعيد قادمة') }}
</h3>
<div class="space-y-2">
@foreach($upcomingPayments->take(5) as $upcoming)
<div class="flex items-center justify-between py-2 px-3 bg-white rounded-lg border border-indigo-100">
<div class="flex items-center gap-2">
@if($upcoming['type'] === 'installment')
<span class="w-2 h-2 rounded-full bg-amber-500"></span>
@else
<span class="w-2 h-2 rounded-full bg-green-500"></span>
@endif
<span class="text-sm text-gray-800">{{ $upcoming['label'] }}</span>
</div>
<div class="flex items-center gap-3">
@if($upcoming['amount'])
<span class="text-sm font-medium text-gray-700" dir="ltr">{{ number_format($upcoming['amount'] / 100, 2) }} {{ __('ج.م') }}</span>
@endif
<span class="text-xs px-2 py-0.5 rounded-full {{ $upcoming['due_date']->isPast() ? 'bg-red-100 text-red-700' : ($upcoming['due_date']->isToday() ? 'bg-amber-100 text-amber-700' : 'bg-gray-100 text-gray-600') }}" dir="ltr">
{{ $upcoming['due_date']->format('Y-m-d') }}
</span>
</div>
</div>
@endforeach
</div>
</div>
@endif
<div class="flex justify-between mt-8"> <div class="flex justify-between mt-8">
<button wire:click="previousStep" <button wire:click="previousStep"
class="inline-flex items-center gap-2 px-6 py-3 min-h-16 text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200 text-base font-medium transition-colors"> class="inline-flex items-center gap-2 px-6 py-3 min-h-16 text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200 text-base font-medium transition-colors">
......
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