Commit 2054907b authored by Mahmoud Aglan's avatar Mahmoud Aglan

kokowawa

parent 280686c4
Subproject commit 280686c4f5a46880d5a32e240b58389d11a56a78
Subproject commit 280686c4f5a46880d5a32e240b58389d11a56a78
Subproject commit 280686c4f5a46880d5a32e240b58389d11a56a78
Subproject commit 280686c4f5a46880d5a32e240b58389d11a56a78
Subproject commit 280686c4f5a46880d5a32e240b58389d11a56a78
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
class AcademyContractController extends Controller
{
/**
* List contracts for an academy.
*/
public function index(Request $request, string $aid): Response
{
$db = App::getInstance()->db();
$academy = $db->selectOne("SELECT * FROM sa_academies WHERE id = ?", [(int) $aid]);
if (!$academy) {
return $this->redirect('/sa/academies')->withError('الأكاديمية غير موجودة');
}
$contracts = $db->select(
"SELECT * FROM sa_academy_contracts WHERE academy_id = ? ORDER BY created_at DESC",
[(int) $aid]
);
return $this->view('SportsActivity.Views.academies.contracts', [
'academy' => $academy,
'contracts' => $contracts,
]);
}
/**
* Show create contract form for an academy.
*/
public function create(Request $request, string $aid): Response
{
$db = App::getInstance()->db();
$academy = $db->selectOne("SELECT * FROM sa_academies WHERE id = ?", [(int) $aid]);
if (!$academy) {
return $this->redirect('/sa/academies')->withError('الأكاديمية غير موجودة');
}
return $this->view('SportsActivity.Views.academies.contract_form', [
'academy' => $academy,
'contract' => null,
]);
}
/**
* Validate and store a new contract.
*/
public function store(Request $request, string $aid): Response
{
$db = App::getInstance()->db();
$session = App::getInstance()->session();
$academy = $db->selectOne("SELECT * FROM sa_academies WHERE id = ?", [(int) $aid]);
if (!$academy) {
return $this->redirect('/sa/academies')->withError('الأكاديمية غير موجودة');
}
$contractNumber = trim((string) $request->post('contract_number', ''));
$contractType = trim((string) $request->post('contract_type', 'revenue_share'));
$startDate = trim((string) $request->post('start_date', ''));
$endDate = trim((string) $request->post('end_date', ''));
$clubCommissionPct = (float) $request->post('club_commission_pct', 0);
$academySharePct = (float) $request->post('academy_share_pct', 0);
$fixedMonthlyRent = (float) $request->post('fixed_monthly_rent', 0);
$depositAmount = (float) $request->post('deposit_amount', 0);
$notes = trim((string) $request->post('notes', ''));
// Validation
$errors = [];
if ($contractNumber === '') {
$errors[] = 'رقم العقد مطلوب';
}
if ($startDate === '') {
$errors[] = 'تاريخ البداية مطلوب';
}
if ($endDate === '') {
$errors[] = 'تاريخ النهاية مطلوب';
}
if ($startDate !== '' && $endDate !== '' && $startDate >= $endDate) {
$errors[] = 'تاريخ البداية يجب أن يكون قبل تاريخ النهاية';
}
// File upload validation
$file = $_FILES['contract_pdf'] ?? null;
if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
$errors[] = 'ملف العقد (PDF) مطلوب';
}
if (!empty($errors)) {
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/academies/' . $aid . '/contracts/create');
}
// Upload PDF
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$newName = uniqid('contract_') . '.' . $ext;
$uploadDir = dirname(__DIR__, 4) . '/public/uploads/sa_contracts/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$dest = $uploadDir . $newName;
move_uploaded_file($file['tmp_name'], $dest);
$pdfPath = 'uploads/sa_contracts/' . $newName;
$data = [
'academy_id' => (int) $aid,
'contract_number' => $contractNumber,
'contract_type' => $contractType,
'start_date' => $startDate,
'end_date' => $endDate,
'club_commission_pct' => $clubCommissionPct,
'academy_share_pct' => $academySharePct,
'fixed_monthly_rent' => $fixedMonthlyRent,
'deposit_amount' => $depositAmount,
'contract_pdf_path' => $pdfPath,
'status' => 'pending_approval',
'notes' => $notes ?: null,
'created_by' => (int) ($session->get('employee_id') ?? 0),
'created_at' => now(),
'updated_at' => now(),
];
$db->insert('sa_academy_contracts', $data);
$contractId = (int) $db->selectOne("SELECT LAST_INSERT_ID() as id")['id'];
return $this->redirect('/sa/academy-contracts/' . $contractId)->withSuccess('تم إضافة العقد بنجاح');
}
/**
* Show contract detail.
*/
public function show(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$contract = $db->selectOne(
"SELECT c.*, a.name_ar as academy_name, a.code as academy_code
FROM sa_academy_contracts c
LEFT JOIN sa_academies a ON a.id = c.academy_id
WHERE c.id = ?",
[(int) $id]
);
if (!$contract) {
return $this->redirect('/sa/academies')->withError('العقد غير موجود');
}
return $this->view('SportsActivity.Views.academies.contract_show', [
'contract' => $contract,
]);
}
/**
* Approve a contract — set status='active', record approved_by and approved_at.
*/
public function approve(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$session = App::getInstance()->session();
$contract = $db->selectOne("SELECT * FROM sa_academy_contracts WHERE id = ?", [(int) $id]);
if (!$contract) {
return $this->redirect('/sa/academies')->withError('العقد غير موجود');
}
if ($contract['status'] !== 'pending_approval') {
return $this->redirect('/sa/academy-contracts/' . $id)->withError('لا يمكن اعتماد هذا العقد - الحالة الحالية لا تسمح بذلك');
}
$db->update('sa_academy_contracts', [
'status' => 'active',
'approved_by' => (int) ($session->get('employee_id') ?? 0),
'approved_at' => now(),
'updated_at' => now(),
], 'id = ?', [(int) $id]);
return $this->redirect('/sa/academy-contracts/' . $id)->withSuccess('تم اعتماد العقد بنجاح');
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\Pagination;
class AcademyController extends Controller
{
/**
* List academies with search, filter by discipline, pagination.
*/
public function index(Request $request): Response
{
$db = App::getInstance()->db();
$search = trim((string) $request->get('q', ''));
$disciplineId = trim((string) $request->get('discipline_id', ''));
$page = max(1, (int) $request->get('page', 1));
$perPage = 25;
$where = ['1=1'];
$params = [];
if ($search !== '') {
$where[] = '(a.name_ar LIKE ? OR a.name_en LIKE ? OR a.code LIKE ?)';
$params[] = "%{$search}%";
$params[] = "%{$search}%";
$params[] = "%{$search}%";
}
if ($disciplineId !== '') {
$where[] = 'a.discipline_id = ?';
$params[] = (int) $disciplineId;
}
$whereSql = implode(' AND ', $where);
// Count total
$countSql = "SELECT COUNT(*) as total FROM sa_academies a WHERE {$whereSql}";
$total = (int) $db->selectOne($countSql, $params)['total'];
$pagination = Pagination::paginate($total, $perPage, $page);
$offset = ($pagination['current_page'] - 1) * $perPage;
// Fetch academies with discipline name
$sql = "SELECT a.*, d.name_ar as discipline_name
FROM sa_academies a
LEFT JOIN sa_disciplines d ON d.id = a.discipline_id
WHERE {$whereSql}
ORDER BY a.created_at DESC
LIMIT {$perPage} OFFSET {$offset}";
$academies = $db->select($sql, $params);
// Count contracts per academy
foreach ($academies as &$academy) {
$contractCount = $db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_academy_contracts WHERE academy_id = ?",
[(int) $academy['id']]
);
$academy['contract_count'] = (int) ($contractCount['cnt'] ?? 0);
}
unset($academy);
// Get disciplines for filter
$disciplines = $db->select("SELECT id, name_ar FROM sa_disciplines WHERE is_active = 1 ORDER BY name_ar");
return $this->view('SportsActivity.Views.academies.index', [
'academies' => $academies,
'pagination' => $pagination,
'filters' => [
'q' => $search,
'discipline_id' => $disciplineId,
],
'disciplines' => $disciplines,
]);
}
/**
* Show the create academy form.
*/
public function create(Request $request): Response
{
$db = App::getInstance()->db();
$disciplines = $db->select("SELECT id, name_ar FROM sa_disciplines WHERE is_active = 1 ORDER BY name_ar");
return $this->view('SportsActivity.Views.academies.create', [
'disciplines' => $disciplines,
]);
}
/**
* Validate and store a new academy.
*/
public function store(Request $request): Response
{
$db = App::getInstance()->db();
$session = App::getInstance()->session();
$code = trim((string) $request->post('code', ''));
$nameAr = trim((string) $request->post('name_ar', ''));
$nameEn = trim((string) $request->post('name_en', ''));
$disciplineId = (int) $request->post('discipline_id', 0);
$academyType = trim((string) $request->post('academy_type', ''));
$contactPerson = trim((string) $request->post('contact_person', ''));
$contactPhone = trim((string) $request->post('contact_phone', ''));
$contactEmail = trim((string) $request->post('contact_email', ''));
$descriptionAr = trim((string) $request->post('description_ar', ''));
// Validation
$errors = [];
if ($code === '') {
$errors[] = 'كود الأكاديمية مطلوب';
}
if ($nameAr === '') {
$errors[] = 'الاسم بالعربي مطلوب';
}
if ($disciplineId <= 0) {
$errors[] = 'اللعبة / النشاط الرياضي مطلوب';
}
$validTypes = ['internal', 'external', 'partnership'];
if ($academyType !== '' && !in_array($academyType, $validTypes, true)) {
$errors[] = 'نوع الأكاديمية غير صالح';
}
// Check unique code
if ($code !== '') {
$existing = $db->selectOne("SELECT id FROM sa_academies WHERE code = ?", [$code]);
if ($existing) {
$errors[] = 'كود الأكاديمية مستخدم بالفعل';
}
}
if (!empty($errors)) {
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/academies/create');
}
$data = [
'code' => strtoupper($code),
'name_ar' => $nameAr,
'name_en' => $nameEn ?: null,
'discipline_id' => $disciplineId,
'academy_type' => $academyType ?: 'internal',
'contact_person' => $contactPerson ?: null,
'contact_phone' => $contactPhone ?: null,
'contact_email' => $contactEmail ?: null,
'description_ar' => $descriptionAr ?: null,
'is_active' => 1,
'created_at' => now(),
'updated_at' => now(),
];
$db->insert('sa_academies', $data);
$academyId = (int) $db->selectOne("SELECT LAST_INSERT_ID() as id")['id'];
return $this->redirect('/sa/academies/' . $academyId)->withSuccess('تم إضافة الأكاديمية بنجاح');
}
/**
* Show academy detail with contracts list.
*/
public function show(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$academy = $db->selectOne("SELECT a.*, d.name_ar as discipline_name
FROM sa_academies a
LEFT JOIN sa_disciplines d ON d.id = a.discipline_id
WHERE a.id = ?", [(int) $id]);
if (!$academy) {
return $this->redirect('/sa/academies')->withError('الأكاديمية غير موجودة');
}
$contracts = $db->select(
"SELECT * FROM sa_academy_contracts WHERE academy_id = ? ORDER BY created_at DESC",
[(int) $id]
);
return $this->view('SportsActivity.Views.academies.show', [
'academy' => $academy,
'contracts' => $contracts,
]);
}
/**
* Show edit form for an academy.
*/
public function edit(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$academy = $db->selectOne("SELECT * FROM sa_academies WHERE id = ?", [(int) $id]);
if (!$academy) {
return $this->redirect('/sa/academies')->withError('الأكاديمية غير موجودة');
}
$disciplines = $db->select("SELECT id, name_ar FROM sa_disciplines WHERE is_active = 1 ORDER BY name_ar");
return $this->view('SportsActivity.Views.academies.edit', [
'academy' => $academy,
'disciplines' => $disciplines,
]);
}
/**
* Validate and update an academy.
*/
public function update(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$session = App::getInstance()->session();
$academy = $db->selectOne("SELECT * FROM sa_academies WHERE id = ?", [(int) $id]);
if (!$academy) {
return $this->redirect('/sa/academies')->withError('الأكاديمية غير موجودة');
}
$code = trim((string) $request->post('code', ''));
$nameAr = trim((string) $request->post('name_ar', ''));
$nameEn = trim((string) $request->post('name_en', ''));
$disciplineId = (int) $request->post('discipline_id', 0);
$academyType = trim((string) $request->post('academy_type', ''));
$contactPerson = trim((string) $request->post('contact_person', ''));
$contactPhone = trim((string) $request->post('contact_phone', ''));
$contactEmail = trim((string) $request->post('contact_email', ''));
$descriptionAr = trim((string) $request->post('description_ar', ''));
// Validation
$errors = [];
if ($code === '') {
$errors[] = 'كود الأكاديمية مطلوب';
}
if ($nameAr === '') {
$errors[] = 'الاسم بالعربي مطلوب';
}
if ($disciplineId <= 0) {
$errors[] = 'اللعبة / النشاط الرياضي مطلوب';
}
// Check unique code (exclude current)
if ($code !== '') {
$existing = $db->selectOne("SELECT id FROM sa_academies WHERE code = ? AND id != ?", [$code, (int) $id]);
if ($existing) {
$errors[] = 'كود الأكاديمية مستخدم بالفعل';
}
}
if (!empty($errors)) {
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/academies/' . $id . '/edit');
}
$data = [
'code' => strtoupper($code),
'name_ar' => $nameAr,
'name_en' => $nameEn ?: null,
'discipline_id' => $disciplineId,
'academy_type' => $academyType ?: 'internal',
'contact_person' => $contactPerson ?: null,
'contact_phone' => $contactPhone ?: null,
'contact_email' => $contactEmail ?: null,
'description_ar' => $descriptionAr ?: null,
'updated_at' => now(),
];
$db->update('sa_academies', $data, 'id = ?', [(int) $id]);
return $this->redirect('/sa/academies/' . $id)->withSuccess('تم تحديث الأكاديمية بنجاح');
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers\Api;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Modules\SportsActivity\Services\PricingCalculatorService;
class BookingApiController extends Controller
{
public function pricePreview(Request $request): Response
{
$unitId = (int) $request->get('unit_id', 0);
$date = $request->get('date', '');
$startTime = $request->get('start_time', '');
$endTime = $request->get('end_time', '');
$participants = (int) $request->get('participants', 1);
$isMember = (bool) $request->get('is_member', 0);
if (!$unitId || !$date || !$startTime || !$endTime) {
return $this->json(['error' => 'معاملات ناقصة'], 400);
}
$pricing = PricingCalculatorService::calculate($unitId, $date, $startTime, $endTime, $participants, $isMember);
if (!$pricing) {
return $this->json(['error' => 'لا توجد قاعدة تسعير لهذه المعاملات', 'pricing' => null]);
}
return $this->json(['pricing' => $pricing]);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers\Api;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Modules\SportsActivity\Services\MirrorStateService;
class MirrorApiController extends Controller
{
public function state(Request $request, string $id): Response
{
$date = $request->get('date', date('Y-m-d'));
$state = MirrorStateService::getFacilityState((int) $id, $date);
if (isset($state['error'])) {
return $this->json(['error' => $state['error']], 404);
}
return $this->json($state);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers\Api;
use App\Core\App;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
class PlayerSearchApiController extends Controller
{
public function search(Request $request): Response
{
$q = trim((string) $request->get('q', ''));
if (strlen($q) < 2) {
return $this->json(['results' => []]);
}
$db = App::getInstance()->db();
$like = '%' . $q . '%';
$results = $db->select(
"SELECT id, full_name_ar, registration_serial, national_id, player_type, medical_status
FROM sa_players
WHERE is_archived = 0
AND (full_name_ar LIKE ? OR registration_serial LIKE ? OR national_id LIKE ? OR phone LIKE ?)
ORDER BY full_name_ar
LIMIT 20",
[$like, $like, $like, $like]
);
return $this->json(['results' => $results]);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers\Api;
use App\Core\App;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Modules\SportsActivity\Services\SlotAvailabilityService;
use App\Modules\SportsActivity\Services\ConflictDetectionService;
class ScheduleApiController extends Controller
{
public function availability(Request $request): Response
{
$unitId = (int) $request->get('unit_id', 0);
$date = $request->get('date', '');
$startTime = $request->get('start_time', '');
$endTime = $request->get('end_time', '');
$spots = (int) $request->get('spots', 1);
if (!$unitId || !$date || !$startTime || !$endTime) {
return $this->json(['error' => 'معاملات ناقصة'], 400);
}
$result = SlotAvailabilityService::check($unitId, $date, $startTime, $endTime, $spots);
return $this->json($result);
}
public function conflicts(Request $request): Response
{
$unitId = (int) $request->get('unit_id', 0);
$date = $request->get('date', '');
$startTime = $request->get('start_time', '');
$endTime = $request->get('end_time', '');
$coachId = $request->get('coach_id') ? (int) $request->get('coach_id') : null;
if (!$unitId || !$date || !$startTime || !$endTime) {
return $this->json(['error' => 'معاملات ناقصة'], 400);
}
$conflicts = ConflictDetectionService::check($unitId, $date, $startTime, $endTime, $coachId);
return $this->json(['conflicts' => $conflicts, 'has_blocking' => !empty(array_filter($conflicts, fn($c) => $c['severity'] === 'blocking'))]);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\Pagination;
class AttendanceController extends Controller
{
/**
* List today's training bookings for attendance recording.
*/
public function index(Request $request): Response
{
$db = App::getInstance()->db();
$today = date('Y-m-d');
$bookings = $db->select(
"SELECT b.*, fu.name_ar as unit_name, f.name_ar as facility_name,
g.name_ar as group_name, c.full_name_ar as coach_name
FROM sa_bookings b
JOIN sa_facility_units fu ON fu.id = b.facility_unit_id
JOIN sa_facilities f ON f.id = fu.facility_id
LEFT JOIN sa_groups g ON g.id = b.group_id
LEFT JOIN sa_coaches c ON c.id = b.coach_id
WHERE b.booking_type = 'training'
AND b.booking_date = ?
AND b.status NOT IN ('cancelled', 'no_show')
ORDER BY b.start_time ASC",
[$today]
);
return $this->view('SportsActivity.Views.attendance.index', [
'bookings' => $bookings,
'today' => $today,
]);
}
/**
* Show attendance form for a specific booking.
*/
public function record(Request $request, string $bookingId): Response
{
$db = App::getInstance()->db();
$booking = $db->selectOne(
"SELECT b.*, fu.name_ar as unit_name, f.name_ar as facility_name,
g.name_ar as group_name, g.id as group_id, c.full_name_ar as coach_name
FROM sa_bookings b
JOIN sa_facility_units fu ON fu.id = b.facility_unit_id
JOIN sa_facilities f ON f.id = fu.facility_id
LEFT JOIN sa_groups g ON g.id = b.group_id
LEFT JOIN sa_coaches c ON c.id = b.coach_id
WHERE b.id = ?",
[(int) $bookingId]
);
if (!$booking) {
return $this->redirect('/sa/attendance')->withError('الحجز غير موجود');
}
$players = [];
if ($booking['group_id']) {
$players = $db->select(
"SELECT gp.player_id, p.full_name_ar as player_name, p.code as player_code
FROM sa_group_players gp
JOIN sa_players p ON p.id = gp.player_id
WHERE gp.group_id = ? AND gp.status = 'active'
ORDER BY p.full_name_ar ASC",
[(int) $booking['group_id']]
);
}
// Get existing attendance records for this booking
$existingAttendance = $db->select(
"SELECT * FROM sa_attendance WHERE booking_id = ?",
[(int) $bookingId]
);
$attendanceMap = [];
foreach ($existingAttendance as $att) {
$attendanceMap[(int) $att['player_id']] = $att['status'];
}
return $this->view('SportsActivity.Views.attendance.record', [
'booking' => $booking,
'players' => $players,
'attendanceMap' => $attendanceMap,
]);
}
/**
* Store attendance records for a booking.
*/
public function store(Request $request, string $bookingId): Response
{
$db = App::getInstance()->db();
$booking = $db->selectOne(
"SELECT * FROM sa_bookings WHERE id = ?",
[(int) $bookingId]
);
if (!$booking) {
return $this->redirect('/sa/attendance')->withError('الحجز غير موجود');
}
$playerIds = $request->post('player_ids', []);
$statuses = $request->post('statuses', []);
if (!is_array($playerIds) || !is_array($statuses)) {
return $this->redirect('/sa/attendance/record/' . $bookingId)->withError('بيانات غير صالحة');
}
$session = App::getInstance()->session();
$employeeId = (int) ($session->get('employee_id') ?? 0);
$now = date('Y-m-d H:i:s');
// Delete existing records for this booking then re-insert
$db->delete('sa_attendance', 'booking_id = ?', [(int) $bookingId]);
$recorded = 0;
foreach ($playerIds as $index => $playerId) {
$status = $statuses[$index] ?? 'absent';
if (!in_array($status, ['present', 'absent', 'late', 'excused'], true)) {
$status = 'absent';
}
$db->insert('sa_attendance', [
'booking_id' => (int) $bookingId,
'player_id' => (int) $playerId,
'status' => $status,
'recorded_by' => $employeeId,
'recorded_at' => $now,
'created_at' => $now,
'updated_at' => $now,
]);
$recorded++;
}
return $this->redirect('/sa/attendance')->withSuccess(
sprintf('تم تسجيل حضور %d لاعب بنجاح', $recorded)
);
}
/**
* Attendance report with filters.
*/
public function report(Request $request): Response
{
$db = App::getInstance()->db();
$filters = [
'group_id' => trim((string) $request->get('group_id', '')),
'player_id' => trim((string) $request->get('player_id', '')),
'date_from' => trim((string) $request->get('date_from', '')),
'date_to' => trim((string) $request->get('date_to', '')),
];
$where = [];
$params = [];
$records = [];
$summary = null;
$hasFilter = $filters['group_id'] !== '' || $filters['player_id'] !== ''
|| $filters['date_from'] !== '' || $filters['date_to'] !== '';
if ($hasFilter) {
if ($filters['group_id'] !== '') {
$where[] = "b.group_id = ?";
$params[] = (int) $filters['group_id'];
}
if ($filters['player_id'] !== '') {
$where[] = "a.player_id = ?";
$params[] = (int) $filters['player_id'];
}
if ($filters['date_from'] !== '') {
$where[] = "b.booking_date >= ?";
$params[] = $filters['date_from'];
}
if ($filters['date_to'] !== '') {
$where[] = "b.booking_date <= ?";
$params[] = $filters['date_to'];
}
$whereClause = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
$records = $db->select(
"SELECT a.*, p.full_name_ar as player_name, g.name_ar as group_name,
b.booking_date, b.start_time, b.end_time
FROM sa_attendance a
JOIN sa_bookings b ON b.id = a.booking_id
JOIN sa_players p ON p.id = a.player_id
LEFT JOIN sa_groups g ON g.id = b.group_id
{$whereClause}
ORDER BY b.booking_date DESC, b.start_time DESC
LIMIT 200",
$params
);
// Summary counts
$summaryRow = $db->selectOne(
"SELECT
COUNT(*) as total,
SUM(CASE WHEN a.status = 'present' THEN 1 ELSE 0 END) as present_count,
SUM(CASE WHEN a.status = 'absent' THEN 1 ELSE 0 END) as absent_count,
SUM(CASE WHEN a.status = 'late' THEN 1 ELSE 0 END) as late_count,
SUM(CASE WHEN a.status = 'excused' THEN 1 ELSE 0 END) as excused_count
FROM sa_attendance a
JOIN sa_bookings b ON b.id = a.booking_id
{$whereClause}",
$params
);
$summary = $summaryRow;
}
$groups = $db->select(
"SELECT id, name_ar FROM sa_groups WHERE status = 'active' AND is_archived = 0 ORDER BY name_ar",
[]
);
return $this->view('SportsActivity.Views.attendance.report', [
'records' => $records,
'summary' => $summary,
'filters' => $filters,
'groups' => $groups,
]);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\Pagination;
use App\Modules\SportsActivity\Models\Booking;
use App\Modules\SportsActivity\Services\BookingService;
class BookingController extends Controller
{
/**
* List bookings with filters (date range, facility, type, status), pagination.
*/
public function index(Request $request): Response
{
$filters = [
'date_from' => trim((string) $request->get('date_from', '')),
'date_to' => trim((string) $request->get('date_to', '')),
'facility_id' => trim((string) $request->get('facility_id', '')),
'booking_type' => trim((string) $request->get('booking_type', '')),
'status' => trim((string) $request->get('status', '')),
];
$page = max(1, (int) $request->get('page', 1));
$perPage = 25;
$db = App::getInstance()->db();
$where = "1=1";
$params = [];
if ($filters['date_from'] !== '') {
$where .= " AND b.booking_date >= ?";
$params[] = $filters['date_from'];
}
if ($filters['date_to'] !== '') {
$where .= " AND b.booking_date <= ?";
$params[] = $filters['date_to'];
}
if ($filters['facility_id'] !== '') {
$where .= " AND fu.facility_id = ?";
$params[] = (int) $filters['facility_id'];
}
if ($filters['booking_type'] !== '') {
$where .= " AND b.booking_type = ?";
$params[] = $filters['booking_type'];
}
if ($filters['status'] !== '') {
$where .= " AND b.status = ?";
$params[] = $filters['status'];
}
$total = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt
FROM sa_bookings b
LEFT JOIN sa_facility_units fu ON fu.id = b.facility_unit_id
WHERE {$where}",
$params
)['cnt'] ?? 0);
$pagination = Pagination::paginate($total, $perPage, $page);
$offset = ($page - 1) * $perPage;
$bookings = $db->select(
"SELECT b.*, fu.name_ar as unit_name, f.name_ar as facility_name
FROM sa_bookings b
LEFT JOIN sa_facility_units fu ON fu.id = b.facility_unit_id
LEFT JOIN sa_facilities f ON f.id = fu.facility_id
WHERE {$where}
ORDER BY b.booking_date DESC, b.start_time ASC
LIMIT {$perPage} OFFSET {$offset}",
$params
);
$facilities = $db->select("SELECT id, name_ar FROM sa_facilities WHERE is_archived = 0 ORDER BY name_ar", []);
return $this->view('SportsActivity.Views.bookings.index', [
'bookings' => $bookings,
'pagination' => $pagination,
'filters' => $filters,
'facilities' => $facilities,
'bookingTypes' => Booking::getBookingTypeOptions(),
'statuses' => Booking::getStatusOptions(),
]);
}
/**
* Show form for creating an hourly booking.
*/
public function create(Request $request): Response
{
$db = App::getInstance()->db();
$facilityUnits = $db->select(
"SELECT fu.id, fu.name_ar, fu.code, f.name_ar as facility_name, f.id as facility_id
FROM sa_facility_units fu
JOIN sa_facilities f ON f.id = fu.facility_id
WHERE fu.is_archived = 0 AND fu.is_active = 1
ORDER BY f.name_ar ASC, fu.name_ar ASC",
[]
);
// Group units by facility
$grouped = [];
foreach ($facilityUnits as $unit) {
$grouped[$unit['facility_name']][] = $unit;
}
return $this->view('SportsActivity.Views.bookings.create', [
'facilityUnitsGrouped' => $grouped,
]);
}
/**
* Store a new hourly booking using BookingService.
*/
public function store(Request $request): Response
{
$data = [
'facility_unit_id' => (int) $request->post('facility_unit_id', 0),
'booking_date' => trim((string) $request->post('booking_date', '')),
'start_time' => trim((string) $request->post('start_time', '')),
'end_time' => trim((string) $request->post('end_time', '')),
'participant_count' => (int) $request->post('participant_count', 1),
'booker_type' => trim((string) $request->post('booker_type', 'guest')),
'booker_id' => $request->post('booker_id', '') !== '' ? (int) $request->post('booker_id', 0) : null,
'booker_name' => trim((string) $request->post('booker_name', '')),
'notes' => trim((string) $request->post('notes', '')) ?: null,
];
// Basic validation
$errors = [];
if ($data['facility_unit_id'] === 0) {
$errors[] = 'يرجى اختيار الوحدة/الملعب';
}
if ($data['booking_date'] === '') {
$errors[] = 'تاريخ الحجز مطلوب';
}
if ($data['start_time'] === '') {
$errors[] = 'وقت البداية مطلوب';
}
if ($data['end_time'] === '') {
$errors[] = 'وقت النهاية مطلوب';
}
if ($data['booker_name'] === '') {
$errors[] = 'اسم الحاجز مطلوب';
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/bookings/create');
}
$result = BookingService::createHourlyBooking($data);
if ($result['success']) {
return $this->redirect('/sa/bookings/' . $result['booking_id'])->withSuccess('تم إنشاء الحجز بنجاح — رقم: ' . $result['booking_number']);
}
$session = App::getInstance()->session();
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/bookings/create')->withError($result['error']);
}
/**
* Show booking detail with participants.
*/
public function show(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$booking = $db->selectOne(
"SELECT b.*, fu.name_ar as unit_name, fu.code as unit_code,
f.name_ar as facility_name, g.name_ar as group_name,
c.name_ar as coach_name
FROM sa_bookings b
LEFT JOIN sa_facility_units fu ON fu.id = b.facility_unit_id
LEFT JOIN sa_facilities f ON f.id = fu.facility_id
LEFT JOIN sa_groups g ON g.id = b.group_id
LEFT JOIN sa_coaches c ON c.id = b.coach_id
WHERE b.id = ?",
[(int) $id]
);
if (!$booking) {
return $this->redirect('/sa/bookings')->withError('الحجز غير موجود');
}
$participants = $db->select(
"SELECT bp.*, p.name_ar as player_name, p.code as player_code
FROM sa_booking_participants bp
LEFT JOIN sa_players p ON p.id = bp.player_id
WHERE bp.booking_id = ?
ORDER BY bp.id ASC",
[(int) $id]
);
return $this->view('SportsActivity.Views.bookings.show', [
'booking' => $booking,
'participants' => $participants,
'statuses' => Booking::getStatusOptions(),
'bookingTypes' => Booking::getBookingTypeOptions(),
]);
}
/**
* Cancel a booking.
*/
public function cancel(Request $request, string $id): Response
{
$reason = trim((string) $request->post('reason', ''));
$result = BookingService::cancel((int) $id, $reason);
if ($result['success']) {
return $this->redirect('/sa/bookings/' . $id)->withSuccess('تم إلغاء الحجز');
}
return $this->redirect('/sa/bookings/' . $id)->withError($result['error']);
}
/**
* Check in a booking.
*/
public function checkin(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$booking = $db->selectOne("SELECT * FROM sa_bookings WHERE id = ?", [(int) $id]);
if (!$booking) {
return $this->redirect('/sa/bookings')->withError('الحجز غير موجود');
}
if ($booking['status'] !== 'confirmed') {
return $this->redirect('/sa/bookings/' . $id)->withError('لا يمكن تسجيل الحضور لهذا الحجز');
}
$db->update('sa_bookings', [
'status' => 'checked_in',
'updated_at' => date('Y-m-d H:i:s'),
], 'id = ?', [(int) $id]);
return $this->redirect('/sa/bookings/' . $id)->withSuccess('تم تسجيل الحضور');
}
/**
* Check out (complete) a booking.
*/
public function checkout(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$booking = $db->selectOne("SELECT * FROM sa_bookings WHERE id = ?", [(int) $id]);
if (!$booking) {
return $this->redirect('/sa/bookings')->withError('الحجز غير موجود');
}
if (!in_array($booking['status'], ['confirmed', 'checked_in'])) {
return $this->redirect('/sa/bookings/' . $id)->withError('لا يمكن إتمام هذا الحجز');
}
$db->update('sa_bookings', [
'status' => 'completed',
'updated_at' => date('Y-m-d H:i:s'),
], 'id = ?', [(int) $id]);
return $this->redirect('/sa/bookings/' . $id)->withSuccess('تم إتمام الحجز');
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\Pagination;
use App\Modules\SportsActivity\Models\Coach;
use App\Modules\SportsActivity\Models\Discipline;
class CoachController extends Controller
{
public function index(Request $request): Response
{
$db = App::getInstance()->db();
$search = trim((string) $request->get('q', ''));
$page = max(1, (int) $request->get('page', 1));
$perPage = 25;
$where = ['c.is_archived = 0'];
$params = [];
if ($search !== '') {
$where[] = '(c.full_name_ar LIKE ? OR c.full_name_en LIKE ? OR c.national_id LIKE ? OR c.code LIKE ?)';
$params[] = "%{$search}%";
$params[] = "%{$search}%";
$params[] = "%{$search}%";
$params[] = "%{$search}%";
}
$whereSql = implode(' AND ', $where);
$total = (int) $db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_coaches c WHERE {$whereSql}",
$params
)['cnt'];
$pagination = Pagination::paginate($total, $perPage, $page);
$offset = ($page - 1) * $perPage;
$coaches = $db->select(
"SELECT c.*,
GROUP_CONCAT(d.name_ar SEPARATOR '، ') as discipline_names
FROM sa_coaches c
LEFT JOIN sa_coach_disciplines cd ON cd.coach_id = c.id
LEFT JOIN sa_disciplines d ON d.id = cd.discipline_id
WHERE {$whereSql}
GROUP BY c.id
ORDER BY c.created_at DESC
LIMIT {$perPage} OFFSET {$offset}",
$params
);
return $this->view('SportsActivity.Views.coaches.index', [
'coaches' => $coaches,
'pagination' => $pagination,
'search' => $search,
]);
}
public function create(Request $request): Response
{
$disciplines = Discipline::getActive();
$employmentTypes = Coach::getEmploymentTypeOptions();
$paymentModels = Coach::getPaymentModelOptions();
return $this->view('SportsActivity.Views.coaches.create', [
'disciplines' => $disciplines,
'employmentTypes' => $employmentTypes,
'paymentModels' => $paymentModels,
]);
}
public function store(Request $request): Response
{
$db = App::getInstance()->db();
$code = trim((string) $request->post('code', ''));
$fullNameAr = trim((string) $request->post('full_name_ar', ''));
$fullNameEn = trim((string) $request->post('full_name_en', ''));
$nationalId = trim((string) $request->post('national_id', ''));
$phone = trim((string) $request->post('phone', ''));
$email = trim((string) $request->post('email', ''));
$dateOfBirth = trim((string) $request->post('date_of_birth', ''));
$gender = trim((string) $request->post('gender', ''));
$employmentType = trim((string) $request->post('employment_type', ''));
$paymentModel = trim((string) $request->post('payment_model', ''));
$hourlyRate = trim((string) $request->post('hourly_rate', ''));
$sessionRate = trim((string) $request->post('session_rate', ''));
$monthlyRate = trim((string) $request->post('monthly_rate', ''));
$maxGroups = trim((string) $request->post('max_groups', ''));
$bioAr = trim((string) $request->post('bio_ar', ''));
// Validation
$errors = [];
if ($code === '') {
$errors[] = 'كود المدرب مطلوب';
}
if ($fullNameAr === '') {
$errors[] = 'الاسم بالعربي مطلوب';
}
if ($code !== '' && Coach::findByCode($code) !== null) {
$errors[] = 'كود المدرب مستخدم بالفعل';
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$alerts = array_map(fn($msg) => ['type' => 'error', 'message' => $msg], $errors);
$session->flash('_alerts', $alerts);
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/coaches/create');
}
$coach = Coach::create([
'code' => $code,
'full_name_ar' => $fullNameAr,
'full_name_en' => $fullNameEn ?: null,
'national_id' => $nationalId ?: null,
'phone' => $phone ?: null,
'email' => $email ?: null,
'date_of_birth' => $dateOfBirth ?: null,
'gender' => $gender ?: null,
'employment_type' => $employmentType ?: null,
'payment_model' => $paymentModel ?: null,
'hourly_rate' => $hourlyRate !== '' ? (float) $hourlyRate : null,
'session_rate' => $sessionRate !== '' ? (float) $sessionRate : null,
'monthly_rate' => $monthlyRate !== '' ? (float) $monthlyRate : null,
'max_groups' => $maxGroups !== '' ? (int) $maxGroups : null,
'bio_ar' => $bioAr ?: null,
'is_active' => 1,
'branch_id' => App::getInstance()->session()->get('branch_id', 1),
]);
// Sync disciplines
$this->syncDisciplines($db, (int) $coach->id, $request);
return $this->redirect('/sa/coaches')->withSuccess('تم إضافة المدرب بنجاح');
}
public function show(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$coach = $db->selectOne(
"SELECT * FROM sa_coaches WHERE id = ? AND is_archived = 0",
[(int) $id]
);
if (!$coach) {
return $this->redirect('/sa/coaches')->withError('المدرب غير موجود');
}
$disciplines = $db->select(
"SELECT cd.*, d.name_ar as discipline_name, d.code as discipline_code
FROM sa_coach_disciplines cd
JOIN sa_disciplines d ON d.id = cd.discipline_id
WHERE cd.coach_id = ?
ORDER BY cd.specialization_level = 'primary' DESC",
[(int) $id]
);
$employmentTypes = Coach::getEmploymentTypeOptions();
$paymentModels = Coach::getPaymentModelOptions();
return $this->view('SportsActivity.Views.coaches.show', [
'coach' => $coach,
'disciplines' => $disciplines,
'employmentTypes' => $employmentTypes,
'paymentModels' => $paymentModels,
]);
}
public function edit(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$coach = $db->selectOne(
"SELECT * FROM sa_coaches WHERE id = ? AND is_archived = 0",
[(int) $id]
);
if (!$coach) {
return $this->redirect('/sa/coaches')->withError('المدرب غير موجود');
}
$coachDisciplines = $db->select(
"SELECT cd.discipline_id, cd.specialization_level
FROM sa_coach_disciplines cd
WHERE cd.coach_id = ?",
[(int) $id]
);
$disciplines = Discipline::getActive();
$employmentTypes = Coach::getEmploymentTypeOptions();
$paymentModels = Coach::getPaymentModelOptions();
return $this->view('SportsActivity.Views.coaches.edit', [
'coach' => $coach,
'coachDisciplines' => $coachDisciplines,
'disciplines' => $disciplines,
'employmentTypes' => $employmentTypes,
'paymentModels' => $paymentModels,
]);
}
public function update(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$coach = $db->selectOne(
"SELECT * FROM sa_coaches WHERE id = ? AND is_archived = 0",
[(int) $id]
);
if (!$coach) {
return $this->redirect('/sa/coaches')->withError('المدرب غير موجود');
}
$code = trim((string) $request->post('code', ''));
$fullNameAr = trim((string) $request->post('full_name_ar', ''));
$fullNameEn = trim((string) $request->post('full_name_en', ''));
$nationalId = trim((string) $request->post('national_id', ''));
$phone = trim((string) $request->post('phone', ''));
$email = trim((string) $request->post('email', ''));
$dateOfBirth = trim((string) $request->post('date_of_birth', ''));
$gender = trim((string) $request->post('gender', ''));
$employmentType = trim((string) $request->post('employment_type', ''));
$paymentModel = trim((string) $request->post('payment_model', ''));
$hourlyRate = trim((string) $request->post('hourly_rate', ''));
$sessionRate = trim((string) $request->post('session_rate', ''));
$monthlyRate = trim((string) $request->post('monthly_rate', ''));
$maxGroups = trim((string) $request->post('max_groups', ''));
$bioAr = trim((string) $request->post('bio_ar', ''));
// Validation
$errors = [];
if ($code === '') {
$errors[] = 'كود المدرب مطلوب';
}
if ($fullNameAr === '') {
$errors[] = 'الاسم بالعربي مطلوب';
}
if ($code !== '' && $code !== $coach['code']) {
$existing = Coach::findByCode($code);
if ($existing !== null && (int) $existing->id !== (int) $id) {
$errors[] = 'كود المدرب مستخدم بالفعل';
}
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$alerts = array_map(fn($msg) => ['type' => 'error', 'message' => $msg], $errors);
$session->flash('_alerts', $alerts);
$session->flash('_old_input', $request->all());
return $this->redirect("/sa/coaches/{$id}/edit");
}
$db->update('sa_coaches', [
'code' => $code,
'full_name_ar' => $fullNameAr,
'full_name_en' => $fullNameEn ?: null,
'national_id' => $nationalId ?: null,
'phone' => $phone ?: null,
'email' => $email ?: null,
'date_of_birth' => $dateOfBirth ?: null,
'gender' => $gender ?: null,
'employment_type' => $employmentType ?: null,
'payment_model' => $paymentModel ?: null,
'hourly_rate' => $hourlyRate !== '' ? (float) $hourlyRate : null,
'session_rate' => $sessionRate !== '' ? (float) $sessionRate : null,
'monthly_rate' => $monthlyRate !== '' ? (float) $monthlyRate : null,
'max_groups' => $maxGroups !== '' ? (int) $maxGroups : null,
'bio_ar' => $bioAr ?: null,
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [(int) $id]);
// Sync disciplines
$this->syncDisciplines($db, (int) $id, $request);
return $this->redirect('/sa/coaches')->withSuccess('تم تحديث بيانات المدرب بنجاح');
}
public function toggle(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$coach = $db->selectOne(
"SELECT * FROM sa_coaches WHERE id = ? AND is_archived = 0",
[(int) $id]
);
if (!$coach) {
return $this->redirect('/sa/coaches')->withError('المدرب غير موجود');
}
$newStatus = (int) $coach['is_active'] === 1 ? 0 : 1;
$db->update('sa_coaches', [
'is_active' => $newStatus,
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [(int) $id]);
$msg = $newStatus === 1 ? 'تم تفعيل المدرب' : 'تم تعطيل المدرب';
return $this->redirect('/sa/coaches')->withSuccess($msg);
}
private function syncDisciplines(\App\Core\Database $db, int $coachId, Request $request): void
{
// Delete existing
$db->delete('sa_coach_disciplines', '`coach_id` = ?', [$coachId]);
// Insert new
$disciplineIds = $request->post('discipline_ids', []);
$levels = $request->post('levels', []);
if (!is_array($disciplineIds)) {
return;
}
foreach ($disciplineIds as $index => $disciplineId) {
$disciplineId = (int) $disciplineId;
if ($disciplineId <= 0) {
continue;
}
$level = $levels[$index] ?? 'secondary';
if (!in_array($level, ['primary', 'secondary'])) {
$level = 'secondary';
}
$db->insert('sa_coach_disciplines', [
'coach_id' => $coachId,
'discipline_id' => $disciplineId,
'specialization_level' => $level,
'created_at' => date('Y-m-d H:i:s'),
]);
}
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
class DashboardController extends Controller
{
/**
* Main SportsActivity dashboard with summary cards.
*/
public function index(Request $request): Response
{
$db = App::getInstance()->db();
$today = date('Y-m-d');
// Total active disciplines
$activeDisciplines = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_disciplines WHERE is_active = 1",
[]
)['cnt'] ?? 0);
// Total active facilities + units
$activeFacilities = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_facilities WHERE is_active = 1 AND is_archived = 0",
[]
)['cnt'] ?? 0);
$activeUnits = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_facility_units WHERE is_active = 1",
[]
)['cnt'] ?? 0);
// Total active coaches
$activeCoaches = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_coaches WHERE status = 'active' AND is_archived = 0",
[]
)['cnt'] ?? 0);
// Total registered players (not archived)
$totalPlayers = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_players WHERE is_archived = 0",
[]
)['cnt'] ?? 0);
// Total active groups + enrolled players
$activeGroups = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_groups WHERE status = 'active' AND is_archived = 0",
[]
)['cnt'] ?? 0);
$enrolledPlayers = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_group_players WHERE status = 'active'",
[]
)['cnt'] ?? 0);
// Today's bookings count
$todayBookings = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_bookings WHERE booking_date = ? AND status NOT IN ('cancelled', 'no_show')",
[$today]
)['cnt'] ?? 0);
// Pending medical approvals
$pendingMedical = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_player_documents WHERE document_type = 'medical' AND status = 'pending'",
[]
)['cnt'] ?? 0);
// Overdue subscriptions
$overdueSubscriptions = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_subscriptions WHERE payment_status IN ('unpaid', 'overdue') AND period_end < ?",
[$today]
)['cnt'] ?? 0);
return $this->view('SportsActivity.Views.dashboard', [
'stats' => [
'active_disciplines' => $activeDisciplines,
'active_facilities' => $activeFacilities,
'active_units' => $activeUnits,
'active_coaches' => $activeCoaches,
'total_players' => $totalPlayers,
'active_groups' => $activeGroups,
'enrolled_players' => $enrolledPlayers,
'today_bookings' => $todayBookings,
'pending_medical' => $pendingMedical,
'overdue_subscriptions' => $overdueSubscriptions,
],
'today' => $today,
]);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\Pagination;
use App\Modules\SportsActivity\Models\Discipline;
class DisciplineController extends Controller
{
/**
* List all disciplines with search filter and pagination.
*/
public function index(Request $request): Response
{
$filters = [
'q' => trim((string) $request->get('q', '')),
'category' => trim((string) $request->get('category', '')),
];
$page = max(1, (int) $request->get('page', 1));
$perPage = 25;
$query = Discipline::query();
if ($filters['q'] !== '') {
$query->where('name_ar', 'LIKE', '%' . $filters['q'] . '%');
}
if ($filters['category'] !== '') {
$query->where('category', '=', $filters['category']);
}
// Count total
$countQuery = Discipline::query();
if ($filters['q'] !== '') {
$countQuery->where('name_ar', 'LIKE', '%' . $filters['q'] . '%');
}
if ($filters['category'] !== '') {
$countQuery->where('category', '=', $filters['category']);
}
$total = count($countQuery->get());
$pagination = Pagination::paginate($total, $perPage, $page);
$disciplines = $query
->orderBy('sort_order', 'ASC')
->orderBy('name_ar', 'ASC')
->limit($perPage)
->offset(($page - 1) * $perPage)
->get();
return $this->view('SportsActivity.Views.disciplines.index', [
'disciplines' => $disciplines,
'pagination' => $pagination,
'filters' => $filters,
'categories' => Discipline::getCategoryOptions(),
]);
}
/**
* Show the create discipline form.
*/
public function create(Request $request): Response
{
return $this->view('SportsActivity.Views.disciplines.create', [
'categories' => Discipline::getCategoryOptions(),
]);
}
/**
* Validate and store a new discipline.
*/
public function store(Request $request): Response
{
$code = strtoupper(trim((string) $request->post('code', '')));
$nameAr = trim((string) $request->post('name_ar', ''));
$nameEn = trim((string) $request->post('name_en', ''));
$category = trim((string) $request->post('category', ''));
$icon = trim((string) $request->post('icon', 'activity'));
$descriptionAr = trim((string) $request->post('description_ar', ''));
$sortOrder = (int) $request->post('sort_order', 0);
// Validation
$errors = [];
if ($code === '') {
$errors[] = 'كود النشاط مطلوب';
}
if ($nameAr === '' || mb_strlen($nameAr) < 2) {
$errors[] = 'الاسم بالعربي مطلوب (حرفان على الأقل)';
}
// Check unique code
if ($code !== '') {
$existing = Discipline::query()
->where('code', '=', $code)
->first();
if ($existing) {
$errors[] = 'كود النشاط مستخدم بالفعل — جرب كود مختلف';
}
}
// Validate category if provided
if ($category !== '' && !array_key_exists($category, Discipline::getCategoryOptions())) {
$errors[] = 'فئة النشاط غير صالحة';
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/disciplines/create');
}
$discipline = Discipline::create([
'code' => $code,
'name_ar' => $nameAr,
'name_en' => $nameEn ?: null,
'category' => $category ?: null,
'icon' => $icon ?: null,
'description_ar' => $descriptionAr ?: null,
'sort_order' => $sortOrder,
'is_active' => 1,
]);
return $this->redirect('/sa/disciplines/' . $discipline->id)->withSuccess('تم إضافة النشاط الرياضي بنجاح');
}
/**
* Show discipline detail page.
*/
public function show(Request $request, string $id): Response
{
$discipline = Discipline::find((int) $id);
if (!$discipline) {
return $this->redirect('/sa/disciplines')->withError('النشاط الرياضي غير موجود');
}
return $this->view('SportsActivity.Views.disciplines.show', [
'discipline' => $discipline,
'categories' => Discipline::getCategoryOptions(),
]);
}
/**
* Show edit form for a discipline.
*/
public function edit(Request $request, string $id): Response
{
$discipline = Discipline::find((int) $id);
if (!$discipline) {
return $this->redirect('/sa/disciplines')->withError('النشاط الرياضي غير موجود');
}
return $this->view('SportsActivity.Views.disciplines.edit', [
'discipline' => $discipline,
'categories' => Discipline::getCategoryOptions(),
]);
}
/**
* Validate and update an existing discipline.
*/
public function update(Request $request, string $id): Response
{
$discipline = Discipline::find((int) $id);
if (!$discipline) {
return $this->redirect('/sa/disciplines')->withError('النشاط الرياضي غير موجود');
}
$code = strtoupper(trim((string) $request->post('code', '')));
$nameAr = trim((string) $request->post('name_ar', ''));
$nameEn = trim((string) $request->post('name_en', ''));
$category = trim((string) $request->post('category', ''));
$icon = trim((string) $request->post('icon', 'activity'));
$descriptionAr = trim((string) $request->post('description_ar', ''));
$sortOrder = (int) $request->post('sort_order', 0);
// Validation
$errors = [];
if ($code === '') {
$errors[] = 'كود النشاط مطلوب';
}
if ($nameAr === '' || mb_strlen($nameAr) < 2) {
$errors[] = 'الاسم بالعربي مطلوب (حرفان على الأقل)';
}
// Check unique code (exclude current)
if ($code !== '') {
$db = App::getInstance()->db();
$existing = $db->selectOne(
"SELECT id FROM disciplines WHERE code = ? AND id != ?",
[$code, (int) $id]
);
if ($existing) {
$errors[] = 'كود النشاط مستخدم بالفعل — جرب كود مختلف';
}
}
// Validate category if provided
if ($category !== '' && !array_key_exists($category, Discipline::getCategoryOptions())) {
$errors[] = 'فئة النشاط غير صالحة';
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/disciplines/' . $id . '/edit');
}
$discipline->update([
'code' => $code,
'name_ar' => $nameAr,
'name_en' => $nameEn ?: null,
'category' => $category ?: null,
'icon' => $icon ?: null,
'description_ar' => $descriptionAr ?: null,
'sort_order' => $sortOrder,
]);
return $this->redirect('/sa/disciplines/' . $id)->withSuccess('تم تحديث النشاط الرياضي بنجاح');
}
/**
* Toggle the is_active status of a discipline.
*/
public function toggle(Request $request, string $id): Response
{
$discipline = Discipline::find((int) $id);
if (!$discipline) {
return $this->redirect('/sa/disciplines')->withError('النشاط الرياضي غير موجود');
}
$newStatus = $discipline->is_active ? 0 : 1;
$discipline->update(['is_active' => $newStatus]);
$message = $newStatus ? 'تم تفعيل النشاط الرياضي' : 'تم إيقاف النشاط الرياضي';
return $this->redirect('/sa/disciplines/' . $id)->withSuccess($message);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\Pagination;
use App\Modules\SportsActivity\Models\Facility;
use App\Modules\SportsActivity\Models\FacilityUnit;
use App\Modules\SportsActivity\Models\TimeBracket;
use App\Modules\SportsActivity\Models\Discipline;
class FacilityController extends Controller
{
public function index(Request $request): Response
{
$this->authorize('sa.facility.view');
$db = App::getInstance()->db();
$search = trim((string) $request->get('q', ''));
$typeFilter = trim((string) $request->get('type', ''));
$page = max(1, (int) $request->get('page', 1));
$perPage = 25;
$where = ['f.is_archived = 0'];
$params = [];
if ($search !== '') {
$where[] = "(f.name_ar LIKE ? OR f.name_en LIKE ? OR f.code LIKE ?)";
$params[] = "%{$search}%";
$params[] = "%{$search}%";
$params[] = "%{$search}%";
}
if ($typeFilter !== '') {
$where[] = "f.facility_type = ?";
$params[] = $typeFilter;
}
$whereSql = implode(' AND ', $where);
$total = (int) $db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_facilities f WHERE {$whereSql}",
$params
)['cnt'];
$pagination = Pagination::paginate($total, $perPage, $page);
$offset = ($pagination['current_page'] - 1) * $perPage;
$rows = $db->select(
"SELECT f.*, d.name_ar as discipline_name,
(SELECT COUNT(*) FROM sa_facility_units u WHERE u.facility_id = f.id AND u.is_active = 1) as unit_count
FROM sa_facilities f
LEFT JOIN sa_disciplines d ON d.id = f.discipline_id
WHERE {$whereSql}
ORDER BY f.name_ar ASC
LIMIT {$perPage} OFFSET {$offset}",
$params
);
return $this->view('SportsActivity.Views.facilities.index', [
'rows' => $rows,
'pagination' => $pagination,
'filters' => ['q' => $search, 'type' => $typeFilter],
'typeOptions' => Facility::getTypeOptions(),
]);
}
public function create(Request $request): Response
{
$this->authorize('sa.facility.manage');
return $this->view('SportsActivity.Views.facilities.create', [
'typeOptions' => Facility::getTypeOptions(),
'disciplines' => Discipline::getActive(),
]);
}
public function store(Request $request): Response
{
$this->authorize('sa.facility.manage');
$code = trim((string) $request->post('code', ''));
$nameAr = trim((string) $request->post('name_ar', ''));
$nameEn = trim((string) $request->post('name_en', ''));
$facilityType = trim((string) $request->post('facility_type', ''));
$disciplineId = $request->post('discipline_id', '');
$location = trim((string) $request->post('location_description', ''));
$opStart = trim((string) $request->post('operating_start', ''));
$opEnd = trim((string) $request->post('operating_end', ''));
$slotMinutes = (int) $request->post('slot_minutes', 60);
// Validation
$errors = [];
if ($code === '') {
$errors[] = 'الكود مطلوب';
}
if ($nameAr === '') {
$errors[] = 'الاسم بالعربية مطلوب';
}
if ($facilityType === '' || !isset(Facility::getTypeOptions()[$facilityType])) {
$errors[] = 'نوع المنشأة مطلوب';
}
if ($opStart === '' || $opEnd === '') {
$errors[] = 'ساعات التشغيل مطلوبة';
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_old_input', $_POST);
$alerts = array_map(fn($msg) => ['type' => 'error', 'message' => $msg], $errors);
$session->flash('_alerts', $alerts);
return $this->redirect('/sa/facilities/create');
}
// Check duplicate code
$db = App::getInstance()->db();
$existing = $db->selectOne("SELECT id FROM sa_facilities WHERE code = ? AND is_archived = 0", [$code]);
if ($existing) {
$session = App::getInstance()->session();
$session->flash('_old_input', $_POST);
$session->flash('_alerts', [['type' => 'error', 'message' => 'الكود مستخدم بالفعل']]);
return $this->redirect('/sa/facilities/create');
}
$operatingHoursJson = json_encode([
'start' => $opStart,
'end' => $opEnd,
'slot_minutes' => $slotMinutes,
], JSON_UNESCAPED_UNICODE);
Facility::create([
'code' => $code,
'name_ar' => $nameAr,
'name_en' => $nameEn ?: null,
'facility_type' => $facilityType,
'discipline_id' => $disciplineId !== '' ? (int) $disciplineId : null,
'location_description' => $location ?: null,
'operating_hours_json' => $operatingHoursJson,
'is_active' => 1,
'branch_id' => null,
]);
return $this->redirect('/sa/facilities')->withSuccess('تم إضافة المنشأة بنجاح');
}
public function show(Request $request, string $id): Response
{
$this->authorize('sa.facility.view');
$facility = Facility::find((int) $id);
if (!$facility) {
return $this->redirect('/sa/facilities')->withError('المنشأة غير موجودة');
}
$units = FacilityUnit::getByFacility((int) $id);
$brackets = TimeBracket::getByFacility((int) $id);
$db = App::getInstance()->db();
$discipline = null;
if ($facility->discipline_id) {
$discipline = $db->selectOne("SELECT * FROM sa_disciplines WHERE id = ?", [(int) $facility->discipline_id]);
}
return $this->view('SportsActivity.Views.facilities.show', [
'facility' => $facility,
'units' => $units,
'brackets' => $brackets,
'discipline' => $discipline,
'typeOptions' => Facility::getTypeOptions(),
'bracketTypes' => TimeBracket::getBracketTypeOptions(),
'unitTypeOptions' => FacilityUnit::getUnitTypeOptions(),
'bookingModes' => FacilityUnit::getBookingModeOptions(),
]);
}
public function edit(Request $request, string $id): Response
{
$this->authorize('sa.facility.manage');
$facility = Facility::find((int) $id);
if (!$facility) {
return $this->redirect('/sa/facilities')->withError('المنشأة غير موجودة');
}
return $this->view('SportsActivity.Views.facilities.edit', [
'facility' => $facility,
'typeOptions' => Facility::getTypeOptions(),
'disciplines' => Discipline::getActive(),
]);
}
public function update(Request $request, string $id): Response
{
$this->authorize('sa.facility.manage');
$facility = Facility::find((int) $id);
if (!$facility) {
return $this->redirect('/sa/facilities')->withError('المنشأة غير موجودة');
}
$code = trim((string) $request->post('code', ''));
$nameAr = trim((string) $request->post('name_ar', ''));
$nameEn = trim((string) $request->post('name_en', ''));
$facilityType = trim((string) $request->post('facility_type', ''));
$disciplineId = $request->post('discipline_id', '');
$location = trim((string) $request->post('location_description', ''));
$opStart = trim((string) $request->post('operating_start', ''));
$opEnd = trim((string) $request->post('operating_end', ''));
$slotMinutes = (int) $request->post('slot_minutes', 60);
// Validation
$errors = [];
if ($code === '') {
$errors[] = 'الكود مطلوب';
}
if ($nameAr === '') {
$errors[] = 'الاسم بالعربية مطلوب';
}
if ($facilityType === '' || !isset(Facility::getTypeOptions()[$facilityType])) {
$errors[] = 'نوع المنشأة مطلوب';
}
if ($opStart === '' || $opEnd === '') {
$errors[] = 'ساعات التشغيل مطلوبة';
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_old_input', $_POST);
$alerts = array_map(fn($msg) => ['type' => 'error', 'message' => $msg], $errors);
$session->flash('_alerts', $alerts);
return $this->redirect("/sa/facilities/{$id}/edit");
}
// Check duplicate code (excluding current)
$db = App::getInstance()->db();
$existing = $db->selectOne(
"SELECT id FROM sa_facilities WHERE code = ? AND is_archived = 0 AND id != ?",
[$code, (int) $id]
);
if ($existing) {
$session = App::getInstance()->session();
$session->flash('_old_input', $_POST);
$session->flash('_alerts', [['type' => 'error', 'message' => 'الكود مستخدم بالفعل']]);
return $this->redirect("/sa/facilities/{$id}/edit");
}
$operatingHoursJson = json_encode([
'start' => $opStart,
'end' => $opEnd,
'slot_minutes' => $slotMinutes,
], JSON_UNESCAPED_UNICODE);
$facility->update([
'code' => $code,
'name_ar' => $nameAr,
'name_en' => $nameEn ?: null,
'facility_type' => $facilityType,
'discipline_id' => $disciplineId !== '' ? (int) $disciplineId : null,
'location_description' => $location ?: null,
'operating_hours_json' => $operatingHoursJson,
]);
return $this->redirect("/sa/facilities/{$id}")->withSuccess('تم تحديث المنشأة بنجاح');
}
public function toggle(Request $request, string $id): Response
{
$this->authorize('sa.facility.manage');
$facility = Facility::find((int) $id);
if (!$facility) {
return $this->redirect('/sa/facilities')->withError('المنشأة غير موجودة');
}
$newStatus = $facility->is_active ? 0 : 1;
$facility->update(['is_active' => $newStatus]);
$msg = $newStatus ? 'تم تفعيل المنشأة' : 'تم تعطيل المنشأة';
return $this->redirect('/sa/facilities')->withSuccess($msg);
}
public function brackets(Request $request, string $fid): Response
{
$this->authorize('sa.facility.view');
$facility = Facility::find((int) $fid);
if (!$facility) {
return $this->redirect('/sa/facilities')->withError('المنشأة غير موجودة');
}
$brackets = TimeBracket::getByFacility((int) $fid);
return $this->view('SportsActivity.Views.facilities.time_brackets', [
'facility' => $facility,
'brackets' => $brackets,
'bracketTypes' => TimeBracket::getBracketTypeOptions(),
]);
}
public function storeBracket(Request $request, string $fid): Response
{
$this->authorize('sa.facility.manage');
$facility = Facility::find((int) $fid);
if (!$facility) {
return $this->redirect('/sa/facilities')->withError('المنشأة غير موجودة');
}
$nameAr = trim((string) $request->post('name_ar', ''));
$bracketType = trim((string) $request->post('bracket_type', ''));
$startTime = trim((string) $request->post('start_time', ''));
$endTime = trim((string) $request->post('end_time', ''));
$daysOfWeek = $request->post('days_of_week', []);
// Validation
$errors = [];
if ($nameAr === '') {
$errors[] = 'اسم الفترة مطلوب';
}
if ($bracketType === '' || !isset(TimeBracket::getBracketTypeOptions()[$bracketType])) {
$errors[] = 'نوع الفترة مطلوب';
}
if ($startTime === '' || $endTime === '') {
$errors[] = 'وقت البداية والنهاية مطلوبان';
}
if (empty($daysOfWeek) || !is_array($daysOfWeek)) {
$errors[] = 'يجب اختيار يوم واحد على الأقل';
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_old_input', $_POST);
$alerts = array_map(fn($msg) => ['type' => 'error', 'message' => $msg], $errors);
$session->flash('_alerts', $alerts);
return $this->redirect("/sa/facilities/{$fid}/brackets");
}
$daysJson = json_encode(array_map('intval', (array) $daysOfWeek));
TimeBracket::create([
'facility_id' => (int) $fid,
'name_ar' => $nameAr,
'name_en' => null,
'bracket_type' => $bracketType,
'start_time' => $startTime,
'end_time' => $endTime,
'days_of_week_json' => $daysJson,
'is_active' => 1,
]);
return $this->redirect("/sa/facilities/{$fid}/brackets")->withSuccess('تم إضافة الفترة الزمنية بنجاح');
}
public function deleteBracket(Request $request, string $fid, string $id): Response
{
$this->authorize('sa.facility.manage');
$facility = Facility::find((int) $fid);
if (!$facility) {
return $this->redirect('/sa/facilities')->withError('المنشأة غير موجودة');
}
$bracket = TimeBracket::find((int) $id);
if (!$bracket || (int) $bracket->facility_id !== (int) $fid) {
return $this->redirect("/sa/facilities/{$fid}/brackets")->withError('الفترة الزمنية غير موجودة');
}
$bracket->update(['is_active' => 0]);
return $this->redirect("/sa/facilities/{$fid}/brackets")->withSuccess('تم حذف الفترة الزمنية');
}
public function addBlackout(Request $request, string $id): Response
{
$this->authorize('sa.facility.manage');
$facility = Facility::find((int) $id);
if (!$facility) {
return $this->redirect('/sa/facilities')->withError('المنشأة غير موجودة');
}
$blackoutDate = trim((string) $request->post('blackout_date', ''));
$reason = trim((string) $request->post('reason', ''));
if ($blackoutDate === '') {
return $this->redirect("/sa/facilities/{$id}")->withError('تاريخ الإيقاف مطلوب');
}
$db = App::getInstance()->db();
$db->insert('sa_facility_blackouts', [
'facility_id' => (int) $id,
'blackout_date' => $blackoutDate,
'reason' => $reason ?: null,
'created_at' => date('Y-m-d H:i:s'),
]);
return $this->redirect("/sa/facilities/{$id}")->withSuccess('تم إضافة تاريخ الإيقاف');
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Modules\SportsActivity\Models\Facility;
use App\Modules\SportsActivity\Models\FacilityUnit;
class FacilityUnitController extends Controller
{
public function index(Request $request, string $fid): Response
{
$this->authorize('sa.facility.view');
$facility = Facility::find((int) $fid);
if (!$facility) {
return $this->redirect('/sa/facilities')->withError('المنشأة غير موجودة');
}
$units = FacilityUnit::getByFacility((int) $fid);
return $this->view('SportsActivity.Views.facilities.units', [
'facility' => $facility,
'units' => $units,
'unitTypeOptions' => FacilityUnit::getUnitTypeOptions(),
'bookingModes' => FacilityUnit::getBookingModeOptions(),
]);
}
public function create(Request $request, string $fid): Response
{
$this->authorize('sa.facility.manage');
$facility = Facility::find((int) $fid);
if (!$facility) {
return $this->redirect('/sa/facilities')->withError('المنشأة غير موجودة');
}
return $this->view('SportsActivity.Views.facilities.unit_form', [
'facility' => $facility,
'unit' => null,
'unitTypeOptions' => FacilityUnit::getUnitTypeOptions(),
'bookingModes' => FacilityUnit::getBookingModeOptions(),
]);
}
public function store(Request $request, string $fid): Response
{
$this->authorize('sa.facility.manage');
$facility = Facility::find((int) $fid);
if (!$facility) {
return $this->redirect('/sa/facilities')->withError('المنشأة غير موجودة');
}
$code = trim((string) $request->post('code', ''));
$nameAr = trim((string) $request->post('name_ar', ''));
$nameEn = trim((string) $request->post('name_en', ''));
$unitType = trim((string) $request->post('unit_type', ''));
$bookingMode = trim((string) $request->post('booking_mode', ''));
$maxCapacity = (int) $request->post('max_capacity', 1);
$sortOrder = (int) $request->post('sort_order', 0);
// Validation
$errors = [];
if ($code === '') {
$errors[] = 'الكود مطلوب';
}
if ($nameAr === '') {
$errors[] = 'الاسم بالعربية مطلوب';
}
if ($unitType === '' || !isset(FacilityUnit::getUnitTypeOptions()[$unitType])) {
$errors[] = 'نوع الوحدة مطلوب';
}
if ($bookingMode === '' || !isset(FacilityUnit::getBookingModeOptions()[$bookingMode])) {
$errors[] = 'نمط الحجز مطلوب';
}
if ($maxCapacity < 1) {
$errors[] = 'السعة القصوى يجب أن تكون 1 على الأقل';
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_old_input', $_POST);
$alerts = array_map(fn($msg) => ['type' => 'error', 'message' => $msg], $errors);
$session->flash('_alerts', $alerts);
return $this->redirect("/sa/facilities/{$fid}/units/create");
}
// Check duplicate code within facility
$db = App::getInstance()->db();
$existing = $db->selectOne(
"SELECT id FROM sa_facility_units WHERE code = ? AND facility_id = ? AND is_active = 1",
[$code, (int) $fid]
);
if ($existing) {
$session = App::getInstance()->session();
$session->flash('_old_input', $_POST);
$session->flash('_alerts', [['type' => 'error', 'message' => 'الكود مستخدم بالفعل في هذه المنشأة']]);
return $this->redirect("/sa/facilities/{$fid}/units/create");
}
FacilityUnit::create([
'facility_id' => (int) $fid,
'code' => $code,
'name_ar' => $nameAr,
'name_en' => $nameEn ?: null,
'unit_type' => $unitType,
'booking_mode' => $bookingMode,
'max_capacity' => $maxCapacity,
'sort_order' => $sortOrder,
'is_active' => 1,
]);
return $this->redirect("/sa/facilities/{$fid}/units")->withSuccess('تم إضافة الوحدة بنجاح');
}
public function edit(Request $request, string $fid, string $id): Response
{
$this->authorize('sa.facility.manage');
$facility = Facility::find((int) $fid);
if (!$facility) {
return $this->redirect('/sa/facilities')->withError('المنشأة غير موجودة');
}
$unit = FacilityUnit::find((int) $id);
if (!$unit || (int) $unit->facility_id !== (int) $fid) {
return $this->redirect("/sa/facilities/{$fid}/units")->withError('الوحدة غير موجودة');
}
return $this->view('SportsActivity.Views.facilities.unit_form', [
'facility' => $facility,
'unit' => $unit,
'unitTypeOptions' => FacilityUnit::getUnitTypeOptions(),
'bookingModes' => FacilityUnit::getBookingModeOptions(),
]);
}
public function update(Request $request, string $fid, string $id): Response
{
$this->authorize('sa.facility.manage');
$facility = Facility::find((int) $fid);
if (!$facility) {
return $this->redirect('/sa/facilities')->withError('المنشأة غير موجودة');
}
$unit = FacilityUnit::find((int) $id);
if (!$unit || (int) $unit->facility_id !== (int) $fid) {
return $this->redirect("/sa/facilities/{$fid}/units")->withError('الوحدة غير موجودة');
}
$code = trim((string) $request->post('code', ''));
$nameAr = trim((string) $request->post('name_ar', ''));
$nameEn = trim((string) $request->post('name_en', ''));
$unitType = trim((string) $request->post('unit_type', ''));
$bookingMode = trim((string) $request->post('booking_mode', ''));
$maxCapacity = (int) $request->post('max_capacity', 1);
$sortOrder = (int) $request->post('sort_order', 0);
// Validation
$errors = [];
if ($code === '') {
$errors[] = 'الكود مطلوب';
}
if ($nameAr === '') {
$errors[] = 'الاسم بالعربية مطلوب';
}
if ($unitType === '' || !isset(FacilityUnit::getUnitTypeOptions()[$unitType])) {
$errors[] = 'نوع الوحدة مطلوب';
}
if ($bookingMode === '' || !isset(FacilityUnit::getBookingModeOptions()[$bookingMode])) {
$errors[] = 'نمط الحجز مطلوب';
}
if ($maxCapacity < 1) {
$errors[] = 'السعة القصوى يجب أن تكون 1 على الأقل';
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_old_input', $_POST);
$alerts = array_map(fn($msg) => ['type' => 'error', 'message' => $msg], $errors);
$session->flash('_alerts', $alerts);
return $this->redirect("/sa/facilities/{$fid}/units/{$id}/edit");
}
// Check duplicate code (excluding current)
$db = App::getInstance()->db();
$existing = $db->selectOne(
"SELECT id FROM sa_facility_units WHERE code = ? AND facility_id = ? AND is_active = 1 AND id != ?",
[$code, (int) $fid, (int) $id]
);
if ($existing) {
$session = App::getInstance()->session();
$session->flash('_old_input', $_POST);
$session->flash('_alerts', [['type' => 'error', 'message' => 'الكود مستخدم بالفعل في هذه المنشأة']]);
return $this->redirect("/sa/facilities/{$fid}/units/{$id}/edit");
}
$unit->update([
'code' => $code,
'name_ar' => $nameAr,
'name_en' => $nameEn ?: null,
'unit_type' => $unitType,
'booking_mode' => $bookingMode,
'max_capacity' => $maxCapacity,
'sort_order' => $sortOrder,
]);
return $this->redirect("/sa/facilities/{$fid}/units")->withSuccess('تم تحديث الوحدة بنجاح');
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\Pagination;
use App\Modules\SportsActivity\Models\Group;
use App\Modules\SportsActivity\Models\Program;
use App\Modules\SportsActivity\Services\EnrollmentService;
class GroupController extends Controller
{
/**
* List groups with search, filter by program/coach, pagination.
*/
public function index(Request $request): Response
{
$filters = [
'q' => trim((string) $request->get('q', '')),
'program_id' => trim((string) $request->get('program_id', '')),
'coach_id' => trim((string) $request->get('coach_id', '')),
];
$page = max(1, (int) $request->get('page', 1));
$perPage = 25;
$db = App::getInstance()->db();
$where = "g.is_archived = 0";
$params = [];
if ($filters['q'] !== '') {
$where .= " AND (g.name_ar LIKE ? OR g.code LIKE ?)";
$params[] = '%' . $filters['q'] . '%';
$params[] = '%' . $filters['q'] . '%';
}
if ($filters['program_id'] !== '') {
$where .= " AND g.program_id = ?";
$params[] = (int) $filters['program_id'];
}
if ($filters['coach_id'] !== '') {
$where .= " AND g.coach_id = ?";
$params[] = (int) $filters['coach_id'];
}
$total = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM sa_groups g WHERE {$where}", $params)['cnt'] ?? 0);
$pagination = Pagination::paginate($total, $perPage, $page);
$offset = ($page - 1) * $perPage;
$groups = $db->select(
"SELECT g.*, p.name_ar as program_name, c.name_ar as coach_name
FROM sa_groups g
LEFT JOIN sa_programs p ON p.id = g.program_id
LEFT JOIN sa_coaches c ON c.id = g.coach_id
WHERE {$where}
ORDER BY g.name_ar ASC
LIMIT {$perPage} OFFSET {$offset}",
$params
);
$programs = Program::getActive();
$coaches = $db->select("SELECT id, name_ar FROM sa_coaches WHERE is_archived = 0 ORDER BY name_ar", []);
return $this->view('SportsActivity.Views.groups.index', [
'groups' => $groups,
'pagination' => $pagination,
'filters' => $filters,
'programs' => $programs,
'coaches' => $coaches,
]);
}
/**
* Show create group form.
*/
public function create(Request $request): Response
{
$db = App::getInstance()->db();
$programs = Program::getActive();
$coaches = $db->select("SELECT id, name_ar FROM sa_coaches WHERE is_archived = 0 ORDER BY name_ar", []);
return $this->view('SportsActivity.Views.groups.create', [
'programs' => $programs,
'coaches' => $coaches,
]);
}
/**
* Validate and store a new group.
*/
public function store(Request $request): Response
{
$code = strtoupper(trim((string) $request->post('code', '')));
$nameAr = trim((string) $request->post('name_ar', ''));
$nameEn = trim((string) $request->post('name_en', ''));
$programId = (int) $request->post('program_id', 0);
$coachId = (int) $request->post('coach_id', 0);
$minCapacity = (int) $request->post('min_capacity', 0);
$maxCapacity = (int) $request->post('max_capacity', 0);
$monthlyFeeMember = (float) $request->post('monthly_fee_member', 0);
$monthlyFeeNonmember = (float) $request->post('monthly_fee_nonmember', 0);
$seasonStart = trim((string) $request->post('season_start', ''));
$seasonEnd = trim((string) $request->post('season_end', ''));
$errors = [];
if ($code === '') {
$errors[] = 'كود المجموعة مطلوب';
}
if ($nameAr === '' || mb_strlen($nameAr) < 2) {
$errors[] = 'اسم المجموعة بالعربي مطلوب (حرفان على الأقل)';
}
if ($programId === 0) {
$errors[] = 'البرنامج مطلوب';
}
if ($coachId === 0) {
$errors[] = 'المدرب مطلوب';
}
// Check unique code
if ($code !== '') {
$db = App::getInstance()->db();
$existing = $db->selectOne("SELECT id FROM sa_groups WHERE code = ?", [$code]);
if ($existing) {
$errors[] = 'كود المجموعة مستخدم بالفعل';
}
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/groups/create');
}
$group = Group::create([
'code' => $code,
'name_ar' => $nameAr,
'name_en' => $nameEn ?: null,
'program_id' => $programId,
'coach_id' => $coachId,
'min_capacity' => $minCapacity,
'max_capacity' => $maxCapacity,
'current_count' => 0,
'is_full' => 0,
'monthly_fee_member' => $monthlyFeeMember,
'monthly_fee_nonmember' => $monthlyFeeNonmember,
'season_start' => $seasonStart ?: null,
'season_end' => $seasonEnd ?: null,
'status' => 'active',
]);
return $this->redirect('/sa/groups/' . $group->id)->withSuccess('تم إضافة المجموعة بنجاح');
}
/**
* Show group detail with players and schedule.
*/
public function show(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$group = $db->selectOne(
"SELECT g.*, p.name_ar as program_name, c.name_ar as coach_name
FROM sa_groups g
LEFT JOIN sa_programs p ON p.id = g.program_id
LEFT JOIN sa_coaches c ON c.id = g.coach_id
WHERE g.id = ? AND g.is_archived = 0",
[(int) $id]
);
if (!$group) {
return $this->redirect('/sa/groups')->withError('المجموعة غير موجودة');
}
$players = Group::getPlayers((int) $id);
$schedule = Group::getSchedule((int) $id);
// Available players for enrollment (not already in this group)
$availablePlayers = $db->select(
"SELECT id, name_ar, code FROM sa_players
WHERE is_archived = 0 AND id NOT IN (
SELECT player_id FROM sa_group_players WHERE group_id = ? AND status = 'active'
)
ORDER BY name_ar ASC LIMIT 100",
[(int) $id]
);
// Facility units for schedule
$facilityUnits = $db->select(
"SELECT fu.id, fu.name_ar, fu.code, f.name_ar as facility_name
FROM sa_facility_units fu
JOIN sa_facilities f ON f.id = fu.facility_id
WHERE fu.is_archived = 0 AND fu.is_active = 1
ORDER BY f.name_ar ASC, fu.name_ar ASC",
[]
);
return $this->view('SportsActivity.Views.groups.show', [
'group' => $group,
'players' => $players,
'schedule' => $schedule,
'availablePlayers' => $availablePlayers,
'facilityUnits' => $facilityUnits,
'statuses' => Group::getStatusOptions(),
]);
}
/**
* Show edit form for a group.
*/
public function edit(Request $request, string $id): Response
{
$group = Group::find((int) $id);
if (!$group) {
return $this->redirect('/sa/groups')->withError('المجموعة غير موجودة');
}
$db = App::getInstance()->db();
$programs = Program::getActive();
$coaches = $db->select("SELECT id, name_ar FROM sa_coaches WHERE is_archived = 0 ORDER BY name_ar", []);
return $this->view('SportsActivity.Views.groups.edit', [
'group' => $group,
'programs' => $programs,
'coaches' => $coaches,
]);
}
/**
* Validate and update an existing group.
*/
public function update(Request $request, string $id): Response
{
$group = Group::find((int) $id);
if (!$group) {
return $this->redirect('/sa/groups')->withError('المجموعة غير موجودة');
}
$code = strtoupper(trim((string) $request->post('code', '')));
$nameAr = trim((string) $request->post('name_ar', ''));
$nameEn = trim((string) $request->post('name_en', ''));
$programId = (int) $request->post('program_id', 0);
$coachId = (int) $request->post('coach_id', 0);
$minCapacity = (int) $request->post('min_capacity', 0);
$maxCapacity = (int) $request->post('max_capacity', 0);
$monthlyFeeMember = (float) $request->post('monthly_fee_member', 0);
$monthlyFeeNonmember = (float) $request->post('monthly_fee_nonmember', 0);
$seasonStart = trim((string) $request->post('season_start', ''));
$seasonEnd = trim((string) $request->post('season_end', ''));
$errors = [];
if ($code === '') {
$errors[] = 'كود المجموعة مطلوب';
}
if ($nameAr === '' || mb_strlen($nameAr) < 2) {
$errors[] = 'اسم المجموعة بالعربي مطلوب (حرفان على الأقل)';
}
if ($programId === 0) {
$errors[] = 'البرنامج مطلوب';
}
if ($coachId === 0) {
$errors[] = 'المدرب مطلوب';
}
// Check unique code (exclude current)
if ($code !== '') {
$db = App::getInstance()->db();
$existing = $db->selectOne("SELECT id FROM sa_groups WHERE code = ? AND id != ?", [$code, (int) $id]);
if ($existing) {
$errors[] = 'كود المجموعة مستخدم بالفعل';
}
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/groups/' . $id . '/edit');
}
$group->update([
'code' => $code,
'name_ar' => $nameAr,
'name_en' => $nameEn ?: null,
'program_id' => $programId,
'coach_id' => $coachId,
'min_capacity' => $minCapacity,
'max_capacity' => $maxCapacity,
'monthly_fee_member' => $monthlyFeeMember,
'monthly_fee_nonmember' => $monthlyFeeNonmember,
'season_start' => $seasonStart ?: null,
'season_end' => $seasonEnd ?: null,
]);
return $this->redirect('/sa/groups/' . $id)->withSuccess('تم تحديث المجموعة بنجاح');
}
/**
* Enroll a player in this group.
*/
public function enroll(Request $request, string $id): Response
{
$playerId = (int) $request->post('player_id', 0);
if ($playerId === 0) {
return $this->redirect('/sa/groups/' . $id)->withError('يرجى اختيار لاعب');
}
$result = EnrollmentService::enroll((int) $id, $playerId);
if ($result['success']) {
return $this->redirect('/sa/groups/' . $id)->withSuccess('تم تسجيل اللاعب في المجموعة بنجاح');
}
return $this->redirect('/sa/groups/' . $id)->withError($result['error']);
}
/**
* Remove (withdraw) a player from this group.
*/
public function removePlayer(Request $request, string $id): Response
{
$playerId = (int) $request->post('player_id', 0);
if ($playerId === 0) {
return $this->redirect('/sa/groups/' . $id)->withError('يرجى تحديد اللاعب');
}
$reason = trim((string) $request->post('reason', ''));
$result = EnrollmentService::withdraw((int) $id, $playerId, $reason);
if ($result['success']) {
return $this->redirect('/sa/groups/' . $id)->withSuccess('تم سحب اللاعب من المجموعة');
}
return $this->redirect('/sa/groups/' . $id)->withError($result['error']);
}
/**
* Save/update group schedule entries.
*/
public function saveSchedule(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$group = Group::find((int) $id);
if (!$group) {
return $this->redirect('/sa/groups')->withError('المجموعة غير موجودة');
}
$facilityUnitIds = $request->post('facility_unit_id', []);
$daysOfWeek = $request->post('day_of_week', []);
$startTimes = $request->post('start_time', []);
$endTimes = $request->post('end_time', []);
if (!is_array($facilityUnitIds)) {
$facilityUnitIds = [];
}
// Deactivate old schedules
$db->update('sa_group_schedule', [
'is_active' => 0,
'updated_at' => date('Y-m-d H:i:s'),
], 'group_id = ? AND is_active = 1', [(int) $id]);
// Insert new entries
$count = min(count($facilityUnitIds), count($daysOfWeek), count($startTimes), count($endTimes));
$inserted = 0;
for ($i = 0; $i < $count; $i++) {
$unitId = (int) ($facilityUnitIds[$i] ?? 0);
$day = (int) ($daysOfWeek[$i] ?? 0);
$start = trim((string) ($startTimes[$i] ?? ''));
$end = trim((string) ($endTimes[$i] ?? ''));
if ($unitId === 0 || $start === '' || $end === '') {
continue;
}
$db->insert('sa_group_schedule', [
'group_id' => (int) $id,
'facility_unit_id' => $unitId,
'day_of_week' => $day,
'start_time' => $start,
'end_time' => $end,
'is_active' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
$inserted++;
}
return $this->redirect('/sa/groups/' . $id)->withSuccess("تم حفظ الجدول ({$inserted} حصة)");
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Modules\SportsActivity\Models\Facility;
use App\Modules\SportsActivity\Services\MirrorStateService;
class MirrorController extends Controller
{
/**
* List all active facilities with links to mirror views.
*/
public function index(Request $request): Response
{
$facilities = Facility::getActive();
return $this->view('SportsActivity.Views.mirror.index', [
'facilities' => $facilities,
]);
}
/**
* Mirror view for a facility on today's date.
*/
public function facility(Request $request, string $id): Response
{
$today = date('Y-m-d');
return $this->renderFacilityView((int) $id, $today);
}
/**
* Mirror view for a facility on a specific date.
*/
public function facilityDate(Request $request, string $id, string $date): Response
{
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
return $this->redirect('/sa/mirror/' . $id)->withError('تاريخ غير صالح');
}
return $this->renderFacilityView((int) $id, $date);
}
/**
* Render the facility mirror grid.
*/
private function renderFacilityView(int $facilityId, string $date): Response
{
$state = MirrorStateService::getFacilityState($facilityId, $date);
if (isset($state['error'])) {
return $this->redirect('/sa/mirror')->withError($state['error']);
}
return $this->view('SportsActivity.Views.mirror.facility_view', [
'facility' => $state['facility'],
'units' => $state['units'],
'grid' => $state['grid'],
'slots' => $state['slots'],
'date' => $state['date'],
]);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\Pagination;
class PlayerController extends Controller
{
/**
* List players with search, filters, and pagination.
*/
public function index(Request $request): Response
{
$db = App::getInstance()->db();
$search = trim((string) $request->get('q', ''));
$playerType = trim((string) $request->get('player_type', ''));
$medicalStatus = trim((string) $request->get('medical_status', ''));
$page = max(1, (int) $request->get('page', 1));
$perPage = 25;
$where = ['1=1'];
$params = [];
if ($search !== '') {
$where[] = '(p.full_name_ar LIKE ? OR p.national_id LIKE ? OR p.serial_number LIKE ?)';
$params[] = "%{$search}%";
$params[] = "%{$search}%";
$params[] = "%{$search}%";
}
if ($playerType !== '') {
$where[] = 'p.player_type = ?';
$params[] = $playerType;
}
if ($medicalStatus !== '') {
$where[] = 'p.medical_status = ?';
$params[] = $medicalStatus;
}
$whereSql = implode(' AND ', $where);
// Count total
$countSql = "SELECT COUNT(*) as total FROM sa_players p WHERE {$whereSql}";
$total = (int) $db->selectOne($countSql, $params)['total'];
$pagination = Pagination::paginate($total, $perPage, $page);
$offset = ($pagination['current_page'] - 1) * $perPage;
// Fetch players
$sql = "SELECT p.* FROM sa_players p WHERE {$whereSql} ORDER BY p.created_at DESC LIMIT {$perPage} OFFSET {$offset}";
$players = $db->select($sql, $params);
return $this->view('SportsActivity.Views.players.index', [
'players' => $players,
'pagination' => $pagination,
'filters' => [
'q' => $search,
'player_type' => $playerType,
'medical_status' => $medicalStatus,
],
]);
}
/**
* Show the create player form.
*/
public function create(Request $request): Response
{
return $this->view('SportsActivity.Views.players.create', []);
}
/**
* Validate and store a new player.
*/
public function store(Request $request): Response
{
$db = App::getInstance()->db();
$session = App::getInstance()->session();
$fullNameAr = trim((string) $request->post('full_name_ar', ''));
$fullNameEn = trim((string) $request->post('full_name_en', ''));
$playerType = trim((string) $request->post('player_type', ''));
$memberId = (int) $request->post('member_id', 0);
$nationalId = trim((string) $request->post('national_id', ''));
$dateOfBirth = trim((string) $request->post('date_of_birth', ''));
$gender = trim((string) $request->post('gender', 'male'));
$phone = trim((string) $request->post('phone', ''));
$email = trim((string) $request->post('email', ''));
$guardianName = trim((string) $request->post('guardian_name', ''));
$guardianPhone = trim((string) $request->post('guardian_phone', ''));
$guardianNationalId = trim((string) $request->post('guardian_national_id', ''));
$guardianRelationship = trim((string) $request->post('guardian_relationship', ''));
$notes = trim((string) $request->post('notes', ''));
// Validation
$errors = [];
if ($fullNameAr === '') {
$errors[] = 'الاسم بالعربي مطلوب';
}
if ($playerType === '') {
$errors[] = 'نوع اللاعب مطلوب';
}
if ($playerType === 'member' && $memberId <= 0) {
$errors[] = 'رقم العضوية مطلوب للأعضاء';
}
$validTypes = ['member', 'non_member', 'academy_player'];
if (!in_array($playerType, $validTypes, true)) {
$errors[] = 'نوع اللاعب غير صالح';
}
if (!empty($errors)) {
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/players/create');
}
// Generate serial number
$lastSerial = $db->selectOne("SELECT serial_number FROM sa_players ORDER BY id DESC LIMIT 1");
$nextNum = 1;
if ($lastSerial && !empty($lastSerial['serial_number'])) {
$parts = explode('-', $lastSerial['serial_number']);
$nextNum = (int) end($parts) + 1;
}
$serialNumber = 'SAP-' . str_pad((string) $nextNum, 5, '0', STR_PAD_LEFT);
$data = [
'serial_number' => $serialNumber,
'full_name_ar' => $fullNameAr,
'full_name_en' => $fullNameEn ?: null,
'player_type' => $playerType,
'member_id' => $memberId > 0 ? $memberId : null,
'national_id' => $nationalId ?: null,
'date_of_birth' => $dateOfBirth ?: null,
'gender' => $gender,
'phone' => $phone ?: null,
'email' => $email ?: null,
'guardian_name' => $guardianName ?: null,
'guardian_phone' => $guardianPhone ?: null,
'guardian_national_id' => $guardianNationalId ?: null,
'guardian_relationship' => $guardianRelationship ?: null,
'medical_status' => 'pending',
'card_status' => 'not_issued',
'notes' => $notes ?: null,
'created_at' => now(),
'updated_at' => now(),
];
$db->insert('sa_players', $data);
$playerId = (int) $db->selectOne("SELECT LAST_INSERT_ID() as id")['id'];
return $this->redirect('/sa/players/' . $playerId)->withSuccess('تم إضافة اللاعب بنجاح');
}
/**
* Show player detail with documents list.
*/
public function show(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$player = $db->selectOne("SELECT * FROM sa_players WHERE id = ?", [(int) $id]);
if (!$player) {
return $this->redirect('/sa/players')->withError('اللاعب غير موجود');
}
$documents = $db->select(
"SELECT * FROM sa_player_documents WHERE player_id = ? ORDER BY created_at DESC",
[(int) $id]
);
return $this->view('SportsActivity.Views.players.show', [
'player' => $player,
'documents' => $documents,
]);
}
/**
* Show edit form for a player.
*/
public function edit(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$player = $db->selectOne("SELECT * FROM sa_players WHERE id = ?", [(int) $id]);
if (!$player) {
return $this->redirect('/sa/players')->withError('اللاعب غير موجود');
}
return $this->view('SportsActivity.Views.players.edit', [
'player' => $player,
]);
}
/**
* Validate and update a player.
*/
public function update(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$session = App::getInstance()->session();
$player = $db->selectOne("SELECT * FROM sa_players WHERE id = ?", [(int) $id]);
if (!$player) {
return $this->redirect('/sa/players')->withError('اللاعب غير موجود');
}
$fullNameAr = trim((string) $request->post('full_name_ar', ''));
$fullNameEn = trim((string) $request->post('full_name_en', ''));
$playerType = trim((string) $request->post('player_type', ''));
$memberId = (int) $request->post('member_id', 0);
$nationalId = trim((string) $request->post('national_id', ''));
$dateOfBirth = trim((string) $request->post('date_of_birth', ''));
$gender = trim((string) $request->post('gender', 'male'));
$phone = trim((string) $request->post('phone', ''));
$email = trim((string) $request->post('email', ''));
$guardianName = trim((string) $request->post('guardian_name', ''));
$guardianPhone = trim((string) $request->post('guardian_phone', ''));
$guardianNationalId = trim((string) $request->post('guardian_national_id', ''));
$guardianRelationship = trim((string) $request->post('guardian_relationship', ''));
$notes = trim((string) $request->post('notes', ''));
// Validation
$errors = [];
if ($fullNameAr === '') {
$errors[] = 'الاسم بالعربي مطلوب';
}
if ($playerType === '') {
$errors[] = 'نوع اللاعب مطلوب';
}
if ($playerType === 'member' && $memberId <= 0) {
$errors[] = 'رقم العضوية مطلوب للأعضاء';
}
if (!empty($errors)) {
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/players/' . $id . '/edit');
}
$data = [
'full_name_ar' => $fullNameAr,
'full_name_en' => $fullNameEn ?: null,
'player_type' => $playerType,
'member_id' => $memberId > 0 ? $memberId : null,
'national_id' => $nationalId ?: null,
'date_of_birth' => $dateOfBirth ?: null,
'gender' => $gender,
'phone' => $phone ?: null,
'email' => $email ?: null,
'guardian_name' => $guardianName ?: null,
'guardian_phone' => $guardianPhone ?: null,
'guardian_national_id' => $guardianNationalId ?: null,
'guardian_relationship' => $guardianRelationship ?: null,
'notes' => $notes ?: null,
'updated_at' => now(),
];
$db->update('sa_players', $data, 'id = ?', [(int) $id]);
return $this->redirect('/sa/players/' . $id)->withSuccess('تم تحديث بيانات اللاعب بنجاح');
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
class PlayerDocumentController extends Controller
{
/**
* List documents for a player.
*/
public function index(Request $request, string $pid): Response
{
$db = App::getInstance()->db();
$player = $db->selectOne("SELECT * FROM sa_players WHERE id = ?", [(int) $pid]);
if (!$player) {
return $this->redirect('/sa/players')->withError('اللاعب غير موجود');
}
$documents = $db->select(
"SELECT * FROM sa_player_documents WHERE player_id = ? ORDER BY created_at DESC",
[(int) $pid]
);
return $this->view('SportsActivity.Views.players.documents', [
'player' => $player,
'documents' => $documents,
]);
}
/**
* Handle file upload for a player document.
*/
public function upload(Request $request, string $pid): Response
{
$db = App::getInstance()->db();
$session = App::getInstance()->session();
$player = $db->selectOne("SELECT * FROM sa_players WHERE id = ?", [(int) $pid]);
if (!$player) {
return $this->redirect('/sa/players')->withError('اللاعب غير موجود');
}
$documentType = trim((string) $request->post('document_type', ''));
$title = trim((string) $request->post('title', ''));
// Validation
$errors = [];
if ($documentType === '') {
$errors[] = 'نوع المستند مطلوب';
}
if ($title === '') {
$errors[] = 'عنوان المستند مطلوب';
}
$file = $_FILES['document_file'] ?? null;
if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
$errors[] = 'الملف مطلوب';
}
if (!empty($errors)) {
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
return $this->redirect('/sa/players/' . $pid . '/documents');
}
// Upload file
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$newName = uniqid('doc_') . '.' . $ext;
$uploadDir = dirname(__DIR__, 4) . '/public/uploads/sa_documents/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$dest = $uploadDir . $newName;
move_uploaded_file($file['tmp_name'], $dest);
$filePath = 'uploads/sa_documents/' . $newName;
$data = [
'player_id' => (int) $pid,
'document_type' => $documentType,
'title' => $title,
'file_path' => $filePath,
'original_name' => $file['name'],
'approval_status' => 'pending',
'uploaded_by' => (int) (App::getInstance()->session()->get('employee_id') ?? 0),
'created_at' => now(),
'updated_at' => now(),
];
$db->insert('sa_player_documents', $data);
return $this->redirect('/sa/players/' . $pid . '/documents')->withSuccess('تم رفع المستند بنجاح');
}
/**
* Approve a document (medical certificate).
* Sets approval_status='approved', updates player medical_status='fit' with expiry_date.
*/
public function approve(Request $request, string $pid, string $id): Response
{
$db = App::getInstance()->db();
$session = App::getInstance()->session();
$player = $db->selectOne("SELECT * FROM sa_players WHERE id = ?", [(int) $pid]);
if (!$player) {
return $this->redirect('/sa/players')->withError('اللاعب غير موجود');
}
$document = $db->selectOne(
"SELECT * FROM sa_player_documents WHERE id = ? AND player_id = ?",
[(int) $id, (int) $pid]
);
if (!$document) {
return $this->redirect('/sa/players/' . $pid . '/documents')->withError('المستند غير موجود');
}
$expiryDate = trim((string) $request->post('expiry_date', ''));
if ($expiryDate === '') {
$session->flash('_alerts', [['type' => 'error', 'message' => 'تاريخ انتهاء الصلاحية مطلوب']]);
return $this->redirect('/sa/players/' . $pid . '/documents');
}
// Update document
$db->update('sa_player_documents', [
'approval_status' => 'approved',
'approved_by' => (int) ($session->get('employee_id') ?? 0),
'approved_at' => now(),
'expiry_date' => $expiryDate,
'updated_at' => now(),
], 'id = ?', [(int) $id]);
// Update player medical status
$db->update('sa_players', [
'medical_status' => 'fit',
'medical_expiry_date' => $expiryDate,
'updated_at' => now(),
], 'id = ?', [(int) $pid]);
return $this->redirect('/sa/players/' . $pid . '/documents')->withSuccess('تم اعتماد المستند وتحديث الحالة الطبية');
}
/**
* Reject a document.
* Sets approval_status='rejected', updates player medical_status='unfit'.
*/
public function reject(Request $request, string $pid, string $id): Response
{
$db = App::getInstance()->db();
$session = App::getInstance()->session();
$player = $db->selectOne("SELECT * FROM sa_players WHERE id = ?", [(int) $pid]);
if (!$player) {
return $this->redirect('/sa/players')->withError('اللاعب غير موجود');
}
$document = $db->selectOne(
"SELECT * FROM sa_player_documents WHERE id = ? AND player_id = ?",
[(int) $id, (int) $pid]
);
if (!$document) {
return $this->redirect('/sa/players/' . $pid . '/documents')->withError('المستند غير موجود');
}
$rejectionReason = trim((string) $request->post('rejection_reason', ''));
if ($rejectionReason === '') {
$session->flash('_alerts', [['type' => 'error', 'message' => 'سبب الرفض مطلوب']]);
return $this->redirect('/sa/players/' . $pid . '/documents');
}
// Update document
$db->update('sa_player_documents', [
'approval_status' => 'rejected',
'rejection_reason' => $rejectionReason,
'approved_by' => (int) ($session->get('employee_id') ?? 0),
'approved_at' => now(),
'updated_at' => now(),
], 'id = ?', [(int) $id]);
// Update player medical status
$db->update('sa_players', [
'medical_status' => 'unfit',
'updated_at' => now(),
], 'id = ?', [(int) $pid]);
return $this->redirect('/sa/players/' . $pid . '/documents')->withSuccess('تم رفض المستند');
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\App;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
class PricingController extends Controller
{
public function index(Request $request): Response
{
$db = App::getInstance()->db();
$facilities = $db->select(
"SELECT f.id, f.code, f.name_ar, f.facility_type,
COUNT(DISTINCT fu.id) as unit_count,
COUNT(DISTINCT pr.id) as rule_count
FROM sa_facilities f
LEFT JOIN sa_facility_units fu ON fu.facility_id = f.id AND fu.is_active = 1
LEFT JOIN sa_pricing_rules pr ON pr.facility_unit_id = fu.id AND pr.is_active = 1
WHERE f.is_archived = 0 AND f.is_active = 1
GROUP BY f.id
ORDER BY f.name_ar",
[]
);
return $this->view('SportsActivity.Views.pricing.index', [
'facilities' => $facilities,
]);
}
public function rules(Request $request): Response
{
$db = App::getInstance()->db();
$unitId = (int) $request->get('unit_id', 0);
$facilityId = (int) $request->get('facility_id', 0);
if ($facilityId > 0) {
$facility = $db->selectOne("SELECT * FROM sa_facilities WHERE id = ? AND is_archived = 0", [$facilityId]);
if (!$facility) {
return $this->redirect('/sa/pricing')->withError('المرفق غير موجود');
}
$units = $db->select(
"SELECT * FROM sa_facility_units WHERE facility_id = ? AND is_active = 1 ORDER BY sort_order",
[$facilityId]
);
$brackets = $db->select(
"SELECT * FROM sa_time_brackets WHERE facility_id = ? AND is_active = 1 ORDER BY start_time",
[$facilityId]
);
} else {
$facility = null;
$units = [];
$brackets = [];
}
$rules = [];
if ($unitId > 0) {
$rules = $db->select(
"SELECT pr.*, tb.name_ar as bracket_name, tb.bracket_type, tb.start_time as bracket_start, tb.end_time as bracket_end
FROM sa_pricing_rules pr
JOIN sa_time_brackets tb ON tb.id = pr.time_bracket_id
WHERE pr.facility_unit_id = ? AND pr.is_active = 1
ORDER BY tb.start_time, pr.group_size_min",
[$unitId]
);
}
$allFacilities = $db->select(
"SELECT id, name_ar FROM sa_facilities WHERE is_archived = 0 AND is_active = 1 ORDER BY name_ar",
[]
);
return $this->view('SportsActivity.Views.pricing.rules', [
'facility' => $facility,
'facilities' => $allFacilities,
'units' => $units,
'brackets' => $brackets,
'rules' => $rules,
'selectedUnit' => $unitId,
'selectedFacility' => $facilityId,
]);
}
public function storeRule(Request $request): Response
{
$db = App::getInstance()->db();
$data = [
'facility_unit_id' => (int) $request->post('facility_unit_id', 0),
'time_bracket_id' => (int) $request->post('time_bracket_id', 0),
'group_size_min' => (int) $request->post('group_size_min', 1),
'group_size_max' => (int) $request->post('group_size_max', 1),
'price_per_person_member' => $request->post('price_per_person_member', '0'),
'price_per_person_nonmember' => $request->post('price_per_person_nonmember', '0'),
'effective_from' => $request->post('effective_from', ''),
'effective_to' => $request->post('effective_to', '') ?: null,
];
$errors = [];
if ($data['facility_unit_id'] <= 0) $errors[] = 'الوحدة مطلوبة';
if ($data['time_bracket_id'] <= 0) $errors[] = 'الفترة الزمنية مطلوبة';
if ($data['group_size_min'] < 1) $errors[] = 'الحد الأدنى للمجموعة يجب أن يكون 1 على الأقل';
if ($data['group_size_max'] < $data['group_size_min']) $errors[] = 'الحد الأقصى يجب أن يكون أكبر أو يساوي الحد الأدنى';
if ((float) $data['price_per_person_member'] <= 0) $errors[] = 'سعر العضو مطلوب';
if ((float) $data['price_per_person_nonmember'] <= 0) $errors[] = 'سعر غير العضو مطلوب';
if ($data['effective_from'] === '') $errors[] = 'تاريخ البداية مطلوب';
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
$unit = $db->selectOne("SELECT facility_id FROM sa_facility_units WHERE id = ?", [$data['facility_unit_id']]);
$facilityId = $unit ? (int) $unit['facility_id'] : 0;
return $this->redirect("/sa/pricing/rules?facility_id={$facilityId}&unit_id={$data['facility_unit_id']}");
}
$data['is_active'] = 1;
$data['created_by'] = (int) (App::getInstance()->session()->get('employee_id') ?? 0);
$data['created_at'] = date('Y-m-d H:i:s');
$data['updated_at'] = date('Y-m-d H:i:s');
$db->insert('sa_pricing_rules', $data);
$unit = $db->selectOne("SELECT facility_id FROM sa_facility_units WHERE id = ?", [$data['facility_unit_id']]);
$facilityId = $unit ? (int) $unit['facility_id'] : 0;
return $this->redirect("/sa/pricing/rules?facility_id={$facilityId}&unit_id={$data['facility_unit_id']}")
->withSuccess('تم إضافة قاعدة التسعير');
}
public function updateRule(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$rule = $db->selectOne("SELECT * FROM sa_pricing_rules WHERE id = ?", [(int) $id]);
if (!$rule) {
return $this->redirect('/sa/pricing')->withError('القاعدة غير موجودة');
}
$update = [
'group_size_min' => (int) $request->post('group_size_min', $rule['group_size_min']),
'group_size_max' => (int) $request->post('group_size_max', $rule['group_size_max']),
'price_per_person_member' => $request->post('price_per_person_member', $rule['price_per_person_member']),
'price_per_person_nonmember' => $request->post('price_per_person_nonmember', $rule['price_per_person_nonmember']),
'effective_from' => $request->post('effective_from', $rule['effective_from']),
'effective_to' => $request->post('effective_to', '') ?: null,
'updated_at' => date('Y-m-d H:i:s'),
];
$db->update('sa_pricing_rules', $update, 'id = ?', [(int) $id]);
$facilityId = 0;
$unit = $db->selectOne("SELECT facility_id FROM sa_facility_units WHERE id = ?", [(int) $rule['facility_unit_id']]);
if ($unit) $facilityId = (int) $unit['facility_id'];
return $this->redirect("/sa/pricing/rules?facility_id={$facilityId}&unit_id={$rule['facility_unit_id']}")
->withSuccess('تم تحديث القاعدة');
}
public function deactivateRule(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$rule = $db->selectOne("SELECT * FROM sa_pricing_rules WHERE id = ?", [(int) $id]);
if (!$rule) {
return $this->redirect('/sa/pricing')->withError('القاعدة غير موجودة');
}
$db->update('sa_pricing_rules', ['is_active' => 0, 'updated_at' => date('Y-m-d H:i:s')], 'id = ?', [(int) $id]);
$facilityId = 0;
$unit = $db->selectOne("SELECT facility_id FROM sa_facility_units WHERE id = ?", [(int) $rule['facility_unit_id']]);
if ($unit) $facilityId = (int) $unit['facility_id'];
return $this->redirect("/sa/pricing/rules?facility_id={$facilityId}&unit_id={$rule['facility_unit_id']}")
->withSuccess('تم تعطيل القاعدة');
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\Pagination;
use App\Modules\SportsActivity\Models\Program;
use App\Modules\SportsActivity\Models\Discipline;
class ProgramController extends Controller
{
/**
* List programs with search, filter by discipline, pagination.
*/
public function index(Request $request): Response
{
$filters = [
'q' => trim((string) $request->get('q', '')),
'discipline_id' => trim((string) $request->get('discipline_id', '')),
];
$page = max(1, (int) $request->get('page', 1));
$perPage = 25;
$db = App::getInstance()->db();
$where = "p.is_archived = 0";
$params = [];
if ($filters['q'] !== '') {
$where .= " AND (p.name_ar LIKE ? OR p.code LIKE ?)";
$params[] = '%' . $filters['q'] . '%';
$params[] = '%' . $filters['q'] . '%';
}
if ($filters['discipline_id'] !== '') {
$where .= " AND p.discipline_id = ?";
$params[] = (int) $filters['discipline_id'];
}
$total = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM sa_programs p WHERE {$where}", $params)['cnt'] ?? 0);
$pagination = Pagination::paginate($total, $perPage, $page);
$offset = ($page - 1) * $perPage;
$programs = $db->select(
"SELECT p.*, d.name_ar as discipline_name,
(SELECT COUNT(*) FROM sa_groups g WHERE g.program_id = p.id AND g.is_archived = 0) as group_count
FROM sa_programs p
LEFT JOIN sa_disciplines d ON d.id = p.discipline_id
WHERE {$where}
ORDER BY p.name_ar ASC
LIMIT {$perPage} OFFSET {$offset}",
$params
);
$disciplines = Discipline::getActive();
return $this->view('SportsActivity.Views.programs.index', [
'programs' => $programs,
'pagination' => $pagination,
'filters' => $filters,
'disciplines' => $disciplines,
]);
}
/**
* Show create program form.
*/
public function create(Request $request): Response
{
$disciplines = Discipline::getActive();
$db = App::getInstance()->db();
$academies = $db->select("SELECT id, name_ar FROM sa_academies WHERE is_archived = 0 ORDER BY name_ar", []);
return $this->view('SportsActivity.Views.programs.create', [
'disciplines' => $disciplines,
'academies' => $academies,
'programTypes' => Program::getProgramTypeOptions(),
'skillLevels' => Program::getSkillLevelOptions(),
]);
}
/**
* Validate and store a new program.
*/
public function store(Request $request): Response
{
$code = strtoupper(trim((string) $request->post('code', '')));
$nameAr = trim((string) $request->post('name_ar', ''));
$nameEn = trim((string) $request->post('name_en', ''));
$disciplineId = (int) $request->post('discipline_id', 0);
$academyId = $request->post('academy_id', '') !== '' ? (int) $request->post('academy_id', 0) : null;
$programType = trim((string) $request->post('program_type', ''));
$ageFrom = $request->post('age_from', '') !== '' ? (int) $request->post('age_from', 0) : null;
$ageTo = $request->post('age_to', '') !== '' ? (int) $request->post('age_to', 0) : null;
$genderRestriction = trim((string) $request->post('gender_restriction', 'mixed'));
$skillLevel = trim((string) $request->post('skill_level', ''));
$sessionDuration = $request->post('session_duration_minutes', '') !== '' ? (int) $request->post('session_duration_minutes', 0) : null;
$sessionsPerWeek = $request->post('sessions_per_week', '') !== '' ? (int) $request->post('sessions_per_week', 0) : null;
$descriptionAr = trim((string) $request->post('description_ar', ''));
$errors = [];
if ($code === '') {
$errors[] = 'كود البرنامج مطلوب';
}
if ($nameAr === '' || mb_strlen($nameAr) < 2) {
$errors[] = 'اسم البرنامج بالعربي مطلوب (حرفان على الأقل)';
}
if ($disciplineId === 0) {
$errors[] = 'النشاط الرياضي (اللعبة) مطلوب';
}
// Check unique code
if ($code !== '') {
$db = App::getInstance()->db();
$existing = $db->selectOne("SELECT id FROM sa_programs WHERE code = ?", [$code]);
if ($existing) {
$errors[] = 'كود البرنامج مستخدم بالفعل';
}
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/programs/create');
}
$program = Program::create([
'code' => $code,
'name_ar' => $nameAr,
'name_en' => $nameEn ?: null,
'discipline_id' => $disciplineId,
'academy_id' => $academyId,
'program_type' => $programType ?: null,
'age_from' => $ageFrom,
'age_to' => $ageTo,
'gender_restriction' => $genderRestriction,
'skill_level' => $skillLevel ?: null,
'session_duration_minutes' => $sessionDuration,
'sessions_per_week' => $sessionsPerWeek,
'description_ar' => $descriptionAr ?: null,
'is_active' => 1,
]);
return $this->redirect('/sa/programs/' . $program->id)->withSuccess('تم إضافة البرنامج بنجاح');
}
/**
* Show program detail page with groups list.
*/
public function show(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$program = $db->selectOne(
"SELECT p.*, d.name_ar as discipline_name, a.name_ar as academy_name
FROM sa_programs p
LEFT JOIN sa_disciplines d ON d.id = p.discipline_id
LEFT JOIN sa_academies a ON a.id = p.academy_id
WHERE p.id = ? AND p.is_archived = 0",
[(int) $id]
);
if (!$program) {
return $this->redirect('/sa/programs')->withError('البرنامج غير موجود');
}
$groups = $db->select(
"SELECT g.*, c.name_ar as coach_name
FROM sa_groups g
LEFT JOIN sa_coaches c ON c.id = g.coach_id
WHERE g.program_id = ? AND g.is_archived = 0
ORDER BY g.name_ar ASC",
[(int) $id]
);
return $this->view('SportsActivity.Views.programs.show', [
'program' => $program,
'groups' => $groups,
'programTypes' => Program::getProgramTypeOptions(),
'skillLevels' => Program::getSkillLevelOptions(),
]);
}
/**
* Show edit form for a program.
*/
public function edit(Request $request, string $id): Response
{
$program = Program::find((int) $id);
if (!$program) {
return $this->redirect('/sa/programs')->withError('البرنامج غير موجود');
}
$disciplines = Discipline::getActive();
$db = App::getInstance()->db();
$academies = $db->select("SELECT id, name_ar FROM sa_academies WHERE is_archived = 0 ORDER BY name_ar", []);
return $this->view('SportsActivity.Views.programs.edit', [
'program' => $program,
'disciplines' => $disciplines,
'academies' => $academies,
'programTypes' => Program::getProgramTypeOptions(),
'skillLevels' => Program::getSkillLevelOptions(),
]);
}
/**
* Validate and update an existing program.
*/
public function update(Request $request, string $id): Response
{
$program = Program::find((int) $id);
if (!$program) {
return $this->redirect('/sa/programs')->withError('البرنامج غير موجود');
}
$code = strtoupper(trim((string) $request->post('code', '')));
$nameAr = trim((string) $request->post('name_ar', ''));
$nameEn = trim((string) $request->post('name_en', ''));
$disciplineId = (int) $request->post('discipline_id', 0);
$academyId = $request->post('academy_id', '') !== '' ? (int) $request->post('academy_id', 0) : null;
$programType = trim((string) $request->post('program_type', ''));
$ageFrom = $request->post('age_from', '') !== '' ? (int) $request->post('age_from', 0) : null;
$ageTo = $request->post('age_to', '') !== '' ? (int) $request->post('age_to', 0) : null;
$genderRestriction = trim((string) $request->post('gender_restriction', 'mixed'));
$skillLevel = trim((string) $request->post('skill_level', ''));
$sessionDuration = $request->post('session_duration_minutes', '') !== '' ? (int) $request->post('session_duration_minutes', 0) : null;
$sessionsPerWeek = $request->post('sessions_per_week', '') !== '' ? (int) $request->post('sessions_per_week', 0) : null;
$descriptionAr = trim((string) $request->post('description_ar', ''));
$errors = [];
if ($code === '') {
$errors[] = 'كود البرنامج مطلوب';
}
if ($nameAr === '' || mb_strlen($nameAr) < 2) {
$errors[] = 'اسم البرنامج بالعربي مطلوب (حرفان على الأقل)';
}
if ($disciplineId === 0) {
$errors[] = 'النشاط الرياضي (اللعبة) مطلوب';
}
// Check unique code (exclude current)
if ($code !== '') {
$db = App::getInstance()->db();
$existing = $db->selectOne("SELECT id FROM sa_programs WHERE code = ? AND id != ?", [$code, (int) $id]);
if ($existing) {
$errors[] = 'كود البرنامج مستخدم بالفعل';
}
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
return $this->redirect('/sa/programs/' . $id . '/edit');
}
$program->update([
'code' => $code,
'name_ar' => $nameAr,
'name_en' => $nameEn ?: null,
'discipline_id' => $disciplineId,
'academy_id' => $academyId,
'program_type' => $programType ?: null,
'age_from' => $ageFrom,
'age_to' => $ageTo,
'gender_restriction' => $genderRestriction,
'skill_level' => $skillLevel ?: null,
'session_duration_minutes' => $sessionDuration,
'sessions_per_week' => $sessionsPerWeek,
'description_ar' => $descriptionAr ?: null,
]);
return $this->redirect('/sa/programs/' . $id)->withSuccess('تم تحديث البرنامج بنجاح');
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Modules\SportsActivity\Services\ScheduleGeneratorService;
class ScheduleController extends Controller
{
/**
* Redirect to daily view with today's date.
*/
public function calendar(Request $request): Response
{
return $this->redirect('/sa/schedule/daily/' . date('Y-m-d'));
}
/**
* Show all bookings for a given date across all facilities.
*/
public function daily(Request $request, string $date): Response
{
$db = App::getInstance()->db();
// Validate date format
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
$date = date('Y-m-d');
}
$bookings = $db->select(
"SELECT b.*, fu.name_ar as unit_name, fu.code as unit_code,
f.name_ar as facility_name, f.id as facility_id,
g.name_ar as group_name, c.name_ar as coach_name
FROM sa_bookings b
JOIN sa_facility_units fu ON fu.id = b.facility_unit_id
JOIN sa_facilities f ON f.id = fu.facility_id
LEFT JOIN sa_groups g ON g.id = b.group_id
LEFT JOIN sa_coaches c ON c.id = b.coach_id
WHERE b.booking_date = ? AND b.status != 'cancelled'
ORDER BY f.name_ar ASC, fu.name_ar ASC, b.start_time ASC",
[$date]
);
// Group bookings by facility -> unit
$byFacility = [];
foreach ($bookings as $b) {
$key = $b['facility_name'];
if (!isset($byFacility[$key])) {
$byFacility[$key] = [];
}
$unitKey = $b['unit_name'] . ' (' . $b['unit_code'] . ')';
if (!isset($byFacility[$key][$unitKey])) {
$byFacility[$key][$unitKey] = [];
}
$byFacility[$key][$unitKey][] = $b;
}
// Get all active facility units for the full grid
$allUnits = $db->select(
"SELECT fu.id, fu.name_ar, fu.code, f.name_ar as facility_name
FROM sa_facility_units fu
JOIN sa_facilities f ON f.id = fu.facility_id
WHERE fu.is_archived = 0 AND fu.is_active = 1
ORDER BY f.name_ar ASC, fu.name_ar ASC",
[]
);
// Groups for the generate form
$groups = $db->select(
"SELECT g.id, g.name_ar, g.code FROM sa_groups g WHERE g.status = 'active' AND g.is_archived = 0 ORDER BY g.name_ar",
[]
);
return $this->view('SportsActivity.Views.schedule.daily', [
'date' => $date,
'byFacility' => $byFacility,
'allUnits' => $allUnits,
'groups' => $groups,
]);
}
/**
* Show current week schedule as grid (facilities x days).
*/
public function weekly(Request $request): Response
{
$db = App::getInstance()->db();
// Determine week start (Saturday) and end (Friday) for Arabic week
$weekStart = trim((string) $request->get('week_start', ''));
if ($weekStart === '' || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $weekStart)) {
// Find last Saturday
$now = new \DateTime();
$dayOfWeek = (int) $now->format('w'); // 0=Sun, 6=Sat
$diff = ($dayOfWeek + 1) % 7; // days since Saturday
$now->modify("-{$diff} days");
$weekStart = $now->format('Y-m-d');
}
$startDate = new \DateTime($weekStart);
$endDate = (clone $startDate)->modify('+6 days');
$days = [];
$dayNames = ['السبت', 'الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة'];
$current = clone $startDate;
for ($i = 0; $i < 7; $i++) {
$days[] = [
'date' => $current->format('Y-m-d'),
'name' => $dayNames[$i],
'day' => $current->format('d'),
];
$current->modify('+1 day');
}
// Get facility units
$units = $db->select(
"SELECT fu.id, fu.name_ar, fu.code, f.name_ar as facility_name
FROM sa_facility_units fu
JOIN sa_facilities f ON f.id = fu.facility_id
WHERE fu.is_archived = 0 AND fu.is_active = 1
ORDER BY f.name_ar ASC, fu.name_ar ASC",
[]
);
// Get booking counts per unit per day
$counts = $db->select(
"SELECT facility_unit_id, booking_date, COUNT(*) as cnt
FROM sa_bookings
WHERE booking_date BETWEEN ? AND ? AND status != 'cancelled'
GROUP BY facility_unit_id, booking_date",
[$startDate->format('Y-m-d'), $endDate->format('Y-m-d')]
);
// Build lookup: unit_id -> date -> count
$countMap = [];
foreach ($counts as $row) {
$countMap[(int) $row['facility_unit_id']][$row['booking_date']] = (int) $row['cnt'];
}
return $this->view('SportsActivity.Views.schedule.weekly', [
'days' => $days,
'units' => $units,
'countMap' => $countMap,
'weekStart' => $startDate->format('Y-m-d'),
'prevWeek' => (clone $startDate)->modify('-7 days')->format('Y-m-d'),
'nextWeek' => (clone $startDate)->modify('+7 days')->format('Y-m-d'),
]);
}
/**
* Generate training bookings for a group + date range.
*/
public function generate(Request $request): Response
{
$groupId = (int) $request->post('group_id', 0);
$fromDate = trim((string) $request->post('from_date', ''));
$toDate = trim((string) $request->post('to_date', ''));
$errors = [];
if ($groupId === 0) {
$errors[] = 'يرجى اختيار المجموعة';
}
if ($fromDate === '') {
$errors[] = 'تاريخ البداية مطلوب';
}
if ($toDate === '') {
$errors[] = 'تاريخ النهاية مطلوب';
}
if (!empty($errors)) {
$session = App::getInstance()->session();
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
return $this->redirect('/sa/schedule/daily/' . ($fromDate ?: date('Y-m-d')));
}
$result = ScheduleGeneratorService::generateForGroup($groupId, $fromDate, $toDate);
if ($result['success']) {
$msg = "تم توليد {$result['generated']} حجز تدريبي";
if ($result['skipped'] > 0) {
$msg .= " (تم تخطي {$result['skipped']})";
}
return $this->redirect('/sa/schedule/daily/' . $fromDate)->withSuccess($msg);
}
return $this->redirect('/sa/schedule/daily/' . ($fromDate ?: date('Y-m-d')))->withError($result['error']);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\Pagination;
use App\Modules\SportsActivity\Models\Subscription;
use App\Modules\SportsActivity\Models\Group;
use App\Modules\SportsActivity\Services\SubscriptionGeneratorService;
class SubscriptionController extends Controller
{
/**
* List subscriptions with filters and pagination.
*/
public function index(Request $request): Response
{
$db = App::getInstance()->db();
$filters = [
'month' => trim((string) $request->get('month', '')),
'group_id' => trim((string) $request->get('group_id', '')),
'payment_status' => trim((string) $request->get('payment_status', '')),
];
$page = max(1, (int) $request->get('page', 1));
$perPage = 25;
$where = [];
$params = [];
if ($filters['month'] !== '') {
$periodStart = $filters['month'] . '-01';
$where[] = "s.period_start = ?";
$params[] = $periodStart;
}
if ($filters['group_id'] !== '') {
$where[] = "s.group_id = ?";
$params[] = (int) $filters['group_id'];
}
if ($filters['payment_status'] !== '') {
$where[] = "s.payment_status = ?";
$params[] = $filters['payment_status'];
}
$whereClause = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
$total = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_subscriptions s {$whereClause}",
$params
)['cnt'] ?? 0);
$pagination = Pagination::paginate($total, $perPage, $page);
$offset = ($page - 1) * $perPage;
$subscriptions = $db->select(
"SELECT s.*, p.full_name_ar as player_name, g.name_ar as group_name
FROM sa_subscriptions s
LEFT JOIN sa_players p ON p.id = s.player_id
LEFT JOIN sa_groups g ON g.id = s.group_id
{$whereClause}
ORDER BY s.period_start DESC, s.created_at DESC
LIMIT {$perPage} OFFSET {$offset}",
$params
);
$groups = Group::getActive();
return $this->view('SportsActivity.Views.subscriptions.index', [
'subscriptions' => $subscriptions,
'pagination' => $pagination,
'filters' => $filters,
'groups' => $groups,
'statusOptions' => Subscription::getPaymentStatusOptions(),
]);
}
/**
* Generate subscriptions for a given month.
*/
public function generate(Request $request): Response
{
$yearMonth = trim((string) $request->post('year_month', ''));
if ($yearMonth === '' || !preg_match('/^\d{4}-\d{2}$/', $yearMonth)) {
return $this->redirect('/sa/subscriptions')->withError('يرجى تحديد شهر صالح');
}
$result = SubscriptionGeneratorService::generateForMonth($yearMonth);
$message = sprintf(
'تم توليد %d اشتراك جديد (تم تخطي %d موجود مسبقا) لشهر %s',
$result['generated'],
$result['skipped'],
$yearMonth
);
return $this->redirect('/sa/subscriptions?month=' . $yearMonth)->withSuccess($message);
}
/**
* Show subscription detail.
*/
public function show(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$subscription = $db->selectOne(
"SELECT s.*, p.full_name_ar as player_name, p.code as player_code,
g.name_ar as group_name, g.code as group_code
FROM sa_subscriptions s
LEFT JOIN sa_players p ON p.id = s.player_id
LEFT JOIN sa_groups g ON g.id = s.group_id
WHERE s.id = ?",
[(int) $id]
);
if (!$subscription) {
return $this->redirect('/sa/subscriptions')->withError('الاشتراك غير موجود');
}
return $this->view('SportsActivity.Views.subscriptions.show', [
'subscription' => $subscription,
'statusOptions' => Subscription::getPaymentStatusOptions(),
]);
}
/**
* Mark subscription as paid.
*/
public function pay(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$subscription = $db->selectOne(
"SELECT * FROM sa_subscriptions WHERE id = ?",
[(int) $id]
);
if (!$subscription) {
return $this->redirect('/sa/subscriptions')->withError('الاشتراك غير موجود');
}
$db->update('sa_subscriptions', [
'payment_status' => 'paid',
'paid_at' => date('Y-m-d H:i:s'),
'paid_amount' => (float) $subscription['final_amount'],
'updated_at' => date('Y-m-d H:i:s'),
], 'id = ?', [(int) $id]);
return $this->redirect('/sa/subscriptions/' . $id)->withSuccess('تم تسجيل الدفع بنجاح');
}
/**
* Mark subscription as exempt.
*/
public function exempt(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$subscription = $db->selectOne(
"SELECT * FROM sa_subscriptions WHERE id = ?",
[(int) $id]
);
if (!$subscription) {
return $this->redirect('/sa/subscriptions')->withError('الاشتراك غير موجود');
}
$reason = trim((string) $request->post('exemption_reason', ''));
if ($reason === '') {
return $this->redirect('/sa/subscriptions/' . $id)->withError('يرجى إدخال سبب الإعفاء');
}
$session = App::getInstance()->session();
$employeeId = (int) ($session->get('employee_id') ?? 0);
$db->update('sa_subscriptions', [
'payment_status' => 'exempt',
'exemption_reason' => $reason,
'exempted_by' => $employeeId,
'updated_at' => date('Y-m-d H:i:s'),
], 'id = ?', [(int) $id]);
return $this->redirect('/sa/subscriptions/' . $id)->withSuccess('تم تسجيل الإعفاء بنجاح');
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\Pagination;
use App\Modules\SportsActivity\Models\Waitlist;
use App\Modules\SportsActivity\Models\Group;
class WaitlistController extends Controller
{
/**
* List all waitlist entries with filters.
*/
public function index(Request $request): Response
{
$db = App::getInstance()->db();
$filters = [
'group_id' => trim((string) $request->get('group_id', '')),
'status' => trim((string) $request->get('status', '')),
];
$page = max(1, (int) $request->get('page', 1));
$perPage = 25;
$where = [];
$params = [];
if ($filters['group_id'] !== '') {
$where[] = "w.group_id = ?";
$params[] = (int) $filters['group_id'];
}
if ($filters['status'] !== '') {
$where[] = "w.status = ?";
$params[] = $filters['status'];
}
$whereClause = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
$total = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_waitlist w {$whereClause}",
$params
)['cnt'] ?? 0);
$pagination = Pagination::paginate($total, $perPage, $page);
$offset = ($page - 1) * $perPage;
$entries = $db->select(
"SELECT w.*, p.full_name_ar as player_name, p.code as player_code,
g.name_ar as group_name
FROM sa_waitlist w
LEFT JOIN sa_players p ON p.id = w.player_id
LEFT JOIN sa_groups g ON g.id = w.group_id
{$whereClause}
ORDER BY w.position ASC, w.created_at ASC
LIMIT {$perPage} OFFSET {$offset}",
$params
);
$groups = Group::getActive();
return $this->view('SportsActivity.Views.waitlist.index', [
'entries' => $entries,
'pagination' => $pagination,
'filters' => $filters,
'groups' => $groups,
'statusOptions' => Waitlist::getStatusOptions(),
]);
}
/**
* Offer a spot to a waitlisted player.
*/
public function offer(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$entry = $db->selectOne(
"SELECT * FROM sa_waitlist WHERE id = ?",
[(int) $id]
);
if (!$entry) {
return $this->redirect('/sa/waitlist')->withError('السجل غير موجود');
}
if ($entry['status'] !== 'waiting') {
return $this->redirect('/sa/waitlist')->withError('لا يمكن تقديم عرض لهذا السجل بحالته الحالية');
}
$now = date('Y-m-d H:i:s');
$expiresAt = date('Y-m-d H:i:s', strtotime('+48 hours'));
$db->update('sa_waitlist', [
'status' => 'offered',
'offered_at' => $now,
'expires_at' => $expiresAt,
'updated_at' => $now,
], 'id = ?', [(int) $id]);
return $this->redirect('/sa/waitlist')->withSuccess('تم تقديم العرض بنجاح - ينتهي خلال 48 ساعة');
}
/**
* Cancel a waitlist entry.
*/
public function cancel(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$entry = $db->selectOne(
"SELECT * FROM sa_waitlist WHERE id = ?",
[(int) $id]
);
if (!$entry) {
return $this->redirect('/sa/waitlist')->withError('السجل غير موجود');
}
$db->update('sa_waitlist', [
'status' => 'cancelled',
'updated_at' => date('Y-m-d H:i:s'),
], 'id = ?', [(int) $id]);
return $this->redirect('/sa/waitlist')->withSuccess('تم إلغاء السجل من قائمة الانتظار');
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
use App\Core\App;
class Academy extends Model
{
protected static string $table = 'sa_academies';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = true;
protected static bool $dispatchEvents = true;
protected static bool $autoTrackAuthor = true;
protected static array $fillable = [
'code',
'name_ar',
'name_en',
'discipline_id',
'academy_type',
'contact_person',
'contact_phone',
'contact_email',
'description_ar',
'logo_path',
'is_active',
'branch_id',
];
/**
* Get academy type options with Arabic labels.
*/
public static function getAcademyTypeOptions(): array
{
return [
'internal' => 'داخلية',
'external' => 'خارجية',
];
}
/**
* Get all contracts for this academy.
*/
public function getContracts(): array
{
$db = App::getInstance()->db();
return $db->select(
"SELECT * FROM `sa_academy_contracts`
WHERE `academy_id` = ? AND `is_archived` = 0
ORDER BY `start_date` DESC",
[$this->id]
);
}
/**
* Get all active (non-archived, active) academies.
*/
public static function getActive(): array
{
return static::query()
->where('is_active', '=', 1)
->where('is_archived', '=', 0)
->orderBy('name_ar', 'ASC')
->get();
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
use App\Core\App;
class AcademyContract extends Model
{
protected static string $table = 'sa_academy_contracts';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = true;
protected static bool $dispatchEvents = true;
protected static bool $autoTrackAuthor = true;
protected static array $fillable = [
'contract_number',
'academy_id',
'contract_type',
'start_date',
'end_date',
'club_commission_pct',
'academy_share_pct',
'fixed_monthly_rent',
'minimum_revenue_guarantee',
'deposit_amount',
'deposit_status',
'contract_pdf_path',
'status',
'approved_by',
'approved_at',
'terminated_by',
'terminated_at',
'termination_reason',
'terms_json',
'notes',
'branch_id',
];
/**
* Get contract type options with Arabic labels.
*/
public static function getContractTypeOptions(): array
{
return [
'revenue_share' => 'نسبة من الإيرادات',
'fixed_rent' => 'إيجار ثابت',
'hybrid' => 'مختلط',
];
}
/**
* Get contract status options with Arabic labels.
*/
public static function getStatusOptions(): array
{
return [
'draft' => 'مسودة',
'pending_approval' => 'قيد الاعتماد',
'active' => 'ساري',
'suspended' => 'موقوف',
'expired' => 'منتهي',
'terminated' => 'ملغى',
];
}
/**
* Get deposit status options with Arabic labels.
*/
public static function getDepositStatusOptions(): array
{
return [
'pending' => 'قيد السداد',
'paid' => 'مدفوع',
'returned' => 'مسترد',
'forfeited' => 'مصادر',
];
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
class Booking extends Model
{
protected static string $table = 'sa_bookings';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = false;
protected static bool $dispatchEvents = true;
protected static bool $autoTrackAuthor = true;
protected static array $fillable = [
'booking_number', 'facility_unit_id', 'booking_type', 'booking_date',
'start_time', 'end_time', 'group_id', 'coach_id', 'booker_type',
'booker_id', 'booker_name', 'participant_count', 'spots_reserved',
'total_amount', 'payment_status', 'status', 'is_recurring',
'recurrence_pattern_json', 'parent_booking_id', 'cancellation_reason',
'cancelled_by', 'cancelled_at', 'notes', 'branch_id',
];
public static function getBookingTypeOptions(): array
{
return [
'hourly' => 'حجز بالساعة',
'training' => 'تدريب',
'maintenance' => 'صيانة',
'blocked' => 'محجوز',
];
}
public static function getStatusOptions(): array
{
return [
'pending' => 'في الانتظار',
'confirmed' => 'مؤكد',
'checked_in' => 'تم الحضور',
'completed' => 'مكتمل',
'cancelled' => 'ملغي',
'no_show' => 'لم يحضر',
];
}
public static function getPaymentStatusOptions(): array
{
return [
'unpaid' => 'غير مدفوع',
'paid' => 'مدفوع',
'partial' => 'مدفوع جزئياً',
'refunded' => 'مسترد',
];
}
public static function generateNumber(): string
{
return 'BK-' . date('Ymd') . '-' . str_pad((string)random_int(1, 9999), 4, '0', STR_PAD_LEFT);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
use App\Core\App;
class Coach extends Model
{
protected static string $table = 'sa_coaches';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = true;
protected static bool $dispatchEvents = true;
protected static bool $autoTrackAuthor = true;
protected static array $fillable = [
'code', 'full_name_ar', 'full_name_en', 'national_id', 'phone', 'email',
'date_of_birth', 'gender', 'photo_path', 'bio_ar', 'certifications_json',
'employment_type', 'employee_id', 'payment_model',
'hourly_rate', 'session_rate', 'monthly_rate',
'max_groups', 'is_active', 'branch_id',
];
public static function getEmploymentTypeOptions(): array
{
return [
'staff' => 'موظف',
'contract' => 'تعاقد',
'freelance' => 'حر',
];
}
public static function getPaymentModelOptions(): array
{
return [
'per_session' => 'بالحصة',
'per_player' => 'باللاعب',
'monthly_fixed' => 'شهري ثابت',
'hybrid' => 'مختلط',
];
}
public function getDisciplines(): array
{
$db = App::getInstance()->db();
return $db->select(
"SELECT cd.*, d.name_ar as discipline_name, d.code as discipline_code
FROM sa_coach_disciplines cd
JOIN sa_disciplines d ON d.id = cd.discipline_id
WHERE cd.coach_id = ?
ORDER BY cd.specialization_level = 'primary' DESC",
[$this->id]
);
}
public static function getActive(): array
{
return static::query()
->where('is_active', '=', 1)
->orderBy('full_name_ar', 'ASC')
->get();
}
public static function findByCode(string $code): ?static
{
$row = static::query()->where('code', '=', $code)->first();
if ($row === null) {
return null;
}
$instance = new static($row);
$instance->exists = true;
return $instance;
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
class Discipline extends Model
{
protected static string $table = 'sa_disciplines';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = true;
protected static bool $dispatchEvents = true;
protected static bool $autoTrackAuthor = true;
protected static array $fillable = [
'code', 'name_ar', 'name_en', 'category', 'icon',
'description_ar', 'config_json', 'sort_order', 'is_active',
];
public static function getCategoryOptions(): array
{
return [
'individual' => 'فردية',
'team' => 'جماعية',
'racket' => 'مضرب',
'aquatic' => 'مائية',
'combat' => 'قتالية',
'leisure' => 'ترفيهية',
];
}
public function getCategoryLabel(): string
{
return static::getCategoryOptions()[$this->category] ?? $this->category;
}
public static function getActive(): array
{
return static::query()
->where('is_active', '=', 1)
->orderBy('sort_order', 'ASC')
->get();
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
use App\Core\App;
class Facility extends Model
{
protected static string $table = 'sa_facilities';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = true;
protected static bool $dispatchEvents = true;
protected static bool $autoTrackAuthor = true;
protected static array $fillable = [
'code', 'name_ar', 'name_en', 'facility_type', 'discipline_id',
'location_description', 'operating_hours_json', 'is_active', 'branch_id',
];
public static function getTypeOptions(): array
{
return [
'pool' => 'حمام سباحة',
'court' => 'ملعب (كورت)',
'pitch' => 'ملعب كبير',
'gym' => 'صالة رياضية',
'track' => 'مضمار',
'multipurpose' => 'متعدد الاستخدام',
];
}
public function getTypeLabel(): string
{
return static::getTypeOptions()[$this->facility_type] ?? $this->facility_type;
}
public function getOperatingHours(): array
{
$json = $this->operating_hours_json;
if (is_string($json)) {
return json_decode($json, true) ?: ['start' => '06:00', 'end' => '22:00', 'slot_minutes' => 60];
}
return $json ?: ['start' => '06:00', 'end' => '22:00', 'slot_minutes' => 60];
}
public function getUnits(): array
{
$db = App::getInstance()->db();
return $db->select(
"SELECT * FROM sa_facility_units WHERE facility_id = ? AND is_active = 1 ORDER BY sort_order, id",
[$this->id]
);
}
public function getTimeBrackets(): array
{
$db = App::getInstance()->db();
return $db->select(
"SELECT * FROM sa_time_brackets WHERE facility_id = ? AND is_active = 1 ORDER BY start_time",
[$this->id]
);
}
public static function getActive(): array
{
return static::query()
->where('is_active', '=', 1)
->orderBy('name_ar', 'ASC')
->get();
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
class FacilityUnit extends Model
{
protected static string $table = 'sa_facility_units';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = false;
protected static bool $dispatchEvents = false;
protected static bool $autoTrackAuthor = true;
protected static array $fillable = [
'facility_id', 'code', 'name_ar', 'name_en', 'unit_type',
'booking_mode', 'max_capacity', 'sort_order', 'dimensions_json',
'config_json', 'is_active',
];
public static function getUnitTypeOptions(): array
{
return [
'lane' => 'حارة',
'court' => 'كورت',
'pitch' => 'ملعب',
'ring' => 'حلبة',
'mat' => 'بساط',
'room' => 'غرفة',
];
}
public static function getBookingModeOptions(): array
{
return [
'exclusive' => 'حصري (حجز كامل)',
'shared' => 'مشترك (سعة متعددة)',
];
}
public function isShared(): bool
{
return $this->booking_mode === 'shared';
}
public function isExclusive(): bool
{
return $this->booking_mode === 'exclusive';
}
public static function getByFacility(int $facilityId): array
{
return static::query()
->where('facility_id', '=', $facilityId)
->where('is_active', '=', 1)
->orderBy('sort_order', 'ASC')
->get();
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
use App\Core\App;
class Group extends Model
{
protected static string $table = 'sa_groups';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = true;
protected static bool $dispatchEvents = true;
protected static array $fillable = [
'code', 'name_ar', 'name_en', 'program_id', 'coach_id',
'min_capacity', 'max_capacity', 'current_count', 'is_full',
'monthly_fee_member', 'monthly_fee_nonmember',
'season_start', 'season_end', 'status',
];
public static function getStatusOptions(): array
{
return [
'active' => 'نشط',
'paused' => 'متوقف',
'completed' => 'مكتمل',
'cancelled' => 'ملغي',
];
}
public static function getSchedule(int $groupId): array
{
$db = App::getInstance()->db();
return $db->select(
"SELECT gs.*, fu.name_ar as unit_name, fu.code as unit_code
FROM sa_group_schedule gs
LEFT JOIN sa_facility_units fu ON fu.id = gs.facility_unit_id
WHERE gs.group_id = ? AND gs.is_active = 1
ORDER BY gs.day_of_week ASC, gs.start_time ASC",
[$groupId]
);
}
public static function getPlayers(int $groupId): array
{
$db = App::getInstance()->db();
return $db->select(
"SELECT gp.*, p.name_ar as player_name, p.code as player_code, p.phone
FROM sa_group_players gp
JOIN sa_players p ON p.id = gp.player_id
WHERE gp.group_id = ? AND gp.status = 'active'
ORDER BY p.name_ar ASC",
[$groupId]
);
}
public static function getActive(): array
{
$db = App::getInstance()->db();
return $db->select(
"SELECT g.*, p.name_ar as program_name, c.name_ar as coach_name
FROM sa_groups g
LEFT JOIN sa_programs p ON p.id = g.program_id
LEFT JOIN sa_coaches c ON c.id = g.coach_id
WHERE g.status = 'active' AND g.is_archived = 0
ORDER BY g.name_ar ASC",
[]
);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
class GroupPlayer extends Model
{
protected static string $table = 'sa_group_players';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = false;
protected static bool $dispatchEvents = true;
protected static array $fillable = [
'group_id', 'player_id', 'enrolled_at',
'left_at', 'status', 'notes',
];
public static function getStatusOptions(): array
{
return [
'active' => 'نشط',
'paused' => 'متوقف',
'transferred' => 'منقول',
'withdrawn' => 'منسحب',
];
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
class GroupSchedule extends Model
{
protected static string $table = 'sa_group_schedule';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = false;
protected static bool $dispatchEvents = true;
protected static array $fillable = [
'group_id', 'facility_unit_id', 'day_of_week',
'start_time', 'end_time', 'is_active',
];
public static function getDayName(int $dayNum): string
{
$days = [
0 => 'الأحد',
1 => 'الاثنين',
2 => 'الثلاثاء',
3 => 'الأربعاء',
4 => 'الخميس',
5 => 'الجمعة',
6 => 'السبت',
];
return $days[$dayNum] ?? '';
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
use App\Core\App;
class Player extends Model
{
protected static string $table = 'sa_players';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = true;
protected static bool $dispatchEvents = true;
protected static bool $autoTrackAuthor = true;
protected static array $fillable = [
'player_type',
'member_id',
'registration_serial',
'full_name_ar',
'full_name_en',
'national_id',
'date_of_birth',
'gender',
'phone',
'email',
'guardian_name',
'guardian_phone',
'guardian_national_id',
'guardian_relationship',
'photo_path',
'medical_status',
'medical_expiry_date',
'card_status',
'registration_fee_paid',
'notes',
'branch_id',
];
/**
* Get medical status options with Arabic labels.
*/
public static function getMedicalStatusOptions(): array
{
return [
'pending' => 'قيد المراجعة',
'fit' => 'لائق',
'conditional' => 'لائق بشروط',
'unfit' => 'غير لائق',
'expired' => 'منتهي',
];
}
/**
* Get player type options with Arabic labels.
*/
public static function getPlayerTypeOptions(): array
{
return [
'member' => 'عضو',
'non_member' => 'غير عضو',
];
}
/**
* Get card status options with Arabic labels.
*/
public static function getCardStatusOptions(): array
{
return [
'inactive' => 'غير مفعل',
'active' => 'مفعل',
'suspended' => 'موقوف',
'revoked' => 'ملغى',
];
}
/**
* Get all documents for this player.
*/
public function getDocuments(): array
{
$db = App::getInstance()->db();
return $db->select(
"SELECT * FROM `sa_player_documents`
WHERE `player_id` = ?
ORDER BY `created_at` DESC",
[$this->id]
);
}
/**
* Get all active (non-archived) players.
*/
public static function getActive(): array
{
return static::query()
->where('is_archived', '=', 0)
->orderBy('full_name_ar', 'ASC')
->get();
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
use App\Core\App;
class PlayerDocument extends Model
{
protected static string $table = 'sa_player_documents';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = false;
protected static bool $dispatchEvents = true;
protected static bool $autoTrackAuthor = true;
protected static array $fillable = [
'player_id',
'document_type',
'file_path',
'file_name',
'exam_date',
'expiry_date',
'doctor_name',
'clinic_name',
'approval_status',
'approved_by',
'approved_at',
'rejection_reason',
'notes',
];
/**
* Get document type options with Arabic labels.
*/
public static function getDocumentTypeOptions(): array
{
return [
'medical_cert' => 'شهادة طبية',
'birth_cert' => 'شهادة ميلاد',
'photo' => 'صورة شخصية',
'national_id' => 'بطاقة رقم قومي',
'other' => 'أخرى',
];
}
/**
* Get approval status options with Arabic labels.
*/
public static function getApprovalStatusOptions(): array
{
return [
'pending' => 'قيد المراجعة',
'approved' => 'معتمد',
'rejected' => 'مرفوض',
'expired' => 'منتهي',
];
}
/**
* Get pending medical certificate documents.
*/
public static function getPendingMedical(): array
{
return static::query()
->where('document_type', '=', 'medical_cert')
->where('approval_status', '=', 'pending')
->orderBy('created_at', 'ASC')
->get();
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
class PricingRule extends Model
{
protected static string $table = 'sa_pricing_rules';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = false;
protected static bool $dispatchEvents = false;
protected static bool $autoTrackAuthor = true;
protected static array $fillable = [
'facility_unit_id', 'time_bracket_id', 'group_size_min', 'group_size_max',
'price_per_person_member', 'price_per_person_nonmember',
'effective_from', 'effective_to', 'is_active',
];
public static function getByUnit(int $unitId): array
{
return static::query()
->where('facility_unit_id', '=', $unitId)
->where('is_active', '=', 1)
->orderBy('group_size_min', 'ASC')
->get();
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
use App\Core\App;
class Program extends Model
{
protected static string $table = 'sa_programs';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = true;
protected static bool $dispatchEvents = true;
protected static array $fillable = [
'code', 'name_ar', 'name_en', 'discipline_id', 'academy_id',
'program_type', 'age_from', 'age_to', 'gender_restriction',
'skill_level', 'description_ar', 'session_duration_minutes',
'sessions_per_week', 'is_active',
];
public static function getProgramTypeOptions(): array
{
return [
'training' => 'تدريب',
'recreational' => 'ترفيهي',
'competitive' => 'تنافسي',
'rehabilitation' => 'تأهيلي',
];
}
public static function getSkillLevelOptions(): array
{
return [
'beginner' => 'مبتدئ',
'intermediate' => 'متوسط',
'advanced' => 'متقدم',
'elite' => 'نخبة',
];
}
public static function getActive(): array
{
$db = App::getInstance()->db();
return $db->select(
"SELECT p.*, d.name_ar as discipline_name
FROM sa_programs p
LEFT JOIN sa_disciplines d ON d.id = p.discipline_id
WHERE p.is_active = 1 AND p.is_archived = 0
ORDER BY p.name_ar ASC",
[]
);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
class Subscription extends Model
{
protected static string $table = 'sa_subscriptions';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = false;
protected static bool $dispatchEvents = true;
protected static array $fillable = [
'subscription_number', 'player_id', 'group_id', 'period_start',
'period_end', 'amount', 'discount_amount', 'final_amount',
'payment_status', 'paid_at', 'paid_amount', 'payment_reference',
'exemption_reason', 'exempted_by', 'notes',
];
public static function getPaymentStatusOptions(): array
{
return [
'unpaid' => 'غير مدفوع',
'paid' => 'مدفوع',
'partial' => 'مدفوع جزئياً',
'exempt' => 'معفى',
'overdue' => 'متأخر',
];
}
public static function generateNumber(): string
{
return 'SUB-' . date('Ymd') . '-' . str_pad((string)random_int(1, 9999), 4, '0', STR_PAD_LEFT);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
class TimeBracket extends Model
{
protected static string $table = 'sa_time_brackets';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = false;
protected static bool $dispatchEvents = false;
protected static bool $autoTrackAuthor = false;
protected static array $fillable = [
'facility_id', 'name_ar', 'name_en', 'bracket_type',
'start_time', 'end_time', 'days_of_week_json', 'is_active',
];
public static function getBracketTypeOptions(): array
{
return [
'peak' => 'ذروة',
'off_peak' => 'عادي',
'weekend' => 'عطلة أسبوعية',
'holiday' => 'إجازة',
];
}
public function getDaysOfWeek(): array
{
$json = $this->days_of_week_json;
if (is_string($json)) {
return json_decode($json, true) ?: [];
}
return $json ?: [];
}
public static function getByFacility(int $facilityId): array
{
return static::query()
->where('facility_id', '=', $facilityId)
->where('is_active', '=', 1)
->orderBy('start_time', 'ASC')
->get();
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Models;
use App\Core\Model;
class Waitlist extends Model
{
protected static string $table = 'sa_waitlist';
protected static string $primaryKey = 'id';
protected static bool $timestamps = true;
protected static bool $softDelete = false;
protected static bool $dispatchEvents = true;
protected static array $fillable = [
'player_id', 'group_id', 'position',
'status', 'offered_at', 'expires_at', 'notes',
];
public static function getStatusOptions(): array
{
return [
'waiting' => 'في الانتظار',
'offered' => 'تم العرض',
'accepted' => 'مقبول',
'expired' => 'منتهي',
'cancelled' => 'ملغي',
];
}
}
<?php
declare(strict_types=1);
return [
// Dashboard
['GET', '/sa', 'SportsActivity\Controllers\DashboardController@index', ['auth'], 'sa.dashboard'],
// Disciplines
['GET', '/sa/disciplines', 'SportsActivity\Controllers\DisciplineController@index', ['auth'], 'sa.discipline.view'],
['GET', '/sa/disciplines/create', 'SportsActivity\Controllers\DisciplineController@create', ['auth'], 'sa.discipline.manage'],
['POST', '/sa/disciplines', 'SportsActivity\Controllers\DisciplineController@store', ['auth', 'csrf'], 'sa.discipline.manage'],
['GET', '/sa/disciplines/{id:\d+}', 'SportsActivity\Controllers\DisciplineController@show', ['auth'], 'sa.discipline.view'],
['GET', '/sa/disciplines/{id:\d+}/edit', 'SportsActivity\Controllers\DisciplineController@edit', ['auth'], 'sa.discipline.manage'],
['POST', '/sa/disciplines/{id:\d+}', 'SportsActivity\Controllers\DisciplineController@update', ['auth', 'csrf'], 'sa.discipline.manage'],
['POST', '/sa/disciplines/{id:\d+}/toggle', 'SportsActivity\Controllers\DisciplineController@toggle', ['auth', 'csrf'], 'sa.discipline.manage'],
// Facilities
['GET', '/sa/facilities', 'SportsActivity\Controllers\FacilityController@index', ['auth'], 'sa.facility.view'],
['GET', '/sa/facilities/create', 'SportsActivity\Controllers\FacilityController@create', ['auth'], 'sa.facility.manage'],
['POST', '/sa/facilities', 'SportsActivity\Controllers\FacilityController@store', ['auth', 'csrf'], 'sa.facility.manage'],
['GET', '/sa/facilities/{id:\d+}', 'SportsActivity\Controllers\FacilityController@show', ['auth'], 'sa.facility.view'],
['GET', '/sa/facilities/{id:\d+}/edit', 'SportsActivity\Controllers\FacilityController@edit', ['auth'], 'sa.facility.manage'],
['POST', '/sa/facilities/{id:\d+}', 'SportsActivity\Controllers\FacilityController@update', ['auth', 'csrf'], 'sa.facility.manage'],
['POST', '/sa/facilities/{id:\d+}/toggle', 'SportsActivity\Controllers\FacilityController@toggle', ['auth', 'csrf'], 'sa.facility.manage'],
['POST', '/sa/facilities/{id:\d+}/blackout', 'SportsActivity\Controllers\FacilityController@addBlackout', ['auth', 'csrf'], 'sa.facility.manage'],
// Facility Units
['GET', '/sa/facilities/{fid:\d+}/units', 'SportsActivity\Controllers\FacilityUnitController@index', ['auth'], 'sa.facility.view'],
['GET', '/sa/facilities/{fid:\d+}/units/create', 'SportsActivity\Controllers\FacilityUnitController@create', ['auth'], 'sa.facility.manage'],
['POST', '/sa/facilities/{fid:\d+}/units', 'SportsActivity\Controllers\FacilityUnitController@store', ['auth', 'csrf'], 'sa.facility.manage'],
['GET', '/sa/facilities/{fid:\d+}/units/{id:\d+}/edit', 'SportsActivity\Controllers\FacilityUnitController@edit', ['auth'], 'sa.facility.manage'],
['POST', '/sa/facilities/{fid:\d+}/units/{id:\d+}', 'SportsActivity\Controllers\FacilityUnitController@update', ['auth', 'csrf'], 'sa.facility.manage'],
// Time Brackets
['GET', '/sa/facilities/{fid:\d+}/brackets', 'SportsActivity\Controllers\FacilityController@brackets', ['auth'], 'sa.facility.view'],
['POST', '/sa/facilities/{fid:\d+}/brackets', 'SportsActivity\Controllers\FacilityController@storeBracket', ['auth', 'csrf'], 'sa.facility.manage'],
['POST', '/sa/facilities/{fid:\d+}/brackets/{id:\d+}/delete', 'SportsActivity\Controllers\FacilityController@deleteBracket', ['auth', 'csrf'], 'sa.facility.manage'],
// Coaches
['GET', '/sa/coaches', 'SportsActivity\Controllers\CoachController@index', ['auth'], 'sa.coach.view'],
['GET', '/sa/coaches/create', 'SportsActivity\Controllers\CoachController@create', ['auth'], 'sa.coach.manage'],
['POST', '/sa/coaches', 'SportsActivity\Controllers\CoachController@store', ['auth', 'csrf'], 'sa.coach.manage'],
['GET', '/sa/coaches/{id:\d+}', 'SportsActivity\Controllers\CoachController@show', ['auth'], 'sa.coach.view'],
['GET', '/sa/coaches/{id:\d+}/edit', 'SportsActivity\Controllers\CoachController@edit', ['auth'], 'sa.coach.manage'],
['POST', '/sa/coaches/{id:\d+}', 'SportsActivity\Controllers\CoachController@update', ['auth', 'csrf'], 'sa.coach.manage'],
['POST', '/sa/coaches/{id:\d+}/toggle', 'SportsActivity\Controllers\CoachController@toggle', ['auth', 'csrf'], 'sa.coach.manage'],
// Players
['GET', '/sa/players', 'SportsActivity\Controllers\PlayerController@index', ['auth'], 'sa.player.view'],
['GET', '/sa/players/create', 'SportsActivity\Controllers\PlayerController@create', ['auth'], 'sa.player.manage'],
['POST', '/sa/players', 'SportsActivity\Controllers\PlayerController@store', ['auth', 'csrf'], 'sa.player.manage'],
['GET', '/sa/players/{id:\d+}', 'SportsActivity\Controllers\PlayerController@show', ['auth'], 'sa.player.view'],
['GET', '/sa/players/{id:\d+}/edit', 'SportsActivity\Controllers\PlayerController@edit', ['auth'], 'sa.player.manage'],
['POST', '/sa/players/{id:\d+}', 'SportsActivity\Controllers\PlayerController@update', ['auth', 'csrf'], 'sa.player.manage'],
// Player Documents
['GET', '/sa/players/{pid:\d+}/documents', 'SportsActivity\Controllers\PlayerDocumentController@index', ['auth'], 'sa.player.view'],
['POST', '/sa/players/{pid:\d+}/documents', 'SportsActivity\Controllers\PlayerDocumentController@upload', ['auth', 'csrf'], 'sa.player.manage'],
['POST', '/sa/players/{pid:\d+}/documents/{id:\d+}/approve', 'SportsActivity\Controllers\PlayerDocumentController@approve', ['auth', 'csrf'], 'sa.medical.approve'],
['POST', '/sa/players/{pid:\d+}/documents/{id:\d+}/reject', 'SportsActivity\Controllers\PlayerDocumentController@reject', ['auth', 'csrf'], 'sa.medical.approve'],
// Academies
['GET', '/sa/academies', 'SportsActivity\Controllers\AcademyController@index', ['auth'], 'sa.academy.view'],
['GET', '/sa/academies/create', 'SportsActivity\Controllers\AcademyController@create', ['auth'], 'sa.academy.manage'],
['POST', '/sa/academies', 'SportsActivity\Controllers\AcademyController@store', ['auth', 'csrf'], 'sa.academy.manage'],
['GET', '/sa/academies/{id:\d+}', 'SportsActivity\Controllers\AcademyController@show', ['auth'], 'sa.academy.view'],
['GET', '/sa/academies/{id:\d+}/edit', 'SportsActivity\Controllers\AcademyController@edit', ['auth'], 'sa.academy.manage'],
['POST', '/sa/academies/{id:\d+}', 'SportsActivity\Controllers\AcademyController@update', ['auth', 'csrf'], 'sa.academy.manage'],
// Academy Contracts
['GET', '/sa/academies/{aid:\d+}/contracts', 'SportsActivity\Controllers\AcademyContractController@index', ['auth'], 'sa.contract.view'],
['GET', '/sa/academies/{aid:\d+}/contracts/create', 'SportsActivity\Controllers\AcademyContractController@create', ['auth'], 'sa.contract.manage'],
['POST', '/sa/academies/{aid:\d+}/contracts', 'SportsActivity\Controllers\AcademyContractController@store', ['auth', 'csrf'], 'sa.contract.manage'],
['GET', '/sa/academy-contracts/{id:\d+}', 'SportsActivity\Controllers\AcademyContractController@show', ['auth'], 'sa.contract.view'],
['POST', '/sa/academy-contracts/{id:\d+}/approve', 'SportsActivity\Controllers\AcademyContractController@approve', ['auth', 'csrf'], 'sa.contract.approve'],
// Programs
['GET', '/sa/programs', 'SportsActivity\Controllers\ProgramController@index', ['auth'], 'sa.program.view'],
['GET', '/sa/programs/create', 'SportsActivity\Controllers\ProgramController@create', ['auth'], 'sa.program.manage'],
['POST', '/sa/programs', 'SportsActivity\Controllers\ProgramController@store', ['auth', 'csrf'], 'sa.program.manage'],
['GET', '/sa/programs/{id:\d+}', 'SportsActivity\Controllers\ProgramController@show', ['auth'], 'sa.program.view'],
['GET', '/sa/programs/{id:\d+}/edit', 'SportsActivity\Controllers\ProgramController@edit', ['auth'], 'sa.program.manage'],
['POST', '/sa/programs/{id:\d+}', 'SportsActivity\Controllers\ProgramController@update', ['auth', 'csrf'], 'sa.program.manage'],
// Groups
['GET', '/sa/groups', 'SportsActivity\Controllers\GroupController@index', ['auth'], 'sa.group.view'],
['GET', '/sa/groups/create', 'SportsActivity\Controllers\GroupController@create', ['auth'], 'sa.group.manage'],
['POST', '/sa/groups', 'SportsActivity\Controllers\GroupController@store', ['auth', 'csrf'], 'sa.group.manage'],
['GET', '/sa/groups/{id:\d+}', 'SportsActivity\Controllers\GroupController@show', ['auth'], 'sa.group.view'],
['GET', '/sa/groups/{id:\d+}/edit', 'SportsActivity\Controllers\GroupController@edit', ['auth'], 'sa.group.manage'],
['POST', '/sa/groups/{id:\d+}', 'SportsActivity\Controllers\GroupController@update', ['auth', 'csrf'], 'sa.group.manage'],
['POST', '/sa/groups/{id:\d+}/enroll', 'SportsActivity\Controllers\GroupController@enroll', ['auth', 'csrf'], 'sa.group.enroll'],
['POST', '/sa/groups/{id:\d+}/remove-player', 'SportsActivity\Controllers\GroupController@removePlayer', ['auth', 'csrf'], 'sa.group.manage'],
['POST', '/sa/groups/{id:\d+}/schedule', 'SportsActivity\Controllers\GroupController@saveSchedule', ['auth', 'csrf'], 'sa.group.manage'],
// Schedule
['GET', '/sa/schedule', 'SportsActivity\Controllers\ScheduleController@calendar', ['auth'], 'sa.schedule.view'],
['GET', '/sa/schedule/daily/{date}', 'SportsActivity\Controllers\ScheduleController@daily', ['auth'], 'sa.schedule.view'],
['GET', '/sa/schedule/weekly', 'SportsActivity\Controllers\ScheduleController@weekly', ['auth'], 'sa.schedule.view'],
['POST', '/sa/schedule/generate', 'SportsActivity\Controllers\ScheduleController@generate', ['auth', 'csrf'], 'sa.schedule.manage'],
// Bookings
['GET', '/sa/bookings', 'SportsActivity\Controllers\BookingController@index', ['auth'], 'sa.booking.view'],
['GET', '/sa/bookings/create', 'SportsActivity\Controllers\BookingController@create', ['auth'], 'sa.booking.create'],
['POST', '/sa/bookings', 'SportsActivity\Controllers\BookingController@store', ['auth', 'csrf'], 'sa.booking.create'],
['GET', '/sa/bookings/{id:\d+}', 'SportsActivity\Controllers\BookingController@show', ['auth'], 'sa.booking.view'],
['POST', '/sa/bookings/{id:\d+}/cancel', 'SportsActivity\Controllers\BookingController@cancel', ['auth', 'csrf'], 'sa.booking.manage'],
['POST', '/sa/bookings/{id:\d+}/checkin', 'SportsActivity\Controllers\BookingController@checkin', ['auth', 'csrf'], 'sa.booking.manage'],
['POST', '/sa/bookings/{id:\d+}/checkout', 'SportsActivity\Controllers\BookingController@checkout', ['auth', 'csrf'], 'sa.booking.manage'],
// Pricing
['GET', '/sa/pricing', 'SportsActivity\Controllers\PricingController@index', ['auth'], 'sa.pricing.view'],
['GET', '/sa/pricing/rules', 'SportsActivity\Controllers\PricingController@rules', ['auth'], 'sa.pricing.manage'],
['POST', '/sa/pricing/rules', 'SportsActivity\Controllers\PricingController@storeRule', ['auth', 'csrf'], 'sa.pricing.manage'],
['POST', '/sa/pricing/rules/{id:\d+}', 'SportsActivity\Controllers\PricingController@updateRule', ['auth', 'csrf'], 'sa.pricing.manage'],
['POST', '/sa/pricing/rules/{id:\d+}/deactivate', 'SportsActivity\Controllers\PricingController@deactivateRule', ['auth', 'csrf'], 'sa.pricing.manage'],
// Subscriptions
['GET', '/sa/subscriptions', 'SportsActivity\Controllers\SubscriptionController@index', ['auth'], 'sa.subscription.view'],
['POST', '/sa/subscriptions/generate', 'SportsActivity\Controllers\SubscriptionController@generate', ['auth', 'csrf'], 'sa.subscription.generate'],
['GET', '/sa/subscriptions/{id:\d+}', 'SportsActivity\Controllers\SubscriptionController@show', ['auth'], 'sa.subscription.view'],
['POST', '/sa/subscriptions/{id:\d+}/pay', 'SportsActivity\Controllers\SubscriptionController@pay', ['auth', 'csrf'], 'sa.subscription.collect'],
['POST', '/sa/subscriptions/{id:\d+}/exempt', 'SportsActivity\Controllers\SubscriptionController@exempt', ['auth', 'csrf'], 'sa.subscription.exempt'],
// Attendance
['GET', '/sa/attendance', 'SportsActivity\Controllers\AttendanceController@index', ['auth'], 'sa.attendance.view'],
['GET', '/sa/attendance/record/{bookingId:\d+}', 'SportsActivity\Controllers\AttendanceController@record', ['auth'], 'sa.attendance.manage'],
['POST', '/sa/attendance/record/{bookingId:\d+}', 'SportsActivity\Controllers\AttendanceController@store', ['auth', 'csrf'], 'sa.attendance.manage'],
['GET', '/sa/attendance/report', 'SportsActivity\Controllers\AttendanceController@report', ['auth'], 'sa.attendance.view'],
// Mirror (read-only display)
['GET', '/sa/mirror', 'SportsActivity\Controllers\MirrorController@index', ['auth'], 'sa.mirror.view'],
['GET', '/sa/mirror/facility/{id:\d+}', 'SportsActivity\Controllers\MirrorController@facility', ['auth'], 'sa.mirror.view'],
['GET', '/sa/mirror/facility/{id:\d+}/{date}', 'SportsActivity\Controllers\MirrorController@facilityDate', ['auth'], 'sa.mirror.view'],
// Waitlist
['GET', '/sa/waitlist', 'SportsActivity\Controllers\WaitlistController@index', ['auth'], 'sa.waitlist.view'],
['POST', '/sa/waitlist/{id:\d+}/offer', 'SportsActivity\Controllers\WaitlistController@offer', ['auth', 'csrf'], 'sa.waitlist.manage'],
['POST', '/sa/waitlist/{id:\d+}/cancel', 'SportsActivity\Controllers\WaitlistController@cancel', ['auth', 'csrf'], 'sa.waitlist.manage'],
// JSON APIs (AJAX)
['GET', '/api/sa/schedule/availability', 'SportsActivity\Controllers\Api\ScheduleApiController@availability', ['auth'], 'sa.schedule.view'],
['GET', '/api/sa/schedule/conflicts', 'SportsActivity\Controllers\Api\ScheduleApiController@conflicts', ['auth'], 'sa.schedule.view'],
['GET', '/api/sa/bookings/price-preview', 'SportsActivity\Controllers\Api\BookingApiController@pricePreview', ['auth'], 'sa.booking.create'],
['GET', '/api/sa/players/search', 'SportsActivity\Controllers\Api\PlayerSearchApiController@search', ['auth'], 'sa.player.view'],
['GET', '/api/sa/mirror/{id:\d+}/state', 'SportsActivity\Controllers\Api\MirrorApiController@state', ['auth'], 'sa.mirror.view'],
];
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Services;
use App\Core\App;
final class BookingService
{
public static function createHourlyBooking(array $data): array
{
$db = App::getInstance()->db();
$unitId = (int) ($data['facility_unit_id'] ?? 0);
$date = $data['booking_date'] ?? '';
$startTime = $data['start_time'] ?? '';
$endTime = $data['end_time'] ?? '';
$participantCount = (int) ($data['participant_count'] ?? 1);
$spotsReserved = (int) ($data['spots_reserved'] ?? $participantCount);
$bookerType = $data['booker_type'] ?? 'guest';
$bookerId = isset($data['booker_id']) ? (int) $data['booker_id'] : null;
$bookerName = $data['booker_name'] ?? '';
$isMember = ($bookerType === 'member');
$availability = SlotAvailabilityService::check($unitId, $date, $startTime, $endTime, $spotsReserved);
if (!$availability['available']) {
return ['success' => false, 'error' => $availability['reason']];
}
$pricing = PricingCalculatorService::calculate($unitId, $date, $startTime, $endTime, $participantCount, $isMember);
$totalAmount = $pricing ? $pricing['total_amount'] : 0.00;
$bookingNumber = self::generateNumber();
$employeeId = (int) (App::getInstance()->session()->get('employee_id') ?? 0);
$bookingData = [
'booking_number' => $bookingNumber,
'facility_unit_id' => $unitId,
'booking_type' => 'hourly',
'booking_date' => $date,
'start_time' => $startTime,
'end_time' => $endTime,
'booker_type' => $bookerType,
'booker_id' => $bookerId,
'booker_name' => $bookerName,
'participant_count' => $participantCount,
'spots_reserved' => $spotsReserved,
'total_amount' => $totalAmount,
'payment_status' => 'unpaid',
'status' => 'confirmed',
'notes' => $data['notes'] ?? null,
'branch_id' => $data['branch_id'] ?? null,
'created_by' => $employeeId,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
$db->insert('sa_bookings', $bookingData);
$bookingId = (int) $db->lastInsertId();
if (!empty($data['participants']) && is_array($data['participants'])) {
foreach ($data['participants'] as $p) {
$db->insert('sa_booking_participants', [
'booking_id' => $bookingId,
'player_id' => isset($p['player_id']) ? (int) $p['player_id'] : null,
'participant_name' => $p['name'] ?? null,
'is_member' => (int) ($p['is_member'] ?? 0),
'price_charged' => $pricing ? $pricing['price_per_person'] * $pricing['duration_hours'] : 0,
'created_at' => date('Y-m-d H:i:s'),
]);
}
}
return [
'success' => true,
'booking_id' => $bookingId,
'booking_number' => $bookingNumber,
'total_amount' => $totalAmount,
'pricing' => $pricing,
];
}
public static function createTrainingBooking(
int $groupId,
int $facilityUnitId,
string $date,
string $startTime,
string $endTime,
?int $coachId = null
): array {
$db = App::getInstance()->db();
$group = $db->selectOne("SELECT * FROM sa_groups WHERE id = ?", [$groupId]);
if (!$group) {
return ['success' => false, 'error' => 'المجموعة غير موجودة'];
}
$unit = $db->selectOne("SELECT * FROM sa_facility_units WHERE id = ?", [$facilityUnitId]);
if (!$unit) {
return ['success' => false, 'error' => 'الوحدة غير موجودة'];
}
$spotsNeeded = $unit['booking_mode'] === 'shared' ? (int) $group['current_count'] : 1;
$availability = SlotAvailabilityService::check($facilityUnitId, $date, $startTime, $endTime, $spotsNeeded);
if (!$availability['available']) {
return ['success' => false, 'error' => $availability['reason']];
}
if ($coachId) {
$conflicts = ConflictDetectionService::check($facilityUnitId, $date, $startTime, $endTime, $coachId);
$blocking = array_filter($conflicts, fn($c) => $c['severity'] === 'blocking');
if (!empty($blocking)) {
return ['success' => false, 'error' => $blocking[0]['message']];
}
}
$bookingNumber = self::generateNumber();
$employeeId = (int) (App::getInstance()->session()->get('employee_id') ?? 0);
$db->insert('sa_bookings', [
'booking_number' => $bookingNumber,
'facility_unit_id' => $facilityUnitId,
'booking_type' => 'training',
'booking_date' => $date,
'start_time' => $startTime,
'end_time' => $endTime,
'group_id' => $groupId,
'coach_id' => $coachId ?? (int) $group['coach_id'],
'participant_count' => (int) $group['current_count'],
'spots_reserved' => $spotsNeeded,
'total_amount' => 0.00,
'payment_status' => 'unpaid',
'status' => 'confirmed',
'is_recurring' => 1,
'branch_id' => null,
'created_by' => $employeeId,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
return [
'success' => true,
'booking_id' => (int) $db->lastInsertId(),
'booking_number' => $bookingNumber,
];
}
public static function cancel(int $bookingId, string $reason = ''): array
{
$db = App::getInstance()->db();
$booking = $db->selectOne("SELECT * FROM sa_bookings WHERE id = ?", [$bookingId]);
if (!$booking) {
return ['success' => false, 'error' => 'الحجز غير موجود'];
}
if (in_array($booking['status'], ['cancelled', 'no_show'])) {
return ['success' => false, 'error' => 'الحجز ملغي بالفعل'];
}
$employeeId = (int) (App::getInstance()->session()->get('employee_id') ?? 0);
$db->update('sa_bookings', [
'status' => 'cancelled',
'cancellation_reason' => $reason,
'cancelled_by' => $employeeId,
'cancelled_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
], 'id = ?', [$bookingId]);
return ['success' => true];
}
private static function generateNumber(): string
{
return 'BK-' . date('Ymd') . '-' . str_pad((string) random_int(1, 9999), 4, '0', STR_PAD_LEFT);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Services;
use App\Core\App;
final class ConflictDetectionService
{
public static function check(
int $facilityUnitId,
string $date,
string $startTime,
string $endTime,
?int $coachId = null,
?int $excludeBookingId = null
): array {
$conflicts = [];
$slotCheck = SlotAvailabilityService::check(
$facilityUnitId, $date, $startTime, $endTime, 1, $excludeBookingId
);
if (!$slotCheck['available']) {
$conflicts[] = [
'type' => 'unit',
'severity' => 'blocking',
'message' => $slotCheck['reason'],
];
}
if ($coachId) {
$coachConflict = self::checkCoachConflict($coachId, $date, $startTime, $endTime, $excludeBookingId);
if ($coachConflict) {
$conflicts[] = $coachConflict;
}
}
return $conflicts;
}
public static function checkCoachConflict(
int $coachId,
string $date,
string $startTime,
string $endTime,
?int $excludeBookingId = null
): ?array {
$db = App::getInstance()->db();
$excludeClause = $excludeBookingId ? " AND b.id != ?" : "";
$params = [$coachId, $date, $endTime, $startTime];
if ($excludeBookingId) {
$params[] = $excludeBookingId;
}
$existing = $db->selectOne(
"SELECT b.id, b.booking_number, fu.name_ar as unit_name
FROM sa_bookings b
JOIN sa_facility_units fu ON fu.id = b.facility_unit_id
WHERE b.coach_id = ? AND b.booking_date = ?
AND b.status NOT IN ('cancelled','no_show')
AND b.start_time < ? AND b.end_time > ?{$excludeClause}
LIMIT 1",
$params
);
if ($existing) {
return [
'type' => 'coach',
'severity' => 'blocking',
'message' => "المدرب لديه حجز آخر ({$existing['booking_number']}) في {$existing['unit_name']} في نفس التوقيت",
];
}
return null;
}
public static function checkPlayerConflict(
int $playerId,
string $date,
string $startTime,
string $endTime,
?int $excludeBookingId = null
): ?array {
$db = App::getInstance()->db();
$excludeClause = $excludeBookingId ? " AND b.id != ?" : "";
$params = [$playerId, $date, $endTime, $startTime];
if ($excludeBookingId) {
$params[] = $excludeBookingId;
}
$existing = $db->selectOne(
"SELECT b.id, b.booking_number
FROM sa_bookings b
JOIN sa_booking_participants bp ON bp.booking_id = b.id
WHERE bp.player_id = ? AND b.booking_date = ?
AND b.status NOT IN ('cancelled','no_show')
AND b.start_time < ? AND b.end_time > ?{$excludeClause}
LIMIT 1",
$params
);
if ($existing) {
return [
'type' => 'player',
'severity' => 'warning',
'message' => "اللاعب لديه حجز آخر ({$existing['booking_number']}) في نفس التوقيت",
];
}
return null;
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Services;
use App\Core\App;
final class EnrollmentService
{
public static function enroll(int $groupId, int $playerId): array
{
$db = App::getInstance()->db();
$player = $db->selectOne(
"SELECT * FROM sa_players WHERE id = ? AND is_archived = 0",
[$playerId]
);
if (!$player) {
return ['success' => false, 'error' => 'اللاعب غير مسجل في النظام'];
}
if (!in_array($player['medical_status'], ['fit', 'conditional'])) {
return ['success' => false, 'error' => 'الحالة الطبية للاعب لا تسمح بالتسجيل (' . $player['medical_status'] . ')'];
}
if ($player['medical_status'] === 'fit' && $player['medical_expiry_date']) {
if ($player['medical_expiry_date'] < date('Y-m-d')) {
return ['success' => false, 'error' => 'الشهادة الطبية منتهية الصلاحية'];
}
}
$group = $db->selectOne("SELECT * FROM sa_groups WHERE id = ? AND is_archived = 0", [$groupId]);
if (!$group) {
return ['success' => false, 'error' => 'المجموعة غير موجودة'];
}
if ($group['status'] !== 'active') {
return ['success' => false, 'error' => 'المجموعة غير نشطة'];
}
$existing = $db->selectOne(
"SELECT id FROM sa_group_players WHERE group_id = ? AND player_id = ? AND status = 'active'",
[$groupId, $playerId]
);
if ($existing) {
return ['success' => false, 'error' => 'اللاعب مسجل بالفعل في هذه المجموعة'];
}
if ((int) $group['current_count'] >= (int) $group['max_capacity']) {
return ['success' => false, 'error' => 'المجموعة ممتلئة', 'suggest_waitlist' => true];
}
$employeeId = (int) (App::getInstance()->session()->get('employee_id') ?? 0);
$db->insert('sa_group_players', [
'group_id' => $groupId,
'player_id' => $playerId,
'enrolled_at' => date('Y-m-d'),
'status' => 'active',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
'created_by' => $employeeId,
]);
$newCount = (int) $group['current_count'] + 1;
$isFull = $newCount >= (int) $group['max_capacity'] ? 1 : 0;
$db->update('sa_groups', [
'current_count' => $newCount,
'is_full' => $isFull,
'updated_at' => date('Y-m-d H:i:s'),
], 'id = ?', [$groupId]);
return ['success' => true, 'new_count' => $newCount];
}
public static function withdraw(int $groupId, int $playerId, string $reason = ''): array
{
$db = App::getInstance()->db();
$enrollment = $db->selectOne(
"SELECT id FROM sa_group_players WHERE group_id = ? AND player_id = ? AND status = 'active'",
[$groupId, $playerId]
);
if (!$enrollment) {
return ['success' => false, 'error' => 'اللاعب غير مسجل في هذه المجموعة'];
}
$db->update('sa_group_players', [
'status' => 'withdrawn',
'left_at' => date('Y-m-d'),
'notes' => $reason ?: null,
'updated_at' => date('Y-m-d H:i:s'),
], 'id = ?', [(int) $enrollment['id']]);
$group = $db->selectOne("SELECT current_count FROM sa_groups WHERE id = ?", [$groupId]);
$newCount = max(0, (int) $group['current_count'] - 1);
$db->update('sa_groups', [
'current_count' => $newCount,
'is_full' => 0,
'updated_at' => date('Y-m-d H:i:s'),
], 'id = ?', [$groupId]);
return ['success' => true, 'new_count' => $newCount];
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Services;
use App\Core\App;
final class MirrorStateService
{
public static function getFacilityState(int $facilityId, string $date): array
{
$db = App::getInstance()->db();
$facility = $db->selectOne(
"SELECT * FROM sa_facilities WHERE id = ? AND is_archived = 0",
[$facilityId]
);
if (!$facility) {
return ['error' => 'المرفق غير موجود'];
}
$units = $db->select(
"SELECT * FROM sa_facility_units WHERE facility_id = ? AND is_active = 1 ORDER BY sort_order",
[$facilityId]
);
$bookings = $db->select(
"SELECT b.*, fu.name_ar as unit_name, fu.booking_mode, fu.max_capacity,
c.full_name_ar as coach_name, g.name_ar as group_name
FROM sa_bookings b
JOIN sa_facility_units fu ON fu.id = b.facility_unit_id
LEFT JOIN sa_coaches c ON c.id = b.coach_id
LEFT JOIN sa_groups g ON g.id = b.group_id
WHERE fu.facility_id = ? AND b.booking_date = ?
AND b.status NOT IN ('cancelled', 'no_show')
ORDER BY fu.sort_order, b.start_time",
[$facilityId, $date]
);
$hours = json_decode($facility['operating_hours_json'], true) ?: ['start' => '06:00', 'end' => '22:00', 'slot_minutes' => 60];
$slots = self::buildTimeSlots($hours['start'], $hours['end'], (int) $hours['slot_minutes']);
$grid = [];
foreach ($units as $unit) {
$unitId = (int) $unit['id'];
$unitBookings = array_filter($bookings, fn($b) => (int) $b['facility_unit_id'] === $unitId);
$grid[$unitId] = [
'unit' => $unit,
'slots' => [],
];
foreach ($slots as $slot) {
$slotBookings = array_filter($unitBookings, function ($b) use ($slot) {
return $b['start_time'] < $slot['end'] && $b['end_time'] > $slot['start'];
});
$occupied = 0;
foreach ($slotBookings as $sb) {
$occupied += (int) $sb['spots_reserved'];
}
$cellStatus = 'free';
if ($unit['booking_mode'] === 'shared') {
if ($occupied >= (int) $unit['max_capacity']) {
$cellStatus = 'full';
} elseif ($occupied > 0) {
$cellStatus = 'partial';
}
} else {
if (!empty($slotBookings)) {
$first = reset($slotBookings);
$cellStatus = $first['booking_type'] === 'training' ? 'training' : 'booked';
if ($first['booking_type'] === 'maintenance' || $first['booking_type'] === 'blocked') {
$cellStatus = 'blocked';
}
}
}
$grid[$unitId]['slots'][] = [
'time_start' => $slot['start'],
'time_end' => $slot['end'],
'status' => $cellStatus,
'occupied' => $occupied,
'max' => (int) $unit['max_capacity'],
'bookings' => array_values($slotBookings),
];
}
}
return [
'facility' => $facility,
'units' => $units,
'grid' => $grid,
'slots' => $slots,
'date' => $date,
];
}
private static function buildTimeSlots(string $start, string $end, int $slotMinutes): array
{
$slots = [];
$current = strtotime($start);
$endTs = strtotime($end);
while ($current < $endTs) {
$next = $current + ($slotMinutes * 60);
$slots[] = [
'start' => date('H:i', $current),
'end' => date('H:i', $next),
];
$current = $next;
}
return $slots;
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Services;
use App\Core\App;
final class PricingCalculatorService
{
public static function calculate(
int $facilityUnitId,
string $date,
string $startTime,
string $endTime,
int $participantCount,
bool $isMember
): ?array {
$db = App::getInstance()->db();
$unit = $db->selectOne(
"SELECT fu.facility_id FROM sa_facility_units fu WHERE fu.id = ?",
[$facilityUnitId]
);
if (!$unit) {
return null;
}
$dayOfWeek = (int) date('w', strtotime($date));
$bracket = $db->selectOne(
"SELECT id, name_ar, bracket_type FROM sa_time_brackets
WHERE facility_id = ? AND is_active = 1
AND start_time <= ? AND end_time > ?
AND JSON_CONTAINS(days_of_week_json, CAST(? AS JSON))
ORDER BY bracket_type = 'peak' DESC
LIMIT 1",
[(int) $unit['facility_id'], $startTime, $startTime, (string) $dayOfWeek]
);
if (!$bracket) {
return null;
}
$today = date('Y-m-d');
$rule = $db->selectOne(
"SELECT * FROM sa_pricing_rules
WHERE facility_unit_id = ? AND time_bracket_id = ?
AND group_size_min <= ? AND group_size_max >= ?
AND effective_from <= ? AND (effective_to IS NULL OR effective_to >= ?)
AND is_active = 1
ORDER BY effective_from DESC
LIMIT 1",
[$facilityUnitId, (int) $bracket['id'], $participantCount, $participantCount, $today, $today]
);
if (!$rule) {
return null;
}
$pricePerPerson = $isMember
? (float) $rule['price_per_person_member']
: (float) $rule['price_per_person_nonmember'];
$durationSeconds = strtotime($endTime) - strtotime($startTime);
$durationHours = $durationSeconds / 3600.0;
$totalAmount = $pricePerPerson * $participantCount * $durationHours;
return [
'price_per_person' => $pricePerPerson,
'participant_count' => $participantCount,
'duration_hours' => $durationHours,
'total_amount' => round($totalAmount, 2),
'bracket_name' => $bracket['name_ar'],
'bracket_type' => $bracket['bracket_type'],
'is_member' => $isMember,
'rule_id' => (int) $rule['id'],
];
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Services;
use App\Core\App;
final class ScheduleGeneratorService
{
public static function generateForGroup(int $groupId, string $fromDate, string $toDate): array
{
$db = App::getInstance()->db();
$group = $db->selectOne("SELECT * FROM sa_groups WHERE id = ?", [$groupId]);
if (!$group || $group['status'] !== 'active') {
return ['success' => false, 'error' => 'المجموعة غير نشطة', 'generated' => 0, 'skipped' => 0];
}
$schedules = $db->select(
"SELECT gs.*, fu.name_ar as unit_name
FROM sa_group_schedule gs
JOIN sa_facility_units fu ON fu.id = gs.facility_unit_id
WHERE gs.group_id = ? AND gs.is_active = 1",
[$groupId]
);
if (empty($schedules)) {
return ['success' => false, 'error' => 'لا يوجد جدول للمجموعة', 'generated' => 0, 'skipped' => 0];
}
$generated = 0;
$skipped = 0;
$errors = [];
$current = new \DateTime($fromDate);
$end = new \DateTime($toDate);
while ($current <= $end) {
$dayOfWeek = (int) $current->format('w');
$dateStr = $current->format('Y-m-d');
foreach ($schedules as $sched) {
if ((int) $sched['day_of_week'] !== $dayOfWeek) {
continue;
}
$existing = $db->selectOne(
"SELECT id FROM sa_bookings
WHERE group_id = ? AND facility_unit_id = ? AND booking_date = ?
AND start_time = ? AND status != 'cancelled'",
[$groupId, (int) $sched['facility_unit_id'], $dateStr, $sched['start_time']]
);
if ($existing) {
$skipped++;
continue;
}
$result = BookingService::createTrainingBooking(
$groupId,
(int) $sched['facility_unit_id'],
$dateStr,
$sched['start_time'],
$sched['end_time'],
(int) $group['coach_id']
);
if ($result['success']) {
$generated++;
} else {
$skipped++;
$errors[] = "{$dateStr}: {$result['error']}";
}
}
$current->modify('+1 day');
}
return [
'success' => true,
'generated' => $generated,
'skipped' => $skipped,
'errors' => $errors,
];
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Services;
use App\Core\App;
final class SlotAvailabilityService
{
public static function check(
int $facilityUnitId,
string $date,
string $startTime,
string $endTime,
int $spotsRequested = 1,
?int $excludeBookingId = null
): array {
$db = App::getInstance()->db();
$unit = $db->selectOne(
"SELECT fu.*, f.id as fac_id FROM sa_facility_units fu
JOIN sa_facilities f ON f.id = fu.facility_id
WHERE fu.id = ? AND fu.is_active = 1",
[$facilityUnitId]
);
if (!$unit) {
return ['available' => false, 'reason' => 'الوحدة غير موجودة أو غير مفعلة', 'remaining' => 0];
}
$blackout = $db->selectOne(
"SELECT id FROM sa_blackout_dates
WHERE (facility_id = ? OR facility_unit_id = ?)
AND blackout_date = ?
AND (start_time IS NULL OR (start_time < ? AND end_time > ?))",
[(int) $unit['fac_id'], $facilityUnitId, $date, $endTime, $startTime]
);
if ($blackout) {
return ['available' => false, 'reason' => 'المرفق محجوب في هذا التوقيت', 'remaining' => 0];
}
$excludeClause = $excludeBookingId ? " AND id != ?" : "";
$params = [$facilityUnitId, $date, $endTime, $startTime];
if ($excludeBookingId) {
$params[] = $excludeBookingId;
}
if ($unit['booking_mode'] === 'exclusive') {
$existing = $db->selectOne(
"SELECT id FROM sa_bookings
WHERE facility_unit_id = ? AND booking_date = ?
AND status NOT IN ('cancelled','no_show')
AND start_time < ? AND end_time > ?{$excludeClause}
LIMIT 1",
$params
);
if ($existing) {
return ['available' => false, 'reason' => 'الوحدة محجوزة بالفعل في هذا التوقيت', 'remaining' => 0];
}
return ['available' => true, 'reason' => null, 'remaining' => (int) $unit['max_capacity']];
}
$occupied = $db->selectOne(
"SELECT COALESCE(SUM(spots_reserved), 0) as total
FROM sa_bookings
WHERE facility_unit_id = ? AND booking_date = ?
AND status NOT IN ('cancelled','no_show')
AND start_time < ? AND end_time > ?{$excludeClause}",
$params
);
$used = (int) ($occupied['total'] ?? 0);
$remaining = (int) $unit['max_capacity'] - $used;
if ($spotsRequested > $remaining) {
return [
'available' => false,
'reason' => "السعة غير كافية. المتبقي: {$remaining}، المطلوب: {$spotsRequested}",
'remaining' => $remaining,
];
}
return ['available' => true, 'reason' => null, 'remaining' => $remaining];
}
public static function getUnitScheduleForDate(int $facilityUnitId, string $date): array
{
$db = App::getInstance()->db();
return $db->select(
"SELECT b.*, g.name_ar as group_name, c.full_name_ar as coach_name
FROM sa_bookings b
LEFT JOIN sa_groups g ON g.id = b.group_id
LEFT JOIN sa_coaches c ON c.id = b.coach_id
WHERE b.facility_unit_id = ? AND b.booking_date = ?
AND b.status NOT IN ('cancelled','no_show')
ORDER BY b.start_time",
[$facilityUnitId, $date]
);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Services;
use App\Core\App;
final class SubscriptionGeneratorService
{
public static function generateForMonth(string $yearMonth): array
{
$db = App::getInstance()->db();
$periodStart = $yearMonth . '-01';
$periodEnd = date('Y-m-t', strtotime($periodStart));
$groups = $db->select(
"SELECT g.*, p.name_ar as program_name
FROM sa_groups g
JOIN sa_programs p ON p.id = g.program_id
WHERE g.status = 'active' AND g.is_archived = 0",
[]
);
$generated = 0;
$skipped = 0;
$employeeId = (int) (App::getInstance()->session()->get('employee_id') ?? 0);
foreach ($groups as $group) {
$players = $db->select(
"SELECT gp.*, sp.player_type, sp.full_name_ar
FROM sa_group_players gp
JOIN sa_players sp ON sp.id = gp.player_id
WHERE gp.group_id = ? AND gp.status = 'active'",
[(int) $group['id']]
);
foreach ($players as $player) {
$existing = $db->selectOne(
"SELECT id FROM sa_subscriptions
WHERE player_id = ? AND group_id = ? AND period_start = ?",
[(int) $player['player_id'], (int) $group['id'], $periodStart]
);
if ($existing) {
$skipped++;
continue;
}
$amount = $player['player_type'] === 'member'
? (float) $group['monthly_fee_member']
: (float) $group['monthly_fee_nonmember'];
$subNumber = 'SUB-' . date('Ymd') . '-' . str_pad((string) random_int(1, 9999), 4, '0', STR_PAD_LEFT);
$db->insert('sa_subscriptions', [
'subscription_number' => $subNumber,
'player_id' => (int) $player['player_id'],
'group_id' => (int) $group['id'],
'period_start' => $periodStart,
'period_end' => $periodEnd,
'amount' => $amount,
'discount_amount' => 0.00,
'final_amount' => $amount,
'payment_status' => 'unpaid',
'paid_amount' => 0.00,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
'created_by' => $employeeId,
]);
$generated++;
}
}
return [
'success' => true,
'generated' => $generated,
'skipped' => $skipped,
'month' => $yearMonth,
];
}
}
<?php
$__template->layout('Layout.main');
?>
<?php $__template->section('title'); ?>إضافة عقد - <?= e($academy['name_ar'] ?? '') ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/academies/<?= (int) $academy['id'] ?>/contracts" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> رجوع</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/sa/academies/<?= (int) $academy['id'] ?>/contracts" enctype="multipart/form-data">
<?= csrf_field() ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">بيانات العقد</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));gap:15px;">
<div>
<label class="form-label">رقم العقد <span style="color:#DC2626;">*</span></label>
<input type="text" name="contract_number" value="<?= e(old('contract_number') ?? '') ?>" class="form-input" dir="ltr" required placeholder="مثال: CTR-2026-001">
</div>
<div>
<label class="form-label">نوع العقد</label>
<select name="contract_type" class="form-select">
<option value="revenue_share" <?= (old('contract_type') ?? '') === 'revenue_share' ? 'selected' : '' ?>>نسبة إيرادات</option>
<option value="fixed_rent" <?= old('contract_type') === 'fixed_rent' ? 'selected' : '' ?>>إيجار ثابت</option>
<option value="hybrid" <?= old('contract_type') === 'hybrid' ? 'selected' : '' ?>>مختلط</option>
</select>
</div>
<div>
<label class="form-label">تاريخ البداية <span style="color:#DC2626;">*</span></label>
<input type="date" name="start_date" value="<?= e(old('start_date') ?? '') ?>" class="form-input" required>
</div>
<div>
<label class="form-label">تاريخ النهاية <span style="color:#DC2626;">*</span></label>
<input type="date" name="end_date" value="<?= e(old('end_date') ?? '') ?>" class="form-input" required>
</div>
<div>
<label class="form-label">نسبة النادي (%)</label>
<input type="number" name="club_commission_pct" value="<?= e(old('club_commission_pct') ?? '0') ?>" class="form-input" step="0.1" min="0" max="100">
</div>
<div>
<label class="form-label">نسبة الأكاديمية (%)</label>
<input type="number" name="academy_share_pct" value="<?= e(old('academy_share_pct') ?? '0') ?>" class="form-input" step="0.1" min="0" max="100">
</div>
<div>
<label class="form-label">الإيجار الشهري الثابت</label>
<input type="number" name="fixed_monthly_rent" value="<?= e(old('fixed_monthly_rent') ?? '0') ?>" class="form-input" step="0.01" min="0">
</div>
<div>
<label class="form-label">مبلغ التأمين</label>
<input type="number" name="deposit_amount" value="<?= e(old('deposit_amount') ?? '0') ?>" class="form-input" step="0.01" min="0">
</div>
<div>
<label class="form-label">ملف العقد (PDF) <span style="color:#DC2626;">*</span></label>
<input type="file" name="contract_pdf" class="form-input" required accept=".pdf">
</div>
<div style="grid-column:1 / -1;">
<label class="form-label">ملاحظات</label>
<textarea name="notes" class="form-input" rows="3" placeholder="ملاحظات على العقد..."><?= e(old('notes') ?? '') ?></textarea>
</div>
</div>
</div>
<div style="display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary"><i data-lucide="save" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ العقد</button>
<a href="/sa/academies/<?= (int) $academy['id'] ?>/contracts" class="btn btn-outline">إلغاء</a>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php
$__template->layout('Layout.main');
?>
<?php $__template->section('title'); ?>تفاصيل العقد: <?= e($contract['contract_number'] ?? '') ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (($contract['status'] ?? '') === 'pending_approval'): ?>
<form method="POST" action="/sa/academy-contracts/<?= (int) $contract['id'] ?>/approve" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-primary" onclick="return confirm('هل تريد اعتماد هذا العقد؟')">
<i data-lucide="check-circle" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> اعتماد العقد
</button>
</form>
<?php endif; ?>
<a href="/sa/academies/<?= (int) $contract['academy_id'] ?>/contracts" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> رجوع</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$contractTypeLabels = ['revenue_share' => 'نسبة إيرادات', 'fixed_rent' => 'إيجار ثابت', 'hybrid' => 'مختلط'];
$statusLabels = ['pending_approval' => 'بانتظار الاعتماد', 'active' => 'فعال', 'expired' => 'منتهي', 'terminated' => 'ملغي'];
$statusColors = ['pending_approval' => '#D97706', 'active' => '#059669', 'expired' => '#6B7280', 'terminated' => '#DC2626'];
$cStatus = $contract['status'] ?? 'pending_approval';
$csColor = $statusColors[$cStatus] ?? '#6B7280';
?>
<!-- Contract Detail -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:20px;display:flex;align-items:center;gap:20px;border-bottom:1px solid #E5E7EB;">
<div style="width:56px;height:56px;border-radius:12px;background:linear-gradient(135deg, <?= $csColor ?>15, <?= $csColor ?>30);display:flex;align-items:center;justify-content:center;">
<i data-lucide="file-signature" style="width:28px;height:28px;color:<?= $csColor ?>;"></i>
</div>
<div>
<h2 style="margin:0 0 4px;font-size:20px;font-weight:700;">عقد رقم <?= e($contract['contract_number'] ?? '') ?></h2>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
<span style="font-size:13px;color:#6B7280;">الأكاديمية: <strong><?= e($contract['academy_name'] ?? '') ?></strong></span>
<span style="padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:<?= $csColor ?>15;color:<?= $csColor ?>;">
<?= e($statusLabels[$cStatus] ?? $cStatus) ?>
</span>
</div>
</div>
</div>
<div style="padding:20px;display:grid;grid-template-columns:repeat(auto-fit, minmax(220px, 1fr));gap:20px;">
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">نوع العقد</div>
<div style="font-size:14px;font-weight:600;"><?= e($contractTypeLabels[$contract['contract_type'] ?? ''] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">تاريخ البداية</div>
<div style="font-size:14px;font-weight:600;"><?= e($contract['start_date'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">تاريخ النهاية</div>
<div style="font-size:14px;font-weight:600;"><?= e($contract['end_date'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">نسبة النادي</div>
<div style="font-size:14px;font-weight:600;"><?= number_format((float) ($contract['club_commission_pct'] ?? 0), 1) ?>%</div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">نسبة الأكاديمية</div>
<div style="font-size:14px;font-weight:600;"><?= number_format((float) ($contract['academy_share_pct'] ?? 0), 1) ?>%</div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">الإيجار الشهري</div>
<div style="font-size:14px;font-weight:600;"><?= number_format((float) ($contract['fixed_monthly_rent'] ?? 0), 2) ?> ج.م</div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">مبلغ التأمين</div>
<div style="font-size:14px;font-weight:600;"><?= number_format((float) ($contract['deposit_amount'] ?? 0), 2) ?> ج.م</div>
</div>
<?php if (!empty($contract['approved_at'])): ?>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">تاريخ الاعتماد</div>
<div style="font-size:14px;font-weight:600;"><?= e($contract['approved_at'] ?? '-') ?></div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Contract PDF -->
<?php if (!empty($contract['contract_pdf_path'])): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">ملف العقد</h3>
</div>
<div style="padding:20px;">
<a href="/<?= e($contract['contract_pdf_path']) ?>" target="_blank" class="btn btn-outline">
<i data-lucide="file-text" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> تحميل ملف العقد (PDF)
</a>
</div>
</div>
<?php endif; ?>
<!-- Notes -->
<?php if (!empty($contract['notes'])): ?>
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">ملاحظات</h3>
</div>
<div style="padding:20px;font-size:14px;line-height:1.8;color:#374151;">
<?= nl2br(e($contract['notes'])) ?>
</div>
</div>
<?php endif; ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php
$__template->layout('Layout.main');
?>
<?php $__template->section('title'); ?>عقود الأكاديمية: <?= e($academy['name_ar'] ?? '') ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/academies/<?= (int) $academy['id'] ?>/contracts/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة عقد</a>
<a href="/sa/academies/<?= (int) $academy['id'] ?>" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> رجوع للأكاديمية</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$contractTypeLabels = ['revenue_share' => 'نسبة إيرادات', 'fixed_rent' => 'إيجار ثابت', 'hybrid' => 'مختلط'];
$statusLabels = ['pending_approval' => 'بانتظار الاعتماد', 'active' => 'فعال', 'expired' => 'منتهي', 'terminated' => 'ملغي'];
$statusColors = ['pending_approval' => '#D97706', 'active' => '#059669', 'expired' => '#6B7280', 'terminated' => '#DC2626'];
?>
<?php if (!empty($contracts)): ?>
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>رقم العقد</th>
<th>النوع</th>
<th>تاريخ البداية</th>
<th>تاريخ النهاية</th>
<th>نسبة النادي</th>
<th>نسبة الأكاديمية</th>
<th>الحالة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php foreach ($contracts as $contract):
$cStatus = $contract['status'] ?? 'pending_approval';
$csColor = $statusColors[$cStatus] ?? '#6B7280';
?>
<tr>
<td><code style="font-size:12px;"><?= e($contract['contract_number'] ?? '') ?></code></td>
<td><?= e($contractTypeLabels[$contract['contract_type'] ?? ''] ?? $contract['contract_type'] ?? '-') ?></td>
<td style="font-size:13px;"><?= e($contract['start_date'] ?? '-') ?></td>
<td style="font-size:13px;"><?= e($contract['end_date'] ?? '-') ?></td>
<td style="text-align:center;"><?= number_format((float) ($contract['club_commission_pct'] ?? 0), 1) ?>%</td>
<td style="text-align:center;"><?= number_format((float) ($contract['academy_share_pct'] ?? 0), 1) ?>%</td>
<td>
<span style="padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:<?= $csColor ?>15;color:<?= $csColor ?>;">
<?= e($statusLabels[$cStatus] ?? $cStatus) ?>
</span>
</td>
<td>
<div style="display:flex;gap:6px;">
<a href="/sa/academy-contracts/<?= (int) $contract['id'] ?>" class="btn btn-sm btn-outline" title="عرض">
<i data-lucide="eye" style="width:14px;height:14px;"></i>
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php else: ?>
<div class="card" style="padding:60px 20px;text-align:center;">
<div style="margin-bottom:15px;">
<i data-lucide="file-text" style="width:48px;height:48px;color:#D1D5DB;"></i>
</div>
<h3 style="color:#6B7280;margin:0 0 8px;">لا توجد عقود</h3>
<p style="color:#9CA3AF;font-size:14px;margin:0 0 20px;">لم يتم إضافة عقود لهذه الأكاديمية بعد.</p>
<a href="/sa/academies/<?= (int) $academy['id'] ?>/contracts/create" class="btn btn-primary">
<i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة عقد
</a>
</div>
<?php endif; ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php
$__template->layout('Layout.main');
?>
<?php $__template->section('title'); ?>إضافة أكاديمية جديدة<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/academies" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> رجوع</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/sa/academies">
<?= csrf_field() ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">بيانات الأكاديمية</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));gap:15px;">
<div>
<label class="form-label">الكود <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code') ?? '') ?>" class="form-input" dir="ltr" required placeholder="مثال: ACAD_SWIM_01">
</div>
<div>
<label class="form-label">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<input type="text" name="name_ar" value="<?= e(old('name_ar') ?? '') ?>" class="form-input" required>
</div>
<div>
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="name_en" value="<?= e(old('name_en') ?? '') ?>" class="form-input" dir="ltr">
</div>
<div>
<label class="form-label">اللعبة / النشاط الرياضي <span style="color:#DC2626;">*</span></label>
<select name="discipline_id" class="form-select" required>
<option value="">اختر...</option>
<?php foreach ($disciplines as $disc): ?>
<option value="<?= (int) $disc['id'] ?>" <?= old('discipline_id') == $disc['id'] ? 'selected' : '' ?>><?= e($disc['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div>
<label class="form-label">نوع الأكاديمية</label>
<select name="academy_type" class="form-select">
<option value="internal" <?= (old('academy_type') ?? '') === 'internal' ? 'selected' : '' ?>>داخلية</option>
<option value="external" <?= old('academy_type') === 'external' ? 'selected' : '' ?>>خارجية</option>
<option value="partnership" <?= old('academy_type') === 'partnership' ? 'selected' : '' ?>>شراكة</option>
</select>
</div>
<div>
<label class="form-label">مسؤول التواصل</label>
<input type="text" name="contact_person" value="<?= e(old('contact_person') ?? '') ?>" class="form-input">
</div>
<div>
<label class="form-label">هاتف التواصل</label>
<input type="text" name="contact_phone" value="<?= e(old('contact_phone') ?? '') ?>" class="form-input" dir="ltr">
</div>
<div>
<label class="form-label">البريد الإلكتروني</label>
<input type="email" name="contact_email" value="<?= e(old('contact_email') ?? '') ?>" class="form-input" dir="ltr">
</div>
<div style="grid-column:1 / -1;">
<label class="form-label">الوصف</label>
<textarea name="description_ar" class="form-input" rows="3" placeholder="وصف الأكاديمية..."><?= e(old('description_ar') ?? '') ?></textarea>
</div>
</div>
</div>
<div style="display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary"><i data-lucide="save" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ</button>
<a href="/sa/academies" class="btn btn-outline">إلغاء</a>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php
$__template->layout('Layout.main');
?>
<?php $__template->section('title'); ?>تعديل الأكاديمية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/academies/<?= (int) $academy['id'] ?>" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> رجوع</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/sa/academies/<?= (int) $academy['id'] ?>/update">
<?= csrf_field() ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">بيانات الأكاديمية</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));gap:15px;">
<div>
<label class="form-label">الكود <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code') ?? $academy['code'] ?? '') ?>" class="form-input" dir="ltr" required>
</div>
<div>
<label class="form-label">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<input type="text" name="name_ar" value="<?= e(old('name_ar') ?? $academy['name_ar'] ?? '') ?>" class="form-input" required>
</div>
<div>
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="name_en" value="<?= e(old('name_en') ?? $academy['name_en'] ?? '') ?>" class="form-input" dir="ltr">
</div>
<div>
<label class="form-label">اللعبة / النشاط الرياضي <span style="color:#DC2626;">*</span></label>
<select name="discipline_id" class="form-select" required>
<option value="">اختر...</option>
<?php foreach ($disciplines as $disc): ?>
<option value="<?= (int) $disc['id'] ?>" <?= (old('discipline_id') ?? $academy['discipline_id'] ?? '') == $disc['id'] ? 'selected' : '' ?>><?= e($disc['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div>
<label class="form-label">نوع الأكاديمية</label>
<select name="academy_type" class="form-select">
<option value="internal" <?= (old('academy_type') ?? $academy['academy_type'] ?? '') === 'internal' ? 'selected' : '' ?>>داخلية</option>
<option value="external" <?= (old('academy_type') ?? $academy['academy_type'] ?? '') === 'external' ? 'selected' : '' ?>>خارجية</option>
<option value="partnership" <?= (old('academy_type') ?? $academy['academy_type'] ?? '') === 'partnership' ? 'selected' : '' ?>>شراكة</option>
</select>
</div>
<div>
<label class="form-label">مسؤول التواصل</label>
<input type="text" name="contact_person" value="<?= e(old('contact_person') ?? $academy['contact_person'] ?? '') ?>" class="form-input">
</div>
<div>
<label class="form-label">هاتف التواصل</label>
<input type="text" name="contact_phone" value="<?= e(old('contact_phone') ?? $academy['contact_phone'] ?? '') ?>" class="form-input" dir="ltr">
</div>
<div>
<label class="form-label">البريد الإلكتروني</label>
<input type="email" name="contact_email" value="<?= e(old('contact_email') ?? $academy['contact_email'] ?? '') ?>" class="form-input" dir="ltr">
</div>
<div style="grid-column:1 / -1;">
<label class="form-label">الوصف</label>
<textarea name="description_ar" class="form-input" rows="3"><?= e(old('description_ar') ?? $academy['description_ar'] ?? '') ?></textarea>
</div>
</div>
</div>
<div style="display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary"><i data-lucide="save" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ التعديلات</button>
<a href="/sa/academies/<?= (int) $academy['id'] ?>" class="btn btn-outline">إلغاء</a>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php
$__template->layout('Layout.main');
?>
<?php $__template->section('title'); ?>الأكاديميات<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/academies/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة أكاديمية</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Search & Filters -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<form method="GET" action="/sa/academies" style="display:flex;gap:10px;align-items:end;flex-wrap:wrap;">
<div style="flex:1;min-width:200px;">
<label class="form-label" style="font-size:12px;">بحث</label>
<input type="text" name="q" value="<?= e($filters['q'] ?? '') ?>" placeholder="ابحث بالاسم أو الكود..." class="form-input">
</div>
<div style="min-width:180px;">
<label class="form-label" style="font-size:12px;">اللعبة</label>
<select name="discipline_id" class="form-select">
<option value="">الكل</option>
<?php foreach ($disciplines as $disc): ?>
<option value="<?= (int) $disc['id'] ?>" <?= ($filters['discipline_id'] ?? '') == $disc['id'] ? 'selected' : '' ?>><?= e($disc['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-primary"><i data-lucide="search" style="width:16px;height:16px;vertical-align:middle;"></i> بحث</button>
<a href="/sa/academies" class="btn btn-outline">مسح</a>
</form>
</div>
<!-- Academies Table -->
<?php if (!empty($academies)): ?>
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الكود</th>
<th>الاسم</th>
<th>اللعبة</th>
<th>النوع</th>
<th>الحالة</th>
<th>العقود</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php
$typeLabels = ['internal' => 'داخلية', 'external' => 'خارجية', 'partnership' => 'شراكة'];
$typeColors = ['internal' => '#059669', 'external' => '#7C3AED', 'partnership' => '#0D7377'];
foreach ($academies as $academy):
$aType = $academy['academy_type'] ?? 'internal';
$atColor = $typeColors[$aType] ?? '#6B7280';
?>
<tr>
<td><code style="font-size:12px;"><?= e($academy['code'] ?? '') ?></code></td>
<td>
<a href="/sa/academies/<?= (int) $academy['id'] ?>" style="text-decoration:none;color:#0D7377;font-weight:600;">
<?= e($academy['name_ar'] ?? '') ?>
</a>
<?php if (!empty($academy['name_en'])): ?>
<div style="font-size:11px;color:#9CA3AF;" dir="ltr"><?= e($academy['name_en']) ?></div>
<?php endif; ?>
</td>
<td><?= e($academy['discipline_name'] ?? '-') ?></td>
<td>
<span style="padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:<?= $atColor ?>15;color:<?= $atColor ?>;">
<?= e($typeLabels[$aType] ?? $aType) ?>
</span>
</td>
<td>
<?php if ((int) ($academy['is_active'] ?? 0)): ?>
<span style="padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:#05966915;color:#059669;">فعالة</span>
<?php else: ?>
<span style="padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:#DC262615;color:#DC2626;">معطلة</span>
<?php endif; ?>
</td>
<td style="text-align:center;">
<span style="padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;background:#F3F4F6;">
<?= (int) ($academy['contract_count'] ?? 0) ?>
</span>
</td>
<td>
<div style="display:flex;gap:6px;">
<a href="/sa/academies/<?= (int) $academy['id'] ?>" class="btn btn-sm btn-outline" title="عرض">
<i data-lucide="eye" style="width:14px;height:14px;"></i>
</a>
<a href="/sa/academies/<?= (int) $academy['id'] ?>/edit" class="btn btn-sm btn-outline" title="تعديل">
<i data-lucide="edit-3" style="width:14px;height:14px;"></i>
</a>
<a href="/sa/academies/<?= (int) $academy['id'] ?>/contracts" class="btn btn-sm btn-outline" title="العقود">
<i data-lucide="file-signature" style="width:14px;height:14px;"></i>
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php if (isset($pagination) && ($pagination['last_page'] ?? 1) > 1): ?>
<div style="padding:15px;">
<?php $__template->include('Shared.Components.pagination', ['pagination' => $pagination, 'baseUrl' => '/sa/academies?' . http_build_query(array_filter($filters))]); ?>
</div>
<?php endif; ?>
</div>
<?php else: ?>
<div class="card" style="padding:60px 20px;text-align:center;">
<div style="margin-bottom:15px;">
<i data-lucide="graduation-cap" style="width:48px;height:48px;color:#D1D5DB;"></i>
</div>
<h3 style="color:#6B7280;margin:0 0 8px;">لا توجد أكاديميات</h3>
<p style="color:#9CA3AF;font-size:14px;margin:0 0 20px;">
<?php if (!empty($filters['q']) || !empty($filters['discipline_id'])): ?>
لا توجد نتائج مطابقة لبحثك.
<?php else: ?>
ابدأ بإضافة أكاديمية جديدة.
<?php endif; ?>
</p>
<a href="/sa/academies/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة أكاديمية</a>
</div>
<?php endif; ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php
$__template->layout('Layout.main');
?>
<?php $__template->section('title'); ?>الأكاديمية: <?= e($academy['name_ar'] ?? '') ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/academies/<?= (int) $academy['id'] ?>/edit" class="btn btn-primary"><i data-lucide="edit-3" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> تعديل</a>
<a href="/sa/academies/<?= (int) $academy['id'] ?>/contracts/create" class="btn btn-outline"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة عقد</a>
<a href="/sa/academies" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> رجوع</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$typeLabels = ['internal' => 'داخلية', 'external' => 'خارجية', 'partnership' => 'شراكة'];
$typeColors = ['internal' => '#059669', 'external' => '#7C3AED', 'partnership' => '#0D7377'];
$aType = $academy['academy_type'] ?? 'internal';
$atColor = $typeColors[$aType] ?? '#6B7280';
?>
<!-- Academy Info -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:20px;display:flex;align-items:center;gap:20px;border-bottom:1px solid #E5E7EB;">
<div style="width:64px;height:64px;border-radius:12px;background:linear-gradient(135deg, <?= $atColor ?>15, <?= $atColor ?>30);display:flex;align-items:center;justify-content:center;">
<i data-lucide="graduation-cap" style="width:32px;height:32px;color:<?= $atColor ?>;"></i>
</div>
<div>
<h2 style="margin:0 0 4px;font-size:20px;font-weight:700;"><?= e($academy['name_ar'] ?? '') ?></h2>
<?php if (!empty($academy['name_en'])): ?>
<div style="font-size:13px;color:#6B7280;margin-bottom:4px;" dir="ltr"><?= e($academy['name_en']) ?></div>
<?php endif; ?>
<div style="display:flex;gap:8px;flex-wrap:wrap;">
<code style="font-size:11px;background:#F3F4F6;padding:2px 8px;border-radius:4px;"><?= e($academy['code'] ?? '') ?></code>
<span style="padding:2px 10px;border-radius:10px;font-size:11px;font-weight:600;background:<?= $atColor ?>15;color:<?= $atColor ?>;"><?= e($typeLabels[$aType] ?? $aType) ?></span>
<?php if ((int) ($academy['is_active'] ?? 0)): ?>
<span style="padding:2px 10px;border-radius:10px;font-size:11px;font-weight:600;background:#05966915;color:#059669;">فعالة</span>
<?php else: ?>
<span style="padding:2px 10px;border-radius:10px;font-size:11px;font-weight:600;background:#DC262615;color:#DC2626;">معطلة</span>
<?php endif; ?>
</div>
</div>
</div>
<div style="padding:20px;display:grid;grid-template-columns:repeat(auto-fit, minmax(220px, 1fr));gap:20px;">
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">اللعبة</div>
<div style="font-size:14px;font-weight:600;"><?= e($academy['discipline_name'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">مسؤول التواصل</div>
<div style="font-size:14px;font-weight:600;"><?= e($academy['contact_person'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">هاتف التواصل</div>
<div style="font-size:14px;font-weight:600;" dir="ltr"><?= e($academy['contact_phone'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">البريد الإلكتروني</div>
<div style="font-size:14px;font-weight:600;" dir="ltr"><?= e($academy['contact_email'] ?? '-') ?></div>
</div>
</div>
<?php if (!empty($academy['description_ar'])): ?>
<div style="padding:0 20px 20px;">
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">الوصف</div>
<div style="font-size:14px;line-height:1.8;color:#374151;"><?= nl2br(e($academy['description_ar'])) ?></div>
</div>
<?php endif; ?>
</div>
<!-- Contracts Section -->
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center;">
<h3 style="margin:0;font-size:16px;font-weight:600;">العقود</h3>
<a href="/sa/academies/<?= (int) $academy['id'] ?>/contracts/create" class="btn btn-sm btn-primary">
<i data-lucide="plus" style="width:14px;height:14px;vertical-align:middle;margin-left:4px;"></i> إضافة عقد
</a>
</div>
<?php if (!empty($contracts)): ?>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>رقم العقد</th>
<th>النوع</th>
<th>من</th>
<th>إلى</th>
<th>الحالة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php
$contractTypeLabels = ['revenue_share' => 'نسبة إيرادات', 'fixed_rent' => 'إيجار ثابت', 'hybrid' => 'مختلط'];
$statusLabels = ['pending_approval' => 'بانتظار الاعتماد', 'active' => 'فعال', 'expired' => 'منتهي', 'terminated' => 'ملغي'];
$statusColors = ['pending_approval' => '#D97706', 'active' => '#059669', 'expired' => '#6B7280', 'terminated' => '#DC2626'];
foreach ($contracts as $contract):
$cStatus = $contract['status'] ?? 'pending_approval';
$csColor = $statusColors[$cStatus] ?? '#6B7280';
?>
<tr>
<td><code style="font-size:12px;"><?= e($contract['contract_number'] ?? '') ?></code></td>
<td><?= e($contractTypeLabels[$contract['contract_type'] ?? ''] ?? $contract['contract_type'] ?? '-') ?></td>
<td style="font-size:13px;"><?= e($contract['start_date'] ?? '-') ?></td>
<td style="font-size:13px;"><?= e($contract['end_date'] ?? '-') ?></td>
<td>
<span style="padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:<?= $csColor ?>15;color:<?= $csColor ?>;">
<?= e($statusLabels[$cStatus] ?? $cStatus) ?>
</span>
</td>
<td>
<a href="/sa/academy-contracts/<?= (int) $contract['id'] ?>" class="btn btn-sm btn-outline" title="عرض">
<i data-lucide="eye" style="width:14px;height:14px;"></i>
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div style="padding:40px 20px;text-align:center;color:#9CA3AF;">
<i data-lucide="file-text" style="width:36px;height:36px;color:#D1D5DB;margin-bottom:10px;"></i>
<p>لا توجد عقود بعد.</p>
</div>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>تسجيل الحضور<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/attendance/report" class="btn btn-outline"><i data-lucide="bar-chart-2" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> تقرير الحضور</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card" style="margin-bottom:15px;padding:15px;background:#F0FDF4;border-right:4px solid #059669;">
<strong>حصص التدريب اليوم:</strong> <?= e($today) ?>
</div>
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الوقت</th>
<th>المجموعة</th>
<th>المدرب</th>
<th>المرفق</th>
<th>الوحدة</th>
<th>الحالة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php if (!empty($bookings)): ?>
<?php foreach ($bookings as $b): ?>
<tr>
<td style="font-weight:600;white-space:nowrap;">
<?= e($b['start_time'] ?? '') ?> - <?= e($b['end_time'] ?? '') ?>
</td>
<td><?= e($b['group_name'] ?? '—') ?></td>
<td><?= e($b['coach_name'] ?? '—') ?></td>
<td><?= e($b['facility_name'] ?? '—') ?></td>
<td><?= e($b['unit_name'] ?? '—') ?></td>
<td>
<?php
$statusMap = ['pending' => 'في الانتظار', 'confirmed' => 'مؤكد', 'checked_in' => 'تم الحضور', 'completed' => 'مكتمل'];
$statusLabel = $statusMap[$b['status'] ?? ''] ?? ($b['status'] ?? '');
?>
<span style="background:#ECFDF5;color:#059669;padding:3px 10px;border-radius:10px;font-size:12px;"><?= e($statusLabel) ?></span>
</td>
<td>
<a href="/sa/attendance/record/<?= (int) $b['id'] ?>" class="btn btn-sm btn-primary" style="font-size:12px;padding:4px 12px;">
<i data-lucide="clipboard-check" style="width:13px;height:13px;vertical-align:middle;margin-left:4px;"></i> تسجيل الحضور
</a>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="7" style="text-align:center;padding:40px;color:#6B7280;">
<i data-lucide="calendar-x" style="width:36px;height:36px;color:#D1D5DB;display:block;margin:0 auto 10px;"></i>
لا توجد حصص تدريب اليوم
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>تسجيل حضور الحصة<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/attendance" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> العودة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Booking Info -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<div style="display:flex;gap:20px;flex-wrap:wrap;font-size:14px;">
<div><strong>المجموعة:</strong> <?= e($booking['group_name'] ?? '—') ?></div>
<div><strong>المدرب:</strong> <?= e($booking['coach_name'] ?? '—') ?></div>
<div><strong>التاريخ:</strong> <?= e($booking['booking_date'] ?? '') ?></div>
<div><strong>الوقت:</strong> <?= e($booking['start_time'] ?? '') ?> - <?= e($booking['end_time'] ?? '') ?></div>
<div><strong>المرفق:</strong> <?= e($booking['facility_name'] ?? '') ?> / <?= e($booking['unit_name'] ?? '') ?></div>
</div>
</div>
<!-- Attendance Form -->
<div class="card">
<form method="POST" action="/sa/attendance/store/<?= (int) $booking['id'] ?>">
<?= csrf_field() ?>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th style="width:40px;">#</th>
<th>اللاعب</th>
<th>الكود</th>
<th style="text-align:center;">حاضر</th>
<th style="text-align:center;">غائب</th>
<th style="text-align:center;">متأخر</th>
<th style="text-align:center;">معذور</th>
</tr>
</thead>
<tbody>
<?php if (!empty($players)): ?>
<?php foreach ($players as $i => $player): ?>
<?php
$playerId = (int) $player['player_id'];
$currentStatus = $attendanceMap[$playerId] ?? 'present';
?>
<tr>
<td><?= $i + 1 ?></td>
<td style="font-weight:600;"><?= e($player['player_name'] ?? '') ?></td>
<td><code style="font-size:11px;background:#F3F4F6;padding:2px 6px;border-radius:4px;"><?= e($player['player_code'] ?? '') ?></code></td>
<td style="text-align:center;">
<input type="hidden" name="player_ids[<?= $i ?>]" value="<?= $playerId ?>">
<input type="radio" name="statuses[<?= $i ?>]" value="present" <?= $currentStatus === 'present' ? 'checked' : '' ?>>
</td>
<td style="text-align:center;">
<input type="radio" name="statuses[<?= $i ?>]" value="absent" <?= $currentStatus === 'absent' ? 'checked' : '' ?>>
</td>
<td style="text-align:center;">
<input type="radio" name="statuses[<?= $i ?>]" value="late" <?= $currentStatus === 'late' ? 'checked' : '' ?>>
</td>
<td style="text-align:center;">
<input type="radio" name="statuses[<?= $i ?>]" value="excused" <?= $currentStatus === 'excused' ? 'checked' : '' ?>>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="7" style="text-align:center;padding:30px;color:#6B7280;">
لا يوجد لاعبون مسجلون في هذه المجموعة
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if (!empty($players)): ?>
<div style="padding:15px;display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary">
<i data-lucide="save" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ الحضور
</button>
<button type="button" class="btn btn-outline" onclick="selectAll('present');">تحديد الكل حاضر</button>
<button type="button" class="btn btn-outline" onclick="selectAll('absent');">تحديد الكل غائب</button>
</div>
<?php endif; ?>
</form>
</div>
<script>
function selectAll(status) {
var radios = document.querySelectorAll('input[type=radio][value=' + status + ']');
radios.forEach(function(r) { r.checked = true; });
}
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>تقرير الحضور<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/attendance" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> العودة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Filters -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<form method="GET" action="/sa/attendance/report" style="display:flex;gap:10px;align-items:end;flex-wrap:wrap;">
<div style="min-width:180px;">
<label class="form-label" style="font-size:12px;">المجموعة</label>
<select name="group_id" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($groups as $g): ?>
<option value="<?= (int) $g['id'] ?>" <?= ($filters['group_id'] ?? '') === (string) $g['id'] ? 'selected' : '' ?>><?= e($g['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="min-width:140px;">
<label class="form-label" style="font-size:12px;">رقم اللاعب</label>
<input type="number" name="player_id" value="<?= e($filters['player_id'] ?? '') ?>" class="form-input" placeholder="ID">
</div>
<div style="min-width:140px;">
<label class="form-label" style="font-size:12px;">من تاريخ</label>
<input type="date" name="date_from" value="<?= e($filters['date_from'] ?? '') ?>" class="form-input">
</div>
<div style="min-width:140px;">
<label class="form-label" style="font-size:12px;">إلى تاريخ</label>
<input type="date" name="date_to" value="<?= e($filters['date_to'] ?? '') ?>" class="form-input">
</div>
<button type="submit" class="btn btn-outline"><i data-lucide="search" style="width:16px;height:16px;vertical-align:middle;"></i> بحث</button>
<a href="/sa/attendance/report" class="btn btn-sm btn-outline" style="color:#6B7280;">مسح</a>
</form>
</div>
<!-- Summary -->
<?php if ($summary): ?>
<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(140px, 1fr));gap:12px;margin-bottom:20px;">
<div class="card" style="padding:15px;text-align:center;">
<div style="font-size:24px;font-weight:700;color:#1A1A2E;"><?= (int) $summary['total'] ?></div>
<div style="font-size:12px;color:#6B7280;">إجمالي السجلات</div>
</div>
<div class="card" style="padding:15px;text-align:center;">
<div style="font-size:24px;font-weight:700;color:#059669;"><?= (int) $summary['present_count'] ?></div>
<div style="font-size:12px;color:#6B7280;">حاضر</div>
</div>
<div class="card" style="padding:15px;text-align:center;">
<div style="font-size:24px;font-weight:700;color:#DC2626;"><?= (int) $summary['absent_count'] ?></div>
<div style="font-size:12px;color:#6B7280;">غائب</div>
</div>
<div class="card" style="padding:15px;text-align:center;">
<div style="font-size:24px;font-weight:700;color:#D97706;"><?= (int) $summary['late_count'] ?></div>
<div style="font-size:12px;color:#6B7280;">متأخر</div>
</div>
<div class="card" style="padding:15px;text-align:center;">
<div style="font-size:24px;font-weight:700;color:#6366F1;"><?= (int) $summary['excused_count'] ?></div>
<div style="font-size:12px;color:#6B7280;">معذور</div>
</div>
</div>
<?php endif; ?>
<!-- Records Table -->
<?php if (!empty($records)): ?>
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>التاريخ</th>
<th>الوقت</th>
<th>اللاعب</th>
<th>المجموعة</th>
<th>الحالة</th>
</tr>
</thead>
<tbody>
<?php foreach ($records as $r): ?>
<tr>
<td><?= e($r['booking_date'] ?? '') ?></td>
<td><?= e($r['start_time'] ?? '') ?> - <?= e($r['end_time'] ?? '') ?></td>
<td style="font-weight:600;"><?= e($r['player_name'] ?? '—') ?></td>
<td><?= e($r['group_name'] ?? '—') ?></td>
<td>
<?php
$attStatusMap = [
'present' => ['حاضر', 'background:#ECFDF5;color:#059669;'],
'absent' => ['غائب', 'background:#FEE2E2;color:#DC2626;'],
'late' => ['متأخر', 'background:#FEF3C7;color:#D97706;'],
'excused' => ['معذور', 'background:#EDE9FE;color:#6366F1;'],
];
$attInfo = $attStatusMap[$r['status'] ?? ''] ?? [$r['status'] ?? '', 'background:#F3F4F6;color:#374151;'];
?>
<span style="<?= $attInfo[1] ?>padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;"><?= e($attInfo[0]) ?></span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php elseif ($summary === null): ?>
<div class="card" style="padding:40px;text-align:center;color:#6B7280;">
<i data-lucide="bar-chart-2" style="width:36px;height:36px;color:#D1D5DB;display:block;margin:0 auto 10px;"></i>
اختر فلاتر البحث لعرض تقرير الحضور
</div>
<?php else: ?>
<div class="card" style="padding:40px;text-align:center;color:#6B7280;">
لا توجد سجلات حضور مطابقة
</div>
<?php endif; ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>حجز جديد (بالساعة)<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/bookings" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/sa/bookings">
<?= csrf_field() ?>
<div style="display:grid;grid-template-columns:2fr 1fr;gap:20px;">
<!-- Left: Booking Form -->
<div>
<!-- Facility & Time -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="map-pin" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">المرفق والوقت</h3>
</div>
<div style="padding:20px;">
<div class="form-group">
<label class="form-label">الوحدة / الملعب <span style="color:#DC2626;">*</span></label>
<select name="facility_unit_id" class="form-select" required id="unitSelect">
<option value="">-- اختر الوحدة --</option>
<?php foreach ($facilityUnitsGrouped as $facilityName => $units): ?>
<optgroup label="<?= e($facilityName) ?>">
<?php foreach ($units as $u): ?>
<option value="<?= (int) $u['id'] ?>" <?= old('facility_unit_id') == $u['id'] ? 'selected' : '' ?>><?= e($u['name_ar']) ?> (<?= e($u['code']) ?>)</option>
<?php endforeach; ?>
</optgroup>
<?php endforeach; ?>
</select>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:15px;margin-top:15px;">
<div class="form-group">
<label class="form-label">تاريخ الحجز <span style="color:#DC2626;">*</span></label>
<input type="date" name="booking_date" value="<?= e(old('booking_date', date('Y-m-d'))) ?>" class="form-input" required style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">وقت البداية <span style="color:#DC2626;">*</span></label>
<input type="time" name="start_time" value="<?= e(old('start_time')) ?>" class="form-input" required style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">وقت النهاية <span style="color:#DC2626;">*</span></label>
<input type="time" name="end_time" value="<?= e(old('end_time')) ?>" class="form-input" required style="direction:ltr;text-align:left;">
</div>
</div>
</div>
</div>
<!-- Booker Info -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="user" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">بيانات الحاجز</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:15px;">
<div class="form-group">
<label class="form-label">اسم الحاجز <span style="color:#DC2626;">*</span></label>
<input type="text" name="booker_name" value="<?= e(old('booker_name')) ?>" class="form-input" required placeholder="اسم الشخص الحاجز">
</div>
<div class="form-group">
<label class="form-label">نوع الحاجز</label>
<select name="booker_type" class="form-select">
<option value="guest" <?= old('booker_type', 'guest') === 'guest' ? 'selected' : '' ?>>زائر</option>
<option value="member" <?= old('booker_type') === 'member' ? 'selected' : '' ?>>عضو</option>
<option value="employee" <?= old('booker_type') === 'employee' ? 'selected' : '' ?>>موظف</option>
</select>
</div>
<div class="form-group">
<label class="form-label">عدد المشاركين</label>
<input type="number" name="participant_count" value="<?= e(old('participant_count', '1')) ?>" class="form-input" min="1" style="direction:ltr;text-align:left;">
</div>
</div>
<div class="form-group" style="margin-top:15px;">
<label class="form-label">ملاحظات</label>
<textarea name="notes" class="form-input" rows="2" placeholder="أي ملاحظات إضافية..."><?= e(old('notes')) ?></textarea>
</div>
</div>
</div>
<!-- Submit -->
<div style="display:flex;gap:10px;">
<button type="submit" class="btn btn-primary" style="padding:12px 30px;font-size:15px;">
<i data-lucide="check" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إنشاء الحجز
</button>
<a href="/sa/bookings" class="btn btn-outline" style="padding:12px 30px;font-size:15px;">إلغاء</a>
</div>
</div>
<!-- Right: Price Preview -->
<div>
<div class="card" style="position:sticky;top:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="receipt" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">معاينة السعر</h3>
</div>
<div style="padding:20px;" id="pricePreview">
<div style="text-align:center;padding:30px 0;color:#9CA3AF;">
<i data-lucide="calculator" style="width:32px;height:32px;display:block;margin:0 auto 10px;color:#D1D5DB;"></i>
<div style="font-size:13px;">اختر الوحدة والوقت لمعرفة السعر</div>
</div>
</div>
</div>
</div>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>الحجوزات<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/bookings/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حجز جديد</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Filters -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<form method="GET" action="/sa/bookings" style="display:flex;gap:10px;align-items:end;flex-wrap:wrap;">
<div style="min-width:140px;">
<label class="form-label" style="font-size:12px;">من تاريخ</label>
<input type="date" name="date_from" value="<?= e($filters['date_from'] ?? '') ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div style="min-width:140px;">
<label class="form-label" style="font-size:12px;">إلى تاريخ</label>
<input type="date" name="date_to" value="<?= e($filters['date_to'] ?? '') ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div style="min-width:160px;">
<label class="form-label" style="font-size:12px;">المرفق</label>
<select name="facility_id" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($facilities as $f): ?>
<option value="<?= (int) $f['id'] ?>" <?= ($filters['facility_id'] ?? '') == $f['id'] ? 'selected' : '' ?>><?= e($f['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="min-width:140px;">
<label class="form-label" style="font-size:12px;">النوع</label>
<select name="booking_type" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($bookingTypes as $key => $label): ?>
<option value="<?= e($key) ?>" <?= ($filters['booking_type'] ?? '') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="min-width:130px;">
<label class="form-label" style="font-size:12px;">الحالة</label>
<select name="status" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($statuses as $key => $label): ?>
<option value="<?= e($key) ?>" <?= ($filters['status'] ?? '') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-outline"><i data-lucide="search" style="width:16px;height:16px;vertical-align:middle;"></i> بحث</button>
<a href="/sa/bookings" class="btn btn-sm btn-outline" style="color:#6B7280;">مسح</a>
</form>
</div>
<!-- Bookings Table -->
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>رقم الحجز</th>
<th>المرفق/الوحدة</th>
<th>النوع</th>
<th>التاريخ</th>
<th>الوقت</th>
<th>الحاجز</th>
<th>المبلغ</th>
<th>الحالة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php if (!empty($bookings)): ?>
<?php
$statusColors = ['pending' => '#D97706', 'confirmed' => '#2563EB', 'checked_in' => '#059669', 'completed' => '#6B7280', 'cancelled' => '#DC2626', 'no_show' => '#9CA3AF'];
$statusBgs = ['pending' => '#FEF3C7', 'confirmed' => '#EFF6FF', 'checked_in' => '#ECFDF5', 'completed' => '#F3F4F6', 'cancelled' => '#FEE2E2', 'no_show' => '#F3F4F6'];
$typeColors = ['hourly' => '#F59E0B', 'training' => '#3B82F6', 'maintenance' => '#6B7280', 'blocked' => '#4B5563'];
$typeBgs = ['hourly' => '#FEF3C7', 'training' => '#EFF6FF', 'maintenance' => '#F3F4F6', 'blocked' => '#E5E7EB'];
?>
<?php foreach ($bookings as $b): ?>
<tr>
<td>
<a href="/sa/bookings/<?= (int) $b['id'] ?>" style="text-decoration:none;color:#1A1A2E;font-weight:600;">
<?= e($b['booking_number']) ?>
</a>
</td>
<td>
<?= e($b['facility_name'] ?? '') ?>
<?php if (!empty($b['unit_name'])): ?>
<div style="font-size:11px;color:#6B7280;"><?= e($b['unit_name']) ?></div>
<?php endif; ?>
</td>
<td>
<?php $bt = $b['booking_type'] ?? ''; ?>
<span style="background:<?= $typeBgs[$bt] ?? '#F3F4F6' ?>;color:<?= $typeColors[$bt] ?? '#6B7280' ?>;padding:2px 8px;border-radius:4px;font-size:12px;">
<?= e($bookingTypes[$bt] ?? $bt) ?>
</span>
</td>
<td style="direction:ltr;text-align:right;"><?= e($b['booking_date']) ?></td>
<td style="direction:ltr;text-align:right;"><?= e(substr($b['start_time'], 0, 5)) ?> - <?= e(substr($b['end_time'], 0, 5)) ?></td>
<td><?= e($b['booker_name'] ?? '—') ?></td>
<td style="direction:ltr;text-align:right;font-weight:600;"><?= money((float) ($b['total_amount'] ?? 0)) ?></td>
<td>
<?php $bs = $b['status'] ?? ''; ?>
<span style="background:<?= $statusBgs[$bs] ?? '#F3F4F6' ?>;color:<?= $statusColors[$bs] ?? '#6B7280' ?>;padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">
<?= e($statuses[$bs] ?? $bs) ?>
</span>
</td>
<td>
<a href="/sa/bookings/<?= (int) $b['id'] ?>" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="eye" style="width:13px;height:13px;vertical-align:middle;"></i> عرض
</a>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="9" style="text-align:center;padding:40px;color:#6B7280;">
<i data-lucide="calendar-x" style="width:36px;height:36px;color:#D1D5DB;display:block;margin:0 auto 10px;"></i>
لا توجد حجوزات مطابقة
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if (isset($pagination) && ($pagination['last_page'] ?? 1) > 1): ?>
<div style="padding:15px;">
<?php $__template->include('Shared.Components.pagination', ['pagination' => $pagination, 'baseUrl' => '/sa/bookings?' . http_build_query(array_filter($filters))]); ?>
</div>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>الحجز: <?= e($booking['booking_number']) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/bookings" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$statusColors = ['pending' => '#D97706', 'confirmed' => '#2563EB', 'checked_in' => '#059669', 'completed' => '#6B7280', 'cancelled' => '#DC2626', 'no_show' => '#9CA3AF'];
$statusBgs = ['pending' => '#FEF3C7', 'confirmed' => '#EFF6FF', 'checked_in' => '#ECFDF5', 'completed' => '#F3F4F6', 'cancelled' => '#FEE2E2', 'no_show' => '#F3F4F6'];
$bs = $booking['status'] ?? '';
?>
<!-- Status Banner -->
<div style="background:<?= $statusBgs[$bs] ?? '#F3F4F6' ?>;border-radius:8px;padding:12px 20px;margin-bottom:20px;display:flex;align-items:center;justify-content:space-between;">
<div style="display:flex;align-items:center;gap:10px;">
<span style="color:<?= $statusColors[$bs] ?? '#6B7280' ?>;font-weight:700;font-size:15px;">
<?= e($statuses[$bs] ?? $bs) ?>
</span>
<span style="color:#6B7280;font-size:13px;"></span>
<span style="color:#6B7280;font-size:13px;"><?= e($bookingTypes[$booking['booking_type'] ?? ''] ?? '') ?></span>
</div>
<div style="display:flex;gap:8px;">
<?php if (in_array($bs, ['confirmed'])): ?>
<form method="POST" action="/sa/bookings/<?= (int) $booking['id'] ?>/checkin" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-sm btn-primary" style="background:#059669;border-color:#059669;">
<i data-lucide="log-in" style="width:14px;height:14px;vertical-align:middle;margin-left:4px;"></i> تسجيل حضور
</button>
</form>
<?php endif; ?>
<?php if (in_array($bs, ['confirmed', 'checked_in'])): ?>
<form method="POST" action="/sa/bookings/<?= (int) $booking['id'] ?>/checkout" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-sm btn-primary" style="background:#2563EB;border-color:#2563EB;">
<i data-lucide="log-out" style="width:14px;height:14px;vertical-align:middle;margin-left:4px;"></i> إتمام
</button>
</form>
<?php endif; ?>
<?php if (!in_array($bs, ['cancelled', 'completed', 'no_show'])): ?>
<form method="POST" action="/sa/bookings/<?= (int) $booking['id'] ?>/cancel" style="display:inline;" onsubmit="return confirm('هل تريد إلغاء هذا الحجز؟');">
<?= csrf_field() ?>
<input type="hidden" name="reason" value="">
<button type="submit" class="btn btn-sm btn-outline" style="color:#DC2626;border-color:#DC2626;">
<i data-lucide="x-circle" style="width:14px;height:14px;vertical-align:middle;margin-left:4px;"></i> إلغاء الحجز
</button>
</form>
<?php endif; ?>
</div>
</div>
<!-- Booking Details -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">بيانات الحجز</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:20px;">
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">رقم الحجز</div>
<div style="font-weight:600;"><code style="background:#F3F4F6;padding:2px 8px;border-radius:4px;"><?= e($booking['booking_number']) ?></code></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">المرفق</div>
<div style="font-weight:600;"><?= e($booking['facility_name'] ?? '—') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الوحدة</div>
<div style="font-weight:600;"><?= e($booking['unit_name'] ?? '—') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">النوع</div>
<div style="font-weight:600;"><?= e($bookingTypes[$booking['booking_type'] ?? ''] ?? '—') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">التاريخ</div>
<div style="font-weight:600;"><?= e($booking['booking_date']) ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الوقت</div>
<div style="font-weight:600;direction:ltr;text-align:right;"><?= e(substr($booking['start_time'], 0, 5)) ?> - <?= e(substr($booking['end_time'], 0, 5)) ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">المبلغ</div>
<div style="font-weight:700;color:#0D7377;font-size:16px;"><?= money((float) ($booking['total_amount'] ?? 0)) ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">عدد المشاركين</div>
<div style="font-weight:600;"><?= (int) ($booking['participant_count'] ?? 0) ?></div>
</div>
</div>
<!-- Booker & Group Info -->
<div style="margin-top:20px;padding-top:15px;border-top:1px solid #F3F4F6;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:20px;">
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الحاجز</div>
<div style="font-weight:600;"><?= e($booking['booker_name'] ?? '—') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">نوع الحاجز</div>
<div style="font-weight:600;"><?= e($booking['booker_type'] ?? '—') ?></div>
</div>
<?php if (!empty($booking['group_name'])): ?>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">المجموعة</div>
<div style="font-weight:600;"><?= e($booking['group_name']) ?></div>
</div>
<?php endif; ?>
<?php if (!empty($booking['coach_name'])): ?>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">المدرب</div>
<div style="font-weight:600;"><?= e($booking['coach_name']) ?></div>
</div>
<?php endif; ?>
</div>
</div>
<?php if (!empty($booking['notes'])): ?>
<div style="margin-top:15px;padding-top:15px;border-top:1px solid #F3F4F6;">
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">ملاحظات</div>
<div><?= e($booking['notes']) ?></div>
</div>
<?php endif; ?>
<?php if ($bs === 'cancelled' && !empty($booking['cancellation_reason'])): ?>
<div style="margin-top:15px;padding:12px;background:#FEE2E2;border-radius:6px;">
<div style="font-size:12px;color:#DC2626;font-weight:600;margin-bottom:4px;">سبب الإلغاء</div>
<div style="color:#991B1B;"><?= e($booking['cancellation_reason']) ?></div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Participants -->
<?php if (!empty($participants)): ?>
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">المشاركون (<?= count($participants) ?>)</h3>
</div>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>#</th>
<th>الاسم</th>
<th>كود اللاعب</th>
<th>عضو</th>
<th>المبلغ</th>
</tr>
</thead>
<tbody>
<?php foreach ($participants as $idx => $p): ?>
<tr>
<td><?= $idx + 1 ?></td>
<td style="font-weight:600;"><?= e($p['player_name'] ?? $p['participant_name'] ?? '—') ?></td>
<td><code style="font-size:11px;background:#F3F4F6;padding:2px 6px;border-radius:4px;"><?= e($p['player_code'] ?? '—') ?></code></td>
<td>
<?php if ((int) ($p['is_member'] ?? 0)): ?>
<span style="background:#ECFDF5;color:#059669;padding:2px 8px;border-radius:4px;font-size:12px;">عضو</span>
<?php else: ?>
<span style="background:#F3F4F6;color:#6B7280;padding:2px 8px;border-radius:4px;font-size:12px;">زائر</span>
<?php endif; ?>
</td>
<td style="direction:ltr;text-align:right;"><?= money((float) ($p['price_charged'] ?? 0)) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php endif; ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>إضافة مدرب جديد<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/coaches" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/sa/coaches" id="coachForm">
<?= csrf_field() ?>
<!-- Basic Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="user" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">البيانات الأساسية</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">كود المدرب <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code')) ?>" class="form-input" required placeholder="مثال: COACH-001" style="direction:ltr;text-align:left;text-transform:uppercase;">
</div>
<div class="form-group">
<label class="form-label">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<input type="text" name="full_name_ar" value="<?= e(old('full_name_ar')) ?>" class="form-input" required placeholder="الاسم الكامل بالعربي">
</div>
<div class="form-group">
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="full_name_en" value="<?= e(old('full_name_en')) ?>" class="form-input" placeholder="Full name in English" style="direction:ltr;text-align:left;">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">الرقم القومي</label>
<input type="text" name="national_id" value="<?= e(old('national_id')) ?>" class="form-input" placeholder="14 رقم" maxlength="14" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">الهاتف</label>
<input type="text" name="phone" value="<?= e(old('phone')) ?>" class="form-input" placeholder="01xxxxxxxxx" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">البريد الإلكتروني</label>
<input type="email" name="email" value="<?= e(old('email')) ?>" class="form-input" placeholder="email@example.com" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">تاريخ الميلاد</label>
<input type="date" name="date_of_birth" value="<?= e(old('date_of_birth')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 3fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">النوع</label>
<select name="gender" class="form-select">
<option value="">-- اختر --</option>
<option value="male" <?= old('gender') === 'male' ? 'selected' : '' ?>>ذكر</option>
<option value="female" <?= old('gender') === 'female' ? 'selected' : '' ?>>أنثى</option>
</select>
</div>
</div>
</div>
</div>
<!-- Employment Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="briefcase" style="width:18px;height:18px;color:#059669;"></i>
<h3 style="margin:0;color:#059669;font-size:15px;">بيانات التوظيف</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">نوع التوظيف</label>
<select name="employment_type" class="form-select">
<option value="">-- اختر --</option>
<?php foreach ($employmentTypes as $key => $label): ?>
<option value="<?= e($key) ?>" <?= old('employment_type') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">نموذج الدفع</label>
<select name="payment_model" class="form-select">
<option value="">-- اختر --</option>
<?php foreach ($paymentModels as $key => $label): ?>
<option value="<?= e($key) ?>" <?= old('payment_model') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">سعر الساعة (ج.م)</label>
<input type="number" name="hourly_rate" value="<?= e(old('hourly_rate')) ?>" class="form-input" min="0" step="0.01" placeholder="0.00" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">سعر الحصة (ج.م)</label>
<input type="number" name="session_rate" value="<?= e(old('session_rate')) ?>" class="form-input" min="0" step="0.01" placeholder="0.00" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">الراتب الشهري (ج.م)</label>
<input type="number" name="monthly_rate" value="<?= e(old('monthly_rate')) ?>" class="form-input" min="0" step="0.01" placeholder="0.00" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">أقصى عدد مجموعات</label>
<input type="number" name="max_groups" value="<?= e(old('max_groups')) ?>" class="form-input" min="0" placeholder="مثال: 5" style="direction:ltr;text-align:left;">
</div>
</div>
</div>
</div>
<!-- Disciplines -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;justify-content:space-between;">
<div style="display:flex;align-items:center;gap:8px;">
<i data-lucide="activity" style="width:18px;height:18px;color:#7C3AED;"></i>
<h3 style="margin:0;color:#7C3AED;font-size:15px;">التخصصات</h3>
</div>
<button type="button" id="addDisciplineBtn" class="btn btn-sm btn-outline" style="color:#7C3AED;border-color:#7C3AED;">
<i data-lucide="plus" style="width:14px;height:14px;vertical-align:middle;margin-left:3px;"></i> إضافة تخصص
</button>
</div>
<div style="padding:20px;" id="disciplinesContainer">
<div id="noDisciplinesMsg" style="text-align:center;padding:20px;color:#9CA3AF;font-size:14px;">
لم يتم إضافة تخصصات بعد. اضغط "إضافة تخصص" لإضافة تخصص جديد.
</div>
</div>
</div>
<!-- Bio -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="file-text" style="width:18px;height:18px;color:#6B7280;"></i>
<h3 style="margin:0;color:#6B7280;font-size:15px;">نبذة</h3>
</div>
<div style="padding:20px;">
<div class="form-group">
<label class="form-label">نبذة عن المدرب</label>
<textarea name="bio_ar" class="form-input" rows="4" placeholder="نبذة مختصرة عن خبرات المدرب ومؤهلاته..."><?= e(old('bio_ar')) ?></textarea>
</div>
</div>
</div>
<!-- Submit -->
<div style="display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary"><i data-lucide="check" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ المدرب</button>
<a href="/sa/coaches" class="btn btn-outline">إلغاء</a>
</div>
</form>
<template id="disciplineRowTemplate">
<div class="discipline-row" style="display:grid;grid-template-columns:2fr 1fr auto;gap:10px;align-items:end;margin-bottom:10px;padding:10px;background:#F9FAFB;border-radius:8px;">
<div class="form-group" style="margin:0;">
<label class="form-label" style="font-size:12px;">التخصص</label>
<select name="discipline_ids[]" class="form-select" required>
<option value="">-- اختر التخصص --</option>
<?php foreach ($disciplines as $disc): ?>
<option value="<?= (int) $disc['id'] ?>"><?= e($disc['name_ar'] ?? '') ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group" style="margin:0;">
<label class="form-label" style="font-size:12px;">المستوى</label>
<select name="levels[]" class="form-select">
<option value="primary">أساسي</option>
<option value="secondary">ثانوي</option>
</select>
</div>
<button type="button" class="btn btn-sm btn-outline removeDisciplineBtn" style="color:#DC2626;border-color:#DC2626;margin-bottom:2px;" title="حذف">
<i data-lucide="trash-2" style="width:14px;height:14px;"></i>
</button>
</div>
</template>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
var container = document.getElementById('disciplinesContainer');
var template = document.getElementById('disciplineRowTemplate');
var addBtn = document.getElementById('addDisciplineBtn');
var noMsg = document.getElementById('noDisciplinesMsg');
function updateVisibility() {
var rows = container.querySelectorAll('.discipline-row');
noMsg.style.display = rows.length === 0 ? 'block' : 'none';
}
addBtn.addEventListener('click', function() {
var clone = template.content.cloneNode(true);
container.appendChild(clone);
updateVisibility();
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
});
container.addEventListener('click', function(e) {
var btn = e.target.closest('.removeDisciplineBtn');
if (btn) {
btn.closest('.discipline-row').remove();
updateVisibility();
}
});
updateVisibility();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>تعديل المدرب: <?= e($coach['full_name_ar'] ?? '') ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/coaches/<?= (int) $coach['id'] ?>" class="btn btn-outline"><i data-lucide="eye" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> عرض</a>
<a href="/sa/coaches" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
// Use old input if available (after validation failure), otherwise use coach data
$val = function(string $field) use ($coach) {
$old = old($field);
return $old !== '' && $old !== null ? $old : ($coach[$field] ?? '');
};
?>
<form method="POST" action="/sa/coaches/<?= (int) $coach['id'] ?>" id="coachForm">
<?= csrf_field() ?>
<!-- Basic Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="user" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">البيانات الأساسية</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">كود المدرب <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e($val('code')) ?>" class="form-input" required placeholder="مثال: COACH-001" style="direction:ltr;text-align:left;text-transform:uppercase;">
</div>
<div class="form-group">
<label class="form-label">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<input type="text" name="full_name_ar" value="<?= e($val('full_name_ar')) ?>" class="form-input" required placeholder="الاسم الكامل بالعربي">
</div>
<div class="form-group">
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="full_name_en" value="<?= e($val('full_name_en')) ?>" class="form-input" placeholder="Full name in English" style="direction:ltr;text-align:left;">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">الرقم القومي</label>
<input type="text" name="national_id" value="<?= e($val('national_id')) ?>" class="form-input" placeholder="14 رقم" maxlength="14" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">الهاتف</label>
<input type="text" name="phone" value="<?= e($val('phone')) ?>" class="form-input" placeholder="01xxxxxxxxx" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">البريد الإلكتروني</label>
<input type="email" name="email" value="<?= e($val('email')) ?>" class="form-input" placeholder="email@example.com" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">تاريخ الميلاد</label>
<input type="date" name="date_of_birth" value="<?= e($val('date_of_birth')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 3fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">النوع</label>
<select name="gender" class="form-select">
<option value="">-- اختر --</option>
<option value="male" <?= $val('gender') === 'male' ? 'selected' : '' ?>>ذكر</option>
<option value="female" <?= $val('gender') === 'female' ? 'selected' : '' ?>>أنثى</option>
</select>
</div>
</div>
</div>
</div>
<!-- Employment Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="briefcase" style="width:18px;height:18px;color:#059669;"></i>
<h3 style="margin:0;color:#059669;font-size:15px;">بيانات التوظيف</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">نوع التوظيف</label>
<select name="employment_type" class="form-select">
<option value="">-- اختر --</option>
<?php foreach ($employmentTypes as $key => $label): ?>
<option value="<?= e($key) ?>" <?= $val('employment_type') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">نموذج الدفع</label>
<select name="payment_model" class="form-select">
<option value="">-- اختر --</option>
<?php foreach ($paymentModels as $key => $label): ?>
<option value="<?= e($key) ?>" <?= $val('payment_model') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">سعر الساعة (ج.م)</label>
<input type="number" name="hourly_rate" value="<?= e($val('hourly_rate')) ?>" class="form-input" min="0" step="0.01" placeholder="0.00" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">سعر الحصة (ج.م)</label>
<input type="number" name="session_rate" value="<?= e($val('session_rate')) ?>" class="form-input" min="0" step="0.01" placeholder="0.00" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">الراتب الشهري (ج.م)</label>
<input type="number" name="monthly_rate" value="<?= e($val('monthly_rate')) ?>" class="form-input" min="0" step="0.01" placeholder="0.00" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">أقصى عدد مجموعات</label>
<input type="number" name="max_groups" value="<?= e($val('max_groups')) ?>" class="form-input" min="0" placeholder="مثال: 5" style="direction:ltr;text-align:left;">
</div>
</div>
</div>
</div>
<!-- Disciplines -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;justify-content:space-between;">
<div style="display:flex;align-items:center;gap:8px;">
<i data-lucide="activity" style="width:18px;height:18px;color:#7C3AED;"></i>
<h3 style="margin:0;color:#7C3AED;font-size:15px;">التخصصات</h3>
</div>
<button type="button" id="addDisciplineBtn" class="btn btn-sm btn-outline" style="color:#7C3AED;border-color:#7C3AED;">
<i data-lucide="plus" style="width:14px;height:14px;vertical-align:middle;margin-left:3px;"></i> إضافة تخصص
</button>
</div>
<div style="padding:20px;" id="disciplinesContainer">
<div id="noDisciplinesMsg" style="text-align:center;padding:20px;color:#9CA3AF;font-size:14px;display:none;">
لم يتم إضافة تخصصات بعد. اضغط "إضافة تخصص" لإضافة تخصص جديد.
</div>
<?php foreach ($coachDisciplines as $cd): ?>
<div class="discipline-row" style="display:grid;grid-template-columns:2fr 1fr auto;gap:10px;align-items:end;margin-bottom:10px;padding:10px;background:#F9FAFB;border-radius:8px;">
<div class="form-group" style="margin:0;">
<label class="form-label" style="font-size:12px;">التخصص</label>
<select name="discipline_ids[]" class="form-select" required>
<option value="">-- اختر التخصص --</option>
<?php foreach ($disciplines as $disc): ?>
<option value="<?= (int) $disc['id'] ?>" <?= (int) $cd['discipline_id'] === (int) $disc['id'] ? 'selected' : '' ?>><?= e($disc['name_ar'] ?? '') ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group" style="margin:0;">
<label class="form-label" style="font-size:12px;">المستوى</label>
<select name="levels[]" class="form-select">
<option value="primary" <?= ($cd['specialization_level'] ?? '') === 'primary' ? 'selected' : '' ?>>أساسي</option>
<option value="secondary" <?= ($cd['specialization_level'] ?? '') === 'secondary' ? 'selected' : '' ?>>ثانوي</option>
</select>
</div>
<button type="button" class="btn btn-sm btn-outline removeDisciplineBtn" style="color:#DC2626;border-color:#DC2626;margin-bottom:2px;" title="حذف">
<i data-lucide="trash-2" style="width:14px;height:14px;"></i>
</button>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- Bio -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="file-text" style="width:18px;height:18px;color:#6B7280;"></i>
<h3 style="margin:0;color:#6B7280;font-size:15px;">نبذة</h3>
</div>
<div style="padding:20px;">
<div class="form-group">
<label class="form-label">نبذة عن المدرب</label>
<textarea name="bio_ar" class="form-input" rows="4" placeholder="نبذة مختصرة عن خبرات المدرب ومؤهلاته..."><?= e($val('bio_ar')) ?></textarea>
</div>
</div>
</div>
<!-- Submit -->
<div style="display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary"><i data-lucide="check" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ التعديلات</button>
<a href="/sa/coaches/<?= (int) $coach['id'] ?>" class="btn btn-outline">إلغاء</a>
</div>
</form>
<template id="disciplineRowTemplate">
<div class="discipline-row" style="display:grid;grid-template-columns:2fr 1fr auto;gap:10px;align-items:end;margin-bottom:10px;padding:10px;background:#F9FAFB;border-radius:8px;">
<div class="form-group" style="margin:0;">
<label class="form-label" style="font-size:12px;">التخصص</label>
<select name="discipline_ids[]" class="form-select" required>
<option value="">-- اختر التخصص --</option>
<?php foreach ($disciplines as $disc): ?>
<option value="<?= (int) $disc['id'] ?>"><?= e($disc['name_ar'] ?? '') ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group" style="margin:0;">
<label class="form-label" style="font-size:12px;">المستوى</label>
<select name="levels[]" class="form-select">
<option value="primary">أساسي</option>
<option value="secondary">ثانوي</option>
</select>
</div>
<button type="button" class="btn btn-sm btn-outline removeDisciplineBtn" style="color:#DC2626;border-color:#DC2626;margin-bottom:2px;" title="حذف">
<i data-lucide="trash-2" style="width:14px;height:14px;"></i>
</button>
</div>
</template>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
var container = document.getElementById('disciplinesContainer');
var template = document.getElementById('disciplineRowTemplate');
var addBtn = document.getElementById('addDisciplineBtn');
var noMsg = document.getElementById('noDisciplinesMsg');
function updateVisibility() {
var rows = container.querySelectorAll('.discipline-row');
noMsg.style.display = rows.length === 0 ? 'block' : 'none';
}
addBtn.addEventListener('click', function() {
var clone = template.content.cloneNode(true);
container.appendChild(clone);
updateVisibility();
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
});
container.addEventListener('click', function(e) {
var btn = e.target.closest('.removeDisciplineBtn');
if (btn) {
btn.closest('.discipline-row').remove();
updateVisibility();
}
});
updateVisibility();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>المدربون<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/coaches/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة مدرب</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Search -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<form method="GET" action="/sa/coaches" style="display:flex;gap:10px;align-items:end;flex-wrap:wrap;">
<div style="flex:1;min-width:250px;">
<label class="form-label" style="font-size:12px;">بحث</label>
<input type="text" name="q" value="<?= e($search ?? '') ?>" placeholder="ابحث بالاسم، الرقم القومي، أو الكود..." class="form-input">
</div>
<button type="submit" class="btn btn-outline"><i data-lucide="search" style="width:16px;height:16px;vertical-align:middle;"></i> بحث</button>
<?php if (($search ?? '') !== ''): ?>
<a href="/sa/coaches" class="btn btn-outline" style="color:#6B7280;">مسح</a>
<?php endif; ?>
</form>
</div>
<!-- Results -->
<?php if (!empty($coaches)): ?>
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الكود</th>
<th>الاسم</th>
<th>الهاتف</th>
<th>نوع التوظيف</th>
<th>التخصصات</th>
<th>الحالة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php
$employmentTypes = \App\Modules\SportsActivity\Models\Coach::getEmploymentTypeOptions();
foreach ($coaches as $coach):
$isActive = (int) ($coach['is_active'] ?? 0);
$empType = $employmentTypes[$coach['employment_type'] ?? ''] ?? '-';
?>
<tr>
<td><code style="font-size:12px;background:#F3F4F6;padding:2px 6px;border-radius:4px;"><?= e($coach['code'] ?? '') ?></code></td>
<td>
<a href="/sa/coaches/<?= (int) $coach['id'] ?>" style="color:#0D7377;text-decoration:none;font-weight:600;">
<?= e($coach['full_name_ar'] ?? '') ?>
</a>
<?php if (!empty($coach['full_name_en'])): ?>
<div style="font-size:11px;color:#9CA3AF;"><?= e($coach['full_name_en']) ?></div>
<?php endif; ?>
</td>
<td style="direction:ltr;text-align:right;"><?= e($coach['phone'] ?? '-') ?></td>
<td><?= e($empType) ?></td>
<td>
<?php if (!empty($coach['discipline_names'])): ?>
<span style="font-size:12px;color:#374151;"><?= e($coach['discipline_names']) ?></span>
<?php else: ?>
<span style="font-size:12px;color:#9CA3AF;">-</span>
<?php endif; ?>
</td>
<td>
<?php if ($isActive): ?>
<span style="display:inline-block;padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:#D1FAE5;color:#065F46;">نشط</span>
<?php else: ?>
<span style="display:inline-block;padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:#FEE2E2;color:#DC2626;">معطّل</span>
<?php endif; ?>
</td>
<td>
<div style="display:flex;gap:4px;">
<a href="/sa/coaches/<?= (int) $coach['id'] ?>" class="btn btn-sm btn-outline" title="عرض">
<i data-lucide="eye" style="width:14px;height:14px;"></i>
</a>
<a href="/sa/coaches/<?= (int) $coach['id'] ?>/edit" class="btn btn-sm btn-outline" title="تعديل">
<i data-lucide="edit-3" style="width:14px;height:14px;"></i>
</a>
<form method="POST" action="/sa/coaches/<?= (int) $coach['id'] ?>/toggle" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-sm btn-outline" title="<?= $isActive ? 'تعطيل' : 'تفعيل' ?>" onclick="return confirm('<?= $isActive ? 'هل أنت متأكد من تعطيل هذا المدرب؟' : 'هل أنت متأكد من تفعيل هذا المدرب؟' ?>');">
<i data-lucide="<?= $isActive ? 'toggle-right' : 'toggle-left' ?>" style="width:14px;height:14px;"></i>
</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php if (($pagination['last_page'] ?? 1) > 1): ?>
<div style="padding:15px;">
<?php $__template->include('Shared.Components.pagination', ['pagination' => $pagination, 'baseUrl' => '/sa/coaches?' . ($search !== '' ? 'q=' . urlencode($search) . '&' : '')]); ?>
</div>
<?php endif; ?>
</div>
<?php else: ?>
<div class="card" style="padding:60px 20px;text-align:center;">
<div style="margin-bottom:15px;">
<i data-lucide="user-check" style="width:48px;height:48px;color:#D1D5DB;"></i>
</div>
<h3 style="color:#6B7280;margin:0 0 8px;">لا يوجد مدربون</h3>
<p style="color:#9CA3AF;font-size:14px;margin:0 0 20px;">
<?php if (($search ?? '') !== ''): ?>
لا توجد نتائج مطابقة لبحثك. جرب تغيير معايير البحث.
<?php else: ?>
ابدأ بإضافة مدرب جديد.
<?php endif; ?>
</p>
<a href="/sa/coaches/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة مدرب</a>
</div>
<?php endif; ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>المدرب: <?= e($coach['full_name_ar'] ?? '') ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/coaches/<?= (int) $coach['id'] ?>/edit" class="btn btn-primary"><i data-lucide="edit-3" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> تعديل</a>
<form method="POST" action="/sa/coaches/<?= (int) $coach['id'] ?>/toggle" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-outline" onclick="return confirm('<?= (int) $coach['is_active'] ? 'هل أنت متأكد من تعطيل هذا المدرب؟' : 'هل أنت متأكد من تفعيل هذا المدرب؟' ?>');">
<i data-lucide="<?= (int) $coach['is_active'] ? 'toggle-right' : 'toggle-left' ?>" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i>
<?= (int) $coach['is_active'] ? 'تعطيل' : 'تفعيل' ?>
</button>
</form>
<a href="/sa/coaches" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$isActive = (int) ($coach['is_active'] ?? 0);
?>
<!-- Status Badge -->
<div style="margin-bottom:20px;">
<?php if ($isActive): ?>
<span style="display:inline-block;padding:5px 14px;border-radius:10px;font-size:13px;font-weight:600;background:#D1FAE5;color:#065F46;">نشط</span>
<?php else: ?>
<span style="display:inline-block;padding:5px 14px;border-radius:10px;font-size:13px;font-weight:600;background:#FEE2E2;color:#DC2626;">معطّل</span>
<?php endif; ?>
</div>
<!-- Basic Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="user" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">البيانات الأساسية</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;">
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الكود</div>
<div style="font-weight:600;"><code style="font-size:13px;background:#F3F4F6;padding:3px 8px;border-radius:4px;"><?= e($coach['code'] ?? '-') ?></code></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الاسم بالعربي</div>
<div style="font-weight:600;"><?= e($coach['full_name_ar'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الاسم بالإنجليزي</div>
<div style="font-weight:600;"><?= e($coach['full_name_en'] ?? '-') ?></div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:20px;margin-top:20px;">
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الرقم القومي</div>
<div style="direction:ltr;text-align:right;"><?= e($coach['national_id'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الهاتف</div>
<div style="direction:ltr;text-align:right;"><?= e($coach['phone'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">البريد الإلكتروني</div>
<div style="direction:ltr;text-align:right;"><?= e($coach['email'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">تاريخ الميلاد</div>
<div><?= !empty($coach['date_of_birth']) ? e($coach['date_of_birth']) : '-' ?></div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 3fr;gap:20px;margin-top:20px;">
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">النوع</div>
<div><?php
$genderLabels = ['male' => 'ذكر', 'female' => 'أنثى'];
echo e($genderLabels[$coach['gender'] ?? ''] ?? '-');
?></div>
</div>
</div>
</div>
</div>
<!-- Employment Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="briefcase" style="width:18px;height:18px;color:#059669;"></i>
<h3 style="margin:0;color:#059669;font-size:15px;">بيانات التوظيف</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;">
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">نوع التوظيف</div>
<div style="font-weight:600;"><?= e($employmentTypes[$coach['employment_type'] ?? ''] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">نموذج الدفع</div>
<div style="font-weight:600;"><?= e($paymentModels[$coach['payment_model'] ?? ''] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">أقصى عدد مجموعات</div>
<div style="font-weight:600;"><?= !empty($coach['max_groups']) ? (int) $coach['max_groups'] : '-' ?></div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;margin-top:20px;">
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">سعر الساعة</div>
<div><?= $coach['hourly_rate'] !== null && $coach['hourly_rate'] !== '' ? number_format((float) $coach['hourly_rate'], 2) . ' ج.م' : '-' ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">سعر الحصة</div>
<div><?= $coach['session_rate'] !== null && $coach['session_rate'] !== '' ? number_format((float) $coach['session_rate'], 2) . ' ج.م' : '-' ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الراتب الشهري</div>
<div><?= $coach['monthly_rate'] !== null && $coach['monthly_rate'] !== '' ? number_format((float) $coach['monthly_rate'], 2) . ' ج.م' : '-' ?></div>
</div>
</div>
</div>
</div>
<!-- Disciplines -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="activity" style="width:18px;height:18px;color:#7C3AED;"></i>
<h3 style="margin:0;color:#7C3AED;font-size:15px;">التخصصات</h3>
</div>
<div style="padding:20px;">
<?php if (!empty($disciplines)): ?>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>التخصص</th>
<th>الكود</th>
<th>المستوى</th>
</tr>
</thead>
<tbody>
<?php foreach ($disciplines as $disc): ?>
<tr>
<td style="font-weight:600;"><?= e($disc['discipline_name'] ?? '') ?></td>
<td><code style="font-size:12px;background:#F3F4F6;padding:2px 6px;border-radius:4px;"><?= e($disc['discipline_code'] ?? '') ?></code></td>
<td>
<?php if (($disc['specialization_level'] ?? '') === 'primary'): ?>
<span style="display:inline-block;padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:#DBEAFE;color:#1D4ED8;">أساسي</span>
<?php else: ?>
<span style="display:inline-block;padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:#F3F4F6;color:#6B7280;">ثانوي</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div style="text-align:center;padding:20px;color:#9CA3AF;font-size:14px;">
لا توجد تخصصات مسجلة لهذا المدرب.
</div>
<?php endif; ?>
</div>
</div>
<!-- Bio -->
<?php if (!empty($coach['bio_ar'])): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="file-text" style="width:18px;height:18px;color:#6B7280;"></i>
<h3 style="margin:0;color:#6B7280;font-size:15px;">نبذة</h3>
</div>
<div style="padding:20px;">
<p style="margin:0;white-space:pre-wrap;color:#374151;line-height:1.8;"><?= e($coach['bio_ar']) ?></p>
</div>
</div>
<?php endif; ?>
<!-- Metadata -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="clock" style="width:18px;height:18px;color:#9CA3AF;"></i>
<h3 style="margin:0;color:#9CA3AF;font-size:15px;">معلومات النظام</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">تاريخ الإنشاء</div>
<div style="font-size:13px;"><?= e($coach['created_at'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">آخر تحديث</div>
<div style="font-size:13px;"><?= e($coach['updated_at'] ?? '-') ?></div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>لوحة تحكم الأنشطة الرياضية<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div style="display:grid;grid-template-columns:repeat(auto-fill, minmax(240px, 1fr));gap:15px;margin-bottom:20px;">
<!-- Active Disciplines -->
<a href="/sa/disciplines" class="card" style="padding:20px;text-decoration:none;color:inherit;border-right:4px solid #8B5CF6;">
<div style="display:flex;align-items:center;gap:12px;">
<div style="width:44px;height:44px;background:#EDE9FE;border-radius:10px;display:flex;align-items:center;justify-content:center;">
<i data-lucide="activity" style="width:22px;height:22px;color:#8B5CF6;"></i>
</div>
<div>
<div style="font-size:24px;font-weight:700;color:#1A1A2E;"><?= (int) $stats['active_disciplines'] ?></div>
<div style="font-size:13px;color:#6B7280;">الأنشطة الرياضية</div>
</div>
</div>
</a>
<!-- Active Facilities + Units -->
<a href="/sa/facilities" class="card" style="padding:20px;text-decoration:none;color:inherit;border-right:4px solid #3B82F6;">
<div style="display:flex;align-items:center;gap:12px;">
<div style="width:44px;height:44px;background:#DBEAFE;border-radius:10px;display:flex;align-items:center;justify-content:center;">
<i data-lucide="building" style="width:22px;height:22px;color:#3B82F6;"></i>
</div>
<div>
<div style="font-size:24px;font-weight:700;color:#1A1A2E;"><?= (int) $stats['active_facilities'] ?> <span style="font-size:14px;color:#6B7280;font-weight:400;">(<?= (int) $stats['active_units'] ?> وحدة)</span></div>
<div style="font-size:13px;color:#6B7280;">المرافق النشطة</div>
</div>
</div>
</a>
<!-- Active Coaches -->
<a href="/sa/coaches" class="card" style="padding:20px;text-decoration:none;color:inherit;border-right:4px solid #059669;">
<div style="display:flex;align-items:center;gap:12px;">
<div style="width:44px;height:44px;background:#ECFDF5;border-radius:10px;display:flex;align-items:center;justify-content:center;">
<i data-lucide="user-check" style="width:22px;height:22px;color:#059669;"></i>
</div>
<div>
<div style="font-size:24px;font-weight:700;color:#1A1A2E;"><?= (int) $stats['active_coaches'] ?></div>
<div style="font-size:13px;color:#6B7280;">المدربين النشطين</div>
</div>
</div>
</a>
<!-- Total Players -->
<a href="/sa/players" class="card" style="padding:20px;text-decoration:none;color:inherit;border-right:4px solid #F59E0B;">
<div style="display:flex;align-items:center;gap:12px;">
<div style="width:44px;height:44px;background:#FEF3C7;border-radius:10px;display:flex;align-items:center;justify-content:center;">
<i data-lucide="users" style="width:22px;height:22px;color:#F59E0B;"></i>
</div>
<div>
<div style="font-size:24px;font-weight:700;color:#1A1A2E;"><?= (int) $stats['total_players'] ?></div>
<div style="font-size:13px;color:#6B7280;">اللاعبين المسجلين</div>
</div>
</div>
</a>
<!-- Active Groups -->
<a href="/sa/groups" class="card" style="padding:20px;text-decoration:none;color:inherit;border-right:4px solid #EC4899;">
<div style="display:flex;align-items:center;gap:12px;">
<div style="width:44px;height:44px;background:#FCE7F3;border-radius:10px;display:flex;align-items:center;justify-content:center;">
<i data-lucide="layers" style="width:22px;height:22px;color:#EC4899;"></i>
</div>
<div>
<div style="font-size:24px;font-weight:700;color:#1A1A2E;"><?= (int) $stats['active_groups'] ?> <span style="font-size:14px;color:#6B7280;font-weight:400;">(<?= (int) $stats['enrolled_players'] ?> مسجل)</span></div>
<div style="font-size:13px;color:#6B7280;">المجموعات النشطة</div>
</div>
</div>
</a>
<!-- Today's Bookings -->
<a href="/sa/mirror" class="card" style="padding:20px;text-decoration:none;color:inherit;border-right:4px solid #06B6D4;">
<div style="display:flex;align-items:center;gap:12px;">
<div style="width:44px;height:44px;background:#CFFAFE;border-radius:10px;display:flex;align-items:center;justify-content:center;">
<i data-lucide="calendar" style="width:22px;height:22px;color:#06B6D4;"></i>
</div>
<div>
<div style="font-size:24px;font-weight:700;color:#1A1A2E;"><?= (int) $stats['today_bookings'] ?></div>
<div style="font-size:13px;color:#6B7280;">حجوزات اليوم</div>
</div>
</div>
</a>
<!-- Pending Medical -->
<a href="/sa/players" class="card" style="padding:20px;text-decoration:none;color:inherit;border-right:4px solid #F97316;">
<div style="display:flex;align-items:center;gap:12px;">
<div style="width:44px;height:44px;background:#FFEDD5;border-radius:10px;display:flex;align-items:center;justify-content:center;">
<i data-lucide="heart-pulse" style="width:22px;height:22px;color:#F97316;"></i>
</div>
<div>
<div style="font-size:24px;font-weight:700;color:#1A1A2E;"><?= (int) $stats['pending_medical'] ?></div>
<div style="font-size:13px;color:#6B7280;">بانتظار موافقة طبية</div>
</div>
</div>
</a>
<!-- Overdue Subscriptions -->
<a href="/sa/subscriptions?payment_status=overdue" class="card" style="padding:20px;text-decoration:none;color:inherit;border-right:4px solid #DC2626;">
<div style="display:flex;align-items:center;gap:12px;">
<div style="width:44px;height:44px;background:#FEE2E2;border-radius:10px;display:flex;align-items:center;justify-content:center;">
<i data-lucide="alert-triangle" style="width:22px;height:22px;color:#DC2626;"></i>
</div>
<div>
<div style="font-size:24px;font-weight:700;color:#1A1A2E;"><?= (int) $stats['overdue_subscriptions'] ?></div>
<div style="font-size:13px;color:#6B7280;">اشتراكات متأخرة</div>
</div>
</div>
</a>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>إضافة نشاط رياضي جديد<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/disciplines" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/sa/disciplines" id="disciplineForm">
<?= csrf_field() ?>
<!-- Basic Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="file-text" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">البيانات الأساسية</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">كود النشاط <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code')) ?>" class="form-input" required placeholder="مثال: FOOTBALL" style="direction:ltr;text-align:left;text-transform:uppercase;">
<small style="color:#9CA3AF;font-size:11px;">كود فريد بالإنجليزية لتمييز النشاط</small>
</div>
<div class="form-group">
<label class="form-label">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<input type="text" name="name_ar" value="<?= e(old('name_ar')) ?>" class="form-input" required placeholder="مثال: كرة القدم">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="name_en" value="<?= e(old('name_en')) ?>" class="form-input" placeholder="e.g. Football" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">التصنيف</label>
<select name="category" class="form-select">
<option value="">-- اختر التصنيف --</option>
<?php foreach ($categories as $key => $label): ?>
<option value="<?= e($key) ?>" <?= old('category') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">ترتيب العرض</label>
<input type="number" name="sort_order" value="<?= e(old('sort_order', '0')) ?>" class="form-input" min="0" style="direction:ltr;text-align:left;">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">الأيقونة</label>
<select name="icon" class="form-select" id="iconSelect">
<?php
$icons = [
'activity' => 'Activity (نشاط)',
'trophy' => 'Trophy (كأس)',
'target' => 'Target (هدف)',
'zap' => 'Zap (طاقة)',
'flame' => 'Flame (شعلة)',
'heart' => 'Heart (قلب)',
'star' => 'Star (نجمة)',
'award' => 'Award (جائزة)',
'shield' => 'Shield (درع)',
'swords' => 'Swords (سيوف)',
'bike' => 'Bike (دراجة)',
'dumbbell' => 'Dumbbell (دمبل)',
'waves' => 'Waves (أمواج)',
'wind' => 'Wind (رياح)',
'mountain' => 'Mountain (جبل)',
'flag' => 'Flag (علم)',
'timer' => 'Timer (مؤقت)',
'gauge' => 'Gauge (مقياس)',
'volleyball' => 'Volleyball (كرة طائرة)',
'dribbble' => 'Ball (كرة)',
'circle-dot' => 'Circle Dot (دائرة)',
'move' => 'Move (حركة)',
'grab' => 'Grab (قبضة)',
'footprints' => 'Footprints (خطوات)',
];
$selectedIcon = old('icon', 'activity');
foreach ($icons as $iconKey => $iconLabel):
?>
<option value="<?= e($iconKey) ?>" <?= $selectedIcon === $iconKey ? 'selected' : '' ?>><?= e($iconLabel) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group" style="display:flex;align-items:end;gap:10px;">
<div id="iconPreview" style="width:48px;height:48px;border-radius:10px;background:#F3F4F6;display:flex;align-items:center;justify-content:center;">
<i data-lucide="activity" style="width:24px;height:24px;color:#0D7377;" id="iconPreviewIcon"></i>
</div>
<span style="font-size:12px;color:#9CA3AF;">معاينة الأيقونة</span>
</div>
</div>
<div class="form-group" style="margin-top:15px;">
<label class="form-label">وصف النشاط</label>
<textarea name="description_ar" class="form-input" rows="3" placeholder="وصف مختصر للنشاط الرياضي..."><?= e(old('description_ar')) ?></textarea>
</div>
</div>
</div>
<!-- Submit Buttons -->
<div style="display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary" style="padding:12px 30px;font-size:15px;">
<i data-lucide="check" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ النشاط الرياضي
</button>
<a href="/sa/disciplines" class="btn btn-outline" style="padding:12px 30px;font-size:15px;">إلغاء</a>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
// Icon preview
var iconSelect = document.getElementById('iconSelect');
if (iconSelect) {
iconSelect.addEventListener('change', function() {
var preview = document.getElementById('iconPreviewIcon');
if (preview) {
preview.setAttribute('data-lucide', this.value);
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
}
});
}
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>تعديل: <?= e($discipline->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/disciplines/<?= (int) $discipline->id ?>" class="btn btn-outline"><i data-lucide="eye" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> عرض</a>
<a href="/sa/disciplines" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/sa/disciplines/<?= (int) $discipline->id ?>" id="disciplineForm">
<?= csrf_field() ?>
<!-- Basic Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="file-text" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">البيانات الأساسية</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">كود النشاط <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code') ?: $discipline->code) ?>" class="form-input" required style="direction:ltr;text-align:left;text-transform:uppercase;">
</div>
<div class="form-group">
<label class="form-label">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<input type="text" name="name_ar" value="<?= e(old('name_ar') ?: $discipline->name_ar) ?>" class="form-input" required>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="name_en" value="<?= e(old('name_en') ?: ($discipline->name_en ?? '')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">التصنيف</label>
<select name="category" class="form-select">
<option value="">-- اختر التصنيف --</option>
<?php foreach ($categories as $key => $label): ?>
<option value="<?= e($key) ?>" <?= (old('category') ?: ($discipline->category ?? '')) === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">ترتيب العرض</label>
<input type="number" name="sort_order" value="<?= e(old('sort_order') ?: (string)($discipline->sort_order ?? 0)) ?>" class="form-input" min="0" style="direction:ltr;text-align:left;">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">الأيقونة</label>
<select name="icon" class="form-select" id="iconSelect">
<?php
$icons = [
'activity' => 'Activity (نشاط)',
'trophy' => 'Trophy (كأس)',
'target' => 'Target (هدف)',
'zap' => 'Zap (طاقة)',
'flame' => 'Flame (شعلة)',
'heart' => 'Heart (قلب)',
'star' => 'Star (نجمة)',
'award' => 'Award (جائزة)',
'shield' => 'Shield (درع)',
'swords' => 'Swords (سيوف)',
'bike' => 'Bike (دراجة)',
'dumbbell' => 'Dumbbell (دمبل)',
'waves' => 'Waves (أمواج)',
'wind' => 'Wind (رياح)',
'mountain' => 'Mountain (جبل)',
'flag' => 'Flag (علم)',
'timer' => 'Timer (مؤقت)',
'gauge' => 'Gauge (مقياس)',
'volleyball' => 'Volleyball (كرة طائرة)',
'dribbble' => 'Ball (كرة)',
'circle-dot' => 'Circle Dot (دائرة)',
'move' => 'Move (حركة)',
'grab' => 'Grab (قبضة)',
'footprints' => 'Footprints (خطوات)',
];
$selectedIcon = old('icon') ?: ($discipline->icon ?? 'activity');
foreach ($icons as $iconKey => $iconLabel):
?>
<option value="<?= e($iconKey) ?>" <?= $selectedIcon === $iconKey ? 'selected' : '' ?>><?= e($iconLabel) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group" style="display:flex;align-items:end;gap:10px;">
<div id="iconPreview" style="width:48px;height:48px;border-radius:10px;background:#F3F4F6;display:flex;align-items:center;justify-content:center;">
<i data-lucide="<?= e($selectedIcon) ?>" style="width:24px;height:24px;color:#0D7377;" id="iconPreviewIcon"></i>
</div>
<span style="font-size:12px;color:#9CA3AF;">معاينة الأيقونة</span>
</div>
</div>
<div class="form-group" style="margin-top:15px;">
<label class="form-label">وصف النشاط</label>
<textarea name="description_ar" class="form-input" rows="3"><?= e(old('description_ar') ?: ($discipline->description_ar ?? '')) ?></textarea>
</div>
</div>
</div>
<!-- Submit Buttons -->
<div style="display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary" style="padding:12px 30px;font-size:15px;">
<i data-lucide="check" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ التعديلات
</button>
<a href="/sa/disciplines/<?= (int) $discipline->id ?>" class="btn btn-outline" style="padding:12px 30px;font-size:15px;">إلغاء</a>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
// Icon preview
var iconSelect = document.getElementById('iconSelect');
if (iconSelect) {
iconSelect.addEventListener('change', function() {
var preview = document.getElementById('iconPreviewIcon');
if (preview) {
preview.setAttribute('data-lucide', this.value);
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
}
});
}
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>الأنشطة الرياضية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/disciplines/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة نشاط رياضي</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$currentCategory = $filters['category'] ?? '';
$allCategories = $categories ?? [];
?>
<!-- Search & Filter Bar -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<form method="GET" action="/sa/disciplines" style="display:flex;gap:10px;align-items:end;flex-wrap:wrap;">
<div style="flex:1;min-width:200px;">
<label class="form-label" style="font-size:12px;">بحث بالاسم</label>
<input type="text" name="q" value="<?= e($filters['q'] ?? '') ?>" placeholder="ابحث بالاسم..." class="form-input">
</div>
<div style="min-width:160px;">
<label class="form-label" style="font-size:12px;">التصنيف</label>
<select name="category" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($allCategories as $catKey => $catLabel): ?>
<option value="<?= e($catKey) ?>" <?= $currentCategory === $catKey ? 'selected' : '' ?>><?= e($catLabel) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-outline"><i data-lucide="search" style="width:16px;height:16px;vertical-align:middle;"></i> بحث</button>
<a href="/sa/disciplines" class="btn btn-sm btn-outline" style="color:#6B7280;">مسح</a>
</form>
</div>
<!-- Disciplines Table -->
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الكود</th>
<th>الاسم</th>
<th>التصنيف</th>
<th>الحالة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php if (!empty($disciplines)): ?>
<?php foreach ($disciplines as $d): ?>
<tr>
<td><code style="font-size:11px;background:#F3F4F6;padding:2px 6px;border-radius:4px;"><?= e($d['code']) ?></code></td>
<td>
<a href="/sa/disciplines/<?= (int) $d['id'] ?>" style="text-decoration:none;color:#1A1A2E;font-weight:600;">
<?= e($d['name_ar']) ?>
</a>
<?php if (!empty($d['name_en'])): ?>
<div style="font-size:11px;color:#9CA3AF;"><?= e($d['name_en']) ?></div>
<?php endif; ?>
</td>
<td>
<?php if (!empty($d['category']) && isset($allCategories[$d['category']])): ?>
<span style="background:#F3F4F6;padding:2px 8px;border-radius:4px;font-size:12px;"><?= e($allCategories[$d['category']]) ?></span>
<?php else: ?>
<span style="color:#9CA3AF;"></span>
<?php endif; ?>
</td>
<td>
<?php if ((int) ($d['is_active'] ?? 0)): ?>
<span style="background:#ECFDF5;color:#059669;padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">فعّال</span>
<?php else: ?>
<span style="background:#FEE2E2;color:#DC2626;padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">معطّل</span>
<?php endif; ?>
</td>
<td>
<div style="display:flex;gap:6px;">
<a href="/sa/disciplines/<?= (int) $d['id'] ?>" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="eye" style="width:13px;height:13px;vertical-align:middle;"></i> عرض
</a>
<a href="/sa/disciplines/<?= (int) $d['id'] ?>/edit" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="edit-3" style="width:13px;height:13px;vertical-align:middle;"></i> تعديل
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="5" style="text-align:center;padding:40px;color:#6B7280;">
<i data-lucide="activity" style="width:36px;height:36px;color:#D1D5DB;display:block;margin:0 auto 10px;"></i>
<?php if (!empty($filters['q']) || !empty($filters['category'])): ?>
لا توجد نتائج مطابقة لبحثك
<?php else: ?>
لا توجد أنشطة رياضية بعد
<?php endif; ?>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if (isset($pagination) && ($pagination['last_page'] ?? 1) > 1): ?>
<div style="padding:15px;">
<?php $__template->include('Shared.Components.pagination', ['pagination' => $pagination, 'baseUrl' => '/sa/disciplines?' . http_build_query(array_filter($filters))]); ?>
</div>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?><?= e($discipline->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/disciplines/<?= (int) $discipline->id ?>/edit" class="btn btn-outline"><i data-lucide="edit-3" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> تعديل</a>
<a href="/sa/disciplines" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$allCategories = $categories ?? [];
$catLabel = $allCategories[$discipline->category ?? ''] ?? '—';
$isActive = (int) $discipline->is_active;
?>
<!-- Header Card -->
<div class="card" style="margin-bottom:20px;overflow:hidden;">
<div style="padding:25px;display:flex;align-items:start;gap:20px;">
<div style="width:72px;height:72px;border-radius:16px;background:#F3F4F6;display:flex;align-items:center;justify-content:center;flex-shrink:0;">
<i data-lucide="<?= e($discipline->icon ?? 'activity') ?>" style="width:36px;height:36px;color:#0D7377;"></i>
</div>
<div style="flex:1;">
<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px;flex-wrap:wrap;">
<h2 style="margin:0;font-size:22px;font-weight:700;color:#1A1A2E;"><?= e($discipline->name_ar) ?></h2>
<?php if ($isActive): ?>
<span style="background:#ECFDF5;color:#059669;font-size:12px;padding:4px 12px;border-radius:12px;font-weight:600;">فعّال</span>
<?php else: ?>
<span style="background:#FEE2E2;color:#DC2626;font-size:12px;padding:4px 12px;border-radius:12px;font-weight:600;">معطّل</span>
<?php endif; ?>
</div>
<?php if ($discipline->name_en): ?>
<div style="font-size:14px;color:#6B7280;margin-bottom:8px;"><?= e($discipline->name_en) ?></div>
<?php endif; ?>
<div style="display:flex;gap:12px;flex-wrap:wrap;font-size:13px;color:#6B7280;">
<span style="display:inline-flex;align-items:center;gap:4px;">
<i data-lucide="tag" style="width:14px;height:14px;"></i>
<code style="font-size:11px;background:#F3F4F6;padding:1px 6px;border-radius:4px;"><?= e($discipline->code) ?></code>
</span>
<?php if ($discipline->category): ?>
<span style="display:inline-flex;align-items:center;gap:4px;padding:2px 10px;border-radius:10px;font-size:12px;font-weight:600;background:#F3F4F6;color:#4B5563;">
<?= e($catLabel) ?>
</span>
<?php endif; ?>
<span style="display:inline-flex;align-items:center;gap:4px;">
<i data-lucide="arrow-up-down" style="width:14px;height:14px;"></i>
ترتيب: <?= (int) $discipline->sort_order ?>
</span>
</div>
<?php if ($discipline->description_ar): ?>
<p style="margin:12px 0 0;font-size:14px;color:#4B5563;line-height:1.7;"><?= e($discipline->description_ar) ?></p>
<?php endif; ?>
</div>
<div style="flex-shrink:0;">
<form method="POST" action="/sa/disciplines/<?= (int) $discipline->id ?>/toggle" style="display:inline;">
<?= csrf_field() ?>
<?php if ($isActive): ?>
<button type="submit" class="btn btn-outline" style="color:#DC2626;border-color:#DC2626;" onclick="return confirm('هل أنت متأكد من إيقاف هذا النشاط؟')">
<i data-lucide="pause-circle" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> إيقاف
</button>
<?php else: ?>
<button type="submit" class="btn btn-primary" onclick="return confirm('هل أنت متأكد من تفعيل هذا النشاط؟')">
<i data-lucide="play-circle" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> تفعيل
</button>
<?php endif; ?>
</form>
</div>
</div>
</div>
<!-- Detail Fields -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="info" style="width:18px;height:18px;color:#6B7280;"></i>
<h3 style="margin:0;color:#6B7280;font-size:15px;">تفاصيل النشاط</h3>
</div>
<div style="padding:15px 20px;">
<table style="width:100%;font-size:13px;">
<tr>
<td style="padding:8px 0;color:#6B7280;width:30%;">رقم السجل</td>
<td style="padding:8px 0;font-weight:600;">#<?= (int) $discipline->id ?></td>
</tr>
<tr>
<td style="padding:8px 0;color:#6B7280;">الكود</td>
<td style="padding:8px 0;"><code style="font-size:12px;background:#F3F4F6;padding:2px 8px;border-radius:4px;"><?= e($discipline->code) ?></code></td>
</tr>
<tr>
<td style="padding:8px 0;color:#6B7280;">الاسم بالعربي</td>
<td style="padding:8px 0;font-weight:600;"><?= e($discipline->name_ar) ?></td>
</tr>
<tr>
<td style="padding:8px 0;color:#6B7280;">الاسم بالإنجليزي</td>
<td style="padding:8px 0;"><?= e($discipline->name_en ?? '—') ?></td>
</tr>
<tr>
<td style="padding:8px 0;color:#6B7280;">التصنيف</td>
<td style="padding:8px 0;"><?= e($catLabel) ?></td>
</tr>
<tr>
<td style="padding:8px 0;color:#6B7280;">الأيقونة</td>
<td style="padding:8px 0;">
<span style="display:inline-flex;align-items:center;gap:6px;">
<i data-lucide="<?= e($discipline->icon ?? 'activity') ?>" style="width:18px;height:18px;color:#0D7377;"></i>
<?= e($discipline->icon ?? 'activity') ?>
</span>
</td>
</tr>
<tr>
<td style="padding:8px 0;color:#6B7280;">ترتيب العرض</td>
<td style="padding:8px 0;"><?= (int) ($discipline->sort_order ?? 0) ?></td>
</tr>
<tr>
<td style="padding:8px 0;color:#6B7280;">الحالة</td>
<td style="padding:8px 0;">
<?php if ($isActive): ?>
<span style="background:#ECFDF5;color:#059669;padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">فعّال</span>
<?php else: ?>
<span style="background:#FEE2E2;color:#DC2626;padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">معطّل</span>
<?php endif; ?>
</td>
</tr>
<?php if ($discipline->description_ar): ?>
<tr>
<td style="padding:8px 0;color:#6B7280;vertical-align:top;">الوصف</td>
<td style="padding:8px 0;line-height:1.7;"><?= e($discipline->description_ar) ?></td>
</tr>
<?php endif; ?>
<?php if (isset($discipline->created_at) && $discipline->created_at): ?>
<tr>
<td style="padding:8px 0;color:#6B7280;">تاريخ الإنشاء</td>
<td style="padding:8px 0;"><?= e($discipline->created_at) ?></td>
</tr>
<?php endif; ?>
<?php if (isset($discipline->updated_at) && $discipline->updated_at): ?>
<tr>
<td style="padding:8px 0;color:#6B7280;">آخر تحديث</td>
<td style="padding:8px 0;"><?= e($discipline->updated_at) ?></td>
</tr>
<?php endif; ?>
</table>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>إضافة منشأة جديدة<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/facilities" class="btn btn-outline">رجوع للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card" style="max-width:700px;padding:25px;">
<h3 style="margin:0 0 20px;color:#1F2937;">إضافة منشأة رياضية</h3>
<form method="POST" action="/sa/facilities">
<?= csrf_field() ?>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-bottom:15px;">
<div>
<label class="form-label">الكود <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code', '')) ?>" class="form-input" required placeholder="مثال: FAC-001">
</div>
<div>
<label class="form-label">نوع المنشأة <span style="color:#DC2626;">*</span></label>
<select name="facility_type" class="form-select" required>
<option value="">اختر النوع</option>
<?php foreach ($typeOptions as $key => $label): ?>
<option value="<?= e($key) ?>" <?= old('facility_type', '') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-bottom:15px;">
<div>
<label class="form-label">الاسم بالعربية <span style="color:#DC2626;">*</span></label>
<input type="text" name="name_ar" value="<?= e(old('name_ar', '')) ?>" class="form-input" required>
</div>
<div>
<label class="form-label">الاسم بالإنجليزية</label>
<input type="text" name="name_en" value="<?= e(old('name_en', '')) ?>" class="form-input" dir="ltr">
</div>
</div>
<div style="margin-bottom:15px;">
<label class="form-label">اللعبة / النشاط</label>
<select name="discipline_id" class="form-select">
<option value="">بدون تحديد</option>
<?php foreach ($disciplines as $disc): ?>
<option value="<?= (int) $disc->id ?>" <?= old('discipline_id', '') == $disc->id ? 'selected' : '' ?>><?= e($disc->name_ar) ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="margin-bottom:15px;">
<label class="form-label">وصف الموقع</label>
<input type="text" name="location_description" value="<?= e(old('location_description', '')) ?>" class="form-input" placeholder="مثال: الدور الأول - جناح B">
</div>
<div style="background:#F9FAFB;border:1px solid #E5E7EB;border-radius:8px;padding:15px;margin-bottom:20px;">
<h4 style="margin:0 0 12px;color:#374151;font-size:14px;">ساعات التشغيل</h4>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:15px;">
<div>
<label class="form-label">وقت البدء <span style="color:#DC2626;">*</span></label>
<input type="time" name="operating_start" value="<?= e(old('operating_start', '06:00')) ?>" class="form-input" required>
</div>
<div>
<label class="form-label">وقت الانتهاء <span style="color:#DC2626;">*</span></label>
<input type="time" name="operating_end" value="<?= e(old('operating_end', '22:00')) ?>" class="form-input" required>
</div>
<div>
<label class="form-label">مدة الفترة (دقيقة)</label>
<input type="number" name="slot_minutes" value="<?= e(old('slot_minutes', '60')) ?>" class="form-input" min="15" max="240" step="15">
</div>
</div>
</div>
<div style="display:flex;gap:10px;">
<button type="submit" class="btn btn-primary">حفظ المنشأة</button>
<a href="/sa/facilities" class="btn btn-outline">إلغاء</a>
</div>
</form>
</div>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>تعديل المنشأة: <?= e($facility->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/facilities/<?= (int) $facility->id ?>" class="btn btn-outline">رجوع للتفاصيل</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$opHours = $facility->getOperatingHours();
?>
<div class="card" style="max-width:700px;padding:25px;">
<h3 style="margin:0 0 20px;color:#1F2937;">تعديل المنشأة</h3>
<form method="POST" action="/sa/facilities/<?= (int) $facility->id ?>">
<?= csrf_field() ?>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-bottom:15px;">
<div>
<label class="form-label">الكود <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code', $facility->code)) ?>" class="form-input" required placeholder="مثال: FAC-001">
</div>
<div>
<label class="form-label">نوع المنشأة <span style="color:#DC2626;">*</span></label>
<select name="facility_type" class="form-select" required>
<option value="">اختر النوع</option>
<?php foreach ($typeOptions as $key => $label): ?>
<option value="<?= e($key) ?>" <?= old('facility_type', $facility->facility_type) === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-bottom:15px;">
<div>
<label class="form-label">الاسم بالعربية <span style="color:#DC2626;">*</span></label>
<input type="text" name="name_ar" value="<?= e(old('name_ar', $facility->name_ar)) ?>" class="form-input" required>
</div>
<div>
<label class="form-label">الاسم بالإنجليزية</label>
<input type="text" name="name_en" value="<?= e(old('name_en', $facility->name_en ?? '')) ?>" class="form-input" dir="ltr">
</div>
</div>
<div style="margin-bottom:15px;">
<label class="form-label">اللعبة / النشاط</label>
<select name="discipline_id" class="form-select">
<option value="">بدون تحديد</option>
<?php foreach ($disciplines as $disc): ?>
<option value="<?= (int) $disc->id ?>" <?= old('discipline_id', $facility->discipline_id ?? '') == $disc->id ? 'selected' : '' ?>><?= e($disc->name_ar) ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="margin-bottom:15px;">
<label class="form-label">وصف الموقع</label>
<input type="text" name="location_description" value="<?= e(old('location_description', $facility->location_description ?? '')) ?>" class="form-input" placeholder="مثال: الدور الأول - جناح B">
</div>
<div style="background:#F9FAFB;border:1px solid #E5E7EB;border-radius:8px;padding:15px;margin-bottom:20px;">
<h4 style="margin:0 0 12px;color:#374151;font-size:14px;">ساعات التشغيل</h4>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:15px;">
<div>
<label class="form-label">وقت البدء <span style="color:#DC2626;">*</span></label>
<input type="time" name="operating_start" value="<?= e(old('operating_start', $opHours['start'] ?? '06:00')) ?>" class="form-input" required>
</div>
<div>
<label class="form-label">وقت الانتهاء <span style="color:#DC2626;">*</span></label>
<input type="time" name="operating_end" value="<?= e(old('operating_end', $opHours['end'] ?? '22:00')) ?>" class="form-input" required>
</div>
<div>
<label class="form-label">مدة الفترة (دقيقة)</label>
<input type="number" name="slot_minutes" value="<?= e(old('slot_minutes', (string) ($opHours['slot_minutes'] ?? 60))) ?>" class="form-input" min="15" max="240" step="15">
</div>
</div>
</div>
<div style="display:flex;gap:10px;">
<button type="submit" class="btn btn-primary">حفظ التعديلات</button>
<a href="/sa/facilities/<?= (int) $facility->id ?>" class="btn btn-outline">إلغاء</a>
</div>
</form>
</div>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>المنشآت الرياضية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/facilities/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة منشأة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card" style="margin-bottom:20px;padding:15px;">
<form method="GET" action="/sa/facilities" style="display:flex;gap:10px;flex-wrap:wrap;align-items:end;">
<div>
<label class="form-label" style="font-size:12px;">بحث</label>
<input type="text" name="q" value="<?= e($filters['q'] ?? '') ?>" placeholder="الكود أو الاسم..." class="form-input" style="min-width:200px;">
</div>
<div>
<label class="form-label" style="font-size:12px;">النوع</label>
<select name="type" class="form-select">
<option value="">الكل</option>
<?php foreach ($typeOptions as $key => $label): ?>
<option value="<?= e($key) ?>" <?= ($filters['type'] ?? '') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-outline">بحث</button>
<a href="/sa/facilities" class="btn btn-sm btn-outline" style="color:#6B7280;">مسح</a>
</form>
</div>
<?php if (!empty($rows)): ?>
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الكود</th>
<th>الاسم</th>
<th>النوع</th>
<th>اللعبة</th>
<th>الوحدات</th>
<th>الحالة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php foreach ($rows as $row): ?>
<tr>
<td style="font-family:monospace;font-size:13px;"><?= e($row['code']) ?></td>
<td>
<a href="/sa/facilities/<?= (int) $row['id'] ?>" style="color:#0D7377;font-weight:600;"><?= e($row['name_ar']) ?></a>
<?php if (!empty($row['name_en'])): ?>
<br><small style="color:#9CA3AF;"><?= e($row['name_en']) ?></small>
<?php endif; ?>
</td>
<td>
<?= e($typeOptions[$row['facility_type']] ?? $row['facility_type']) ?>
</td>
<td style="font-size:13px;"><?= e($row['discipline_name'] ?? '—') ?></td>
<td style="text-align:center;">
<a href="/sa/facilities/<?= (int) $row['id'] ?>/units" style="color:#0D7377;"><?= (int) $row['unit_count'] ?></a>
</td>
<td>
<?php if ($row['is_active']): ?>
<span style="color:#059669;font-weight:600;">مفعّلة</span>
<?php else: ?>
<span style="color:#DC2626;font-weight:600;">معطّلة</span>
<?php endif; ?>
</td>
<td>
<div style="display:flex;gap:5px;flex-wrap:wrap;">
<a href="/sa/facilities/<?= (int) $row['id'] ?>" class="btn btn-sm btn-outline">عرض</a>
<a href="/sa/facilities/<?= (int) $row['id'] ?>/edit" class="btn btn-sm btn-outline">تعديل</a>
<form method="POST" action="/sa/facilities/<?= (int) $row['id'] ?>/toggle" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-sm <?= $row['is_active'] ? 'btn-outline' : 'btn-primary' ?>" style="font-size:11px;">
<?= $row['is_active'] ? 'تعطيل' : 'تفعيل' ?>
</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php if (isset($pagination) && ($pagination['last_page'] ?? 1) > 1): ?>
<div style="padding:15px;">
<?php $__template->include('Shared.Components.pagination', ['pagination' => $pagination]); ?>
</div>
<?php endif; ?>
</div>
<?php else: ?>
<div class="card" style="padding:60px 20px;text-align:center;">
<h3 style="color:#6B7280;margin:0 0 8px;">لا توجد منشآت</h3>
<p style="color:#9CA3AF;font-size:14px;margin:0 0 20px;">
<?php if (!empty($filters['q']) || !empty($filters['type'])): ?>
لا توجد نتائج مطابقة لبحثك. جرب تغيير معايير البحث.
<?php else: ?>
ابدأ بإضافة منشأة رياضية جديدة.
<?php endif; ?>
</p>
<a href="/sa/facilities/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة منشأة</a>
</div>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>المنشأة: <?= e($facility->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<div style="display:flex;gap:8px;">
<a href="/sa/facilities/<?= (int) $facility->id ?>/edit" class="btn btn-outline">تعديل</a>
<a href="/sa/facilities/<?= (int) $facility->id ?>/units" class="btn btn-outline">الوحدات</a>
<a href="/sa/facilities/<?= (int) $facility->id ?>/brackets" class="btn btn-outline">الفترات الزمنية</a>
<a href="/sa/facilities" class="btn btn-outline">رجوع للقائمة</a>
</div>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php $opHours = $facility->getOperatingHours(); ?>
<!-- Facility Details -->
<div class="card" style="padding:20px;margin-bottom:20px;">
<div style="display:flex;justify-content:space-between;align-items:start;margin-bottom:15px;">
<h3 style="margin:0;color:#1F2937;"><?= e($facility->name_ar) ?></h3>
<span style="padding:4px 12px;border-radius:20px;font-size:12px;font-weight:600;<?= $facility->is_active ? 'background:#D1FAE5;color:#059669;' : 'background:#FEE2E2;color:#DC2626;' ?>">
<?= $facility->is_active ? 'مفعّلة' : 'معطّلة' ?>
</span>
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:15px;">
<div>
<span style="font-size:12px;color:#6B7280;">الكود</span>
<div style="font-weight:600;font-family:monospace;"><?= e($facility->code) ?></div>
</div>
<div>
<span style="font-size:12px;color:#6B7280;">النوع</span>
<div style="font-weight:600;"><?= e($typeOptions[$facility->facility_type] ?? $facility->facility_type) ?></div>
</div>
<div>
<span style="font-size:12px;color:#6B7280;">اللعبة</span>
<div style="font-weight:600;"><?= e($discipline['name_ar'] ?? '—') ?></div>
</div>
<div>
<span style="font-size:12px;color:#6B7280;">الموقع</span>
<div style="font-weight:600;"><?= e($facility->location_description ?? '—') ?></div>
</div>
<div>
<span style="font-size:12px;color:#6B7280;">ساعات التشغيل</span>
<div style="font-weight:600;"><?= e($opHours['start'] ?? '—') ?> - <?= e($opHours['end'] ?? '—') ?></div>
</div>
<div>
<span style="font-size:12px;color:#6B7280;">مدة الفترة</span>
<div style="font-weight:600;"><?= (int) ($opHours['slot_minutes'] ?? 60) ?> دقيقة</div>
</div>
<?php if (!empty($facility->name_en)): ?>
<div>
<span style="font-size:12px;color:#6B7280;">الاسم بالإنجليزية</span>
<div style="font-weight:600;" dir="ltr"><?= e($facility->name_en) ?></div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Units Section -->
<div class="card" style="padding:20px;margin-bottom:20px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:15px;">
<h4 style="margin:0;color:#1F2937;">الوحدات (<?= count($units) ?>)</h4>
<a href="/sa/facilities/<?= (int) $facility->id ?>/units/create" class="btn btn-sm btn-primary">إضافة وحدة</a>
</div>
<?php if (!empty($units)): ?>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الكود</th>
<th>الاسم</th>
<th>النوع</th>
<th>نمط الحجز</th>
<th>السعة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php foreach ($units as $unit): ?>
<tr>
<td style="font-family:monospace;font-size:13px;"><?= e(is_object($unit) ? $unit->code : $unit['code']) ?></td>
<td style="font-weight:600;"><?= e(is_object($unit) ? $unit->name_ar : $unit['name_ar']) ?></td>
<td><?= e($unitTypeOptions[is_object($unit) ? $unit->unit_type : $unit['unit_type']] ?? '') ?></td>
<td><?= e($bookingModes[is_object($unit) ? $unit->booking_mode : $unit['booking_mode']] ?? '') ?></td>
<td style="text-align:center;"><?= (int) (is_object($unit) ? $unit->max_capacity : $unit['max_capacity']) ?></td>
<td>
<a href="/sa/facilities/<?= (int) $facility->id ?>/units/<?= (int) (is_object($unit) ? $unit->id : $unit['id']) ?>/edit" class="btn btn-sm btn-outline">تعديل</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<p style="color:#6B7280;text-align:center;padding:20px 0;margin:0;">لا توجد وحدات مسجلة</p>
<?php endif; ?>
</div>
<!-- Time Brackets Section -->
<div class="card" style="padding:20px;margin-bottom:20px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:15px;">
<h4 style="margin:0;color:#1F2937;">الفترات الزمنية (<?= count($brackets) ?>)</h4>
<a href="/sa/facilities/<?= (int) $facility->id ?>/brackets" class="btn btn-sm btn-outline">إدارة الفترات</a>
</div>
<?php if (!empty($brackets)): ?>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الاسم</th>
<th>النوع</th>
<th>من</th>
<th>إلى</th>
<th>الأيام</th>
</tr>
</thead>
<tbody>
<?php
$dayNames = ['الأحد', 'الإثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'];
?>
<?php foreach ($brackets as $bracket): ?>
<?php
$bObj = is_object($bracket) ? $bracket : (object) $bracket;
$days = is_string($bObj->days_of_week_json) ? json_decode($bObj->days_of_week_json, true) : ($bObj->days_of_week_json ?: []);
$dayLabels = array_map(fn($d) => $dayNames[(int) $d] ?? '', $days ?: []);
?>
<tr>
<td style="font-weight:600;"><?= e($bObj->name_ar) ?></td>
<td><?= e($bracketTypes[$bObj->bracket_type] ?? $bObj->bracket_type) ?></td>
<td style="font-family:monospace;"><?= e($bObj->start_time) ?></td>
<td style="font-family:monospace;"><?= e($bObj->end_time) ?></td>
<td style="font-size:12px;"><?= e(implode('، ', $dayLabels)) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<p style="color:#6B7280;text-align:center;padding:20px 0;margin:0;">لا توجد فترات زمنية</p>
<?php endif; ?>
</div>
<!-- Blackout Date Form -->
<div class="card" style="padding:20px;">
<h4 style="margin:0 0 15px;color:#1F2937;">إضافة تاريخ إيقاف</h4>
<form method="POST" action="/sa/facilities/<?= (int) $facility->id ?>/blackout" style="display:flex;gap:10px;flex-wrap:wrap;align-items:end;">
<?= csrf_field() ?>
<div>
<label class="form-label" style="font-size:12px;">التاريخ</label>
<input type="date" name="blackout_date" class="form-input" required min="<?= e(date('Y-m-d')) ?>">
</div>
<div style="flex:1;">
<label class="form-label" style="font-size:12px;">السبب</label>
<input type="text" name="reason" class="form-input" placeholder="سبب الإيقاف (اختياري)">
</div>
<button type="submit" class="btn btn-outline">إضافة إيقاف</button>
</form>
</div>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>الفترات الزمنية: <?= e($facility->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/facilities/<?= (int) $facility->id ?>" class="btn btn-outline">رجوع للمنشأة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$dayNames = [
0 => 'الأحد',
1 => 'الإثنين',
2 => 'الثلاثاء',
3 => 'الأربعاء',
4 => 'الخميس',
5 => 'الجمعة',
6 => 'السبت',
];
?>
<div class="card" style="padding:10px 15px;margin-bottom:15px;background:#F9FAFB;">
<span style="font-size:13px;color:#6B7280;">المنشأة:</span>
<a href="/sa/facilities/<?= (int) $facility->id ?>" style="color:#0D7377;font-weight:600;margin-right:5px;"><?= e($facility->name_ar) ?></a>
<span style="font-size:12px;color:#9CA3AF;">(<?= e($facility->code) ?>)</span>
</div>
<!-- Add Bracket Form -->
<div class="card" style="padding:20px;margin-bottom:20px;">
<h4 style="margin:0 0 15px;color:#1F2937;">إضافة فترة زمنية جديدة</h4>
<form method="POST" action="/sa/facilities/<?= (int) $facility->id ?>/brackets">
<?= csrf_field() ?>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-bottom:15px;">
<div>
<label class="form-label">اسم الفترة <span style="color:#DC2626;">*</span></label>
<input type="text" name="name_ar" value="<?= e(old('name_ar', '')) ?>" class="form-input" required placeholder="مثال: فترة الذروة الصباحية">
</div>
<div>
<label class="form-label">نوع الفترة <span style="color:#DC2626;">*</span></label>
<select name="bracket_type" class="form-select" required>
<option value="">اختر النوع</option>
<?php foreach ($bracketTypes as $key => $label): ?>
<option value="<?= e($key) ?>" <?= old('bracket_type', '') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-bottom:15px;">
<div>
<label class="form-label">وقت البدء <span style="color:#DC2626;">*</span></label>
<input type="time" name="start_time" value="<?= e(old('start_time', '')) ?>" class="form-input" required>
</div>
<div>
<label class="form-label">وقت الانتهاء <span style="color:#DC2626;">*</span></label>
<input type="time" name="end_time" value="<?= e(old('end_time', '')) ?>" class="form-input" required>
</div>
</div>
<div style="margin-bottom:15px;">
<label class="form-label">أيام الأسبوع <span style="color:#DC2626;">*</span></label>
<div style="display:flex;gap:12px;flex-wrap:wrap;padding:10px;background:#F9FAFB;border:1px solid #E5E7EB;border-radius:6px;">
<?php foreach ($dayNames as $dayNum => $dayLabel): ?>
<label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;">
<input type="checkbox" name="days_of_week[]" value="<?= $dayNum ?>" style="width:16px;height:16px;">
<?= e($dayLabel) ?>
</label>
<?php endforeach; ?>
</div>
</div>
<button type="submit" class="btn btn-primary">إضافة الفترة</button>
</form>
</div>
<!-- Existing Brackets -->
<div class="card" style="padding:20px;">
<h4 style="margin:0 0 15px;color:#1F2937;">الفترات الزمنية الحالية (<?= count($brackets) ?>)</h4>
<?php if (!empty($brackets)): ?>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الاسم</th>
<th>النوع</th>
<th>من</th>
<th>إلى</th>
<th>الأيام</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php foreach ($brackets as $bracket): ?>
<?php
$b = is_object($bracket) ? $bracket : (object) $bracket;
$days = is_string($b->days_of_week_json) ? json_decode($b->days_of_week_json, true) : ($b->days_of_week_json ?: []);
$dayLabels = array_map(fn($d) => $dayNames[(int) $d] ?? '', $days ?: []);
$typeColor = match($b->bracket_type) {
'peak' => '#DC2626',
'off_peak' => '#059669',
'weekend' => '#7C3AED',
'holiday' => '#D97706',
default => '#6B7280',
};
?>
<tr>
<td style="font-weight:600;"><?= e($b->name_ar) ?></td>
<td>
<span style="color:<?= $typeColor ?>;font-weight:600;"><?= e($bracketTypes[$b->bracket_type] ?? $b->bracket_type) ?></span>
</td>
<td style="font-family:monospace;font-size:13px;"><?= e($b->start_time) ?></td>
<td style="font-family:monospace;font-size:13px;"><?= e($b->end_time) ?></td>
<td style="font-size:12px;"><?= e(implode('، ', $dayLabels)) ?></td>
<td>
<form method="POST" action="/sa/facilities/<?= (int) $facility->id ?>/brackets/<?= (int) $b->id ?>/delete" style="display:inline;" onsubmit="return confirm('هل أنت متأكد من حذف هذه الفترة؟');">
<?= csrf_field() ?>
<button type="submit" class="btn btn-sm btn-outline" style="color:#DC2626;border-color:#DC2626;">حذف</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<p style="color:#6B7280;text-align:center;padding:20px 0;margin:0;">لا توجد فترات زمنية مسجلة. أضف فترة جديدة من النموذج أعلاه.</p>
<?php endif; ?>
</div>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?><?= $unit ? 'تعديل الوحدة: ' . e($unit->name_ar) : 'إضافة وحدة جديدة' ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/facilities/<?= (int) $facility->id ?>/units" class="btn btn-outline">رجوع للوحدات</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card" style="padding:10px 15px;margin-bottom:15px;background:#F9FAFB;">
<span style="font-size:13px;color:#6B7280;">المنشأة:</span>
<a href="/sa/facilities/<?= (int) $facility->id ?>" style="color:#0D7377;font-weight:600;margin-right:5px;"><?= e($facility->name_ar) ?></a>
<span style="font-size:12px;color:#9CA3AF;">(<?= e($facility->code) ?>)</span>
</div>
<div class="card" style="max-width:600px;padding:25px;">
<h3 style="margin:0 0 20px;color:#1F2937;"><?= $unit ? 'تعديل الوحدة' : 'إضافة وحدة جديدة' ?></h3>
<form method="POST" action="<?= $unit ? "/sa/facilities/{$facility->id}/units/{$unit->id}" : "/sa/facilities/{$facility->id}/units" ?>">
<?= csrf_field() ?>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-bottom:15px;">
<div>
<label class="form-label">الكود <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code', $unit ? $unit->code : '')) ?>" class="form-input" required placeholder="مثال: LANE-01">
</div>
<div>
<label class="form-label">نوع الوحدة <span style="color:#DC2626;">*</span></label>
<select name="unit_type" class="form-select" required>
<option value="">اختر النوع</option>
<?php foreach ($unitTypeOptions as $key => $label): ?>
<option value="<?= e($key) ?>" <?= old('unit_type', $unit ? $unit->unit_type : '') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-bottom:15px;">
<div>
<label class="form-label">الاسم بالعربية <span style="color:#DC2626;">*</span></label>
<input type="text" name="name_ar" value="<?= e(old('name_ar', $unit ? $unit->name_ar : '')) ?>" class="form-input" required>
</div>
<div>
<label class="form-label">الاسم بالإنجليزية</label>
<input type="text" name="name_en" value="<?= e(old('name_en', $unit ? ($unit->name_en ?? '') : '')) ?>" class="form-input" dir="ltr">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-bottom:15px;">
<div>
<label class="form-label">نمط الحجز <span style="color:#DC2626;">*</span></label>
<select name="booking_mode" class="form-select" required>
<option value="">اختر النمط</option>
<?php foreach ($bookingModes as $key => $label): ?>
<option value="<?= e($key) ?>" <?= old('booking_mode', $unit ? $unit->booking_mode : '') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div>
<label class="form-label">السعة القصوى <span style="color:#DC2626;">*</span></label>
<input type="number" name="max_capacity" value="<?= e(old('max_capacity', $unit ? (string) $unit->max_capacity : '1')) ?>" class="form-input" required min="1">
</div>
</div>
<div style="margin-bottom:20px;">
<label class="form-label">ترتيب العرض</label>
<input type="number" name="sort_order" value="<?= e(old('sort_order', $unit ? (string) $unit->sort_order : '0')) ?>" class="form-input" style="max-width:150px;" min="0">
<small style="color:#6B7280;display:block;margin-top:4px;">رقم أصغر = يظهر أولاً</small>
</div>
<div style="display:flex;gap:10px;">
<button type="submit" class="btn btn-primary"><?= $unit ? 'حفظ التعديلات' : 'إضافة الوحدة' ?></button>
<a href="/sa/facilities/<?= (int) $facility->id ?>/units" class="btn btn-outline">إلغاء</a>
</div>
</form>
</div>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>وحدات المنشأة: <?= e($facility->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<div style="display:flex;gap:8px;">
<a href="/sa/facilities/<?= (int) $facility->id ?>/units/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة وحدة</a>
<a href="/sa/facilities/<?= (int) $facility->id ?>" class="btn btn-outline">رجوع للمنشأة</a>
</div>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card" style="padding:10px 15px;margin-bottom:15px;background:#F9FAFB;">
<span style="font-size:13px;color:#6B7280;">المنشأة:</span>
<a href="/sa/facilities/<?= (int) $facility->id ?>" style="color:#0D7377;font-weight:600;margin-right:5px;"><?= e($facility->name_ar) ?></a>
<span style="font-size:12px;color:#9CA3AF;">(<?= e($facility->code) ?>)</span>
</div>
<?php if (!empty($units)): ?>
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الترتيب</th>
<th>الكود</th>
<th>الاسم</th>
<th>النوع</th>
<th>نمط الحجز</th>
<th>السعة القصوى</th>
<th>الحالة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php foreach ($units as $unit): ?>
<?php $u = is_object($unit) ? $unit : (object) $unit; ?>
<tr>
<td style="text-align:center;color:#6B7280;"><?= (int) $u->sort_order ?></td>
<td style="font-family:monospace;font-size:13px;"><?= e($u->code) ?></td>
<td>
<span style="font-weight:600;"><?= e($u->name_ar) ?></span>
<?php if (!empty($u->name_en)): ?>
<br><small style="color:#9CA3AF;" dir="ltr"><?= e($u->name_en) ?></small>
<?php endif; ?>
</td>
<td><?= e($unitTypeOptions[$u->unit_type] ?? $u->unit_type) ?></td>
<td>
<?php
$modeColor = $u->booking_mode === 'exclusive' ? '#7C3AED' : '#0284C7';
?>
<span style="color:<?= $modeColor ?>;font-weight:600;"><?= e($bookingModes[$u->booking_mode] ?? $u->booking_mode) ?></span>
</td>
<td style="text-align:center;font-weight:600;"><?= (int) $u->max_capacity ?></td>
<td>
<?php if ($u->is_active): ?>
<span style="color:#059669;font-weight:600;">مفعّلة</span>
<?php else: ?>
<span style="color:#DC2626;font-weight:600;">معطّلة</span>
<?php endif; ?>
</td>
<td>
<a href="/sa/facilities/<?= (int) $facility->id ?>/units/<?= (int) $u->id ?>/edit" class="btn btn-sm btn-outline">تعديل</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php else: ?>
<div class="card" style="padding:60px 20px;text-align:center;">
<h3 style="color:#6B7280;margin:0 0 8px;">لا توجد وحدات</h3>
<p style="color:#9CA3AF;font-size:14px;margin:0 0 20px;">لم يتم إضافة وحدات لهذه المنشأة بعد.</p>
<a href="/sa/facilities/<?= (int) $facility->id ?>/units/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة وحدة</a>
</div>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>إضافة مجموعة تدريبية جديدة<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/groups" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/sa/groups">
<?= csrf_field() ?>
<!-- Basic Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="users" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">البيانات الأساسية</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">كود المجموعة <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code')) ?>" class="form-input" required placeholder="مثال: GRP-SWIM-01" style="direction:ltr;text-align:left;text-transform:uppercase;">
</div>
<div class="form-group">
<label class="form-label">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<input type="text" name="name_ar" value="<?= e(old('name_ar')) ?>" class="form-input" required placeholder="مثال: مجموعة السباحة أ">
</div>
<div class="form-group">
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="name_en" value="<?= e(old('name_en')) ?>" class="form-input" placeholder="e.g. Swimming Group A" style="direction:ltr;text-align:left;">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">البرنامج <span style="color:#DC2626;">*</span></label>
<select name="program_id" class="form-select" required>
<option value="">-- اختر البرنامج --</option>
<?php foreach ($programs as $p): ?>
<option value="<?= (int) $p['id'] ?>" <?= old('program_id', $_GET['program_id'] ?? '') == $p['id'] ? 'selected' : '' ?>><?= e($p['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">المدرب <span style="color:#DC2626;">*</span></label>
<select name="coach_id" class="form-select" required>
<option value="">-- اختر المدرب --</option>
<?php foreach ($coaches as $c): ?>
<option value="<?= (int) $c['id'] ?>" <?= old('coach_id') == $c['id'] ? 'selected' : '' ?>><?= e($c['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
</div>
<!-- Capacity & Fees -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="settings" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">السعة والرسوم</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">الحد الأدنى</label>
<input type="number" name="min_capacity" value="<?= e(old('min_capacity', '5')) ?>" class="form-input" min="1" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">الحد الأقصى</label>
<input type="number" name="max_capacity" value="<?= e(old('max_capacity', '20')) ?>" class="form-input" min="1" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">رسوم شهرية (أعضاء)</label>
<input type="number" name="monthly_fee_member" value="<?= e(old('monthly_fee_member', '0')) ?>" class="form-input" min="0" step="0.01" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">رسوم شهرية (غير أعضاء)</label>
<input type="number" name="monthly_fee_nonmember" value="<?= e(old('monthly_fee_nonmember', '0')) ?>" class="form-input" min="0" step="0.01" style="direction:ltr;text-align:left;">
</div>
</div>
</div>
</div>
<!-- Season -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="calendar" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">الموسم</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">بداية الموسم</label>
<input type="date" name="season_start" value="<?= e(old('season_start')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">نهاية الموسم</label>
<input type="date" name="season_end" value="<?= e(old('season_end')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
</div>
</div>
</div>
<!-- Submit -->
<div style="display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary" style="padding:12px 30px;font-size:15px;">
<i data-lucide="check" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ المجموعة
</button>
<a href="/sa/groups" class="btn btn-outline" style="padding:12px 30px;font-size:15px;">إلغاء</a>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>تعديل المجموعة: <?= e($group->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/groups/<?= (int) $group->id ?>" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للتفاصيل</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/sa/groups/<?= (int) $group->id ?>/update">
<?= csrf_field() ?>
<!-- Basic Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="users" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">البيانات الأساسية</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">كود المجموعة <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code', $group->code)) ?>" class="form-input" required style="direction:ltr;text-align:left;text-transform:uppercase;">
</div>
<div class="form-group">
<label class="form-label">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<input type="text" name="name_ar" value="<?= e(old('name_ar', $group->name_ar)) ?>" class="form-input" required>
</div>
<div class="form-group">
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="name_en" value="<?= e(old('name_en', $group->name_en ?? '')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">البرنامج <span style="color:#DC2626;">*</span></label>
<select name="program_id" class="form-select" required>
<option value="">-- اختر البرنامج --</option>
<?php foreach ($programs as $p): ?>
<option value="<?= (int) $p['id'] ?>" <?= (old('program_id', $group->program_id)) == $p['id'] ? 'selected' : '' ?>><?= e($p['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">المدرب <span style="color:#DC2626;">*</span></label>
<select name="coach_id" class="form-select" required>
<option value="">-- اختر المدرب --</option>
<?php foreach ($coaches as $c): ?>
<option value="<?= (int) $c['id'] ?>" <?= (old('coach_id', $group->coach_id)) == $c['id'] ? 'selected' : '' ?>><?= e($c['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
</div>
<!-- Capacity & Fees -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="settings" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">السعة والرسوم</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">الحد الأدنى</label>
<input type="number" name="min_capacity" value="<?= e(old('min_capacity', $group->min_capacity ?? '5')) ?>" class="form-input" min="1" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">الحد الأقصى</label>
<input type="number" name="max_capacity" value="<?= e(old('max_capacity', $group->max_capacity ?? '20')) ?>" class="form-input" min="1" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">رسوم شهرية (أعضاء)</label>
<input type="number" name="monthly_fee_member" value="<?= e(old('monthly_fee_member', $group->monthly_fee_member ?? '0')) ?>" class="form-input" min="0" step="0.01" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">رسوم شهرية (غير أعضاء)</label>
<input type="number" name="monthly_fee_nonmember" value="<?= e(old('monthly_fee_nonmember', $group->monthly_fee_nonmember ?? '0')) ?>" class="form-input" min="0" step="0.01" style="direction:ltr;text-align:left;">
</div>
</div>
</div>
</div>
<!-- Season -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="calendar" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">الموسم</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">بداية الموسم</label>
<input type="date" name="season_start" value="<?= e(old('season_start', $group->season_start ?? '')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">نهاية الموسم</label>
<input type="date" name="season_end" value="<?= e(old('season_end', $group->season_end ?? '')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
</div>
</div>
</div>
<!-- Submit -->
<div style="display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary" style="padding:12px 30px;font-size:15px;">
<i data-lucide="check" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ التعديلات
</button>
<a href="/sa/groups/<?= (int) $group->id ?>" class="btn btn-outline" style="padding:12px 30px;font-size:15px;">إلغاء</a>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>المجموعات التدريبية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/groups/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة مجموعة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Search & Filter Bar -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<form method="GET" action="/sa/groups" style="display:flex;gap:10px;align-items:end;flex-wrap:wrap;">
<div style="flex:1;min-width:200px;">
<label class="form-label" style="font-size:12px;">بحث</label>
<input type="text" name="q" value="<?= e($filters['q'] ?? '') ?>" placeholder="ابحث بالاسم أو الكود..." class="form-input">
</div>
<div style="min-width:180px;">
<label class="form-label" style="font-size:12px;">البرنامج</label>
<select name="program_id" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($programs as $p): ?>
<option value="<?= (int) $p['id'] ?>" <?= ($filters['program_id'] ?? '') == $p['id'] ? 'selected' : '' ?>><?= e($p['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="min-width:160px;">
<label class="form-label" style="font-size:12px;">المدرب</label>
<select name="coach_id" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($coaches as $c): ?>
<option value="<?= (int) $c['id'] ?>" <?= ($filters['coach_id'] ?? '') == $c['id'] ? 'selected' : '' ?>><?= e($c['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-outline"><i data-lucide="search" style="width:16px;height:16px;vertical-align:middle;"></i> بحث</button>
<a href="/sa/groups" class="btn btn-sm btn-outline" style="color:#6B7280;">مسح</a>
</form>
</div>
<!-- Groups Table -->
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الكود</th>
<th>الاسم</th>
<th>البرنامج</th>
<th>المدرب</th>
<th>السعة</th>
<th>الرسوم</th>
<th>الحالة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php if (!empty($groups)): ?>
<?php
$statusOptions = \App\Modules\SportsActivity\Models\Group::getStatusOptions();
$statusColors = ['active' => '#059669', 'paused' => '#D97706', 'completed' => '#6B7280', 'cancelled' => '#DC2626'];
$statusBgs = ['active' => '#ECFDF5', 'paused' => '#FEF3C7', 'completed' => '#F3F4F6', 'cancelled' => '#FEE2E2'];
?>
<?php foreach ($groups as $g): ?>
<tr>
<td><code style="font-size:11px;background:#F3F4F6;padding:2px 6px;border-radius:4px;"><?= e($g['code']) ?></code></td>
<td>
<a href="/sa/groups/<?= (int) $g['id'] ?>" style="text-decoration:none;color:#1A1A2E;font-weight:600;">
<?= e($g['name_ar']) ?>
</a>
<?php if (!empty($g['name_en'])): ?>
<div style="font-size:11px;color:#9CA3AF;"><?= e($g['name_en']) ?></div>
<?php endif; ?>
</td>
<td><?= e($g['program_name'] ?? '—') ?></td>
<td><?= e($g['coach_name'] ?? '—') ?></td>
<td>
<span style="font-weight:600;color:<?= (int)$g['current_count'] >= (int)$g['max_capacity'] ? '#DC2626' : '#059669' ?>;">
<?= (int) $g['current_count'] ?>
</span>
/ <?= (int) $g['max_capacity'] ?>
</td>
<td style="direction:ltr;text-align:right;"><?= money((float) ($g['monthly_fee_member'] ?? 0)) ?></td>
<td>
<?php $st = $g['status'] ?? 'active'; ?>
<span style="background:<?= $statusBgs[$st] ?? '#F3F4F6' ?>;color:<?= $statusColors[$st] ?? '#6B7280' ?>;padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">
<?= e($statusOptions[$st] ?? $st) ?>
</span>
</td>
<td>
<div style="display:flex;gap:6px;">
<a href="/sa/groups/<?= (int) $g['id'] ?>" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="eye" style="width:13px;height:13px;vertical-align:middle;"></i> عرض
</a>
<a href="/sa/groups/<?= (int) $g['id'] ?>/edit" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="edit-3" style="width:13px;height:13px;vertical-align:middle;"></i> تعديل
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="8" style="text-align:center;padding:40px;color:#6B7280;">
<i data-lucide="users" style="width:36px;height:36px;color:#D1D5DB;display:block;margin:0 auto 10px;"></i>
<?php if (!empty($filters['q']) || !empty($filters['program_id']) || !empty($filters['coach_id'])): ?>
لا توجد نتائج مطابقة لبحثك
<?php else: ?>
لا توجد مجموعات تدريبية بعد
<?php endif; ?>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if (isset($pagination) && ($pagination['last_page'] ?? 1) > 1): ?>
<div style="padding:15px;">
<?php $__template->include('Shared.Components.pagination', ['pagination' => $pagination, 'baseUrl' => '/sa/groups?' . http_build_query(array_filter($filters))]); ?>
</div>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>المجموعة: <?= e($group['name_ar']) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/groups/<?= (int) $group['id'] ?>/edit" class="btn btn-primary" style="margin-left:8px;"><i data-lucide="edit-3" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> تعديل</a>
<a href="/sa/groups" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$dayNames = ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'];
$statusColors = ['active' => '#059669', 'paused' => '#D97706', 'completed' => '#6B7280', 'cancelled' => '#DC2626'];
$statusBgs = ['active' => '#ECFDF5', 'paused' => '#FEF3C7', 'completed' => '#F3F4F6', 'cancelled' => '#FEE2E2'];
$st = $group['status'] ?? 'active';
?>
<!-- Group Details -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">بيانات المجموعة</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:20px;">
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الكود</div>
<div style="font-weight:600;"><code style="background:#F3F4F6;padding:2px 8px;border-radius:4px;"><?= e($group['code']) ?></code></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">البرنامج</div>
<div style="font-weight:600;"><?= e($group['program_name'] ?? '—') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">المدرب</div>
<div style="font-weight:600;"><?= e($group['coach_name'] ?? '—') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الحالة</div>
<div>
<span style="background:<?= $statusBgs[$st] ?? '#F3F4F6' ?>;color:<?= $statusColors[$st] ?? '#6B7280' ?>;padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">
<?= e($statuses[$st] ?? $st) ?>
</span>
</div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">السعة</div>
<div style="font-weight:600;">
<span style="color:<?= (int)$group['current_count'] >= (int)$group['max_capacity'] ? '#DC2626' : '#059669' ?>;"><?= (int) $group['current_count'] ?></span>
/ <?= (int) $group['max_capacity'] ?>
(أدنى: <?= (int) ($group['min_capacity'] ?? 0) ?>)
</div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">رسوم الأعضاء</div>
<div style="font-weight:600;"><?= money((float) ($group['monthly_fee_member'] ?? 0)) ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">رسوم غير الأعضاء</div>
<div style="font-weight:600;"><?= money((float) ($group['monthly_fee_nonmember'] ?? 0)) ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الموسم</div>
<div style="font-weight:600;">
<?php if (!empty($group['season_start'])): ?>
<?= e($group['season_start']) ?><?= e($group['season_end'] ?? '?') ?>
<?php else: ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<!-- Enrolled Players -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;justify-content:space-between;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">اللاعبون المسجلون (<?= count($players) ?>)</h3>
</div>
<!-- Enroll Form -->
<?php if ((int) $group['current_count'] < (int) $group['max_capacity']): ?>
<div style="padding:15px 20px;background:#F9FAFB;border-bottom:1px solid #E5E7EB;">
<form method="POST" action="/sa/groups/<?= (int) $group['id'] ?>/enroll" style="display:flex;gap:10px;align-items:end;">
<?= csrf_field() ?>
<div style="flex:1;">
<label class="form-label" style="font-size:12px;">تسجيل لاعب جديد</label>
<select name="player_id" class="form-select" required>
<option value="">-- اختر لاعب --</option>
<?php foreach ($availablePlayers as $ap): ?>
<option value="<?= (int) $ap['id'] ?>"><?= e($ap['name_ar']) ?> (<?= e($ap['code']) ?>)</option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-primary" style="padding:8px 20px;">
<i data-lucide="user-plus" style="width:14px;height:14px;vertical-align:middle;margin-left:4px;"></i> تسجيل
</button>
</form>
</div>
<?php endif; ?>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الكود</th>
<th>الاسم</th>
<th>الهاتف</th>
<th>تاريخ التسجيل</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php if (!empty($players)): ?>
<?php foreach ($players as $pl): ?>
<tr>
<td><code style="font-size:11px;background:#F3F4F6;padding:2px 6px;border-radius:4px;"><?= e($pl['player_code'] ?? '') ?></code></td>
<td style="font-weight:600;"><?= e($pl['player_name']) ?></td>
<td style="direction:ltr;text-align:right;"><?= e($pl['phone'] ?? '—') ?></td>
<td><?= e($pl['enrolled_at'] ?? '') ?></td>
<td>
<form method="POST" action="/sa/groups/<?= (int) $group['id'] ?>/remove-player" style="display:inline;" onsubmit="return confirm('هل تريد سحب هذا اللاعب من المجموعة؟');">
<?= csrf_field() ?>
<input type="hidden" name="player_id" value="<?= (int) $pl['player_id'] ?>">
<button type="submit" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;color:#DC2626;border-color:#DC2626;">
<i data-lucide="user-minus" style="width:13px;height:13px;vertical-align:middle;"></i> سحب
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="5" style="text-align:center;padding:30px;color:#6B7280;">لا يوجد لاعبون مسجلون بعد</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<!-- Schedule -->
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;justify-content:space-between;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">الجدول الأسبوعي (<?= count($schedule) ?> حصة)</h3>
</div>
<!-- Current Schedule -->
<?php if (!empty($schedule)): ?>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>اليوم</th>
<th>من</th>
<th>إلى</th>
<th>الوحدة/الملعب</th>
</tr>
</thead>
<tbody>
<?php foreach ($schedule as $s): ?>
<tr>
<td style="font-weight:600;"><?= e($dayNames[(int) $s['day_of_week']] ?? '') ?></td>
<td style="direction:ltr;text-align:right;"><?= e(substr($s['start_time'], 0, 5)) ?></td>
<td style="direction:ltr;text-align:right;"><?= e(substr($s['end_time'], 0, 5)) ?></td>
<td><?= e($s['unit_name'] ?? '—') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- Save Schedule Form -->
<div style="padding:20px;border-top:1px solid #E5E7EB;">
<h4 style="margin:0 0 15px 0;font-size:14px;color:#374151;">تحديث الجدول</h4>
<form method="POST" action="/sa/groups/<?= (int) $group['id'] ?>/schedule" id="scheduleForm">
<?= csrf_field() ?>
<div id="scheduleEntries">
<?php if (!empty($schedule)): ?>
<?php foreach ($schedule as $idx => $s): ?>
<div class="schedule-entry" style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr auto;gap:10px;margin-bottom:10px;align-items:end;">
<div>
<?php if ($idx === 0): ?><label class="form-label" style="font-size:11px;">اليوم</label><?php endif; ?>
<select name="day_of_week[]" class="form-select">
<?php for ($d = 0; $d < 7; $d++): ?>
<option value="<?= $d ?>" <?= (int)$s['day_of_week'] === $d ? 'selected' : '' ?>><?= e($dayNames[$d]) ?></option>
<?php endfor; ?>
</select>
</div>
<div>
<?php if ($idx === 0): ?><label class="form-label" style="font-size:11px;">من</label><?php endif; ?>
<input type="time" name="start_time[]" value="<?= e(substr($s['start_time'], 0, 5)) ?>" class="form-input" style="direction:ltr;">
</div>
<div>
<?php if ($idx === 0): ?><label class="form-label" style="font-size:11px;">إلى</label><?php endif; ?>
<input type="time" name="end_time[]" value="<?= e(substr($s['end_time'], 0, 5)) ?>" class="form-input" style="direction:ltr;">
</div>
<div>
<?php if ($idx === 0): ?><label class="form-label" style="font-size:11px;">الوحدة</label><?php endif; ?>
<select name="facility_unit_id[]" class="form-select">
<?php foreach ($facilityUnits as $fu): ?>
<option value="<?= (int) $fu['id'] ?>" <?= (int)$s['facility_unit_id'] === (int)$fu['id'] ? 'selected' : '' ?>><?= e($fu['facility_name'] . ' - ' . $fu['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div>
<button type="button" class="btn btn-sm btn-outline" style="color:#DC2626;border-color:#DC2626;padding:6px 8px;" onclick="this.closest('.schedule-entry').remove();">
<i data-lucide="trash-2" style="width:14px;height:14px;"></i>
</button>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<div style="display:flex;gap:10px;margin-top:10px;">
<button type="button" class="btn btn-sm btn-outline" id="addScheduleEntry">
<i data-lucide="plus" style="width:14px;height:14px;vertical-align:middle;margin-left:4px;"></i> إضافة حصة
</button>
<button type="submit" class="btn btn-sm btn-primary">
<i data-lucide="save" style="width:14px;height:14px;vertical-align:middle;margin-left:4px;"></i> حفظ الجدول
</button>
</div>
</form>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
var dayNames = <?= json_encode($dayNames) ?>;
var facilityUnits = <?= json_encode($facilityUnits) ?>;
document.getElementById('addScheduleEntry').addEventListener('click', function() {
var container = document.getElementById('scheduleEntries');
var entry = document.createElement('div');
entry.className = 'schedule-entry';
entry.style.cssText = 'display:grid;grid-template-columns:1fr 1fr 1fr 1fr auto;gap:10px;margin-bottom:10px;align-items:end;';
var dayOptions = '';
for (var d = 0; d < 7; d++) {
dayOptions += '<option value="' + d + '">' + dayNames[d] + '</option>';
}
var unitOptions = '';
for (var i = 0; i < facilityUnits.length; i++) {
var u = facilityUnits[i];
unitOptions += '<option value="' + u.id + '">' + u.facility_name + ' - ' + u.name_ar + '</option>';
}
entry.innerHTML = '<div><select name="day_of_week[]" class="form-select">' + dayOptions + '</select></div>' +
'<div><input type="time" name="start_time[]" class="form-input" style="direction:ltr;"></div>' +
'<div><input type="time" name="end_time[]" class="form-input" style="direction:ltr;"></div>' +
'<div><select name="facility_unit_id[]" class="form-select">' + unitOptions + '</select></div>' +
'<div><button type="button" class="btn btn-sm btn-outline" style="color:#DC2626;border-color:#DC2626;padding:6px 8px;" onclick="this.closest(\'.schedule-entry\').remove();"><i data-lucide="trash-2" style="width:14px;height:14px;"></i></button></div>';
container.appendChild(entry);
if (typeof lucide !== 'undefined') lucide.createIcons();
});
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>مرآة: <?= e($facility['name_ar'] ?? '') ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/mirror" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> كل المرافق</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<meta http-equiv="refresh" content="30">
<!-- Date Picker -->
<div class="card" style="margin-bottom:20px;padding:15px;display:flex;align-items:center;gap:15px;">
<label style="font-weight:600;font-size:14px;">التاريخ:</label>
<input type="date" id="mirror-date" value="<?= e($date) ?>" class="form-input" style="width:180px;"
onchange="window.location.href='/sa/mirror/<?= (int) $facility['id'] ?>/' + this.value;">
<?php if ($date === date('Y-m-d')): ?>
<span style="background:#ECFDF5;color:#059669;padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">اليوم</span>
<?php endif; ?>
<span style="font-size:11px;color:#9CA3AF;margin-right:auto;">يتم التحديث تلقائيا كل 30 ثانية</span>
</div>
<!-- Legend -->
<div class="card" style="margin-bottom:20px;padding:12px;display:flex;gap:15px;flex-wrap:wrap;font-size:12px;">
<span><span style="display:inline-block;width:14px;height:14px;background:#D1FAE5;border-radius:3px;vertical-align:middle;margin-left:4px;"></span> متاح</span>
<span><span style="display:inline-block;width:14px;height:14px;background:#FEF3C7;border-radius:3px;vertical-align:middle;margin-left:4px;"></span> جزئي</span>
<span><span style="display:inline-block;width:14px;height:14px;background:#FEE2E2;border-radius:3px;vertical-align:middle;margin-left:4px;"></span> ممتلئ</span>
<span><span style="display:inline-block;width:14px;height:14px;background:#DBEAFE;border-radius:3px;vertical-align:middle;margin-left:4px;"></span> تدريب</span>
<span><span style="display:inline-block;width:14px;height:14px;background:#FFEDD5;border-radius:3px;vertical-align:middle;margin-left:4px;"></span> محجوز</span>
<span><span style="display:inline-block;width:14px;height:14px;background:#E5E7EB;border-radius:3px;vertical-align:middle;margin-left:4px;"></span> مغلق</span>
</div>
<!-- Mirror Grid -->
<div class="card" style="overflow-x:auto;">
<table style="width:100%;border-collapse:collapse;font-size:12px;min-width:800px;">
<thead>
<tr>
<th style="padding:10px;border:1px solid #E5E7EB;background:#F9FAFB;position:sticky;right:0;z-index:2;min-width:100px;">الوحدة</th>
<?php foreach ($slots as $slot): ?>
<th style="padding:8px 4px;border:1px solid #E5E7EB;background:#F9FAFB;text-align:center;white-space:nowrap;min-width:70px;">
<?= e($slot['start']) ?>
</th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<?php foreach ($grid as $unitId => $unitData): ?>
<tr>
<td style="padding:10px;border:1px solid #E5E7EB;font-weight:700;background:#FAFAFA;position:sticky;right:0;z-index:1;">
<?= e($unitData['unit']['name_ar'] ?? '') ?>
</td>
<?php foreach ($unitData['slots'] as $cell): ?>
<?php
$cellColors = [
'free' => '#D1FAE5',
'partial' => '#FEF3C7',
'full' => '#FEE2E2',
'training' => '#DBEAFE',
'booked' => '#FFEDD5',
'blocked' => '#E5E7EB',
];
$bgColor = $cellColors[$cell['status']] ?? '#FFFFFF';
$cellText = '';
if ($cell['status'] === 'partial') {
$cellText = $cell['occupied'] . '/' . $cell['max'];
} elseif ($cell['status'] === 'training' && !empty($cell['bookings'])) {
$firstBooking = reset($cell['bookings']);
$cellText = $firstBooking['group_name'] ?? '';
} elseif ($cell['status'] === 'booked' && !empty($cell['bookings'])) {
$firstBooking = reset($cell['bookings']);
$cellText = $firstBooking['booker_name'] ?? '';
}
?>
<td style="padding:6px 4px;border:1px solid #E5E7EB;background:<?= $bgColor ?>;text-align:center;vertical-align:middle;font-size:11px;font-weight:500;">
<?= e($cellText) ?>
</td>
<?php endforeach; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>المرايا - حالة المرافق<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div style="display:grid;grid-template-columns:repeat(auto-fill, minmax(280px, 1fr));gap:15px;">
<?php if (!empty($facilities)): ?>
<?php foreach ($facilities as $f): ?>
<a href="/sa/mirror/<?= (int) $f['id'] ?>" class="card" style="padding:20px;text-decoration:none;color:inherit;transition:box-shadow 0.2s;border-right:4px solid #3B82F6;">
<div style="display:flex;align-items:center;gap:12px;">
<div style="width:44px;height:44px;background:#DBEAFE;border-radius:10px;display:flex;align-items:center;justify-content:center;">
<i data-lucide="monitor" style="width:22px;height:22px;color:#3B82F6;"></i>
</div>
<div>
<div style="font-weight:700;font-size:15px;"><?= e($f['name_ar'] ?? '') ?></div>
<?php
$typeLabels = ['pool' => 'حمام سباحة', 'court' => 'ملعب', 'pitch' => 'ملعب كبير', 'gym' => 'صالة رياضية', 'track' => 'مضمار', 'multipurpose' => 'متعدد'];
?>
<div style="font-size:12px;color:#6B7280;"><?= e($typeLabels[$f['facility_type'] ?? ''] ?? '') ?></div>
</div>
</div>
</a>
<?php endforeach; ?>
<?php else: ?>
<div class="card" style="padding:40px;text-align:center;color:#6B7280;grid-column:1/-1;">
<i data-lucide="monitor" style="width:36px;height:36px;color:#D1D5DB;display:block;margin:0 auto 10px;"></i>
لا توجد مرافق نشطة
</div>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
});
</script>
<?php $__template->endSection(); ?>
<?php
$__template->layout('Layout.main');
?>
<?php $__template->section('title'); ?>إضافة لاعب جديد<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/players" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> رجوع</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/sa/players">
<?= csrf_field() ?>
<!-- Basic Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">البيانات الأساسية</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));gap:15px;">
<div>
<label class="form-label">نوع اللاعب <span style="color:#DC2626;">*</span></label>
<select name="player_type" class="form-select" id="player_type" required>
<option value="">اختر...</option>
<option value="member" <?= old('player_type') === 'member' ? 'selected' : '' ?>>عضو</option>
<option value="non_member" <?= old('player_type') === 'non_member' ? 'selected' : '' ?>>غير عضو</option>
<option value="academy_player" <?= old('player_type') === 'academy_player' ? 'selected' : '' ?>>لاعب أكاديمية</option>
</select>
</div>
<div id="member_id_field" style="display:none;">
<label class="form-label">رقم العضوية <span style="color:#DC2626;">*</span></label>
<input type="number" name="member_id" value="<?= e(old('member_id') ?? '') ?>" class="form-input" placeholder="أدخل رقم العضوية">
</div>
<div>
<label class="form-label">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<input type="text" name="full_name_ar" value="<?= e(old('full_name_ar') ?? '') ?>" class="form-input" required>
</div>
<div>
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="full_name_en" value="<?= e(old('full_name_en') ?? '') ?>" class="form-input" dir="ltr">
</div>
<div>
<label class="form-label">الرقم القومي</label>
<input type="text" name="national_id" value="<?= e(old('national_id') ?? '') ?>" class="form-input" maxlength="14" dir="ltr">
</div>
<div>
<label class="form-label">تاريخ الميلاد</label>
<input type="date" name="date_of_birth" value="<?= e(old('date_of_birth') ?? '') ?>" class="form-input">
</div>
<div>
<label class="form-label">النوع</label>
<select name="gender" class="form-select">
<option value="male" <?= old('gender') === 'male' || old('gender') === '' ? 'selected' : '' ?>>ذكر</option>
<option value="female" <?= old('gender') === 'female' ? 'selected' : '' ?>>أنثى</option>
</select>
</div>
<div>
<label class="form-label">الهاتف</label>
<input type="text" name="phone" value="<?= e(old('phone') ?? '') ?>" class="form-input" dir="ltr">
</div>
<div>
<label class="form-label">البريد الإلكتروني</label>
<input type="email" name="email" value="<?= e(old('email') ?? '') ?>" class="form-input" dir="ltr">
</div>
</div>
</div>
<!-- Guardian Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">بيانات ولي الأمر</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));gap:15px;">
<div>
<label class="form-label">اسم ولي الأمر</label>
<input type="text" name="guardian_name" value="<?= e(old('guardian_name') ?? '') ?>" class="form-input">
</div>
<div>
<label class="form-label">هاتف ولي الأمر</label>
<input type="text" name="guardian_phone" value="<?= e(old('guardian_phone') ?? '') ?>" class="form-input" dir="ltr">
</div>
<div>
<label class="form-label">الرقم القومي لولي الأمر</label>
<input type="text" name="guardian_national_id" value="<?= e(old('guardian_national_id') ?? '') ?>" class="form-input" maxlength="14" dir="ltr">
</div>
<div>
<label class="form-label">صلة القرابة</label>
<select name="guardian_relationship" class="form-select">
<option value="">اختر...</option>
<option value="father" <?= old('guardian_relationship') === 'father' ? 'selected' : '' ?>>أب</option>
<option value="mother" <?= old('guardian_relationship') === 'mother' ? 'selected' : '' ?>>أم</option>
<option value="brother" <?= old('guardian_relationship') === 'brother' ? 'selected' : '' ?>>أخ</option>
<option value="sister" <?= old('guardian_relationship') === 'sister' ? 'selected' : '' ?>>أخت</option>
<option value="uncle" <?= old('guardian_relationship') === 'uncle' ? 'selected' : '' ?>>عم / خال</option>
<option value="other" <?= old('guardian_relationship') === 'other' ? 'selected' : '' ?>>أخرى</option>
</select>
</div>
</div>
</div>
<!-- Notes -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">ملاحظات</h3>
</div>
<div style="padding:20px;">
<textarea name="notes" class="form-input" rows="4" placeholder="ملاحظات إضافية..."><?= e(old('notes') ?? '') ?></textarea>
</div>
</div>
<div style="display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary"><i data-lucide="save" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ</button>
<a href="/sa/players" class="btn btn-outline">إلغاء</a>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
var playerType = document.getElementById('player_type');
var memberField = document.getElementById('member_id_field');
function toggleMemberId() {
memberField.style.display = playerType.value === 'member' ? '' : 'none';
}
playerType.addEventListener('change', toggleMemberId);
toggleMemberId();
});
</script>
<?php $__template->endSection(); ?>
<?php
$__template->layout('Layout.main');
$session = \App\Core\App::getInstance()->session();
$employeePermissions = $session->get('permissions') ?? [];
$canApprove = in_array('sa.medical.approve', $employeePermissions, true) || ($session->get('is_super_admin') ?? false);
?>
<?php $__template->section('title'); ?>مستندات اللاعب: <?= e($player['full_name_ar'] ?? '') ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/players/<?= (int) $player['id'] ?>" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> رجوع للاعب</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$docTypes = ['medical_cert' => 'شهادة طبية', 'id_copy' => 'صورة بطاقة', 'photo' => 'صورة شخصية', 'birth_cert' => 'شهادة ميلاد', 'other' => 'أخرى'];
$approvalLabels = ['pending' => 'في الانتظار', 'approved' => 'معتمد', 'rejected' => 'مرفوض'];
$approvalColors = ['pending' => '#D97706', 'approved' => '#059669', 'rejected' => '#DC2626'];
?>
<!-- Documents List -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">المستندات المرفوعة</h3>
</div>
<?php if (!empty($documents)): ?>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>العنوان</th>
<th>النوع</th>
<th>الحالة</th>
<th>تاريخ الرفع</th>
<th>الملف</th>
<?php if ($canApprove): ?>
<th>إجراءات</th>
<?php endif; ?>
</tr>
</thead>
<tbody>
<?php foreach ($documents as $doc):
$aStatus = $doc['approval_status'] ?? 'pending';
$aColor = $approvalColors[$aStatus] ?? '#6B7280';
$isMedicalPending = ($doc['document_type'] ?? '') === 'medical_cert' && $aStatus === 'pending';
?>
<tr>
<td style="font-weight:600;"><?= e($doc['title'] ?? '') ?></td>
<td><?= e($docTypes[$doc['document_type'] ?? ''] ?? $doc['document_type'] ?? '') ?></td>
<td>
<span style="padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:<?= $aColor ?>15;color:<?= $aColor ?>;">
<?= e($approvalLabels[$aStatus] ?? $aStatus) ?>
</span>
<?php if ($aStatus === 'rejected' && !empty($doc['rejection_reason'])): ?>
<div style="font-size:11px;color:#DC2626;margin-top:4px;"><?= e($doc['rejection_reason']) ?></div>
<?php endif; ?>
<?php if ($aStatus === 'approved' && !empty($doc['expiry_date'])): ?>
<div style="font-size:11px;color:#059669;margin-top:4px;">صالح حتى: <?= e($doc['expiry_date']) ?></div>
<?php endif; ?>
</td>
<td style="font-size:13px;"><?= e($doc['created_at'] ?? '') ?></td>
<td>
<?php if (!empty($doc['file_path'])): ?>
<a href="/<?= e($doc['file_path']) ?>" target="_blank" class="btn btn-sm btn-outline" title="تحميل">
<i data-lucide="download" style="width:14px;height:14px;"></i>
</a>
<?php endif; ?>
</td>
<?php if ($canApprove): ?>
<td>
<?php if ($isMedicalPending): ?>
<div style="display:flex;gap:6px;flex-wrap:wrap;">
<!-- Approve Form -->
<form method="POST" action="/sa/players/<?= (int) $player['id'] ?>/documents/<?= (int) $doc['id'] ?>/approve" style="display:inline-flex;gap:4px;align-items:center;">
<?= csrf_field() ?>
<input type="date" name="expiry_date" class="form-input" style="width:140px;height:30px;font-size:12px;" required title="تاريخ انتهاء الصلاحية">
<button type="submit" class="btn btn-sm" style="background:#059669;color:#fff;padding:4px 8px;font-size:11px;" title="اعتماد">
<i data-lucide="check" style="width:12px;height:12px;"></i>
</button>
</form>
<!-- Reject Form -->
<button type="button" class="btn btn-sm" style="background:#DC2626;color:#fff;padding:4px 8px;font-size:11px;" title="رفض"
onclick="document.getElementById('reject_form_<?= (int) $doc['id'] ?>').style.display='block'">
<i data-lucide="x" style="width:12px;height:12px;"></i>
</button>
</div>
<!-- Hidden Reject Form -->
<form method="POST" action="/sa/players/<?= (int) $player['id'] ?>/documents/<?= (int) $doc['id'] ?>/reject" id="reject_form_<?= (int) $doc['id'] ?>" style="display:none;margin-top:8px;">
<?= csrf_field() ?>
<div style="display:flex;gap:4px;align-items:center;">
<input type="text" name="rejection_reason" class="form-input" style="width:200px;height:30px;font-size:12px;" placeholder="سبب الرفض..." required>
<button type="submit" class="btn btn-sm" style="background:#DC2626;color:#fff;padding:4px 8px;font-size:11px;">رفض</button>
</div>
</form>
<?php endif; ?>
</td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div style="padding:40px 20px;text-align:center;color:#9CA3AF;">
<i data-lucide="file-x" style="width:36px;height:36px;color:#D1D5DB;margin-bottom:10px;"></i>
<p>لا توجد مستندات مرفوعة بعد.</p>
</div>
<?php endif; ?>
</div>
<!-- Upload Form -->
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">رفع مستند جديد</h3>
</div>
<div style="padding:20px;">
<form method="POST" action="/sa/players/<?= (int) $player['id'] ?>/documents/upload" enctype="multipart/form-data">
<?= csrf_field() ?>
<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:15px;margin-bottom:15px;">
<div>
<label class="form-label">نوع المستند <span style="color:#DC2626;">*</span></label>
<select name="document_type" class="form-select" required>
<option value="">اختر...</option>
<?php foreach ($docTypes as $key => $label): ?>
<option value="<?= e($key) ?>"><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div>
<label class="form-label">العنوان <span style="color:#DC2626;">*</span></label>
<input type="text" name="title" class="form-input" placeholder="عنوان المستند" required>
</div>
<div>
<label class="form-label">الملف <span style="color:#DC2626;">*</span></label>
<input type="file" name="document_file" class="form-input" required accept=".pdf,.jpg,.jpeg,.png,.doc,.docx">
</div>
</div>
<button type="submit" class="btn btn-primary">
<i data-lucide="upload" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> رفع المستند
</button>
</form>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php
$__template->layout('Layout.main');
?>
<?php $__template->section('title'); ?>تعديل بيانات اللاعب<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/players/<?= (int) $player['id'] ?>" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> رجوع</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/sa/players/<?= (int) $player['id'] ?>/update">
<?= csrf_field() ?>
<!-- Basic Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">البيانات الأساسية</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));gap:15px;">
<div>
<label class="form-label">نوع اللاعب <span style="color:#DC2626;">*</span></label>
<select name="player_type" class="form-select" id="player_type" required>
<option value="">اختر...</option>
<option value="member" <?= (old('player_type') ?? $player['player_type']) === 'member' ? 'selected' : '' ?>>عضو</option>
<option value="non_member" <?= (old('player_type') ?? $player['player_type']) === 'non_member' ? 'selected' : '' ?>>غير عضو</option>
<option value="academy_player" <?= (old('player_type') ?? $player['player_type']) === 'academy_player' ? 'selected' : '' ?>>لاعب أكاديمية</option>
</select>
</div>
<div id="member_id_field" style="<?= ($player['player_type'] ?? '') !== 'member' ? 'display:none;' : '' ?>">
<label class="form-label">رقم العضوية <span style="color:#DC2626;">*</span></label>
<input type="number" name="member_id" value="<?= e(old('member_id') ?? $player['member_id'] ?? '') ?>" class="form-input" placeholder="أدخل رقم العضوية">
</div>
<div>
<label class="form-label">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<input type="text" name="full_name_ar" value="<?= e(old('full_name_ar') ?? $player['full_name_ar'] ?? '') ?>" class="form-input" required>
</div>
<div>
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="full_name_en" value="<?= e(old('full_name_en') ?? $player['full_name_en'] ?? '') ?>" class="form-input" dir="ltr">
</div>
<div>
<label class="form-label">الرقم القومي</label>
<input type="text" name="national_id" value="<?= e(old('national_id') ?? $player['national_id'] ?? '') ?>" class="form-input" maxlength="14" dir="ltr">
</div>
<div>
<label class="form-label">تاريخ الميلاد</label>
<input type="date" name="date_of_birth" value="<?= e(old('date_of_birth') ?? $player['date_of_birth'] ?? '') ?>" class="form-input">
</div>
<div>
<label class="form-label">النوع</label>
<select name="gender" class="form-select">
<option value="male" <?= (old('gender') ?? $player['gender'] ?? '') === 'male' ? 'selected' : '' ?>>ذكر</option>
<option value="female" <?= (old('gender') ?? $player['gender'] ?? '') === 'female' ? 'selected' : '' ?>>أنثى</option>
</select>
</div>
<div>
<label class="form-label">الهاتف</label>
<input type="text" name="phone" value="<?= e(old('phone') ?? $player['phone'] ?? '') ?>" class="form-input" dir="ltr">
</div>
<div>
<label class="form-label">البريد الإلكتروني</label>
<input type="email" name="email" value="<?= e(old('email') ?? $player['email'] ?? '') ?>" class="form-input" dir="ltr">
</div>
</div>
</div>
<!-- Guardian Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">بيانات ولي الأمر</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));gap:15px;">
<div>
<label class="form-label">اسم ولي الأمر</label>
<input type="text" name="guardian_name" value="<?= e(old('guardian_name') ?? $player['guardian_name'] ?? '') ?>" class="form-input">
</div>
<div>
<label class="form-label">هاتف ولي الأمر</label>
<input type="text" name="guardian_phone" value="<?= e(old('guardian_phone') ?? $player['guardian_phone'] ?? '') ?>" class="form-input" dir="ltr">
</div>
<div>
<label class="form-label">الرقم القومي لولي الأمر</label>
<input type="text" name="guardian_national_id" value="<?= e(old('guardian_national_id') ?? $player['guardian_national_id'] ?? '') ?>" class="form-input" maxlength="14" dir="ltr">
</div>
<div>
<label class="form-label">صلة القرابة</label>
<select name="guardian_relationship" class="form-select">
<option value="">اختر...</option>
<option value="father" <?= (old('guardian_relationship') ?? $player['guardian_relationship'] ?? '') === 'father' ? 'selected' : '' ?>>أب</option>
<option value="mother" <?= (old('guardian_relationship') ?? $player['guardian_relationship'] ?? '') === 'mother' ? 'selected' : '' ?>>أم</option>
<option value="brother" <?= (old('guardian_relationship') ?? $player['guardian_relationship'] ?? '') === 'brother' ? 'selected' : '' ?>>أخ</option>
<option value="sister" <?= (old('guardian_relationship') ?? $player['guardian_relationship'] ?? '') === 'sister' ? 'selected' : '' ?>>أخت</option>
<option value="uncle" <?= (old('guardian_relationship') ?? $player['guardian_relationship'] ?? '') === 'uncle' ? 'selected' : '' ?>>عم / خال</option>
<option value="other" <?= (old('guardian_relationship') ?? $player['guardian_relationship'] ?? '') === 'other' ? 'selected' : '' ?>>أخرى</option>
</select>
</div>
</div>
</div>
<!-- Notes -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">ملاحظات</h3>
</div>
<div style="padding:20px;">
<textarea name="notes" class="form-input" rows="4" placeholder="ملاحظات إضافية..."><?= e(old('notes') ?? $player['notes'] ?? '') ?></textarea>
</div>
</div>
<div style="display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary"><i data-lucide="save" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ التعديلات</button>
<a href="/sa/players/<?= (int) $player['id'] ?>" class="btn btn-outline">إلغاء</a>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
var playerType = document.getElementById('player_type');
var memberField = document.getElementById('member_id_field');
function toggleMemberId() {
memberField.style.display = playerType.value === 'member' ? '' : 'none';
}
playerType.addEventListener('change', toggleMemberId);
});
</script>
<?php $__template->endSection(); ?>
<?php
$__template->layout('Layout.main');
?>
<?php $__template->section('title'); ?>اللاعبون<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/players/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة لاعب</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Search & Filters -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<form method="GET" action="/sa/players" style="display:flex;gap:10px;align-items:end;flex-wrap:wrap;">
<div style="flex:1;min-width:200px;">
<label class="form-label" style="font-size:12px;">بحث</label>
<input type="text" name="q" value="<?= e($filters['q'] ?? '') ?>" placeholder="ابحث بالاسم أو الرقم القومي أو المسلسل..." class="form-input">
</div>
<div style="min-width:150px;">
<label class="form-label" style="font-size:12px;">نوع اللاعب</label>
<select name="player_type" class="form-select">
<option value="">الكل</option>
<option value="member" <?= ($filters['player_type'] ?? '') === 'member' ? 'selected' : '' ?>>عضو</option>
<option value="non_member" <?= ($filters['player_type'] ?? '') === 'non_member' ? 'selected' : '' ?>>غير عضو</option>
<option value="academy_player" <?= ($filters['player_type'] ?? '') === 'academy_player' ? 'selected' : '' ?>>لاعب أكاديمية</option>
</select>
</div>
<div style="min-width:150px;">
<label class="form-label" style="font-size:12px;">الحالة الطبية</label>
<select name="medical_status" class="form-select">
<option value="">الكل</option>
<option value="fit" <?= ($filters['medical_status'] ?? '') === 'fit' ? 'selected' : '' ?>>لائق</option>
<option value="unfit" <?= ($filters['medical_status'] ?? '') === 'unfit' ? 'selected' : '' ?>>غير لائق</option>
<option value="pending" <?= ($filters['medical_status'] ?? '') === 'pending' ? 'selected' : '' ?>>في الانتظار</option>
<option value="expired" <?= ($filters['medical_status'] ?? '') === 'expired' ? 'selected' : '' ?>>منتهي</option>
</select>
</div>
<button type="submit" class="btn btn-primary"><i data-lucide="search" style="width:16px;height:16px;vertical-align:middle;"></i> بحث</button>
<a href="/sa/players" class="btn btn-outline">مسح</a>
</form>
</div>
<!-- Players Table -->
<?php if (!empty($players)): ?>
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>المسلسل</th>
<th>الاسم</th>
<th>النوع</th>
<th>الرقم القومي</th>
<th>الحالة الطبية</th>
<th>حالة البطاقة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php foreach ($players as $player): ?>
<tr>
<td><code style="font-size:12px;"><?= e($player['serial_number'] ?? '') ?></code></td>
<td>
<a href="/sa/players/<?= (int) $player['id'] ?>" style="text-decoration:none;color:#0D7377;font-weight:600;">
<?= e($player['full_name_ar'] ?? '') ?>
</a>
</td>
<td>
<?php
$typeLabels = ['member' => 'عضو', 'non_member' => 'غير عضو', 'academy_player' => 'لاعب أكاديمية'];
$typeColors = ['member' => '#059669', 'non_member' => '#D97706', 'academy_player' => '#7C3AED'];
$pType = $player['player_type'] ?? '';
$tColor = $typeColors[$pType] ?? '#6B7280';
?>
<span style="padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:<?= $tColor ?>15;color:<?= $tColor ?>;">
<?= e($typeLabels[$pType] ?? $pType) ?>
</span>
</td>
<td style="font-size:13px;"><?= e($player['national_id'] ?? '-') ?></td>
<td>
<?php
$medLabels = ['fit' => 'لائق', 'unfit' => 'غير لائق', 'pending' => 'في الانتظار', 'expired' => 'منتهي'];
$medColors = ['fit' => '#059669', 'unfit' => '#DC2626', 'pending' => '#D97706', 'expired' => '#6B7280'];
$mStatus = $player['medical_status'] ?? 'pending';
$mColor = $medColors[$mStatus] ?? '#6B7280';
?>
<span style="padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:<?= $mColor ?>15;color:<?= $mColor ?>;">
<?= e($medLabels[$mStatus] ?? $mStatus) ?>
</span>
</td>
<td>
<?php
$cardLabels = ['not_issued' => 'لم تصدر', 'active' => 'فعالة', 'expired' => 'منتهية', 'suspended' => 'موقوفة'];
$cardColors = ['not_issued' => '#6B7280', 'active' => '#059669', 'expired' => '#DC2626', 'suspended' => '#D97706'];
$cStatus = $player['card_status'] ?? 'not_issued';
$cColor = $cardColors[$cStatus] ?? '#6B7280';
?>
<span style="padding:3px 10px;border-radius:10px;font-size:11px;font-weight:600;background:<?= $cColor ?>15;color:<?= $cColor ?>;">
<?= e($cardLabels[$cStatus] ?? $cStatus) ?>
</span>
</td>
<td>
<div style="display:flex;gap:6px;">
<a href="/sa/players/<?= (int) $player['id'] ?>" class="btn btn-sm btn-outline" title="عرض">
<i data-lucide="eye" style="width:14px;height:14px;"></i>
</a>
<a href="/sa/players/<?= (int) $player['id'] ?>/edit" class="btn btn-sm btn-outline" title="تعديل">
<i data-lucide="edit-3" style="width:14px;height:14px;"></i>
</a>
<a href="/sa/players/<?= (int) $player['id'] ?>/documents" class="btn btn-sm btn-outline" title="المستندات">
<i data-lucide="file-text" style="width:14px;height:14px;"></i>
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php if (isset($pagination) && ($pagination['last_page'] ?? 1) > 1): ?>
<div style="padding:15px;">
<?php $__template->include('Shared.Components.pagination', ['pagination' => $pagination, 'baseUrl' => '/sa/players?' . http_build_query(array_filter($filters))]); ?>
</div>
<?php endif; ?>
</div>
<?php else: ?>
<div class="card" style="padding:60px 20px;text-align:center;">
<div style="margin-bottom:15px;">
<i data-lucide="users" style="width:48px;height:48px;color:#D1D5DB;"></i>
</div>
<h3 style="color:#6B7280;margin:0 0 8px;">لا يوجد لاعبون</h3>
<p style="color:#9CA3AF;font-size:14px;margin:0 0 20px;">
<?php if (!empty($filters['q']) || !empty($filters['player_type']) || !empty($filters['medical_status'])): ?>
لا توجد نتائج مطابقة لبحثك.
<?php else: ?>
ابدأ بإضافة لاعب جديد.
<?php endif; ?>
</p>
<a href="/sa/players/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة لاعب</a>
</div>
<?php endif; ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php
$__template->layout('Layout.main');
?>
<?php $__template->section('title'); ?>بيانات اللاعب: <?= e($player['full_name_ar'] ?? '') ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/players/<?= (int) $player['id'] ?>/edit" class="btn btn-primary"><i data-lucide="edit-3" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> تعديل</a>
<a href="/sa/players/<?= (int) $player['id'] ?>/documents" class="btn btn-outline"><i data-lucide="file-text" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> المستندات</a>
<a href="/sa/players" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> رجوع</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$typeLabels = ['member' => 'عضو', 'non_member' => 'غير عضو', 'academy_player' => 'لاعب أكاديمية'];
$medLabels = ['fit' => 'لائق', 'unfit' => 'غير لائق', 'pending' => 'في الانتظار', 'expired' => 'منتهي'];
$medColors = ['fit' => '#059669', 'unfit' => '#DC2626', 'pending' => '#D97706', 'expired' => '#6B7280'];
$cardLabels = ['not_issued' => 'لم تصدر', 'active' => 'فعالة', 'expired' => 'منتهية', 'suspended' => 'موقوفة'];
$genderLabels = ['male' => 'ذكر', 'female' => 'أنثى'];
$relationLabels = ['father' => 'أب', 'mother' => 'أم', 'brother' => 'أخ', 'sister' => 'أخت', 'uncle' => 'عم / خال', 'other' => 'أخرى'];
?>
<!-- Player Info Card -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:20px;display:flex;align-items:center;gap:20px;border-bottom:1px solid #E5E7EB;">
<div style="width:64px;height:64px;border-radius:50%;background:linear-gradient(135deg, #0D737715, #0D737730);display:flex;align-items:center;justify-content:center;">
<i data-lucide="user" style="width:32px;height:32px;color:#0D7377;"></i>
</div>
<div>
<h2 style="margin:0 0 4px;font-size:20px;font-weight:700;"><?= e($player['full_name_ar'] ?? '') ?></h2>
<?php if (!empty($player['full_name_en'])): ?>
<div style="font-size:13px;color:#6B7280;margin-bottom:4px;" dir="ltr"><?= e($player['full_name_en']) ?></div>
<?php endif; ?>
<div style="display:flex;gap:8px;flex-wrap:wrap;">
<code style="font-size:11px;background:#F3F4F6;padding:2px 8px;border-radius:4px;"><?= e($player['serial_number'] ?? '') ?></code>
<?php $mStatus = $player['medical_status'] ?? 'pending'; $mColor = $medColors[$mStatus] ?? '#6B7280'; ?>
<span style="padding:2px 10px;border-radius:10px;font-size:11px;font-weight:600;background:<?= $mColor ?>15;color:<?= $mColor ?>;"><?= e($medLabels[$mStatus] ?? $mStatus) ?></span>
</div>
</div>
</div>
<div style="padding:20px;display:grid;grid-template-columns:repeat(auto-fit, minmax(220px, 1fr));gap:20px;">
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">نوع اللاعب</div>
<div style="font-size:14px;font-weight:600;"><?= e($typeLabels[$player['player_type'] ?? ''] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">الرقم القومي</div>
<div style="font-size:14px;font-weight:600;" dir="ltr"><?= e($player['national_id'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">تاريخ الميلاد</div>
<div style="font-size:14px;font-weight:600;"><?= e($player['date_of_birth'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">النوع</div>
<div style="font-size:14px;font-weight:600;"><?= e($genderLabels[$player['gender'] ?? ''] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">الهاتف</div>
<div style="font-size:14px;font-weight:600;" dir="ltr"><?= e($player['phone'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">البريد الإلكتروني</div>
<div style="font-size:14px;font-weight:600;" dir="ltr"><?= e($player['email'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">حالة البطاقة</div>
<div style="font-size:14px;font-weight:600;"><?= e($cardLabels[$player['card_status'] ?? ''] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">انتهاء الكشف الطبي</div>
<div style="font-size:14px;font-weight:600;"><?= e($player['medical_expiry_date'] ?? '-') ?></div>
</div>
<?php if (!empty($player['member_id'])): ?>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">رقم العضوية</div>
<div style="font-size:14px;font-weight:600;"><?= (int) $player['member_id'] ?></div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Guardian Info -->
<?php if (!empty($player['guardian_name'])): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">بيانات ولي الأمر</h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:repeat(auto-fit, minmax(220px, 1fr));gap:20px;">
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">الاسم</div>
<div style="font-size:14px;font-weight:600;"><?= e($player['guardian_name'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">الهاتف</div>
<div style="font-size:14px;font-weight:600;" dir="ltr"><?= e($player['guardian_phone'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">الرقم القومي</div>
<div style="font-size:14px;font-weight:600;" dir="ltr"><?= e($player['guardian_national_id'] ?? '-') ?></div>
</div>
<div>
<div style="font-size:12px;color:#9CA3AF;margin-bottom:4px;">صلة القرابة</div>
<div style="font-size:14px;font-weight:600;"><?= e($relationLabels[$player['guardian_relationship'] ?? ''] ?? '-') ?></div>
</div>
</div>
</div>
<?php endif; ?>
<!-- Notes -->
<?php if (!empty($player['notes'])): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;font-weight:600;">ملاحظات</h3>
</div>
<div style="padding:20px;font-size:14px;line-height:1.8;color:#374151;">
<?= nl2br(e($player['notes'])) ?>
</div>
</div>
<?php endif; ?>
<!-- Documents Section -->
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center;">
<h3 style="margin:0;font-size:16px;font-weight:600;">المستندات</h3>
<a href="/sa/players/<?= (int) $player['id'] ?>/documents" class="btn btn-sm btn-primary">
<i data-lucide="upload" style="width:14px;height:14px;vertical-align:middle;margin-left:4px;"></i> إدارة المستندات
</a>
</div>
<?php if (!empty($documents)): ?>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>العنوان</th>
<th>النوع</th>
<th>الحالة</th>
<th>تاريخ الرفع</th>
</tr>
</thead>
<tbody>
<?php foreach ($documents as $doc): ?>
<tr>
<td><?= e($doc['title'] ?? '') ?></td>
<td>
<?php
$docTypes = ['medical_cert' => 'شهادة طبية', 'id_copy' => 'صورة بطاقة', 'photo' => 'صورة شخصية', 'birth_cert' => 'شهادة ميلاد', 'other' => 'أخرى'];
?>
<?= e($docTypes[$doc['document_type'] ?? ''] ?? $doc['document_type'] ?? '') ?>
</td>
<td>
<?php
$approvalLabels = ['pending' => 'في الانتظار', 'approved' => 'معتمد', 'rejected' => 'مرفوض'];
$approvalColors = ['pending' => '#D97706', 'approved' => '#059669', 'rejected' => '#DC2626'];
$aStatus = $doc['approval_status'] ?? 'pending';
$aColor = $approvalColors[$aStatus] ?? '#6B7280';
?>
<span style="padding:2px 8px;border-radius:8px;font-size:11px;font-weight:600;background:<?= $aColor ?>15;color:<?= $aColor ?>;">
<?= e($approvalLabels[$aStatus] ?? $aStatus) ?>
</span>
</td>
<td style="font-size:13px;"><?= e($doc['created_at'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div style="padding:40px 20px;text-align:center;color:#9CA3AF;">
لا توجد مستندات مرفوعة بعد.
</div>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>التسعير<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card">
<h3 style="margin-bottom:15px;">مصفوفة التسعير حسب المرفق</h3>
<p style="color:#6B7280;margin-bottom:20px;">اختر مرفق لإدارة قواعد التسعير الخاصة بوحداته (الكورتات / الحارات / الملاعب)</p>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>المرفق</th>
<th>النوع</th>
<th>عدد الوحدات</th>
<th>قواعد التسعير</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php if (empty($facilities)): ?>
<tr><td colspan="5" style="text-align:center;color:#9CA3AF;">لا توجد مرافق مفعلة</td></tr>
<?php else: ?>
<?php foreach ($facilities as $f): ?>
<tr>
<td><strong><?= e($f['name_ar']) ?></strong><br><small style="color:#6B7280;"><?= e($f['code']) ?></small></td>
<td><?= e($f['facility_type']) ?></td>
<td><?= (int) $f['unit_count'] ?></td>
<td>
<?php if ((int) $f['rule_count'] > 0): ?>
<span style="color:#059669;"><?= (int) $f['rule_count'] ?> قاعدة</span>
<?php else: ?>
<span style="color:#DC2626;">لا يوجد</span>
<?php endif; ?>
</td>
<td>
<a href="/sa/pricing/rules?facility_id=<?= (int) $f['id'] ?>" class="btn btn-sm btn-outline">إدارة الأسعار</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>قواعد التسعير<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/pricing" class="btn btn-outline">← العودة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card" style="margin-bottom:20px;padding:15px;">
<form method="GET" action="/sa/pricing/rules" style="display:flex;gap:10px;flex-wrap:wrap;align-items:end;">
<div>
<label class="form-label" style="font-size:12px;">المرفق</label>
<select name="facility_id" class="form-select" style="min-width:180px;" onchange="this.form.submit()">
<option value="">اختر المرفق</option>
<?php foreach ($facilities as $f): ?>
<option value="<?= (int) $f['id'] ?>" <?= $selectedFacility == $f['id'] ? 'selected' : '' ?>><?= e($f['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<?php if (!empty($units)): ?>
<div>
<label class="form-label" style="font-size:12px;">الوحدة</label>
<select name="unit_id" class="form-select" style="min-width:150px;" onchange="this.form.submit()">
<option value="">اختر الوحدة</option>
<?php foreach ($units as $u): ?>
<option value="<?= (int) $u['id'] ?>" <?= $selectedUnit == $u['id'] ? 'selected' : '' ?>><?= e($u['name_ar']) ?> (<?= e($u['booking_mode']) ?>)</option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
</form>
</div>
<?php if ($selectedUnit > 0): ?>
<div class="card" style="margin-bottom:20px;">
<h4 style="margin-bottom:15px;">القواعد الحالية</h4>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الفترة</th>
<th>النوع</th>
<th>الحجم (من-إلى)</th>
<th>سعر العضو</th>
<th>سعر غير العضو</th>
<th>من تاريخ</th>
<th>إلى تاريخ</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php if (empty($rules)): ?>
<tr><td colspan="8" style="text-align:center;color:#9CA3AF;">لا توجد قواعد تسعير لهذه الوحدة</td></tr>
<?php else: ?>
<?php foreach ($rules as $r): ?>
<tr>
<td><?= e($r['bracket_name']) ?><br><small><?= e($r['bracket_start']) ?> - <?= e($r['bracket_end']) ?></small></td>
<td><?= e($r['bracket_type']) ?></td>
<td><?= (int) $r['group_size_min'] ?> - <?= (int) $r['group_size_max'] ?></td>
<td><strong><?= number_format((float) $r['price_per_person_member'], 2) ?></strong></td>
<td><?= number_format((float) $r['price_per_person_nonmember'], 2) ?></td>
<td><?= e($r['effective_from']) ?></td>
<td><?= $r['effective_to'] ? e($r['effective_to']) : '—' ?></td>
<td>
<form method="POST" action="/sa/pricing/rules/<?= (int) $r['id'] ?>/deactivate" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('تعطيل هذه القاعدة؟')">تعطيل</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<div class="card">
<h4 style="margin-bottom:15px;">إضافة قاعدة تسعير جديدة</h4>
<form method="POST" action="/sa/pricing/rules">
<?= csrf_field() ?>
<input type="hidden" name="facility_unit_id" value="<?= (int) $selectedUnit ?>">
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:15px;">
<div>
<label class="form-label">الفترة الزمنية</label>
<select name="time_bracket_id" class="form-select" required>
<option value="">اختر</option>
<?php foreach ($brackets as $b): ?>
<option value="<?= (int) $b['id'] ?>"><?= e($b['name_ar']) ?> (<?= e($b['start_time']) ?> - <?= e($b['end_time']) ?>)</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label class="form-label">حجم المجموعة (من)</label>
<input type="number" name="group_size_min" class="form-input" min="1" value="<?= e(old('group_size_min', '1')) ?>" required>
</div>
<div>
<label class="form-label">حجم المجموعة (إلى)</label>
<input type="number" name="group_size_max" class="form-input" min="1" value="<?= e(old('group_size_max', '1')) ?>" required>
</div>
<div>
<label class="form-label">سعر العضو (للفرد/ساعة)</label>
<input type="number" name="price_per_person_member" class="form-input" step="0.01" min="0" value="<?= e(old('price_per_person_member', '')) ?>" required>
</div>
<div>
<label class="form-label">سعر غير العضو (للفرد/ساعة)</label>
<input type="number" name="price_per_person_nonmember" class="form-input" step="0.01" min="0" value="<?= e(old('price_per_person_nonmember', '')) ?>" required>
</div>
<div>
<label class="form-label">تاريخ البداية</label>
<input type="date" name="effective_from" class="form-input" value="<?= e(old('effective_from', date('Y-m-d'))) ?>" required>
</div>
<div>
<label class="form-label">تاريخ الانتهاء (اختياري)</label>
<input type="date" name="effective_to" class="form-input" value="<?= e(old('effective_to', '')) ?>">
</div>
</div>
<div style="margin-top:20px;">
<button type="submit" class="btn btn-primary">إضافة القاعدة</button>
</div>
</form>
</div>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>إضافة برنامج تدريبي جديد<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/programs" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/sa/programs">
<?= csrf_field() ?>
<!-- Basic Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="book-open" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">البيانات الأساسية</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">كود البرنامج <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code')) ?>" class="form-input" required placeholder="مثال: PROG-SWIM-01" style="direction:ltr;text-align:left;text-transform:uppercase;">
</div>
<div class="form-group">
<label class="form-label">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<input type="text" name="name_ar" value="<?= e(old('name_ar')) ?>" class="form-input" required placeholder="مثال: برنامج السباحة للمبتدئين">
</div>
<div class="form-group">
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="name_en" value="<?= e(old('name_en')) ?>" class="form-input" placeholder="e.g. Beginner Swimming" style="direction:ltr;text-align:left;">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">اللعبة / النشاط <span style="color:#DC2626;">*</span></label>
<select name="discipline_id" class="form-select" required>
<option value="">-- اختر اللعبة --</option>
<?php foreach ($disciplines as $d): ?>
<option value="<?= (int) $d['id'] ?>" <?= old('discipline_id') == $d['id'] ? 'selected' : '' ?>><?= e($d['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">الأكاديمية (اختياري)</label>
<select name="academy_id" class="form-select">
<option value="">-- بدون أكاديمية --</option>
<?php foreach ($academies as $a): ?>
<option value="<?= (int) $a['id'] ?>" <?= old('academy_id') == $a['id'] ? 'selected' : '' ?>><?= e($a['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">نوع البرنامج</label>
<select name="program_type" class="form-select">
<option value="">-- اختر النوع --</option>
<?php foreach ($programTypes as $key => $label): ?>
<option value="<?= e($key) ?>" <?= old('program_type') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
</div>
<!-- Age & Level -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="users" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">الفئة العمرية والمستوى</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">العمر من</label>
<input type="number" name="age_from" value="<?= e(old('age_from')) ?>" class="form-input" min="0" max="99" placeholder="4" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">العمر إلى</label>
<input type="number" name="age_to" value="<?= e(old('age_to')) ?>" class="form-input" min="0" max="99" placeholder="12" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">الجنس</label>
<select name="gender_restriction" class="form-select">
<option value="mixed" <?= old('gender_restriction', 'mixed') === 'mixed' ? 'selected' : '' ?>>مختلط</option>
<option value="male" <?= old('gender_restriction') === 'male' ? 'selected' : '' ?>>ذكور فقط</option>
<option value="female" <?= old('gender_restriction') === 'female' ? 'selected' : '' ?>>إناث فقط</option>
</select>
</div>
<div class="form-group">
<label class="form-label">المستوى</label>
<select name="skill_level" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($skillLevels as $key => $label): ?>
<option value="<?= e($key) ?>" <?= old('skill_level') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
</div>
<!-- Session Details -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="clock" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">تفاصيل الحصص</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">مدة الحصة (دقيقة)</label>
<input type="number" name="session_duration_minutes" value="<?= e(old('session_duration_minutes')) ?>" class="form-input" min="15" max="300" placeholder="60" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">عدد الحصص أسبوعياً</label>
<input type="number" name="sessions_per_week" value="<?= e(old('sessions_per_week')) ?>" class="form-input" min="1" max="14" placeholder="3" style="direction:ltr;text-align:left;">
</div>
</div>
<div class="form-group" style="margin-top:15px;">
<label class="form-label">وصف البرنامج</label>
<textarea name="description_ar" class="form-input" rows="3" placeholder="وصف مختصر للبرنامج..."><?= e(old('description_ar')) ?></textarea>
</div>
</div>
</div>
<!-- Submit -->
<div style="display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary" style="padding:12px 30px;font-size:15px;">
<i data-lucide="check" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ البرنامج
</button>
<a href="/sa/programs" class="btn btn-outline" style="padding:12px 30px;font-size:15px;">إلغاء</a>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>تعديل البرنامج: <?= e($program->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/programs/<?= (int) $program->id ?>" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للتفاصيل</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<form method="POST" action="/sa/programs/<?= (int) $program->id ?>/update">
<?= csrf_field() ?>
<!-- Basic Information -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="book-open" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">البيانات الأساسية</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">كود البرنامج <span style="color:#DC2626;">*</span></label>
<input type="text" name="code" value="<?= e(old('code', $program->code)) ?>" class="form-input" required style="direction:ltr;text-align:left;text-transform:uppercase;">
</div>
<div class="form-group">
<label class="form-label">الاسم بالعربي <span style="color:#DC2626;">*</span></label>
<input type="text" name="name_ar" value="<?= e(old('name_ar', $program->name_ar)) ?>" class="form-input" required>
</div>
<div class="form-group">
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="name_en" value="<?= e(old('name_en', $program->name_en ?? '')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;margin-top:15px;">
<div class="form-group">
<label class="form-label">اللعبة / النشاط <span style="color:#DC2626;">*</span></label>
<select name="discipline_id" class="form-select" required>
<option value="">-- اختر اللعبة --</option>
<?php foreach ($disciplines as $d): ?>
<option value="<?= (int) $d['id'] ?>" <?= (old('discipline_id', $program->discipline_id)) == $d['id'] ? 'selected' : '' ?>><?= e($d['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">الأكاديمية (اختياري)</label>
<select name="academy_id" class="form-select">
<option value="">-- بدون أكاديمية --</option>
<?php foreach ($academies as $a): ?>
<option value="<?= (int) $a['id'] ?>" <?= (old('academy_id', $program->academy_id ?? '')) == $a['id'] ? 'selected' : '' ?>><?= e($a['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">نوع البرنامج</label>
<select name="program_type" class="form-select">
<option value="">-- اختر النوع --</option>
<?php foreach ($programTypes as $key => $label): ?>
<option value="<?= e($key) ?>" <?= old('program_type', $program->program_type ?? '') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
</div>
<!-- Age & Level -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="users" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">الفئة العمرية والمستوى</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">العمر من</label>
<input type="number" name="age_from" value="<?= e(old('age_from', $program->age_from ?? '')) ?>" class="form-input" min="0" max="99" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">العمر إلى</label>
<input type="number" name="age_to" value="<?= e(old('age_to', $program->age_to ?? '')) ?>" class="form-input" min="0" max="99" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">الجنس</label>
<select name="gender_restriction" class="form-select">
<option value="mixed" <?= old('gender_restriction', $program->gender_restriction ?? 'mixed') === 'mixed' ? 'selected' : '' ?>>مختلط</option>
<option value="male" <?= old('gender_restriction', $program->gender_restriction ?? '') === 'male' ? 'selected' : '' ?>>ذكور فقط</option>
<option value="female" <?= old('gender_restriction', $program->gender_restriction ?? '') === 'female' ? 'selected' : '' ?>>إناث فقط</option>
</select>
</div>
<div class="form-group">
<label class="form-label">المستوى</label>
<select name="skill_level" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($skillLevels as $key => $label): ?>
<option value="<?= e($key) ?>" <?= old('skill_level', $program->skill_level ?? '') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
</div>
<!-- Session Details -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="clock" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">تفاصيل الحصص</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
<div class="form-group">
<label class="form-label">مدة الحصة (دقيقة)</label>
<input type="number" name="session_duration_minutes" value="<?= e(old('session_duration_minutes', $program->session_duration_minutes ?? '')) ?>" class="form-input" min="15" max="300" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">عدد الحصص أسبوعياً</label>
<input type="number" name="sessions_per_week" value="<?= e(old('sessions_per_week', $program->sessions_per_week ?? '')) ?>" class="form-input" min="1" max="14" style="direction:ltr;text-align:left;">
</div>
</div>
<div class="form-group" style="margin-top:15px;">
<label class="form-label">وصف البرنامج</label>
<textarea name="description_ar" class="form-input" rows="3"><?= e(old('description_ar', $program->description_ar ?? '')) ?></textarea>
</div>
</div>
</div>
<!-- Submit -->
<div style="display:flex;gap:10px;justify-content:flex-start;">
<button type="submit" class="btn btn-primary" style="padding:12px 30px;font-size:15px;">
<i data-lucide="check" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> حفظ التعديلات
</button>
<a href="/sa/programs/<?= (int) $program->id ?>" class="btn btn-outline" style="padding:12px 30px;font-size:15px;">إلغاء</a>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>البرامج التدريبية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/programs/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة برنامج</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Search & Filter Bar -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<form method="GET" action="/sa/programs" style="display:flex;gap:10px;align-items:end;flex-wrap:wrap;">
<div style="flex:1;min-width:200px;">
<label class="form-label" style="font-size:12px;">بحث</label>
<input type="text" name="q" value="<?= e($filters['q'] ?? '') ?>" placeholder="ابحث بالاسم أو الكود..." class="form-input">
</div>
<div style="min-width:180px;">
<label class="form-label" style="font-size:12px;">اللعبة</label>
<select name="discipline_id" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($disciplines as $d): ?>
<option value="<?= (int) $d['id'] ?>" <?= ($filters['discipline_id'] ?? '') == $d['id'] ? 'selected' : '' ?>><?= e($d['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-outline"><i data-lucide="search" style="width:16px;height:16px;vertical-align:middle;"></i> بحث</button>
<a href="/sa/programs" class="btn btn-sm btn-outline" style="color:#6B7280;">مسح</a>
</form>
</div>
<!-- Programs Table -->
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الكود</th>
<th>الاسم</th>
<th>اللعبة</th>
<th>النوع</th>
<th>الأعمار</th>
<th>المجموعات</th>
<th>الحالة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php if (!empty($programs)): ?>
<?php
$typeOptions = \App\Modules\SportsActivity\Models\Program::getProgramTypeOptions();
?>
<?php foreach ($programs as $p): ?>
<tr>
<td><code style="font-size:11px;background:#F3F4F6;padding:2px 6px;border-radius:4px;"><?= e($p['code']) ?></code></td>
<td>
<a href="/sa/programs/<?= (int) $p['id'] ?>" style="text-decoration:none;color:#1A1A2E;font-weight:600;">
<?= e($p['name_ar']) ?>
</a>
<?php if (!empty($p['name_en'])): ?>
<div style="font-size:11px;color:#9CA3AF;"><?= e($p['name_en']) ?></div>
<?php endif; ?>
</td>
<td><?= e($p['discipline_name'] ?? '—') ?></td>
<td>
<?php if (!empty($p['program_type']) && isset($typeOptions[$p['program_type']])): ?>
<span style="background:#F3F4F6;padding:2px 8px;border-radius:4px;font-size:12px;"><?= e($typeOptions[$p['program_type']]) ?></span>
<?php else: ?>
<span style="color:#9CA3AF;"></span>
<?php endif; ?>
</td>
<td>
<?php if ($p['age_from'] !== null || $p['age_to'] !== null): ?>
<?= $p['age_from'] ?? '?' ?> - <?= $p['age_to'] ?? '?' ?> سنة
<?php else: ?>
<span style="color:#9CA3AF;"></span>
<?php endif; ?>
</td>
<td>
<span style="background:#EEF2FF;color:#4F46E5;padding:2px 8px;border-radius:10px;font-size:12px;font-weight:600;">
<?= (int) ($p['group_count'] ?? 0) ?>
</span>
</td>
<td>
<?php if ((int) ($p['is_active'] ?? 0)): ?>
<span style="background:#ECFDF5;color:#059669;padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">فعّال</span>
<?php else: ?>
<span style="background:#FEE2E2;color:#DC2626;padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">معطّل</span>
<?php endif; ?>
</td>
<td>
<div style="display:flex;gap:6px;">
<a href="/sa/programs/<?= (int) $p['id'] ?>" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="eye" style="width:13px;height:13px;vertical-align:middle;"></i> عرض
</a>
<a href="/sa/programs/<?= (int) $p['id'] ?>/edit" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="edit-3" style="width:13px;height:13px;vertical-align:middle;"></i> تعديل
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="8" style="text-align:center;padding:40px;color:#6B7280;">
<i data-lucide="book-open" style="width:36px;height:36px;color:#D1D5DB;display:block;margin:0 auto 10px;"></i>
<?php if (!empty($filters['q']) || !empty($filters['discipline_id'])): ?>
لا توجد نتائج مطابقة لبحثك
<?php else: ?>
لا توجد برامج تدريبية بعد
<?php endif; ?>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if (isset($pagination) && ($pagination['last_page'] ?? 1) > 1): ?>
<div style="padding:15px;">
<?php $__template->include('Shared.Components.pagination', ['pagination' => $pagination, 'baseUrl' => '/sa/programs?' . http_build_query(array_filter($filters))]); ?>
</div>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>البرنامج: <?= e($program['name_ar']) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/programs/<?= (int) $program['id'] ?>/edit" class="btn btn-primary" style="margin-left:8px;"><i data-lucide="edit-3" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> تعديل</a>
<a href="/sa/programs" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$genderLabels = ['mixed' => 'مختلط', 'male' => 'ذكور فقط', 'female' => 'إناث فقط'];
?>
<!-- Program Details -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">بيانات البرنامج</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;">
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الكود</div>
<div style="font-weight:600;"><code style="background:#F3F4F6;padding:2px 8px;border-radius:4px;"><?= e($program['code']) ?></code></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">اللعبة / النشاط</div>
<div style="font-weight:600;"><?= e($program['discipline_name'] ?? '—') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الأكاديمية</div>
<div style="font-weight:600;"><?= e($program['academy_name'] ?? '—') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">نوع البرنامج</div>
<div style="font-weight:600;"><?= e($programTypes[$program['program_type'] ?? ''] ?? '—') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الفئة العمرية</div>
<div style="font-weight:600;">
<?php if ($program['age_from'] !== null || $program['age_to'] !== null): ?>
<?= $program['age_from'] ?? '?' ?> - <?= $program['age_to'] ?? '?' ?> سنة
<?php else: ?>
<?php endif; ?>
</div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الجنس</div>
<div style="font-weight:600;"><?= e($genderLabels[$program['gender_restriction'] ?? 'mixed'] ?? '—') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">المستوى</div>
<div style="font-weight:600;"><?= e($skillLevels[$program['skill_level'] ?? ''] ?? '—') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">مدة الحصة</div>
<div style="font-weight:600;"><?= $program['session_duration_minutes'] ? $program['session_duration_minutes'] . ' دقيقة' : '—' ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الحصص أسبوعياً</div>
<div style="font-weight:600;"><?= $program['sessions_per_week'] ?? '—' ?></div>
</div>
</div>
<?php if (!empty($program['description_ar'])): ?>
<div style="margin-top:15px;padding-top:15px;border-top:1px solid #F3F4F6;">
<div style="font-size:12px;color:#6B7280;margin-bottom:4px;">الوصف</div>
<div><?= e($program['description_ar']) ?></div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Groups Table -->
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;justify-content:space-between;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">المجموعات (<?= count($groups) ?>)</h3>
<a href="/sa/groups/create?program_id=<?= (int) $program['id'] ?>" class="btn btn-sm btn-primary">
<i data-lucide="plus" style="width:14px;height:14px;vertical-align:middle;margin-left:4px;"></i> إضافة مجموعة
</a>
</div>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الكود</th>
<th>الاسم</th>
<th>المدرب</th>
<th>السعة</th>
<th>الرسوم</th>
<th>الحالة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php if (!empty($groups)): ?>
<?php
$statusOptions = \App\Modules\SportsActivity\Models\Group::getStatusOptions();
?>
<?php foreach ($groups as $g): ?>
<tr>
<td><code style="font-size:11px;background:#F3F4F6;padding:2px 6px;border-radius:4px;"><?= e($g['code']) ?></code></td>
<td><a href="/sa/groups/<?= (int) $g['id'] ?>" style="text-decoration:none;color:#1A1A2E;font-weight:600;"><?= e($g['name_ar']) ?></a></td>
<td><?= e($g['coach_name'] ?? '—') ?></td>
<td>
<span style="font-weight:600;color:<?= (int)$g['current_count'] >= (int)$g['max_capacity'] ? '#DC2626' : '#059669' ?>;">
<?= (int) $g['current_count'] ?>
</span>
/ <?= (int) $g['max_capacity'] ?>
</td>
<td><?= money((float) ($g['monthly_fee_member'] ?? 0)) ?></td>
<td>
<?php
$statusColors = ['active' => '#059669', 'paused' => '#D97706', 'completed' => '#6B7280', 'cancelled' => '#DC2626'];
$statusBgs = ['active' => '#ECFDF5', 'paused' => '#FEF3C7', 'completed' => '#F3F4F6', 'cancelled' => '#FEE2E2'];
$st = $g['status'] ?? 'active';
?>
<span style="background:<?= $statusBgs[$st] ?? '#F3F4F6' ?>;color:<?= $statusColors[$st] ?? '#6B7280' ?>;padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">
<?= e($statusOptions[$st] ?? $st) ?>
</span>
</td>
<td>
<a href="/sa/groups/<?= (int) $g['id'] ?>" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="eye" style="width:13px;height:13px;vertical-align:middle;"></i> عرض
</a>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="7" style="text-align:center;padding:30px;color:#6B7280;">لا توجد مجموعات لهذا البرنامج بعد</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>الجدول اليومي — <?= e($date) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/schedule/weekly" class="btn btn-outline" style="margin-left:8px;"><i data-lucide="grid" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العرض الأسبوعي</a>
<a href="/sa/bookings/create" class="btn btn-primary"><i data-lucide="plus" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> حجز جديد</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$prevDate = date('Y-m-d', strtotime($date . ' -1 day'));
$nextDate = date('Y-m-d', strtotime($date . ' +1 day'));
$dayNames = ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'];
$dayOfWeek = (int) date('w', strtotime($date));
?>
<!-- Date Navigation -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<div style="display:flex;align-items:center;justify-content:space-between;">
<a href="/sa/schedule/daily/<?= e($prevDate) ?>" class="btn btn-sm btn-outline">
<i data-lucide="chevron-right" style="width:16px;height:16px;vertical-align:middle;"></i> اليوم السابق
</a>
<div style="display:flex;align-items:center;gap:15px;">
<span style="font-weight:700;font-size:16px;"><?= e($dayNames[$dayOfWeek]) ?><?= e($date) ?></span>
<form method="GET" action="/sa/schedule/daily" style="display:inline;">
<input type="date" name="goto" value="<?= e($date) ?>" class="form-input" style="width:auto;direction:ltr;padding:4px 10px;" onchange="window.location='/sa/schedule/daily/' + this.value;">
</form>
<?php if ($date !== date('Y-m-d')): ?>
<a href="/sa/schedule/daily/<?= date('Y-m-d') ?>" class="btn btn-sm btn-outline">اليوم</a>
<?php endif; ?>
</div>
<a href="/sa/schedule/daily/<?= e($nextDate) ?>" class="btn btn-sm btn-outline">
اليوم التالي <i data-lucide="chevron-left" style="width:16px;height:16px;vertical-align:middle;"></i>
</a>
</div>
</div>
<!-- Daily Schedule by Facility -->
<?php if (!empty($byFacility)): ?>
<?php foreach ($byFacility as $facilityName => $units): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:12px 20px;border-bottom:1px solid #E5E7EB;background:#F9FAFB;">
<h3 style="margin:0;font-size:14px;color:#374151;"><i data-lucide="building" style="width:16px;height:16px;vertical-align:middle;margin-left:6px;color:#0D7377;"></i> <?= e($facilityName) ?></h3>
</div>
<?php foreach ($units as $unitName => $bookings): ?>
<div style="padding:12px 20px;border-bottom:1px solid #F3F4F6;">
<div style="font-weight:600;font-size:13px;color:#4B5563;margin-bottom:8px;"><?= e($unitName) ?></div>
<div style="display:flex;gap:8px;flex-wrap:wrap;">
<?php foreach ($bookings as $b): ?>
<?php
$colors = [
'training' => ['bg' => '#DBEAFE', 'border' => '#3B82F6', 'text' => '#1E40AF'],
'hourly' => ['bg' => '#FEF3C7', 'border' => '#F59E0B', 'text' => '#92400E'],
'blocked' => ['bg' => '#E5E7EB', 'border' => '#6B7280', 'text' => '#374151'],
'maintenance' => ['bg' => '#E5E7EB', 'border' => '#9CA3AF', 'text' => '#4B5563'],
];
$c = $colors[$b['booking_type']] ?? $colors['hourly'];
?>
<a href="/sa/bookings/<?= (int) $b['id'] ?>" style="text-decoration:none;background:<?= $c['bg'] ?>;border-right:3px solid <?= $c['border'] ?>;border-radius:6px;padding:8px 12px;min-width:150px;">
<div style="font-size:12px;font-weight:700;color:<?= $c['text'] ?>;direction:ltr;text-align:right;">
<?= e(substr($b['start_time'], 0, 5)) ?> - <?= e(substr($b['end_time'], 0, 5)) ?>
</div>
<div style="font-size:11px;color:<?= $c['text'] ?>;margin-top:2px;">
<?php if ($b['booking_type'] === 'training' && !empty($b['group_name'])): ?>
<?= e($b['group_name']) ?>
<?php elseif (!empty($b['booker_name'])): ?>
<?= e($b['booker_name']) ?>
<?php else: ?>
<?= e($b['booking_number']) ?>
<?php endif; ?>
</div>
<?php if (!empty($b['coach_name'])): ?>
<div style="font-size:10px;color:#6B7280;margin-top:2px;">مدرب: <?= e($b['coach_name']) ?></div>
<?php endif; ?>
</a>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
<?php else: ?>
<div class="card" style="padding:50px;text-align:center;color:#6B7280;">
<i data-lucide="calendar-check" style="width:48px;height:48px;color:#D1D5DB;display:block;margin:0 auto 15px;"></i>
<div style="font-size:15px;">لا توجد حجوزات لهذا اليوم</div>
<div style="font-size:13px;margin-top:8px;">جميع الوحدات متاحة</div>
</div>
<?php endif; ?>
<!-- Legend -->
<div style="margin-top:15px;display:flex;gap:20px;font-size:12px;color:#6B7280;">
<span><span style="display:inline-block;width:12px;height:12px;background:#DBEAFE;border:1px solid #3B82F6;border-radius:2px;vertical-align:middle;margin-left:4px;"></span> تدريب</span>
<span><span style="display:inline-block;width:12px;height:12px;background:#FEF3C7;border:1px solid #F59E0B;border-radius:2px;vertical-align:middle;margin-left:4px;"></span> حجز بالساعة</span>
<span><span style="display:inline-block;width:12px;height:12px;background:#E5E7EB;border:1px solid #6B7280;border-radius:2px;vertical-align:middle;margin-left:4px;"></span> محجوز/صيانة</span>
</div>
<!-- Generate Schedule Form -->
<div class="card" style="margin-top:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
<i data-lucide="wand-2" style="width:18px;height:18px;color:#0D7377;"></i>
<h3 style="margin:0;color:#0D7377;font-size:15px;">توليد حجوزات تدريبية</h3>
</div>
<div style="padding:20px;">
<form method="POST" action="/sa/schedule/generate" style="display:flex;gap:12px;align-items:end;flex-wrap:wrap;">
<?= csrf_field() ?>
<div style="min-width:200px;">
<label class="form-label" style="font-size:12px;">المجموعة</label>
<select name="group_id" class="form-select" required>
<option value="">-- اختر المجموعة --</option>
<?php foreach ($groups as $g): ?>
<option value="<?= (int) $g['id'] ?>"><?= e($g['name_ar']) ?> (<?= e($g['code']) ?>)</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label class="form-label" style="font-size:12px;">من تاريخ</label>
<input type="date" name="from_date" value="<?= e($date) ?>" class="form-input" required style="direction:ltr;text-align:left;">
</div>
<div>
<label class="form-label" style="font-size:12px;">إلى تاريخ</label>
<input type="date" name="to_date" value="<?= e(date('Y-m-d', strtotime($date . ' +30 days'))) ?>" class="form-input" required style="direction:ltr;text-align:left;">
</div>
<button type="submit" class="btn btn-primary" onclick="return confirm('سيتم توليد حجوزات تدريبية بناءً على جدول المجموعة. متأكد؟');">
<i data-lucide="sparkles" style="width:14px;height:14px;vertical-align:middle;margin-left:4px;"></i> توليد
</button>
</form>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>الجدول الأسبوعي<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/schedule/daily/<?= date('Y-m-d') ?>" class="btn btn-outline"><i data-lucide="calendar" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العرض اليومي</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Week Navigation -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<div style="display:flex;align-items:center;justify-content:space-between;">
<a href="/sa/schedule/weekly?week_start=<?= e($prevWeek) ?>" class="btn btn-sm btn-outline">
<i data-lucide="chevron-right" style="width:16px;height:16px;vertical-align:middle;"></i> الأسبوع السابق
</a>
<div style="font-weight:700;font-size:15px;">
<?= e($days[0]['date']) ?><?= e($days[6]['date']) ?>
</div>
<a href="/sa/schedule/weekly?week_start=<?= e($nextWeek) ?>" class="btn btn-sm btn-outline">
الأسبوع التالي <i data-lucide="chevron-left" style="width:16px;height:16px;vertical-align:middle;"></i>
</a>
</div>
</div>
<!-- Weekly Grid -->
<div class="card">
<div class="table-responsive">
<table class="data-table" style="table-layout:fixed;">
<thead>
<tr>
<th style="width:180px;">الوحدة</th>
<?php foreach ($days as $day): ?>
<th style="text-align:center;">
<div style="font-size:12px;color:#6B7280;"><?= e($day['name']) ?></div>
<div style="font-size:11px;color:#9CA3AF;"><?= e($day['day']) ?></div>
</th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<?php if (!empty($units)): ?>
<?php
$currentFacility = '';
?>
<?php foreach ($units as $unit): ?>
<?php if ($unit['facility_name'] !== $currentFacility): ?>
<?php $currentFacility = $unit['facility_name']; ?>
<tr style="background:#F9FAFB;">
<td colspan="8" style="font-weight:700;font-size:13px;color:#374151;padding:8px 15px;">
<i data-lucide="building" style="width:14px;height:14px;vertical-align:middle;margin-left:4px;color:#0D7377;"></i>
<?= e($currentFacility) ?>
</td>
</tr>
<?php endif; ?>
<tr>
<td style="font-size:12px;font-weight:600;padding:8px 15px;"><?= e($unit['name_ar']) ?></td>
<?php foreach ($days as $day): ?>
<?php
$count = $countMap[(int) $unit['id']][$day['date']] ?? 0;
$bgColor = $count > 0 ? '#EEF2FF' : '#FFFFFF';
$textColor = $count > 0 ? '#4F46E5' : '#D1D5DB';
?>
<td style="text-align:center;background:<?= $bgColor ?>;padding:8px;">
<a href="/sa/schedule/daily/<?= e($day['date']) ?>" style="text-decoration:none;color:<?= $textColor ?>;font-weight:<?= $count > 0 ? '700' : '400' ?>;font-size:<?= $count > 0 ? '14px' : '12px' ?>;">
<?php if ($count > 0): ?>
<?= $count ?>
<?php else: ?>
<?php endif; ?>
</a>
</td>
<?php endforeach; ?>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="8" style="text-align:center;padding:40px;color:#6B7280;">
لا توجد وحدات/ملاعب مسجلة
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<!-- Legend -->
<div style="margin-top:15px;display:flex;gap:20px;font-size:12px;color:#6B7280;">
<span><span style="display:inline-block;width:16px;height:16px;background:#EEF2FF;border:1px solid #C7D2FE;border-radius:3px;vertical-align:middle;margin-left:4px;text-align:center;font-size:10px;font-weight:700;color:#4F46E5;line-height:16px;">3</span> يوجد حجوزات</span>
<span><span style="display:inline-block;width:16px;height:16px;background:#FFF;border:1px solid #E5E7EB;border-radius:3px;vertical-align:middle;margin-left:4px;text-align:center;font-size:10px;color:#D1D5DB;line-height:16px;"></span> لا توجد حجوزات</span>
<span style="margin-right:auto;"><em>اضغط على أي خلية للانتقال للعرض اليومي</em></span>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>الاشتراكات الشهرية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<form method="POST" action="/sa/subscriptions/generate" style="display:inline-flex;gap:8px;align-items:center;">
<?= csrf_field() ?>
<input type="month" name="year_month" value="<?= e($filters['month'] ?? date('Y-m')) ?>" class="form-input" style="width:160px;">
<button type="submit" class="btn btn-primary" onclick="return confirm('هل تريد توليد الاشتراكات لهذا الشهر؟');">
<i data-lucide="refresh-cw" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> توليد الاشتراكات
</button>
</form>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Filters -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<form method="GET" action="/sa/subscriptions" style="display:flex;gap:10px;align-items:end;flex-wrap:wrap;">
<div style="min-width:160px;">
<label class="form-label" style="font-size:12px;">الشهر</label>
<input type="month" name="month" value="<?= e($filters['month'] ?? '') ?>" class="form-input">
</div>
<div style="min-width:180px;">
<label class="form-label" style="font-size:12px;">المجموعة</label>
<select name="group_id" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($groups as $g): ?>
<option value="<?= (int) $g['id'] ?>" <?= ($filters['group_id'] ?? '') === (string) $g['id'] ? 'selected' : '' ?>><?= e($g['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="min-width:150px;">
<label class="form-label" style="font-size:12px;">حالة الدفع</label>
<select name="payment_status" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($statusOptions as $key => $label): ?>
<option value="<?= e($key) ?>" <?= ($filters['payment_status'] ?? '') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-outline"><i data-lucide="search" style="width:16px;height:16px;vertical-align:middle;"></i> بحث</button>
<a href="/sa/subscriptions" class="btn btn-sm btn-outline" style="color:#6B7280;">مسح</a>
</form>
</div>
<!-- Subscriptions Table -->
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الرقم</th>
<th>اللاعب</th>
<th>المجموعة</th>
<th>الفترة</th>
<th>المبلغ</th>
<th>الحالة</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php if (!empty($subscriptions)): ?>
<?php foreach ($subscriptions as $sub): ?>
<tr>
<td><code style="font-size:11px;background:#F3F4F6;padding:2px 6px;border-radius:4px;"><?= e($sub['subscription_number'] ?? '') ?></code></td>
<td><?= e($sub['player_name'] ?? '—') ?></td>
<td><?= e($sub['group_name'] ?? '—') ?></td>
<td style="font-size:12px;"><?= e($sub['period_start'] ?? '') ?> - <?= e($sub['period_end'] ?? '') ?></td>
<td style="font-weight:600;"><?= money((float) ($sub['final_amount'] ?? 0)) ?></td>
<td>
<?php
$statusColors = [
'unpaid' => 'background:#FEF3C7;color:#92400E;',
'paid' => 'background:#ECFDF5;color:#059669;',
'partial' => 'background:#DBEAFE;color:#1D4ED8;',
'exempt' => 'background:#F3E8FF;color:#7C3AED;',
'overdue' => 'background:#FEE2E2;color:#DC2626;',
];
$statusStyle = $statusColors[$sub['payment_status'] ?? ''] ?? 'background:#F3F4F6;color:#374151;';
?>
<span style="<?= $statusStyle ?>padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">
<?= e($statusOptions[$sub['payment_status'] ?? ''] ?? $sub['payment_status'] ?? '') ?>
</span>
</td>
<td>
<a href="/sa/subscriptions/<?= (int) $sub['id'] ?>" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="eye" style="width:13px;height:13px;vertical-align:middle;"></i> عرض
</a>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="7" style="text-align:center;padding:40px;color:#6B7280;">
<i data-lucide="credit-card" style="width:36px;height:36px;color:#D1D5DB;display:block;margin:0 auto 10px;"></i>
لا توجد اشتراكات
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if (isset($pagination) && ($pagination['last_page'] ?? 1) > 1): ?>
<div style="padding:15px;">
<?php $__template->include('Shared.Components.pagination', ['pagination' => $pagination, 'baseUrl' => '/sa/subscriptions?' . http_build_query(array_filter($filters))]); ?>
</div>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>تفاصيل الاشتراك<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/sa/subscriptions" class="btn btn-outline"><i data-lucide="arrow-right" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$sub = $subscription;
$statusColors = [
'unpaid' => 'background:#FEF3C7;color:#92400E;',
'paid' => 'background:#ECFDF5;color:#059669;',
'partial' => 'background:#DBEAFE;color:#1D4ED8;',
'exempt' => 'background:#F3E8FF;color:#7C3AED;',
'overdue' => 'background:#FEE2E2;color:#DC2626;',
];
$statusStyle = $statusColors[$sub['payment_status'] ?? ''] ?? 'background:#F3F4F6;color:#374151;';
?>
<div class="card" style="margin-bottom:20px;padding:20px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;">
<h3 style="margin:0;font-size:18px;">اشتراك: <?= e($sub['subscription_number'] ?? '') ?></h3>
<span style="<?= $statusStyle ?>padding:5px 14px;border-radius:10px;font-size:13px;font-weight:600;">
<?= e($statusOptions[$sub['payment_status'] ?? ''] ?? '') ?>
</span>
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:15px;">
<div>
<div style="font-size:12px;color:#6B7280;">اللاعب</div>
<div style="font-weight:600;"><?= e($sub['player_name'] ?? '—') ?></div>
<?php if (!empty($sub['player_code'])): ?>
<div style="font-size:11px;color:#9CA3AF;"><?= e($sub['player_code']) ?></div>
<?php endif; ?>
</div>
<div>
<div style="font-size:12px;color:#6B7280;">المجموعة</div>
<div style="font-weight:600;"><?= e($sub['group_name'] ?? '—') ?></div>
<?php if (!empty($sub['group_code'])): ?>
<div style="font-size:11px;color:#9CA3AF;"><?= e($sub['group_code']) ?></div>
<?php endif; ?>
</div>
<div>
<div style="font-size:12px;color:#6B7280;">الفترة</div>
<div style="font-weight:600;"><?= e($sub['period_start'] ?? '') ?> - <?= e($sub['period_end'] ?? '') ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;">المبلغ الأصلي</div>
<div style="font-weight:600;"><?= money((float) ($sub['amount'] ?? 0)) ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;">الخصم</div>
<div style="font-weight:600;"><?= money((float) ($sub['discount_amount'] ?? 0)) ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;">المبلغ النهائي</div>
<div style="font-weight:600;font-size:16px;color:#059669;"><?= money((float) ($sub['final_amount'] ?? 0)) ?></div>
</div>
<?php if (!empty($sub['paid_at'])): ?>
<div>
<div style="font-size:12px;color:#6B7280;">تاريخ الدفع</div>
<div style="font-weight:600;"><?= e($sub['paid_at']) ?></div>
</div>
<div>
<div style="font-size:12px;color:#6B7280;">المبلغ المدفوع</div>
<div style="font-weight:600;"><?= money((float) ($sub['paid_amount'] ?? 0)) ?></div>
</div>
<?php endif; ?>
<?php if (!empty($sub['exemption_reason'])): ?>
<div>
<div style="font-size:12px;color:#6B7280;">سبب الإعفاء</div>
<div style="font-weight:600;"><?= e($sub['exemption_reason']) ?></div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Actions -->
<?php if (in_array($sub['payment_status'] ?? '', ['unpaid', 'overdue', 'partial'], true)): ?>
<div style="display:flex;gap:15px;flex-wrap:wrap;">
<!-- Pay Button -->
<div class="card" style="flex:1;min-width:280px;padding:20px;">
<h4 style="margin:0 0 15px;font-size:15px;">تسجيل الدفع</h4>
<form method="POST" action="/sa/subscriptions/<?= (int) $sub['id'] ?>/pay">
<?= csrf_field() ?>
<p style="color:#6B7280;font-size:13px;margin-bottom:15px;">
سيتم تسجيل دفع المبلغ الكامل: <strong><?= money((float) ($sub['final_amount'] ?? 0)) ?></strong>
</p>
<button type="submit" class="btn btn-primary" onclick="return confirm('تأكيد تسجيل الدفع؟');">
<i data-lucide="check-circle" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> تأكيد الدفع
</button>
</form>
</div>
<!-- Exempt Button -->
<div class="card" style="flex:1;min-width:280px;padding:20px;">
<h4 style="margin:0 0 15px;font-size:15px;">إعفاء من الدفع</h4>
<form method="POST" action="/sa/subscriptions/<?= (int) $sub['id'] ?>/exempt">
<?= csrf_field() ?>
<div style="margin-bottom:12px;">
<label class="form-label" style="font-size:12px;">سبب الإعفاء</label>
<input type="text" name="exemption_reason" class="form-input" required placeholder="اذكر سبب الإعفاء...">
</div>
<button type="submit" class="btn btn-outline" onclick="return confirm('تأكيد الإعفاء؟');">
<i data-lucide="shield" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إعفاء
</button>
</form>
</div>
</div>
<?php endif; ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
});
</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>قائمة الانتظار<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Filters -->
<div class="card" style="margin-bottom:20px;padding:15px;">
<form method="GET" action="/sa/waitlist" style="display:flex;gap:10px;align-items:end;flex-wrap:wrap;">
<div style="min-width:180px;">
<label class="form-label" style="font-size:12px;">المجموعة</label>
<select name="group_id" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($groups as $g): ?>
<option value="<?= (int) $g['id'] ?>" <?= ($filters['group_id'] ?? '') === (string) $g['id'] ? 'selected' : '' ?>><?= e($g['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="min-width:150px;">
<label class="form-label" style="font-size:12px;">الحالة</label>
<select name="status" class="form-select">
<option value="">-- الكل --</option>
<?php foreach ($statusOptions as $key => $label): ?>
<option value="<?= e($key) ?>" <?= ($filters['status'] ?? '') === $key ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-outline"><i data-lucide="search" style="width:16px;height:16px;vertical-align:middle;"></i> بحث</button>
<a href="/sa/waitlist" class="btn btn-sm btn-outline" style="color:#6B7280;">مسح</a>
</form>
</div>
<!-- Waitlist Table -->
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>الترتيب</th>
<th>اللاعب</th>
<th>المجموعة</th>
<th>الحالة</th>
<th>تاريخ التسجيل</th>
<th>إجراءات</th>
</tr>
</thead>
<tbody>
<?php if (!empty($entries)): ?>
<?php foreach ($entries as $entry): ?>
<tr>
<td>
<span style="background:#F3F4F6;padding:4px 10px;border-radius:50%;font-weight:700;font-size:13px;">
<?= (int) ($entry['position'] ?? 0) ?>
</span>
</td>
<td>
<div style="font-weight:600;"><?= e($entry['player_name'] ?? '—') ?></div>
<?php if (!empty($entry['player_code'])): ?>
<div style="font-size:11px;color:#9CA3AF;"><?= e($entry['player_code']) ?></div>
<?php endif; ?>
</td>
<td><?= e($entry['group_name'] ?? '—') ?></td>
<td>
<?php
$wStatusColors = [
'waiting' => 'background:#FEF3C7;color:#92400E;',
'offered' => 'background:#DBEAFE;color:#1D4ED8;',
'accepted' => 'background:#ECFDF5;color:#059669;',
'expired' => 'background:#F3F4F6;color:#6B7280;',
'cancelled' => 'background:#FEE2E2;color:#DC2626;',
];
$wStyle = $wStatusColors[$entry['status'] ?? ''] ?? 'background:#F3F4F6;color:#374151;';
?>
<span style="<?= $wStyle ?>padding:3px 10px;border-radius:10px;font-size:12px;font-weight:600;">
<?= e($statusOptions[$entry['status'] ?? ''] ?? $entry['status'] ?? '') ?>
</span>
<?php if (!empty($entry['expires_at']) && $entry['status'] === 'offered'): ?>
<div style="font-size:10px;color:#9CA3AF;margin-top:2px;">ينتهي: <?= e($entry['expires_at']) ?></div>
<?php endif; ?>
</td>
<td style="font-size:12px;"><?= e($entry['created_at'] ?? '') ?></td>
<td>
<div style="display:flex;gap:6px;">
<?php if (($entry['status'] ?? '') === 'waiting'): ?>
<form method="POST" action="/sa/waitlist/<?= (int) $entry['id'] ?>/offer" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-sm btn-primary" style="font-size:12px;padding:4px 10px;" onclick="return confirm('تأكيد تقديم العرض؟');">
<i data-lucide="send" style="width:12px;height:12px;vertical-align:middle;margin-left:3px;"></i> عرض
</button>
</form>
<?php endif; ?>
<?php if (in_array($entry['status'] ?? '', ['waiting', 'offered'], true)): ?>
<form method="POST" action="/sa/waitlist/<?= (int) $entry['id'] ?>/cancel" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;color:#DC2626;border-color:#DC2626;" onclick="return confirm('تأكيد الإلغاء؟');">
<i data-lucide="x-circle" style="width:12px;height:12px;vertical-align:middle;margin-left:3px;"></i> إلغاء
</button>
</form>
<?php endif; ?>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="6" style="text-align:center;padding:40px;color:#6B7280;">
<i data-lucide="list" style="width:36px;height:36px;color:#D1D5DB;display:block;margin:0 auto 10px;"></i>
لا توجد سجلات في قائمة الانتظار
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if (isset($pagination) && ($pagination['last_page'] ?? 1) > 1): ?>
<div style="padding:15px;">
<?php $__template->include('Shared.Components.pagination', ['pagination' => $pagination, 'baseUrl' => '/sa/waitlist?' . http_build_query(array_filter($filters))]); ?>
</div>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') { lucide.createIcons(); }
});
</script>
<?php $__template->endSection(); ?>
<?php
declare(strict_types=1);
use App\Core\Registries\MenuRegistry;
use App\Core\Registries\PermissionRegistry;
MenuRegistry::register('sports_activity', [
'label_ar' => 'الأنشطة الرياضية',
'label_en' => 'Sports & Academy',
'icon' => 'activity',
'route' => '/sa',
'permission' => 'sa.dashboard',
'parent' => null,
'order' => 395,
'children' => [
['label_ar' => 'لوحة التحكم', 'label_en' => 'Dashboard', 'route' => '/sa', 'permission' => 'sa.dashboard', 'order' => 1],
['label_ar' => 'المراية', 'label_en' => 'Mirror', 'route' => '/sa/mirror', 'permission' => 'sa.mirror.view', 'order' => 2],
['label_ar' => 'الألعاب', 'label_en' => 'Disciplines', 'route' => '/sa/disciplines', 'permission' => 'sa.discipline.view', 'order' => 3],
['label_ar' => 'المرافق', 'label_en' => 'Facilities', 'route' => '/sa/facilities', 'permission' => 'sa.facility.view', 'order' => 4],
['label_ar' => 'المدربين', 'label_en' => 'Coaches', 'route' => '/sa/coaches', 'permission' => 'sa.coach.view', 'order' => 5],
['label_ar' => 'اللاعبين', 'label_en' => 'Players', 'route' => '/sa/players', 'permission' => 'sa.player.view', 'order' => 6],
['label_ar' => 'الأكاديميات', 'label_en' => 'Academies', 'route' => '/sa/academies', 'permission' => 'sa.academy.view', 'order' => 7],
['label_ar' => 'البرامج', 'label_en' => 'Programs', 'route' => '/sa/programs', 'permission' => 'sa.program.view', 'order' => 8],
['label_ar' => 'المجموعات', 'label_en' => 'Groups', 'route' => '/sa/groups', 'permission' => 'sa.group.view', 'order' => 9],
['label_ar' => 'الجدول', 'label_en' => 'Schedule', 'route' => '/sa/schedule', 'permission' => 'sa.schedule.view', 'order' => 10],
['label_ar' => 'الحجوزات', 'label_en' => 'Bookings', 'route' => '/sa/bookings', 'permission' => 'sa.booking.view', 'order' => 11],
['label_ar' => 'التسعير', 'label_en' => 'Pricing', 'route' => '/sa/pricing', 'permission' => 'sa.pricing.view', 'order' => 12],
['label_ar' => 'الاشتراكات', 'label_en' => 'Subscriptions', 'route' => '/sa/subscriptions', 'permission' => 'sa.subscription.view', 'order' => 13],
['label_ar' => 'الحضور', 'label_en' => 'Attendance', 'route' => '/sa/attendance', 'permission' => 'sa.attendance.view', 'order' => 14],
['label_ar' => 'قائمة الانتظار', 'label_en' => 'Waitlist', 'route' => '/sa/waitlist', 'permission' => 'sa.waitlist.view', 'order' => 15],
],
]);
PermissionRegistry::register('sports_activity', [
'sa.dashboard' => ['ar' => 'لوحة تحكم الأنشطة الرياضية', 'en' => 'Sports Activity Dashboard'],
'sa.discipline.view' => ['ar' => 'عرض الألعاب الرياضية', 'en' => 'View Disciplines'],
'sa.discipline.manage' => ['ar' => 'إدارة الألعاب الرياضية', 'en' => 'Manage Disciplines'],
'sa.facility.view' => ['ar' => 'عرض المرافق', 'en' => 'View Facilities'],
'sa.facility.manage' => ['ar' => 'إدارة المرافق', 'en' => 'Manage Facilities'],
'sa.coach.view' => ['ar' => 'عرض المدربين', 'en' => 'View Coaches'],
'sa.coach.manage' => ['ar' => 'إدارة المدربين', 'en' => 'Manage Coaches'],
'sa.player.view' => ['ar' => 'عرض اللاعبين', 'en' => 'View Players'],
'sa.player.manage' => ['ar' => 'إدارة اللاعبين', 'en' => 'Manage Players'],
'sa.medical.approve' => ['ar' => 'اعتماد الشهادات الطبية', 'en' => 'Approve Medical Certificates'],
'sa.academy.view' => ['ar' => 'عرض الأكاديميات', 'en' => 'View Academies'],
'sa.academy.manage' => ['ar' => 'إدارة الأكاديميات', 'en' => 'Manage Academies'],
'sa.contract.view' => ['ar' => 'عرض العقود', 'en' => 'View Contracts'],
'sa.contract.manage' => ['ar' => 'إدارة العقود', 'en' => 'Manage Contracts'],
'sa.contract.approve' => ['ar' => 'اعتماد العقود', 'en' => 'Approve Contracts'],
'sa.program.view' => ['ar' => 'عرض البرامج', 'en' => 'View Programs'],
'sa.program.manage' => ['ar' => 'إدارة البرامج', 'en' => 'Manage Programs'],
'sa.group.view' => ['ar' => 'عرض المجموعات', 'en' => 'View Groups'],
'sa.group.manage' => ['ar' => 'إدارة المجموعات', 'en' => 'Manage Groups'],
'sa.group.enroll' => ['ar' => 'تسجيل لاعب في مجموعة', 'en' => 'Enroll Player in Group'],
'sa.schedule.view' => ['ar' => 'عرض الجدول', 'en' => 'View Schedule'],
'sa.schedule.manage' => ['ar' => 'إدارة الجدول', 'en' => 'Manage Schedule'],
'sa.booking.view' => ['ar' => 'عرض الحجوزات', 'en' => 'View Bookings'],
'sa.booking.create' => ['ar' => 'إنشاء حجز', 'en' => 'Create Booking'],
'sa.booking.manage' => ['ar' => 'إدارة الحجوزات', 'en' => 'Manage Bookings'],
'sa.pricing.view' => ['ar' => 'عرض التسعير', 'en' => 'View Pricing'],
'sa.pricing.manage' => ['ar' => 'إدارة التسعير', 'en' => 'Manage Pricing'],
'sa.subscription.view' => ['ar' => 'عرض الاشتراكات', 'en' => 'View Subscriptions'],
'sa.subscription.generate' => ['ar' => 'توليد الاشتراكات', 'en' => 'Generate Subscriptions'],
'sa.subscription.collect' => ['ar' => 'تحصيل الاشتراكات', 'en' => 'Collect Subscription Payments'],
'sa.subscription.exempt' => ['ar' => 'إعفاء من الاشتراك', 'en' => 'Exempt from Subscription'],
'sa.attendance.view' => ['ar' => 'عرض الحضور', 'en' => 'View Attendance'],
'sa.attendance.manage' => ['ar' => 'تسجيل الحضور', 'en' => 'Record Attendance'],
'sa.mirror.view' => ['ar' => 'عرض المراية', 'en' => 'View Mirror Display'],
'sa.waitlist.view' => ['ar' => 'عرض قائمة الانتظار', 'en' => 'View Waitlist'],
'sa.waitlist.manage' => ['ar' => 'إدارة قائمة الانتظار', 'en' => 'Manage Waitlist'],
]);
<?php
declare(strict_types=1);
return [
'up' => "CREATE TABLE `sa_disciplines` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`code` VARCHAR(30) NOT NULL,
`name_ar` VARCHAR(200) NOT NULL,
`name_en` VARCHAR(200) NULL,
`category` VARCHAR(30) NOT NULL COMMENT 'individual, team, racket, aquatic, combat, leisure',
`icon` VARCHAR(50) NULL,
`description_ar` TEXT NULL,
`config_json` JSON NULL COMMENT 'age_groups, skill_levels, special_requirements',
`sort_order` INT UNSIGNED NOT NULL DEFAULT 0,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL,
`archived_by` BIGINT UNSIGNED NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_sa_disc_code` (`code`),
INDEX `idx_sa_disc_category` (`category`),
INDEX `idx_sa_disc_active` (`is_active`, `is_archived`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
'down' => "DROP TABLE IF EXISTS sa_disciplines;"
];
<?php
declare(strict_types=1);
return [
'up' => "CREATE TABLE `sa_facilities` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`code` VARCHAR(30) NOT NULL,
`name_ar` VARCHAR(300) NOT NULL,
`name_en` VARCHAR(300) NULL,
`facility_type` VARCHAR(30) NOT NULL COMMENT 'pool, court, pitch, gym, track, multipurpose',
`discipline_id` BIGINT UNSIGNED NULL COMMENT 'primary discipline, NULL for multipurpose',
`location_description` VARCHAR(500) NULL,
`operating_hours_json` JSON NOT NULL COMMENT '{\"start\":\"06:00\",\"end\":\"22:00\",\"slot_minutes\":60}',
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL,
`archived_by` BIGINT UNSIGNED NULL,
`branch_id` BIGINT UNSIGNED NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_sa_fac_code` (`code`),
INDEX `idx_sa_fac_type` (`facility_type`),
INDEX `idx_sa_fac_disc` (`discipline_id`),
INDEX `idx_sa_fac_active` (`is_active`, `is_archived`),
CONSTRAINT `fk_sa_fac_disc` FOREIGN KEY (`discipline_id`) REFERENCES `sa_disciplines`(`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
'down' => "DROP TABLE IF EXISTS sa_facilities;"
];
<?php
declare(strict_types=1);
return [
'up' => "CREATE TABLE `sa_facility_units` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`facility_id` BIGINT UNSIGNED NOT NULL,
`code` VARCHAR(30) NOT NULL,
`name_ar` VARCHAR(200) NOT NULL,
`name_en` VARCHAR(200) NULL,
`unit_type` VARCHAR(30) NOT NULL COMMENT 'lane, court, pitch, ring, mat, room',
`booking_mode` VARCHAR(20) NOT NULL DEFAULT 'exclusive' COMMENT 'exclusive, shared',
`max_capacity` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'shared: max concurrent; exclusive: max participants',
`sort_order` INT UNSIGNED NOT NULL DEFAULT 0,
`dimensions_json` JSON NULL COMMENT '{\"length\":50,\"width\":2.5}',
`config_json` JSON NULL COMMENT 'unit-specific config',
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_sa_fu_code` (`facility_id`, `code`),
INDEX `idx_sa_fu_facility` (`facility_id`),
INDEX `idx_sa_fu_mode` (`booking_mode`),
CONSTRAINT `fk_sa_fu_facility` FOREIGN KEY (`facility_id`) REFERENCES `sa_facilities`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
'down' => "DROP TABLE IF EXISTS sa_facility_units;"
];
<?php
declare(strict_types=1);
return [
'up' => "CREATE TABLE `sa_time_brackets` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`facility_id` BIGINT UNSIGNED NOT NULL,
`name_ar` VARCHAR(100) NOT NULL,
`name_en` VARCHAR(100) NULL,
`bracket_type` VARCHAR(20) NOT NULL COMMENT 'peak, off_peak, weekend, holiday',
`start_time` TIME NOT NULL,
`end_time` TIME NOT NULL,
`days_of_week_json` JSON NOT NULL COMMENT '[0,1,2,3,4,5,6]',
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_sa_tb_facility` (`facility_id`),
INDEX `idx_sa_tb_type` (`bracket_type`),
CONSTRAINT `fk_sa_tb_facility` FOREIGN KEY (`facility_id`) REFERENCES `sa_facilities`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
'down' => "DROP TABLE IF EXISTS sa_time_brackets;"
];
<?php
declare(strict_types=1);
return [
'up' => "CREATE TABLE `sa_pricing_rules` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`facility_unit_id` BIGINT UNSIGNED NOT NULL,
`time_bracket_id` BIGINT UNSIGNED NOT NULL,
`group_size_min` INT UNSIGNED NOT NULL DEFAULT 1,
`group_size_max` INT UNSIGNED NOT NULL DEFAULT 1,
`price_per_person_member` DECIMAL(10,2) NOT NULL,
`price_per_person_nonmember` DECIMAL(10,2) NOT NULL,
`effective_from` DATE NOT NULL,
`effective_to` DATE NULL,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
INDEX `idx_sa_pr_unit` (`facility_unit_id`),
INDEX `idx_sa_pr_bracket` (`time_bracket_id`),
INDEX `idx_sa_pr_active` (`is_active`, `effective_from`, `effective_to`),
UNIQUE KEY `uq_sa_pr_rule` (`facility_unit_id`, `time_bracket_id`, `group_size_min`, `group_size_max`, `effective_from`),
CONSTRAINT `fk_sa_pr_unit` FOREIGN KEY (`facility_unit_id`) REFERENCES `sa_facility_units`(`id`) ON DELETE CASCADE,
CONSTRAINT `fk_sa_pr_bracket` FOREIGN KEY (`time_bracket_id`) REFERENCES `sa_time_brackets`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
'down' => "DROP TABLE IF EXISTS `sa_pricing_rules`;"
];
<?php
declare(strict_types=1);
return [
'up' => "CREATE TABLE `sa_coaches` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`code` VARCHAR(30) NOT NULL,
`full_name_ar` VARCHAR(300) NOT NULL,
`full_name_en` VARCHAR(300) NULL,
`national_id` VARCHAR(20) NULL,
`phone` VARCHAR(30) NULL,
`email` VARCHAR(200) NULL,
`date_of_birth` DATE NULL,
`gender` VARCHAR(10) NULL,
`photo_path` VARCHAR(500) NULL,
`bio_ar` TEXT NULL,
`certifications_json` JSON NULL,
`employment_type` VARCHAR(20) NOT NULL DEFAULT 'contract' COMMENT 'staff, contract, freelance',
`employee_id` BIGINT UNSIGNED NULL,
`payment_model` VARCHAR(20) NOT NULL DEFAULT 'per_session' COMMENT 'per_session, per_player, monthly_fixed, hybrid',
`hourly_rate` DECIMAL(10,2) NULL,
`session_rate` DECIMAL(10,2) NULL,
`monthly_rate` DECIMAL(10,2) NULL,
`max_groups` INT UNSIGNED NOT NULL DEFAULT 10,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL,
`archived_by` BIGINT UNSIGNED NULL,
`branch_id` BIGINT UNSIGNED NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_sa_coach_code` (`code`),
INDEX `idx_sa_coach_nid` (`national_id`),
INDEX `idx_sa_coach_active` (`is_active`, `is_archived`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
'down' => "DROP TABLE IF EXISTS `sa_coaches`;"
];
<?php
declare(strict_types=1);
return [
'up' => "CREATE TABLE `sa_coach_disciplines` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`coach_id` BIGINT UNSIGNED NOT NULL,
`discipline_id` BIGINT UNSIGNED NOT NULL,
`specialization_level` VARCHAR(20) NOT NULL DEFAULT 'primary' COMMENT 'primary, secondary',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY `uq_sa_cd` (`coach_id`, `discipline_id`),
CONSTRAINT `fk_sa_cd_coach` FOREIGN KEY (`coach_id`) REFERENCES `sa_coaches`(`id`) ON DELETE CASCADE,
CONSTRAINT `fk_sa_cd_disc` FOREIGN KEY (`discipline_id`) REFERENCES `sa_disciplines`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
'down' => "DROP TABLE IF EXISTS `sa_coach_disciplines`;"
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_players` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`player_type` VARCHAR(20) NOT NULL COMMENT 'member, non_member',
`member_id` BIGINT UNSIGNED NULL,
`registration_serial` VARCHAR(50) NULL,
`full_name_ar` VARCHAR(300) NOT NULL,
`full_name_en` VARCHAR(300) NULL,
`national_id` VARCHAR(20) NULL,
`date_of_birth` DATE NULL,
`gender` VARCHAR(10) NULL,
`phone` VARCHAR(30) NULL,
`email` VARCHAR(200) NULL,
`guardian_name` VARCHAR(300) NULL,
`guardian_phone` VARCHAR(30) NULL,
`guardian_national_id` VARCHAR(20) NULL,
`guardian_relationship` VARCHAR(50) NULL,
`photo_path` VARCHAR(500) NULL,
`medical_status` VARCHAR(30) NOT NULL DEFAULT 'pending' COMMENT 'pending, fit, conditional, unfit, expired',
`medical_expiry_date` DATE NULL,
`card_status` VARCHAR(20) NOT NULL DEFAULT 'inactive' COMMENT 'inactive, active, suspended, revoked',
`registration_fee_paid` TINYINT(1) NOT NULL DEFAULT 0,
`notes` TEXT NULL,
`branch_id` BIGINT UNSIGNED NULL,
`is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL,
`archived_by` BIGINT UNSIGNED NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_sa_player_serial` (`registration_serial`),
INDEX `idx_sa_player_type` (`player_type`),
INDEX `idx_sa_player_member` (`member_id`),
INDEX `idx_sa_player_nid` (`national_id`),
INDEX `idx_sa_player_medical` (`medical_status`),
INDEX `idx_sa_player_card` (`card_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_players`",
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_player_documents` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`player_id` BIGINT UNSIGNED NOT NULL,
`document_type` VARCHAR(30) NOT NULL COMMENT 'medical_cert, birth_cert, photo, national_id, other',
`file_path` VARCHAR(500) NOT NULL,
`file_name` VARCHAR(255) NOT NULL,
`exam_date` DATE NULL,
`expiry_date` DATE NULL,
`doctor_name` VARCHAR(200) NULL,
`clinic_name` VARCHAR(200) NULL,
`approval_status` VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending, approved, rejected, expired',
`approved_by` BIGINT UNSIGNED NULL,
`approved_at` TIMESTAMP NULL,
`rejection_reason` TEXT NULL,
`notes` TEXT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
INDEX `idx_sa_pd_player` (`player_id`),
INDEX `idx_sa_pd_type` (`document_type`),
INDEX `idx_sa_pd_approval` (`approval_status`),
INDEX `idx_sa_pd_expiry` (`expiry_date`),
CONSTRAINT `fk_sa_pd_player` FOREIGN KEY (`player_id`) REFERENCES `sa_players`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_player_documents`",
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_academies` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`code` VARCHAR(30) NOT NULL,
`name_ar` VARCHAR(300) NOT NULL,
`name_en` VARCHAR(300) NULL,
`discipline_id` BIGINT UNSIGNED NOT NULL,
`academy_type` VARCHAR(30) NOT NULL DEFAULT 'external' COMMENT 'internal, external',
`contact_person` VARCHAR(200) NULL,
`contact_phone` VARCHAR(30) NULL,
`contact_email` VARCHAR(200) NULL,
`description_ar` TEXT NULL,
`logo_path` VARCHAR(500) NULL,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL,
`archived_by` BIGINT UNSIGNED NULL,
`branch_id` BIGINT UNSIGNED NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_sa_acad_code` (`code`),
INDEX `idx_sa_acad_disc` (`discipline_id`),
INDEX `idx_sa_acad_active` (`is_active`, `is_archived`),
CONSTRAINT `fk_sa_acad_disc` FOREIGN KEY (`discipline_id`) REFERENCES `sa_disciplines`(`id`) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_academies`",
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_academy_contracts` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`contract_number` VARCHAR(50) NOT NULL,
`academy_id` BIGINT UNSIGNED NOT NULL,
`contract_type` VARCHAR(30) NOT NULL DEFAULT 'revenue_share' COMMENT 'revenue_share, fixed_rent, hybrid',
`start_date` DATE NOT NULL,
`end_date` DATE NOT NULL,
`club_commission_pct` DECIMAL(5,2) NOT NULL DEFAULT 0.00,
`academy_share_pct` DECIMAL(5,2) NOT NULL DEFAULT 0.00,
`fixed_monthly_rent` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`minimum_revenue_guarantee` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`deposit_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`deposit_status` VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending, paid, returned, forfeited',
`contract_pdf_path` VARCHAR(500) NOT NULL,
`status` VARCHAR(20) NOT NULL DEFAULT 'draft' COMMENT 'draft, pending_approval, active, suspended, expired, terminated',
`approved_by` BIGINT UNSIGNED NULL,
`approved_at` TIMESTAMP NULL,
`terminated_by` BIGINT UNSIGNED NULL,
`terminated_at` TIMESTAMP NULL,
`termination_reason` TEXT NULL,
`terms_json` JSON NULL,
`notes` TEXT NULL,
`is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL,
`archived_by` BIGINT UNSIGNED NULL,
`branch_id` BIGINT UNSIGNED NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_sa_ac_number` (`contract_number`),
INDEX `idx_sa_ac_academy` (`academy_id`),
INDEX `idx_sa_ac_status` (`status`),
INDEX `idx_sa_ac_dates` (`start_date`, `end_date`),
CONSTRAINT `fk_sa_ac_academy` FOREIGN KEY (`academy_id`) REFERENCES `sa_academies`(`id`) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_academy_contracts`",
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_programs` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`code` VARCHAR(30) NOT NULL,
`name_ar` VARCHAR(300) NOT NULL,
`name_en` VARCHAR(300) NULL,
`discipline_id` BIGINT UNSIGNED NOT NULL,
`academy_id` BIGINT UNSIGNED NULL,
`program_type` VARCHAR(30) NOT NULL DEFAULT 'training' COMMENT 'training, recreational, competitive, rehabilitation',
`age_from` INT UNSIGNED NULL,
`age_to` INT UNSIGNED NULL,
`gender_restriction` VARCHAR(10) NULL COMMENT 'male, female, NULL=mixed',
`skill_level` VARCHAR(30) NULL COMMENT 'beginner, intermediate, advanced, elite',
`description_ar` TEXT NULL,
`session_duration_minutes` INT UNSIGNED NOT NULL DEFAULT 60,
`sessions_per_week` INT UNSIGNED NOT NULL DEFAULT 3,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL,
`archived_by` BIGINT UNSIGNED NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_sa_prog_code` (`code`),
INDEX `idx_sa_prog_disc` (`discipline_id`),
INDEX `idx_sa_prog_academy` (`academy_id`),
INDEX `idx_sa_prog_active` (`is_active`, `is_archived`),
CONSTRAINT `fk_sa_prog_disc` FOREIGN KEY (`discipline_id`) REFERENCES `sa_disciplines`(`id`) ON DELETE RESTRICT,
CONSTRAINT `fk_sa_prog_academy` FOREIGN KEY (`academy_id`) REFERENCES `sa_academies`(`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_programs`",
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_groups` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`code` VARCHAR(30) NOT NULL,
`name_ar` VARCHAR(300) NOT NULL,
`name_en` VARCHAR(300) NULL,
`program_id` BIGINT UNSIGNED NOT NULL,
`coach_id` BIGINT UNSIGNED NOT NULL,
`min_capacity` INT UNSIGNED NOT NULL DEFAULT 1,
`max_capacity` INT UNSIGNED NOT NULL DEFAULT 20,
`current_count` INT UNSIGNED NOT NULL DEFAULT 0,
`is_full` TINYINT(1) NOT NULL DEFAULT 0,
`monthly_fee_member` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`monthly_fee_nonmember` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`season_start` DATE NULL,
`season_end` DATE NULL,
`status` VARCHAR(20) NOT NULL DEFAULT 'active' COMMENT 'active, paused, completed, cancelled',
`is_archived` TINYINT(1) NOT NULL DEFAULT 0,
`archived_at` TIMESTAMP NULL,
`archived_by` BIGINT UNSIGNED NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_sa_grp_code` (`code`),
INDEX `idx_sa_grp_program` (`program_id`),
INDEX `idx_sa_grp_coach` (`coach_id`),
INDEX `idx_sa_grp_status` (`status`),
CONSTRAINT `fk_sa_grp_program` FOREIGN KEY (`program_id`) REFERENCES `sa_programs`(`id`) ON DELETE RESTRICT,
CONSTRAINT `fk_sa_grp_coach` FOREIGN KEY (`coach_id`) REFERENCES `sa_coaches`(`id`) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_groups`",
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_group_schedule` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`group_id` BIGINT UNSIGNED NOT NULL,
`facility_unit_id` BIGINT UNSIGNED NOT NULL,
`day_of_week` TINYINT UNSIGNED NOT NULL COMMENT '0=Sunday, 6=Saturday',
`start_time` TIME NOT NULL,
`end_time` TIME NOT NULL,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_sa_gs_group` (`group_id`),
INDEX `idx_sa_gs_unit` (`facility_unit_id`),
INDEX `idx_sa_gs_day` (`day_of_week`, `start_time`),
UNIQUE KEY `uq_sa_gs_slot` (`group_id`, `facility_unit_id`, `day_of_week`, `start_time`),
CONSTRAINT `fk_sa_gs_group` FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE CASCADE,
CONSTRAINT `fk_sa_gs_unit` FOREIGN KEY (`facility_unit_id`) REFERENCES `sa_facility_units`(`id`) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_group_schedule`",
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_group_players` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`group_id` BIGINT UNSIGNED NOT NULL,
`player_id` BIGINT UNSIGNED NOT NULL,
`enrolled_at` DATE NOT NULL,
`left_at` DATE NULL,
`status` VARCHAR(20) NOT NULL DEFAULT 'active' COMMENT 'active, paused, transferred, withdrawn',
`notes` TEXT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
INDEX `idx_sa_gp_group` (`group_id`),
INDEX `idx_sa_gp_player` (`player_id`),
INDEX `idx_sa_gp_status` (`status`),
CONSTRAINT `fk_sa_gp_group` FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE CASCADE,
CONSTRAINT `fk_sa_gp_player` FOREIGN KEY (`player_id`) REFERENCES `sa_players`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_group_players`",
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_bookings` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`booking_number` VARCHAR(50) NOT NULL,
`facility_unit_id` BIGINT UNSIGNED NOT NULL,
`booking_type` VARCHAR(20) NOT NULL COMMENT 'hourly, training, maintenance, blocked',
`booking_date` DATE NOT NULL,
`start_time` TIME NOT NULL,
`end_time` TIME NOT NULL,
`group_id` BIGINT UNSIGNED NULL,
`coach_id` BIGINT UNSIGNED NULL,
`booker_type` VARCHAR(20) NULL COMMENT 'player, member, guest, academy',
`booker_id` BIGINT UNSIGNED NULL,
`booker_name` VARCHAR(300) NULL,
`participant_count` INT UNSIGNED NOT NULL DEFAULT 1,
`spots_reserved` INT UNSIGNED NOT NULL DEFAULT 1,
`total_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`payment_status` VARCHAR(20) NOT NULL DEFAULT 'unpaid' COMMENT 'unpaid, paid, partial, refunded',
`status` VARCHAR(20) NOT NULL DEFAULT 'confirmed' COMMENT 'pending, confirmed, checked_in, completed, cancelled, no_show',
`is_recurring` TINYINT(1) NOT NULL DEFAULT 0,
`recurrence_pattern_json` JSON NULL,
`parent_booking_id` BIGINT UNSIGNED NULL,
`cancellation_reason` TEXT NULL,
`cancelled_by` BIGINT UNSIGNED NULL,
`cancelled_at` TIMESTAMP NULL,
`notes` TEXT NULL,
`branch_id` BIGINT UNSIGNED NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
`updated_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_sa_bk_number` (`booking_number`),
INDEX `idx_sa_bk_unit_date` (`facility_unit_id`, `booking_date`, `start_time`),
INDEX `idx_sa_bk_type` (`booking_type`),
INDEX `idx_sa_bk_status` (`status`),
INDEX `idx_sa_bk_group` (`group_id`),
INDEX `idx_sa_bk_coach` (`coach_id`),
INDEX `idx_sa_bk_date` (`booking_date`),
INDEX `idx_sa_bk_parent` (`parent_booking_id`),
CONSTRAINT `fk_sa_bk_unit` FOREIGN KEY (`facility_unit_id`) REFERENCES `sa_facility_units`(`id`) ON DELETE RESTRICT,
CONSTRAINT `fk_sa_bk_group` FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE SET NULL,
CONSTRAINT `fk_sa_bk_coach` FOREIGN KEY (`coach_id`) REFERENCES `sa_coaches`(`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_bookings`",
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_booking_participants` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`booking_id` BIGINT UNSIGNED NOT NULL,
`player_id` BIGINT UNSIGNED NULL,
`participant_name` VARCHAR(300) NULL,
`is_member` TINYINT(1) NOT NULL DEFAULT 0,
`price_charged` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`checked_in_at` TIMESTAMP NULL,
`checked_out_at` TIMESTAMP NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_sa_bp_booking` (`booking_id`),
INDEX `idx_sa_bp_player` (`player_id`),
CONSTRAINT `fk_sa_bp_booking` FOREIGN KEY (`booking_id`) REFERENCES `sa_bookings`(`id`) ON DELETE CASCADE,
CONSTRAINT `fk_sa_bp_player` FOREIGN KEY (`player_id`) REFERENCES `sa_players`(`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_booking_participants`",
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_subscriptions` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`subscription_number` VARCHAR(50) NOT NULL,
`player_id` BIGINT UNSIGNED NOT NULL,
`group_id` BIGINT UNSIGNED NOT NULL,
`period_start` DATE NOT NULL,
`period_end` DATE NOT NULL,
`amount` DECIMAL(10,2) NOT NULL,
`discount_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`final_amount` DECIMAL(10,2) NOT NULL,
`payment_status` VARCHAR(20) NOT NULL DEFAULT 'unpaid' COMMENT 'unpaid, paid, partial, exempt, overdue',
`paid_at` TIMESTAMP NULL,
`paid_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`payment_reference` VARCHAR(100) NULL,
`exemption_reason` TEXT NULL,
`exempted_by` BIGINT UNSIGNED NULL,
`notes` TEXT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
UNIQUE KEY `uq_sa_sub_number` (`subscription_number`),
INDEX `idx_sa_sub_player` (`player_id`),
INDEX `idx_sa_sub_group` (`group_id`),
INDEX `idx_sa_sub_period` (`period_start`, `period_end`),
INDEX `idx_sa_sub_status` (`payment_status`),
CONSTRAINT `fk_sa_sub_player` FOREIGN KEY (`player_id`) REFERENCES `sa_players`(`id`) ON DELETE RESTRICT,
CONSTRAINT `fk_sa_sub_group` FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_subscriptions`",
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_attendance` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`booking_id` BIGINT UNSIGNED NOT NULL,
`player_id` BIGINT UNSIGNED NOT NULL,
`attendance_date` DATE NOT NULL,
`status` VARCHAR(20) NOT NULL DEFAULT 'present' COMMENT 'present, absent, late, excused',
`check_in_time` TIME NULL,
`check_out_time` TIME NULL,
`recorded_by` BIGINT UNSIGNED NULL,
`notes` TEXT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY `uq_sa_att_record` (`booking_id`, `player_id`, `attendance_date`),
INDEX `idx_sa_att_player` (`player_id`),
INDEX `idx_sa_att_date` (`attendance_date`),
INDEX `idx_sa_att_status` (`status`),
CONSTRAINT `fk_sa_att_booking` FOREIGN KEY (`booking_id`) REFERENCES `sa_bookings`(`id`) ON DELETE CASCADE,
CONSTRAINT `fk_sa_att_player` FOREIGN KEY (`player_id`) REFERENCES `sa_players`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_attendance`",
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_blackout_dates` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`facility_id` BIGINT UNSIGNED NOT NULL,
`facility_unit_id` BIGINT UNSIGNED NULL,
`blackout_date` DATE NOT NULL,
`start_time` TIME NULL,
`end_time` TIME NULL,
`reason` VARCHAR(500) NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
INDEX `idx_sa_bo_facility` (`facility_id`, `blackout_date`),
INDEX `idx_sa_bo_unit` (`facility_unit_id`, `blackout_date`),
CONSTRAINT `fk_sa_bo_facility` FOREIGN KEY (`facility_id`) REFERENCES `sa_facilities`(`id`) ON DELETE CASCADE,
CONSTRAINT `fk_sa_bo_unit` FOREIGN KEY (`facility_unit_id`) REFERENCES `sa_facility_units`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_blackout_dates`",
];
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE `sa_waitlist` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`player_id` BIGINT UNSIGNED NOT NULL,
`group_id` BIGINT UNSIGNED NOT NULL,
`position` INT UNSIGNED NOT NULL DEFAULT 0,
`status` VARCHAR(20) NOT NULL DEFAULT 'waiting' COMMENT 'waiting, offered, accepted, expired, cancelled',
`offered_at` TIMESTAMP NULL,
`expires_at` TIMESTAMP NULL,
`notes` TEXT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created_by` BIGINT UNSIGNED NULL,
INDEX `idx_sa_wl_player` (`player_id`),
INDEX `idx_sa_wl_group` (`group_id`),
INDEX `idx_sa_wl_status` (`status`, `position`),
CONSTRAINT `fk_sa_wl_player` FOREIGN KEY (`player_id`) REFERENCES `sa_players`(`id`) ON DELETE CASCADE,
CONSTRAINT `fk_sa_wl_group` FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
",
'down' => "DROP TABLE IF EXISTS `sa_waitlist`",
];
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