Commit 38768a29 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Enhance setup wizard: grid builder, groups, participants import

Setup wizard now covers 10 steps (was 8):
- Facilities step includes visual grid layout builder (rows×columns)
- New Groups step: create groups with interactive segment multi-select
- New Participants step: import existing players with guardian data
- completeSetup() creates SpaceLayout + SpaceSegments for each facility grid
- Hint encourages max division for scheduling flexibility
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 0c8f5bdb
......@@ -3,15 +3,20 @@
namespace App\Livewire\Wizards;
use App\Domain\Facility\Enums\FacilityType;
use App\Domain\Facility\Models\Facility;
use App\Domain\Facility\Models\SpaceLayout;
use App\Domain\Facility\Models\SpaceSegment;
use App\Domain\Identity\Models\Branch;
use App\Domain\Identity\Models\Person;
use App\Domain\Identity\Models\Role;
use App\Domain\Participant\Models\Participant;
use App\Domain\Pricing\Models\BasePrice;
use App\Domain\Shared\Models\Academy;
use App\Domain\Shared\Services\SettingsService;
use App\Domain\Training\Enums\ActivityCategory;
use App\Domain\Training\Models\Activity;
use App\Domain\Training\Models\TrainingGroup;
use App\Domain\Training\Models\TrainingProgram;
use App\Domain\Facility\Models\Facility;
use App\Domain\Pricing\Models\BasePrice;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
......@@ -25,7 +30,7 @@
class SetupWizard extends Component
{
public int $currentStep = 1;
public int $totalSteps = 8;
public int $totalSteps = 10;
// Step 1: Welcome
public string $academyName = '';
......@@ -40,13 +45,19 @@ class SetupWizard extends Component
// Step 4: Programs
public array $programs = [];
// Step 5: Facilities
// Step 5: Facilities + Grid
public array $facilities = [];
// Step 6: Users
// Step 6: Groups (NEW)
public array $groups = [];
// Step 7: Participants (NEW)
public array $participants = [];
// Step 8: Users
public array $users = [];
// Step 7: Settings
// Step 9: Settings
public string $defaultCurrency = 'EGP';
public string $timezone = 'Africa/Cairo';
public int $gracePeriodParticipant = 15;
......@@ -183,6 +194,8 @@ public function addFacility(): void
'operating_start' => '08:00',
'operating_end' => '22:00',
'address' => '',
'grid_rows' => 3,
'grid_cols' => 3,
];
}
......@@ -192,6 +205,50 @@ public function removeFacility(int $index): void
$this->facilities = array_values($this->facilities);
}
// ─── Group Management (NEW) ──────────────────────────────────
public function addGroup(): void
{
$this->groups[] = [
'name_ar' => '',
'program_index' => 0,
'facility_index' => 0,
'assigned_segments' => [],
'head_trainer_name' => '',
'max_capacity' => 20,
'schedule_summary' => '',
];
}
public function removeGroup(int $index): void
{
unset($this->groups[$index]);
$this->groups = array_values($this->groups);
}
// ─── Participant Management (NEW) ────────────────────────────
public function addParticipant(): void
{
$this->participants[] = [
'name_ar' => '',
'name' => '',
'phone' => '',
'date_of_birth' => '',
'gender' => 'male',
'guardian_name' => '',
'guardian_phone' => '',
'activity_index' => 0,
'group_index' => null,
];
}
public function removeParticipant(int $index): void
{
unset($this->participants[$index]);
$this->participants = array_values($this->participants);
}
// ─── User Management ─────────────────────────────────────────
public function addUser(): void
......@@ -226,8 +283,10 @@ protected function validateCurrentStep(): void
3 => $this->validateActivities(),
4 => $this->validatePrograms(),
5 => $this->validateFacilities(),
6 => $this->validateUsers(),
7 => $this->validate([
6 => $this->validateGroups(),
7 => $this->validateParticipants(),
8 => $this->validateUsers(),
9 => $this->validate([
'defaultCurrency' => 'required|string|in:EGP,SAR,AED,USD',
'timezone' => 'required|string|timezone',
'gracePeriodParticipant' => 'required|integer|min:0|max:60',
......@@ -324,10 +383,61 @@ protected function validateFacilities(): void
'facilities.*.operating_start' => 'nullable|date_format:H:i',
'facilities.*.operating_end' => 'nullable|date_format:H:i',
'facilities.*.address' => 'nullable|string|max:500',
'facilities.*.grid_rows' => 'required|integer|min:1|max:10',
'facilities.*.grid_cols' => 'required|integer|min:1|max:10',
], [
'facilities.*.name_ar.required' => __('اسم المنشأة بالعربية مطلوب'),
'facilities.*.type.required' => __('نوع المنشأة مطلوب'),
'facilities.*.monthly_cost.required' => __('الإيجار الشهري مطلوب (أدخل 0 إذا كانت ملك)'),
'facilities.*.grid_rows.required' => __('عدد الصفوف مطلوب'),
'facilities.*.grid_cols.required' => __('عدد الأعمدة مطلوب'),
]);
}
protected function validateGroups(): void
{
// Groups are optional — skip if empty
if (empty($this->groups)) {
return;
}
$this->validate([
'groups.*.name_ar' => 'required|string|min:2|max:255',
'groups.*.program_index' => 'required|integer|min:0',
'groups.*.facility_index' => 'required|integer|min:0',
'groups.*.max_capacity' => 'required|integer|min:1|max:500',
'groups.*.head_trainer_name' => 'nullable|string|max:255',
'groups.*.schedule_summary' => 'nullable|string|max:500',
], [
'groups.*.name_ar.required' => __('اسم المجموعة بالعربية مطلوب'),
'groups.*.name_ar.min' => __('اسم المجموعة يجب أن يكون حرفين على الأقل'),
'groups.*.program_index.required' => __('يجب اختيار البرنامج'),
'groups.*.facility_index.required' => __('يجب اختيار المنشأة'),
'groups.*.max_capacity.required' => __('الحد الأقصى للسعة مطلوب'),
]);
}
protected function validateParticipants(): void
{
// Participants are optional — skip if empty
if (empty($this->participants)) {
return;
}
$this->validate([
'participants.*.name_ar' => 'required|string|min:2|max:255',
'participants.*.name' => 'nullable|string|max:255',
'participants.*.phone' => 'nullable|string|max:20',
'participants.*.date_of_birth' => 'nullable|date',
'participants.*.gender' => 'required|in:male,female',
'participants.*.guardian_name' => 'nullable|string|max:255',
'participants.*.guardian_phone' => 'nullable|string|max:20',
'participants.*.activity_index' => 'nullable|integer|min:0',
], [
'participants.*.name_ar.required' => __('اسم المشترك بالعربية مطلوب'),
'participants.*.name_ar.min' => __('اسم المشترك يجب أن يكون حرفين على الأقل'),
'participants.*.gender.required' => __('الجنس مطلوب'),
'participants.*.gender.in' => __('الجنس يجب أن يكون ذكر أو أنثى'),
]);
}
......@@ -397,6 +507,7 @@ public function completeSetup(): void
}
// Create programs + base prices
$createdPrograms = [];
foreach ($this->programs as $programData) {
$activityIndex = (int) $programData['activity_index'];
$activity = $createdActivities[$activityIndex] ?? null;
......@@ -419,6 +530,8 @@ public function completeSetup(): void
'metadata' => ['monthly_fee_piasters' => $monthlyFeePiasters],
]);
$createdPrograms[] = $program;
BasePrice::create([
'academy_id' => $academyId,
'priceable_type' => TrainingProgram::class,
......@@ -437,9 +550,11 @@ public function completeSetup(): void
}
}
// Create facilities
// Create facilities + space layouts + segments
$createdFacilities = [];
$facilityLayouts = []; // index => layout
foreach ($this->facilities as $facilityData) {
Facility::create([
$facility = Facility::create([
'academy_id' => $academyId,
'branch_id' => $createdBranches[0]->id ?? null,
'name_ar' => $facilityData['name_ar'],
......@@ -453,6 +568,120 @@ public function completeSetup(): void
'status' => 'active',
'created_by' => $actor->id,
]);
$createdFacilities[] = $facility;
// Create space layout for this facility
$rows = (int) ($facilityData['grid_rows'] ?? 3);
$cols = (int) ($facilityData['grid_cols'] ?? 3);
$layout = SpaceLayout::create([
'academy_id' => $academyId,
'facility_id' => $facility->id,
'name' => 'التقسيم الافتراضي',
'name_ar' => 'التقسيم الافتراضي',
'layout_type' => 'grid',
'layout_config' => ['rows' => $rows, 'columns' => $cols],
'is_recurring' => true,
'effective_day_of_week' => null,
'start_time' => $facilityData['operating_start'] ?? '08:00',
'end_time' => $facilityData['operating_end'] ?? '22:00',
'is_active' => true,
'sort_order' => 0,
'metadata' => [],
'created_by' => $actor->id,
]);
$facilityLayouts[] = $layout;
// Generate segments for the grid
for ($r = 1; $r <= $rows; $r++) {
for ($c = 1; $c <= $cols; $c++) {
SpaceSegment::create([
'space_layout_id' => $layout->id,
'code' => "R{$r}C{$c}",
'name' => "Row {$r} Col {$c}",
'name_ar' => "صف {$r} عمود {$c}",
'row_index' => $r,
'col_index' => $c,
'is_available' => true,
'sort_order' => ($r - 1) * $cols + $c,
'metadata' => [],
]);
}
}
}
// Create training groups
$createdGroups = [];
foreach ($this->groups as $groupData) {
$programIndex = (int) ($groupData['program_index'] ?? 0);
$linkedProgram = $createdPrograms[$programIndex] ?? null;
if ($linkedProgram) {
$group = TrainingGroup::create([
'academy_id' => $academyId,
'training_program_id' => $linkedProgram->id,
'branch_id' => $createdBranches[0]->id,
'name' => $groupData['name_ar'],
'name_ar' => $groupData['name_ar'],
'code' => Str::upper(Str::limit(Str::slug($groupData['name_ar'], ''), 8, '')),
'max_capacity' => (int) ($groupData['max_capacity'] ?? 20),
'current_count' => 0,
'waitlist_count' => 0,
'head_trainer_id' => null,
'status' => 'forming',
'metadata' => [
'head_trainer_name' => $groupData['head_trainer_name'] ?? '',
'schedule_summary' => $groupData['schedule_summary'] ?? '',
'assigned_segments' => $groupData['assigned_segments'] ?? [],
'assigned_facility_index' => (int) ($groupData['facility_index'] ?? 0),
],
'created_by' => $actor->id,
]);
$createdGroups[] = $group;
}
}
// Create participants (Person + Participant)
foreach ($this->participants as $idx => $pData) {
$person = Person::create([
'academy_id' => $academyId,
'name_ar' => $pData['name_ar'],
'name' => !empty($pData['name']) ? $pData['name'] : $pData['name_ar'],
'phone' => !empty($pData['phone']) ? $pData['phone'] : null,
'date_of_birth' => !empty($pData['date_of_birth']) ? $pData['date_of_birth'] : null,
'gender' => $pData['gender'] ?? null,
'emergency_contact_name' => !empty($pData['guardian_name']) ? $pData['guardian_name'] : null,
'emergency_contact_phone' => !empty($pData['guardian_phone']) ? $pData['guardian_phone'] : null,
'created_by' => $actor->id,
]);
$activityIndex = isset($pData['activity_index']) ? (int) $pData['activity_index'] : null;
$linkedActivity = ($activityIndex !== null) ? ($createdActivities[$activityIndex] ?? null) : null;
$participant = Participant::create([
'academy_id' => $academyId,
'person_id' => $person->id,
'participant_number' => $this->generateParticipantNumber($academyId, $idx),
'registration_date' => now()->toDateString(),
'registration_source' => 'walk_in',
'primary_activity_id' => $linkedActivity?->id,
'status' => 'registered',
'created_by' => $actor->id,
]);
// Store group assignment in metadata if specified
if (!empty($pData['group_index']) && isset($createdGroups[(int) $pData['group_index']])) {
$assignedGroup = $createdGroups[(int) $pData['group_index']];
$participant->update([
'metadata' => [
'pending_group_id' => $assignedGroup->id,
'pending_group_name' => $assignedGroup->name_ar,
],
]);
}
}
// Create users
......@@ -504,6 +733,14 @@ public function completeSetup(): void
// ─── Helpers ─────────────────────────────────────────────────
private function generateParticipantNumber(int $academyId, int $index): string
{
$prefix = 'P';
$year = now()->format('y');
$seq = str_pad($index + 1, 4, '0', STR_PAD_LEFT);
return "{$prefix}{$year}{$seq}";
}
public function getStepLabels(): array
{
return [
......@@ -512,9 +749,11 @@ public function getStepLabels(): array
3 => __('الأنشطة'),
4 => __('البرامج'),
5 => __('المنشآت'),
6 => __('المستخدمين'),
7 => __('الإعدادات'),
8 => __('تم!'),
6 => __('المجموعات'),
7 => __('المشتركين'),
8 => __('المستخدمين'),
9 => __('الإعدادات'),
10 => __('تم!'),
];
}
......
......@@ -37,7 +37,7 @@
@if(!$loop->last)
<div @class([
'flex-1 h-0.5 mx-2 rounded-full transition-all duration-500',
'flex-1 h-0.5 mx-1 rounded-full transition-all duration-500',
'bg-green-400' => $step < $currentStep,
'bg-gray-200' => $step >= $currentStep,
])></div>
......@@ -385,12 +385,12 @@ class="mt-4 w-full border-2 border-dashed border-gray-300 rounded-xl p-4 text-gr
</div>
@endif
{{-- Step 5: Facilities --}}
{{-- Step 5: Facilities + Grid --}}
@if($currentStep === 5)
<div class="p-8">
<div class="mb-6">
<h2 class="text-xl font-bold text-gray-800">{{ __('المنشآت') }}</h2>
<p class="text-sm text-gray-500 mt-1">{{ __('أضف الملاعب والمنشآت الرياضية. حدد الإيجار الشهري وساعات العمل لحساب نقطة التعادل.') }}</p>
<h2 class="text-xl font-bold text-gray-800">{{ __('المنشآت والتقسيم') }}</h2>
<p class="text-sm text-gray-500 mt-1">{{ __('أضف الملاعب والمنشآت الرياضية مع تحديد تقسيمها لتعيين المجموعات لاحقاً.') }}</p>
<div class="mt-2 p-3 bg-amber-50 border border-amber-200 rounded-lg text-xs text-amber-700">
<strong>{{ __('مهم:') }}</strong> {{ __('الإيجار الشهري يستخدم في حساب الأرباح والخسائر. إذا كانت المنشأة ملكك أدخل 0.') }}
</div>
......@@ -400,7 +400,7 @@ class="mt-4 w-full border-2 border-dashed border-gray-300 rounded-xl p-4 text-gr
<div class="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">{{ $message }}</div>
@enderror
<div class="space-y-4">
<div class="space-y-6">
@foreach($facilities as $index => $facility)
<div class="border border-gray-200 rounded-xl p-5 relative group hover:border-blue-200 transition-colors"
wire:key="facility-{{ $index }}">
......@@ -477,6 +477,52 @@ class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:r
placeholder="{{ __('عنوان المنشأة') }}">
</div>
</div>
{{-- Grid Division Section --}}
<div class="mt-4 p-4 bg-blue-50 border border-blue-200 rounded-xl">
<div class="flex items-center gap-2 mb-3">
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"/>
</svg>
<span class="text-sm font-semibold text-blue-800">{{ __('تقسيم المنشأة') }}</span>
</div>
<p class="text-xs text-blue-600 mb-3">
{{ __('كلما زاد تقسيم المنشأة، زادت مرونتك في تعيين المجموعات — يمكنك دائماً اختيار عدة خلايا معاً عند التعيين') }}
</p>
<div class="flex items-center gap-4 mb-3">
<div>
<label class="block text-xs font-medium text-blue-700 mb-1">{{ __('الصفوف') }}</label>
<input type="number" min="1" max="10" dir="ltr"
wire:model.live="facilities.{{ $index }}.grid_rows"
class="w-16 rounded-lg border-blue-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-center text-sm">
</div>
<span class="text-blue-400 font-bold text-lg mt-5">x</span>
<div>
<label class="block text-xs font-medium text-blue-700 mb-1">{{ __('الأعمدة') }}</label>
<input type="number" min="1" max="10" dir="ltr"
wire:model.live="facilities.{{ $index }}.grid_cols"
class="w-16 rounded-lg border-blue-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-center text-sm">
</div>
<div class="mt-5 text-xs text-blue-600">
= {{ (int)($facility['grid_rows'] ?? 3) * (int)($facility['grid_cols'] ?? 3) }} {{ __('منطقة') }}
</div>
</div>
{{-- Visual Grid Preview (READ-ONLY) --}}
@php
$rows = min(10, max(1, (int)($facility['grid_rows'] ?? 3)));
$cols = min(10, max(1, (int)($facility['grid_cols'] ?? 3)));
@endphp
<div class="grid gap-1" style="grid-template-columns: repeat({{ $cols }}, 1fr)">
@for ($r = 1; $r <= $rows; $r++)
@for ($c = 1; $c <= $cols; $c++)
<div class="h-8 bg-blue-100 border border-blue-300 rounded flex items-center justify-center text-xs text-blue-700 font-mono">
R{{ $r }}C{{ $c }}
</div>
@endfor
@endfor
</div>
</div>
</div>
@endforeach
</div>
......@@ -501,8 +547,324 @@ class="mt-4 w-full border-2 border-dashed border-gray-300 rounded-xl p-4 text-gr
</div>
@endif
{{-- Step 6: Users --}}
{{-- Step 6: Groups (NEW) --}}
@if($currentStep === 6)
<div class="p-8">
<div class="mb-6">
<h2 class="text-xl font-bold text-gray-800">{{ __('المجموعات التدريبية') }}</h2>
<p class="text-sm text-gray-500 mt-1">{{ __('أنشئ مجموعات التدريب وحدد المنشأة والمساحة المخصصة لكل مجموعة. هذه الخطوة اختيارية.') }}</p>
</div>
@if(empty($programs))
<div class="text-center py-8 border-2 border-dashed border-gray-200 rounded-xl">
<svg class="w-12 h-12 text-amber-300 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" 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>
<p class="text-gray-500 text-sm">{{ __('يجب إضافة برامج ومنشآت أولاً لإنشاء المجموعات.') }}</p>
</div>
@else
<div class="space-y-6">
@foreach($groups as $index => $group)
<div class="border border-gray-200 rounded-xl p-5 relative group/card hover:border-blue-200 transition-colors"
wire:key="group-{{ $index }}">
<button wire:click="removeGroup({{ $index }})" type="button"
class="absolute top-3 start-3 w-7 h-7 rounded-full bg-red-50 text-red-500 hover:bg-red-100 flex items-center justify-center opacity-0 group-card:hover:opacity-100 group-hover/card:opacity-100 transition-opacity">
<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="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<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="groups.{{ $index }}.name_ar"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
placeholder="{{ __('مثال: ناشئين أ') }}">
@error("groups.{$index}.name_ar")
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
@enderror
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
{{ __('البرنامج') }} <span class="text-red-500">*</span>
</label>
<select wire:model="groups.{{ $index }}.program_index"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
@foreach($programs as $pIdx => $prog)
<option value="{{ $pIdx }}">{{ $prog['name_ar'] ?: __('برنامج') . ' ' . ($pIdx + 1) }}</option>
@endforeach
</select>
@error("groups.{$index}.program_index")
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
@enderror
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
{{ __('المنشأة') }} <span class="text-red-500">*</span>
</label>
<select wire:model.live="groups.{{ $index }}.facility_index"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
@foreach($facilities as $fIdx => $fac)
<option value="{{ $fIdx }}">{{ $fac['name_ar'] ?: __('منشأة') . ' ' . ($fIdx + 1) }}</option>
@endforeach
</select>
@error("groups.{$index}.facility_index")
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
@enderror
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
{{ __('المدرب المسؤول') }}
</label>
<input type="text" wire:model="groups.{{ $index }}.head_trainer_name"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
placeholder="{{ __('اسم المدرب') }}">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
{{ __('الحد الأقصى') }} <span class="text-red-500">*</span>
</label>
<input type="number" wire:model="groups.{{ $index }}.max_capacity" dir="ltr"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
min="1" max="500" placeholder="20">
@error("groups.{$index}.max_capacity")
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
@enderror
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
{{ __('مواعيد التدريب') }}
</label>
<input type="text" wire:model="groups.{{ $index }}.schedule_summary"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
placeholder="{{ __('مثال: أحد وثلاثاء ٤-٦ م') }}">
</div>
</div>
{{-- Interactive Grid Segment Selection --}}
@if(!empty($facilities))
@php
$facIdx = (int)($group['facility_index'] ?? 0);
$selectedFacility = $facilities[$facIdx] ?? $facilities[0] ?? null;
$gRows = $selectedFacility ? min(10, max(1, (int)($selectedFacility['grid_rows'] ?? 3))) : 3;
$gCols = $selectedFacility ? min(10, max(1, (int)($selectedFacility['grid_cols'] ?? 3))) : 3;
@endphp
<div class="mt-4 p-4 bg-green-50 border border-green-200 rounded-xl"
x-data="{ selectedSegments: @js($group['assigned_segments'] ?? []) }"
x-init="$watch('selectedSegments', value => $wire.set('groups.{{ $index }}.assigned_segments', value))">
<div class="flex items-center gap-2 mb-3">
<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7"/>
</svg>
<span class="text-sm font-semibold text-green-800">{{ __('اختر المساحة المخصصة') }}</span>
<span class="text-xs text-green-600 ms-2" x-show="selectedSegments.length > 0" x-text="'(' + selectedSegments.length + ' {{ __('خلية') }})'"></span>
</div>
<p class="text-xs text-green-600 mb-3">{{ __('اضغط على الخلايا لتحديد المساحة المخصصة لهذه المجموعة') }}</p>
<div class="grid gap-1" style="grid-template-columns: repeat({{ $gCols }}, 1fr)">
@for ($r = 1; $r <= $gRows; $r++)
@for ($c = 1; $c <= $gCols; $c++)
<button type="button"
@click="
let code = 'R{{ $r }}C{{ $c }}';
if (selectedSegments.includes(code)) {
selectedSegments = selectedSegments.filter(s => s !== code);
} else {
selectedSegments = [...selectedSegments, code];
}
"
:class="selectedSegments.includes('R{{ $r }}C{{ $c }}') ? 'bg-green-500 text-white border-green-600 shadow-sm' : 'bg-white border-gray-300 text-gray-600 hover:bg-green-100 hover:border-green-400'"
class="h-10 border rounded flex items-center justify-center text-xs font-mono transition-colors">
R{{ $r }}C{{ $c }}
</button>
@endfor
@endfor
</div>
</div>
@endif
</div>
@endforeach
</div>
@if(empty($groups))
<div class="text-center py-8 border-2 border-dashed border-gray-200 rounded-xl">
<svg class="w-12 h-12 text-gray-300 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"/>
</svg>
<p class="text-gray-400 text-sm">{{ __('لم تضف مجموعات بعد. يمكنك إضافتها لاحقاً أو إضافة واحدة الآن.') }}</p>
</div>
@endif
<button wire:click="addGroup" type="button"
class="mt-4 w-full border-2 border-dashed border-gray-300 rounded-xl p-4 text-gray-500 hover:border-blue-400 hover:text-blue-600 transition-colors flex items-center justify-center gap-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
{{ __('إضافة مجموعة') }}
</button>
@endif
</div>
@endif
{{-- Step 7: Participants (NEW) --}}
@if($currentStep === 7)
<div class="p-8">
<div class="mb-6">
<h2 class="text-xl font-bold text-gray-800">{{ __('المشتركين') }}</h2>
<p class="text-sm text-gray-500 mt-1">{{ __('أضف اللاعبين والمشتركين الحاليين. هذه الخطوة اختيارية — يمكنك إضافتهم لاحقاً من النظام.') }}</p>
</div>
<div class="space-y-4">
@foreach($participants as $index => $participant)
<div class="border border-gray-200 rounded-xl p-5 relative group/card hover:border-blue-200 transition-colors"
wire:key="participant-{{ $index }}">
<button wire:click="removeParticipant({{ $index }})" type="button"
class="absolute top-3 start-3 w-7 h-7 rounded-full bg-red-50 text-red-500 hover:bg-red-100 flex items-center justify-center opacity-0 group-hover/card:opacity-100 transition-opacity">
<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="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
{{-- Participant number badge --}}
<span class="absolute top-3 end-3 text-xs bg-gray-100 text-gray-600 font-mono px-2 py-0.5 rounded-full">
#{{ $index + 1 }}
</span>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<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="participants.{{ $index }}.name_ar"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
placeholder="{{ __('مثال: أحمد محمد') }}">
@error("participants.{$index}.name_ar")
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
@enderror
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
{{ __('الاسم بالإنجليزية') }}
</label>
<input type="text" wire:model="participants.{{ $index }}.name" dir="ltr"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
placeholder="Ahmed Mohamed">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
{{ __('الهاتف') }}
</label>
<input type="text" wire:model="participants.{{ $index }}.phone" dir="ltr"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
placeholder="01012345678">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
{{ __('تاريخ الميلاد') }}
</label>
<input type="date" wire:model="participants.{{ $index }}.date_of_birth" dir="ltr"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
{{ __('الجنس') }} <span class="text-red-500">*</span>
</label>
<div class="flex items-center gap-4 mt-2">
<label class="flex items-center gap-2 cursor-pointer">
<input type="radio" wire:model="participants.{{ $index }}.gender" value="male"
class="text-blue-600 focus:ring-blue-500">
<span class="text-sm text-gray-700">{{ __('ذكر') }}</span>
</label>
<label class="flex items-center gap-2 cursor-pointer">
<input type="radio" wire:model="participants.{{ $index }}.gender" value="female"
class="text-pink-600 focus:ring-pink-500">
<span class="text-sm text-gray-700">{{ __('أنثى') }}</span>
</label>
</div>
@error("participants.{$index}.gender")
<p class="mt-1 text-xs text-red-600">{{ $message }}</p>
@enderror
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
{{ __('النشاط') }}
</label>
<select wire:model="participants.{{ $index }}.activity_index"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
@foreach($activities as $aIdx => $act)
<option value="{{ $aIdx }}">{{ $act['name_ar'] ?: __('نشاط') . ' ' . ($aIdx + 1) }}</option>
@endforeach
</select>
</div>
</div>
{{-- Guardian info --}}
<div class="mt-4 pt-4 border-t border-gray-100">
<p class="text-xs font-medium text-gray-500 mb-3">{{ __('بيانات ولي الأمر') }}</p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
{{ __('اسم ولي الأمر') }}
</label>
<input type="text" wire:model="participants.{{ $index }}.guardian_name"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
placeholder="{{ __('اسم ولي الأمر') }}">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
{{ __('هاتف ولي الأمر') }}
</label>
<input type="text" wire:model="participants.{{ $index }}.guardian_phone" dir="ltr"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
placeholder="01012345678">
</div>
</div>
</div>
{{-- Optional group assignment --}}
@if(!empty($groups))
<div class="mt-4 pt-4 border-t border-gray-100">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
{{ __('المجموعة (اختياري)') }}
</label>
<select wire:model="participants.{{ $index }}.group_index"
class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
<option value="">{{ __('— بدون مجموعة —') }}</option>
@foreach($groups as $gIdx => $grp)
<option value="{{ $gIdx }}">{{ $grp['name_ar'] ?: __('مجموعة') . ' ' . ($gIdx + 1) }}</option>
@endforeach
</select>
</div>
</div>
@endif
</div>
@endforeach
</div>
@if(empty($participants))
<div class="text-center py-8 border-2 border-dashed border-gray-200 rounded-xl">
<svg class="w-12 h-12 text-gray-300 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" 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>
<p class="text-gray-400 text-sm">{{ __('لم تضف مشتركين بعد. يمكنك إضافتهم الآن أو لاحقاً من النظام.') }}</p>
</div>
@endif
<button wire:click="addParticipant" type="button"
class="mt-4 w-full border-2 border-dashed border-gray-300 rounded-xl p-4 text-gray-500 hover:border-blue-400 hover:text-blue-600 transition-colors flex items-center justify-center gap-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
{{ __('إضافة مشترك') }}
</button>
</div>
@endif
{{-- Step 8: Users --}}
@if($currentStep === 8)
<div class="p-8">
<div class="mb-6">
<h2 class="text-xl font-bold text-gray-800">{{ __('المستخدمين') }}</h2>
......@@ -592,8 +954,8 @@ class="mt-4 w-full border-2 border-dashed border-gray-300 rounded-xl p-4 text-gr
</div>
@endif
{{-- Step 7: Settings --}}
@if($currentStep === 7)
{{-- Step 9: Settings --}}
@if($currentStep === 9)
<div class="p-8">
<div class="mb-6">
<h2 class="text-xl font-bold text-gray-800">{{ __('الإعدادات العامة') }}</h2>
......@@ -669,8 +1031,8 @@ class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:r
</div>
@endif
{{-- Step 8: Done --}}
@if($currentStep === 8)
{{-- Step 10: Done --}}
@if($currentStep === 10)
<div class="p-8">
<div class="text-center mb-8">
<div class="w-20 h-20 bg-gradient-to-br from-green-100 to-emerald-100 rounded-full flex items-center justify-center mx-auto mb-4">
......@@ -706,6 +1068,14 @@ class="w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:r
<span class="text-sm font-medium text-gray-700">{{ __('المنشآت') }}</span>
<span class="text-sm text-blue-600 font-semibold">{{ count($facilities) }}</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<span class="text-sm font-medium text-gray-700">{{ __('المجموعات') }}</span>
<span class="text-sm text-blue-600 font-semibold">{{ count($groups) }}</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<span class="text-sm font-medium text-gray-700">{{ __('المشتركين') }}</span>
<span class="text-sm text-blue-600 font-semibold">{{ count($participants) }}</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<span class="text-sm font-medium text-gray-700">{{ __('المستخدمين الجدد') }}</span>
<span class="text-sm text-blue-600 font-semibold">{{ count($users) }}</span>
......
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