Commit 2aaa27a2 authored by Mahmoud Aglan's avatar Mahmoud Aglan

searches and filters sa

parent a5018bba
......@@ -20,14 +20,39 @@ class PlayerSearchApiController extends Controller
$db = App::getInstance()->db();
$like = '%' . $q . '%';
$disciplineId = (int) $request->get('discipline_id', 0);
$groupId = (int) $request->get('group_id', 0);
$excludeGroup = (int) $request->get('exclude_group', 0);
$joins = '';
$where = 'p.is_archived = 0 AND (p.full_name_ar LIKE ? OR p.registration_serial LIKE ? OR p.national_id LIKE ? OR p.phone LIKE ?)';
$params = [$like, $like, $like, $like];
if ($disciplineId > 0) {
$joins .= ' INNER JOIN sa_group_players gp_d ON gp_d.player_id = p.id AND gp_d.status = "active"';
$joins .= ' INNER JOIN sa_groups g_d ON g_d.id = gp_d.group_id AND g_d.is_archived = 0';
$joins .= ' INNER JOIN sa_programs pr_d ON pr_d.id = g_d.program_id AND pr_d.discipline_id = ?';
$params[] = $disciplineId;
}
if ($groupId > 0) {
$joins .= ' INNER JOIN sa_group_players gp_g ON gp_g.player_id = p.id AND gp_g.group_id = ? AND gp_g.status = "active"';
$params[] = $groupId;
}
if ($excludeGroup > 0) {
$where .= ' AND p.id NOT IN (SELECT player_id FROM sa_group_players WHERE group_id = ? AND status IN ("active","pending_payment"))';
$params[] = $excludeGroup;
}
$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
"SELECT DISTINCT p.id, p.full_name_ar, p.registration_serial, p.national_id, p.player_type, p.medical_status
FROM sa_players p
{$joins}
WHERE {$where}
ORDER BY p.full_name_ar
LIMIT 20",
[$like, $like, $like, $like]
$params
);
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;
class SmartFilterApiController extends Controller
{
public function groups(Request $request): Response
{
$db = App::getInstance()->db();
$q = trim((string) $request->get('q', ''));
$disciplineId = (int) $request->get('discipline_id', 0);
$programId = (int) $request->get('program_id', 0);
$coachId = (int) $request->get('coach_id', 0);
$activeOnly = (int) $request->get('active_only', 1);
$where = ['g.is_archived = 0'];
$params = [];
if ($activeOnly) {
$where[] = "g.status = 'active'";
}
if ($q !== '' && strlen($q) >= 2) {
$where[] = '(g.name_ar LIKE ? OR g.code LIKE ?)';
$params[] = "%{$q}%";
$params[] = "%{$q}%";
}
if ($disciplineId > 0) {
$where[] = 'p.discipline_id = ?';
$params[] = $disciplineId;
}
if ($programId > 0) {
$where[] = 'g.program_id = ?';
$params[] = $programId;
}
if ($coachId > 0) {
$where[] = 'g.coach_id = ?';
$params[] = $coachId;
}
$whereSql = implode(' AND ', $where);
$results = $db->select(
"SELECT g.id, g.code, g.name_ar, g.current_count, g.max_capacity, g.is_full,
c.full_name_ar as coach_name, p.name_ar as program_name, d.name_ar as discipline_name
FROM sa_groups g
LEFT JOIN sa_programs p ON p.id = g.program_id
LEFT JOIN sa_disciplines d ON d.id = p.discipline_id
LEFT JOIN sa_coaches c ON c.id = g.coach_id
WHERE {$whereSql}
ORDER BY g.name_ar ASC
LIMIT 30",
$params
);
return $this->json(['results' => $results]);
}
public function coaches(Request $request): Response
{
$db = App::getInstance()->db();
$q = trim((string) $request->get('q', ''));
$disciplineId = (int) $request->get('discipline_id', 0);
$where = ['c.is_archived = 0', 'c.is_active = 1'];
$params = [];
$joins = '';
if ($q !== '' && strlen($q) >= 2) {
$where[] = '(c.full_name_ar LIKE ? OR c.code LIKE ?)';
$params[] = "%{$q}%";
$params[] = "%{$q}%";
}
if ($disciplineId > 0) {
$joins = 'INNER JOIN sa_coach_disciplines cd ON cd.coach_id = c.id AND cd.discipline_id = ?';
$params[] = $disciplineId;
}
$whereSql = implode(' AND ', $where);
$results = $db->select(
"SELECT DISTINCT c.id, c.code, c.full_name_ar, c.phone
FROM sa_coaches c
{$joins}
WHERE {$whereSql}
ORDER BY c.full_name_ar ASC
LIMIT 20",
$params
);
return $this->json(['results' => $results]);
}
public function disciplines(Request $request): Response
{
$db = App::getInstance()->db();
$results = $db->select(
"SELECT id, code, name_ar, icon
FROM sa_disciplines
WHERE is_active = 1 AND is_archived = 0
ORDER BY sort_order ASC, name_ar ASC",
[]
);
return $this->json(['results' => $results]);
}
}
......@@ -8,6 +8,7 @@ use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\Pagination;
use App\Modules\SportsActivity\Models\Discipline;
class AttendanceController extends Controller
{
......@@ -19,6 +20,22 @@ class AttendanceController extends Controller
$db = App::getInstance()->db();
$today = date('Y-m-d');
$disciplineId = trim((string) $request->get('discipline_id', ''));
$coachId = trim((string) $request->get('coach_id', ''));
$where = "b.booking_type = 'training' AND b.booking_date = ? AND b.status NOT IN ('cancelled', 'no_show')";
$params = [$today];
if ($disciplineId !== '') {
$where .= " AND pr.discipline_id = ?";
$params[] = (int) $disciplineId;
}
if ($coachId !== '') {
$where .= " AND b.coach_id = ?";
$params[] = (int) $coachId;
}
$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
......@@ -26,17 +43,22 @@ class AttendanceController extends Controller
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_programs pr ON pr.id = g.program_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')
WHERE {$where}
ORDER BY b.start_time ASC",
[$today]
$params
);
$disciplines = Discipline::getActive();
$coaches = $db->select("SELECT id, full_name_ar as name_ar FROM sa_coaches WHERE is_archived = 0 AND is_active = 1 ORDER BY full_name_ar", []);
return $this->view('SportsActivity.Views.attendance.index', [
'bookings' => $bookings,
'today' => $today,
'disciplines' => $disciplines,
'coaches' => $coaches,
'filters' => ['discipline_id' => $disciplineId, 'coach_id' => $coachId],
]);
}
......
......@@ -19,11 +19,13 @@ class CoachController extends Controller
$db = App::getInstance()->db();
$search = trim((string) $request->get('q', ''));
$disciplineFilter = trim((string) $request->get('discipline_id', ''));
$page = max(1, (int) $request->get('page', 1));
$perPage = 25;
$where = ['c.is_archived = 0'];
$params = [];
$joins = '';
if ($search !== '') {
$where[] = '(c.full_name_ar LIKE ? OR c.full_name_en LIKE ? OR c.national_id LIKE ? OR c.code LIKE ?)';
......@@ -33,10 +35,15 @@ class CoachController extends Controller
$params[] = "%{$search}%";
}
if ($disciplineFilter !== '') {
$joins = 'INNER JOIN sa_coach_disciplines cdf ON cdf.coach_id = c.id AND cdf.discipline_id = ?';
$params[] = (int) $disciplineFilter;
}
$whereSql = implode(' AND ', $where);
$total = (int) $db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_coaches c WHERE {$whereSql}",
"SELECT COUNT(DISTINCT c.id) as cnt FROM sa_coaches c {$joins} WHERE {$whereSql}",
$params
)['cnt'];
......@@ -47,6 +54,7 @@ class CoachController extends Controller
"SELECT c.*,
GROUP_CONCAT(d.name_ar SEPARATOR '، ') as discipline_names
FROM sa_coaches c
{$joins}
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}
......@@ -56,10 +64,14 @@ class CoachController extends Controller
$params
);
$disciplines = Discipline::getActive();
return $this->view('SportsActivity.Views.coaches.index', [
'coaches' => $coaches,
'pagination' => $pagination,
'search' => $search,
'disciplines' => $disciplines,
'disciplineFilter' => $disciplineFilter,
]);
}
......
......@@ -24,6 +24,7 @@ class GroupController extends Controller
{
$filters = [
'q' => trim((string) $request->get('q', '')),
'discipline_id' => trim((string) $request->get('discipline_id', '')),
'program_id' => trim((string) $request->get('program_id', '')),
'coach_id' => trim((string) $request->get('coach_id', '')),
];
......@@ -41,6 +42,11 @@ class GroupController extends Controller
$params[] = '%' . $filters['q'] . '%';
}
if ($filters['discipline_id'] !== '') {
$where .= " AND p.discipline_id = ?";
$params[] = (int) $filters['discipline_id'];
}
if ($filters['program_id'] !== '') {
$where .= " AND g.program_id = ?";
$params[] = (int) $filters['program_id'];
......@@ -51,7 +57,10 @@ class GroupController extends Controller
$params[] = (int) $filters['coach_id'];
}
$total = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM sa_groups g WHERE {$where}", $params)['cnt'] ?? 0);
$total = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_groups g LEFT JOIN sa_programs p ON p.id = g.program_id WHERE {$where}",
$params
)['cnt'] ?? 0);
$pagination = Pagination::paginate($total, $perPage, $page);
$offset = ($page - 1) * $perPage;
......@@ -66,6 +75,7 @@ class GroupController extends Controller
$params
);
$disciplines = Discipline::getActive();
$programs = Program::getActive();
$coaches = $db->select("SELECT id, full_name_ar as name_ar FROM sa_coaches WHERE is_archived = 0 ORDER BY full_name_ar", []);
......@@ -73,6 +83,7 @@ class GroupController extends Controller
'groups' => $groups,
'pagination' => $pagination,
'filters' => $filters,
'disciplines' => $disciplines,
'programs' => $programs,
'coaches' => $coaches,
]);
......@@ -188,19 +199,6 @@ class GroupController extends Controller
$players = Group::getPlayers((int) $id);
$schedule = Group::getSchedule((int) $id);
// Available players for enrollment (not already in this group)
$availablePlayers = $db->select(
"SELECT p.id, p.full_name_ar as name_ar, p.registration_serial as code,
(SELECT COUNT(*) FROM sa_subscriptions s
WHERE s.player_id = p.id AND s.payment_status IN ('unpaid','overdue','partial')) as unpaid_count
FROM sa_players p
WHERE p.is_archived = 0 AND p.id NOT IN (
SELECT player_id FROM sa_group_players WHERE group_id = ? AND status IN ('active','pending_payment')
)
ORDER BY p.full_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
......@@ -215,7 +213,6 @@ class GroupController extends Controller
'group' => $group,
'players' => $players,
'schedule' => $schedule,
'availablePlayers' => $availablePlayers,
'facilityUnits' => $facilityUnits,
'statuses' => Group::getStatusOptions(),
]);
......
......@@ -104,10 +104,16 @@ class LockerRentalController extends Controller
ORDER BY l.code"
);
// Players
$players = [];
$session = App::getInstance()->session();
$oldInput = $session->get('_old_input', []);
$oldPlayerId = (int) ($oldInput['player_id'] ?? 0);
if ($oldPlayerId > 0) {
$players = $db->select(
"SELECT id, full_name_ar, registration_serial FROM sa_players WHERE is_archived = 0 ORDER BY full_name_ar LIMIT 500"
"SELECT id, full_name_ar, registration_serial FROM sa_players WHERE id = ?",
[$oldPlayerId]
);
}
return $this->view('SportsActivity.Views.locker-rentals.create', [
'lockers' => $lockers,
......
......@@ -9,6 +9,8 @@ use App\Core\Response;
use App\Core\App;
use App\Core\Pagination;
use App\Modules\Members\Services\NationalIdParser;
use App\Modules\SportsActivity\Models\Discipline;
use App\Modules\SportsActivity\Models\Group;
class PlayerController extends Controller
{
......@@ -22,11 +24,14 @@ class PlayerController extends Controller
$search = trim((string) $request->get('q', ''));
$playerType = trim((string) $request->get('player_type', ''));
$medicalStatus = trim((string) $request->get('medical_status', ''));
$disciplineId = trim((string) $request->get('discipline_id', ''));
$groupId = trim((string) $request->get('group_id', ''));
$page = max(1, (int) $request->get('page', 1));
$perPage = 25;
$where = ['1=1'];
$where = ['p.is_archived = 0'];
$params = [];
$joins = '';
if ($search !== '') {
$where[] = '(p.full_name_ar LIKE ? OR p.national_id LIKE ? OR p.registration_serial LIKE ?)';
......@@ -45,26 +50,41 @@ class PlayerController extends Controller
$params[] = $medicalStatus;
}
if ($groupId !== '') {
$joins .= ' INNER JOIN sa_group_players gp ON gp.player_id = p.id AND gp.group_id = ? AND gp.status = "active"';
$params[] = (int) $groupId;
} elseif ($disciplineId !== '') {
$joins .= ' INNER JOIN sa_group_players gp ON gp.player_id = p.id AND gp.status = "active"';
$joins .= ' INNER JOIN sa_groups g ON g.id = gp.group_id AND g.is_archived = 0';
$joins .= ' INNER JOIN sa_programs pr ON pr.id = g.program_id AND pr.discipline_id = ?';
$params[] = (int) $disciplineId;
}
$whereSql = implode(' AND ', $where);
// Count total
$countSql = "SELECT COUNT(*) as total FROM sa_players p WHERE {$whereSql}";
$countSql = "SELECT COUNT(DISTINCT p.id) as total FROM sa_players p {$joins} 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}";
$sql = "SELECT DISTINCT p.* FROM sa_players p {$joins} WHERE {$whereSql} ORDER BY p.created_at DESC LIMIT {$perPage} OFFSET {$offset}";
$players = $db->select($sql, $params);
$disciplines = Discipline::getActive();
$groups = Group::getActive();
return $this->view('SportsActivity.Views.players.index', [
'players' => $players,
'pagination' => $pagination,
'disciplines' => $disciplines,
'groups' => $groups,
'filters' => [
'q' => $search,
'player_type' => $playerType,
'medical_status' => $medicalStatus,
'discipline_id' => $disciplineId,
'group_id' => $groupId,
],
]);
}
......
......@@ -118,7 +118,7 @@ class RegistrationWizardController extends Controller
}
$groups = $db->select(
"SELECT g.*, p.name_ar as program_name, d.name_ar as discipline_name
"SELECT g.*, p.name_ar as program_name, p.discipline_id, d.name_ar as discipline_name
FROM sa_groups g
LEFT JOIN sa_programs p ON p.id = g.program_id
LEFT JOIN sa_disciplines d ON d.id = p.discipline_id
......
......@@ -10,6 +10,7 @@ use App\Core\App;
use App\Core\Pagination;
use App\Modules\SportsActivity\Models\Subscription;
use App\Modules\SportsActivity\Models\Group;
use App\Modules\SportsActivity\Models\Discipline;
use App\Modules\SportsActivity\Services\SubscriptionGeneratorService;
use App\Modules\SportsActivity\Services\SaPaymentService;
......@@ -23,7 +24,9 @@ class SubscriptionController extends Controller
$db = App::getInstance()->db();
$filters = [
'q' => trim((string) $request->get('q', '')),
'month' => trim((string) $request->get('month', '')),
'discipline_id' => trim((string) $request->get('discipline_id', '')),
'group_id' => trim((string) $request->get('group_id', '')),
'payment_status' => trim((string) $request->get('payment_status', '')),
];
......@@ -33,6 +36,13 @@ class SubscriptionController extends Controller
$where = [];
$params = [];
$joins = 'LEFT JOIN sa_players p ON p.id = s.player_id LEFT JOIN sa_groups g ON g.id = s.group_id';
if ($filters['q'] !== '') {
$where[] = '(p.full_name_ar LIKE ? OR p.national_id LIKE ?)';
$params[] = '%' . $filters['q'] . '%';
$params[] = '%' . $filters['q'] . '%';
}
if ($filters['month'] !== '') {
$periodStart = $filters['month'] . '-01';
......@@ -40,6 +50,12 @@ class SubscriptionController extends Controller
$params[] = $periodStart;
}
if ($filters['discipline_id'] !== '') {
$joins .= ' LEFT JOIN sa_programs pr ON pr.id = g.program_id';
$where[] = 'pr.discipline_id = ?';
$params[] = (int) $filters['discipline_id'];
}
if ($filters['group_id'] !== '') {
$where[] = "s.group_id = ?";
$params[] = (int) $filters['group_id'];
......@@ -53,7 +69,7 @@ class SubscriptionController extends Controller
$whereClause = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
$total = (int) ($db->selectOne(
"SELECT COUNT(*) as cnt FROM sa_subscriptions s {$whereClause}",
"SELECT COUNT(*) as cnt FROM sa_subscriptions s {$joins} {$whereClause}",
$params
)['cnt'] ?? 0);
......@@ -63,20 +79,21 @@ class SubscriptionController extends Controller
$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
{$joins}
{$whereClause}
ORDER BY s.period_start DESC, s.created_at DESC
LIMIT {$perPage} OFFSET {$offset}",
$params
);
$disciplines = Discipline::getActive();
$groups = Group::getActive();
return $this->view('SportsActivity.Views.subscriptions.index', [
'subscriptions' => $subscriptions,
'pagination' => $pagination,
'filters' => $filters,
'disciplines' => $disciplines,
'groups' => $groups,
'statusOptions' => Subscription::getPaymentStatusOptions(),
]);
......
......@@ -184,6 +184,9 @@ return [
['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.view'],
['GET', '/api/sa/players/search', 'SportsActivity\Controllers\Api\PlayerSearchApiController@search', ['auth'], 'sa.player.view'],
['GET', '/api/sa/groups/search', 'SportsActivity\Controllers\Api\SmartFilterApiController@groups', ['auth'], 'sa.group.view'],
['GET', '/api/sa/coaches/search', 'SportsActivity\Controllers\Api\SmartFilterApiController@coaches', ['auth'], 'sa.group.view'],
['GET', '/api/sa/disciplines/list', 'SportsActivity\Controllers\Api\SmartFilterApiController@disciplines', ['auth'], 'sa.group.view'],
['GET', '/api/sa/mirror/{id:\d+}/state', 'SportsActivity\Controllers\Api\MirrorApiController@state', ['auth'], 'sa.mirror.view'],
['GET', '/api/sa/pool-grid/{id:\d+}/state', 'SportsActivity\Controllers\Api\PoolGridApiController@state', ['auth'], 'sa.pool-grid.manage'],
['POST', '/api/sa/pool-grid/{id:\d+}/assign', 'SportsActivity\Controllers\Api\PoolGridApiController@assign', ['auth', 'csrf'], 'sa.pool-grid.manage'],
......
......@@ -11,6 +11,34 @@
<strong>حصص التدريب اليوم:</strong> <?= e($today) ?>
</div>
<!-- Filters -->
<div class="card" style="margin-bottom:15px;padding:12px 15px;">
<form method="GET" action="/sa/attendance" 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>
<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>
<div style="min-width:160px;">
<label class="form-label" style="font-size:12px;">المدرب</label>
<select name="coach_id" class="form-select" data-searchable="true" data-placeholder="-- الكل --">
<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="filter" style="width:14px;height:14px;vertical-align:middle;"></i> تصفية</button>
<?php if (($filters['discipline_id'] ?? '') !== '' || ($filters['coach_id'] ?? '') !== ''): ?>
<a href="/sa/attendance" class="btn btn-sm btn-outline" style="color:#6B7280;">مسح</a>
<?php endif; ?>
</form>
</div>
<div class="card">
<div class="table-responsive">
<table class="data-table">
......
......@@ -22,7 +22,7 @@
<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">
<select name="facility_unit_id" class="form-select" required id="unitSelect" data-searchable="true" data-placeholder="ابحث عن الوحدة...">
<option value="">-- اختر الوحدة --</option>
<?php foreach ($facilityUnitsGrouped as $facilityName => $units): ?>
<optgroup label="<?= e($facilityName) ?>">
......
......@@ -22,7 +22,7 @@
</div>
<div style="min-width:160px;">
<label class="form-label" style="font-size:12px;">المرفق</label>
<select name="facility_id" class="form-select">
<select name="facility_id" class="form-select" data-searchable="true" data-placeholder="-- الكل --">
<option value="">-- الكل --</option>
<?php foreach ($facilities as $f): ?>
<option value="<?= (int) $f['id'] ?>" <?= ($filters['facility_id'] ?? '') == $f['id'] ? 'selected' : '' ?>><?= e($f['name_ar']) ?></option>
......
......@@ -67,6 +67,9 @@
<h3 style="margin:0;font-size:16px;font-weight:600;"><i data-lucide="map-pin" style="width:18px;height:18px;vertical-align:middle;margin-left:6px;"></i> اختيار المرفق</h3>
</div>
<div style="padding:20px;">
<div style="margin-bottom:12px;">
<input type="text" id="unitSearchInput" class="form-input" placeholder="ابحث عن مرفق أو ملعب..." style="padding:12px;font-size:14px;border-radius:10px;">
</div>
<div id="facilitiesList" style="display:grid;grid-template-columns:1fr;gap:12px;">
<div style="text-align:center;padding:40px;color:#9CA3AF;">جاري التحميل...</div>
</div>
......@@ -337,6 +340,20 @@
document.getElementById('btnStep2Back').addEventListener('click', function() { setStep(1); });
document.getElementById('unitSearchInput').addEventListener('input', function() {
var q = this.value.toLowerCase();
document.querySelectorAll('#facilitiesList > div').forEach(function(group) {
var cards = group.querySelectorAll('.unit-card');
var anyVisible = false;
cards.forEach(function(card) {
var match = (card.dataset.unitName + ' ' + card.dataset.facilityName).toLowerCase().indexOf(q) >= 0;
card.style.display = match ? '' : 'none';
if (match) anyVisible = true;
});
group.style.display = (!q || anyVisible) ? '' : 'none';
});
});
// Step 3: Time slots
var dateInput = document.getElementById('bkDate');
var participantsInput = document.getElementById('bkParticipants');
......
......@@ -16,8 +16,17 @@
<label class="form-label" style="font-size:12px;">بحث</label>
<input type="text" name="q" value="<?= e($search ?? '') ?>" placeholder="ابحث بالاسم، الرقم القومي، أو الكود..." class="form-input">
</div>
<div style="min-width:160px;">
<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'] ?>" <?= ($disciplineFilter ?? '') == $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>
<?php if (($search ?? '') !== ''): ?>
<?php if (($search ?? '') !== '' || ($disciplineFilter ?? '') !== ''): ?>
<a href="/sa/coaches" class="btn btn-outline" style="color:#6B7280;">مسح</a>
<?php endif; ?>
</form>
......
......@@ -53,7 +53,7 @@
</div>
<div class="form-group">
<label class="form-label">المدرب <span style="color:#DC2626;">*</span></label>
<select name="coach_id" id="coach_id" class="form-select" required>
<select name="coach_id" id="coach_id" class="form-select" required data-searchable="true" data-placeholder="-- اختر المدرب --">
<option value="">-- اختر المدرب --</option>
<?php foreach ($coaches as $c): ?>
<option value="<?= (int) $c['id'] ?>" <?= old('coach_id') == $c['id'] ? 'selected' : '' ?>><?= e($c['name_ar']) ?></option>
......
......@@ -53,7 +53,7 @@
</div>
<div class="form-group">
<label class="form-label">المدرب <span style="color:#DC2626;">*</span></label>
<select name="coach_id" id="coach_id" class="form-select" required>
<select name="coach_id" id="coach_id" class="form-select" required data-searchable="true" data-placeholder="-- اختر المدرب --">
<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>
......
......@@ -16,9 +16,22 @@
<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="discipline_id" class="form-select" id="filterDiscipline">
<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>
<div style="min-width:180px;">
<label class="form-label" style="font-size:12px;">البرنامج</label>
<select name="program_id" class="form-select">
<select name="program_id" class="form-select" id="filterProgram"
data-searchable="true"
data-cascade-from="discipline_id"
data-cascade-url="/api/sa/activities/programs-by-discipline?discipline_id={value}"
data-placeholder="-- الكل --">
<option value="">-- الكل --</option>
<?php foreach ($programs as $p): ?>
<option value="<?= (int) $p['id'] ?>" <?= ($filters['program_id'] ?? '') == $p['id'] ? 'selected' : '' ?>><?= e($p['name_ar']) ?></option>
......@@ -27,7 +40,11 @@
</div>
<div style="min-width:160px;">
<label class="form-label" style="font-size:12px;">المدرب</label>
<select name="coach_id" class="form-select">
<select name="coach_id" class="form-select"
data-searchable="true"
data-cascade-from="discipline_id"
data-cascade-url="/api/sa/activities/coaches-by-discipline?discipline_id={value}"
data-placeholder="-- الكل --">
<option value="">-- الكل --</option>
<?php foreach ($coaches as $c): ?>
<option value="<?= (int) $c['id'] ?>" <?= ($filters['coach_id'] ?? '') == $c['id'] ? 'selected' : '' ?>><?= e($c['name_ar']) ?></option>
......
......@@ -87,13 +87,12 @@ $st = $group['status'] ?? 'active';
<?= csrf_field() ?>
<div style="flex:1;">
<label class="form-label" style="font-size:12px;">تسجيل لاعب جديد</label>
<select name="player_id" class="form-select" required id="enrollPlayerSelect">
<option value="" data-unpaid="0">-- اختر لاعب --</option>
<?php foreach ($availablePlayers as $ap): ?>
<option value="<?= (int) $ap['id'] ?>" data-unpaid="<?= (int) ($ap['unpaid_count'] ?? 0) ?>">
<?= e($ap['name_ar']) ?> (<?= e($ap['code']) ?>)<?= (int)($ap['unpaid_count'] ?? 0) > 0 ? ' ⚠️ ' . (int)$ap['unpaid_count'] . ' اشتراك غير مدفوع' : '' ?>
</option>
<?php endforeach; ?>
<select name="player_id" class="form-select" required id="enrollPlayerSelect"
data-searchable="true"
data-search-url="/api/sa/players/search?exclude_group=<?= (int) $group['id'] ?>"
data-search-min="2"
data-placeholder="ابحث بالاسم أو الرقم القومي أو المسلسل...">
<option value="">ابحث بالاسم أو الرقم القومي...</option>
</select>
</div>
<button type="submit" class="btn btn-primary" style="padding:8px 20px;">
......
......@@ -19,7 +19,7 @@
<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>
<select name="locker_id" class="form-select" required>
<select name="locker_id" class="form-select" required data-searchable="true" data-placeholder="ابحث بالكود أو الاسم...">
<option value="">-- اختر لوكر متاح --</option>
<?php foreach ($lockers as $locker): ?>
<option value="<?= (int) $locker['id'] ?>" <?= old('locker_id') == $locker['id'] ? 'selected' : '' ?>>
......@@ -33,13 +33,21 @@
</div>
<div class="form-group">
<label class="form-label">اللاعب <span style="color:#DC2626;">*</span></label>
<select name="player_id" class="form-select" required id="playerSelect">
<option value="">-- اختر اللاعب --</option>
<select name="player_id" class="form-select" required id="playerSelect"
data-searchable="true"
data-search-url="/api/sa/players/search"
data-search-min="2"
data-placeholder="ابحث بالاسم أو الرقم القومي...">
<option value="">ابحث بالاسم أو الرقم القومي...</option>
<?php if (old('player_id')): ?>
<?php foreach ($players as $player): ?>
<option value="<?= (int) $player['id'] ?>" <?= old('player_id') == $player['id'] ? 'selected' : '' ?>>
<?php if ((string) $player['id'] === old('player_id')): ?>
<option value="<?= (int) $player['id'] ?>" selected>
<?= e($player['full_name_ar']) ?> (<?= e($player['registration_serial'] ?? '') ?>)
</option>
<?php endif; ?>
<?php endforeach; ?>
<?php endif; ?>
</select>
</div>
</div>
......
......@@ -19,6 +19,28 @@ $__template->layout('Layout.main');
<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="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>
<div style="min-width:150px;">
<label class="form-label" style="font-size:12px;">المجموعة</label>
<select name="group_id" class="form-select"
data-searchable="true"
data-cascade-from="discipline_id"
data-cascade-url="/api/sa/groups/search?active_only=1&discipline_id={value}"
data-placeholder="-- الكل --">
<option value="">الكل</option>
<?php foreach ($groups as $g): ?>
<option value="<?= (int) $g['id'] ?>" <?= ($filters['group_id'] ?? '') == $g['id'] ? 'selected' : '' ?>><?= e($g['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="min-width:130px;">
<label class="form-label" style="font-size:12px;">نوع اللاعب</label>
<select name="player_type" class="form-select">
<option value="">الكل</option>
......@@ -26,7 +48,7 @@ $__template->layout('Layout.main');
<option value="non_member" <?= ($filters['player_type'] ?? '') === 'non_member' ? 'selected' : '' ?>>غير عضو</option>
</select>
</div>
<div style="min-width:150px;">
<div style="min-width:130px;">
<label class="form-label" style="font-size:12px;">الحالة الطبية</label>
<select name="medical_status" class="form-select">
<option value="">الكل</option>
......
......@@ -246,7 +246,7 @@ $gridCols = (int) ($facility['pool_grid_cols'] ?? 0);
<div id="pgModalContent">
<div id="pgModalGroup">
<label>المجموعة</label>
<select id="pgGroupSelect">
<select id="pgGroupSelect" data-searchable="true" data-placeholder="ابحث عن مجموعة...">
<option value="">اختر مجموعة...</option>
<?php foreach ($groups as $g): ?>
<option value="<?= (int) $g['id'] ?>">
......
......@@ -134,12 +134,27 @@
<h3 style="margin:0;font-size:16px;font-weight:600;"><i data-lucide="trophy" style="width:18px;height:18px;vertical-align:middle;margin-left:6px;"></i> اختيار النشاط والمجموعة</h3>
</div>
<div style="padding:16px;">
<!-- Discipline Filter Chips -->
<div id="disciplineChips" style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px;">
<button type="button" class="disc-chip active" data-disc="" style="padding:6px 14px;border-radius:16px;border:1px solid #E5E7EB;background:#2563EB;color:white;font-size:12px;font-weight:600;cursor:pointer;">الكل</button>
<?php
$seenDisc = [];
foreach ($groups as $g) {
$dName = $g['discipline_name'] ?? '';
$dId = $g['discipline_id'] ?? '';
if ($dName && !isset($seenDisc[$dId])) {
$seenDisc[$dId] = $dName;
echo '<button type="button" class="disc-chip" data-disc="' . e($dId) . '" style="padding:6px 14px;border-radius:16px;border:1px solid #E5E7EB;background:white;color:#374151;font-size:12px;font-weight:600;cursor:pointer;">' . e($dName) . '</button>';
}
}
?>
</div>
<div style="margin-bottom:12px;">
<input type="text" id="groupSearch" class="form-input" placeholder="ابحث عن نشاط أو مجموعة..." style="padding:14px;font-size:15px;border-radius:10px;">
</div>
<div id="groupsList" style="display:grid;grid-template-columns:1fr;gap:10px;max-height:50vh;overflow-y:auto;-webkit-overflow-scrolling:touch;">
<?php foreach ($groups as $g): ?>
<div class="group-option" data-group-id="<?= (int) $g['id'] ?>" data-name="<?= e($g['name_ar'] . ' ' . ($g['discipline_name'] ?? '')) ?>"
<div class="group-option" data-group-id="<?= (int) $g['id'] ?>" data-disc-id="<?= (int) ($g['discipline_id'] ?? 0) ?>" data-name="<?= e($g['name_ar'] . ' ' . ($g['discipline_name'] ?? '')) ?>"
style="border:2px solid <?= (isset($selectedGroup) && (int)$selectedGroup['id'] === (int)$g['id']) ? '#2563EB' : '#E5E7EB' ?>;border-radius:12px;padding:16px;cursor:pointer;transition:all 0.2s;-webkit-tap-highlight-color:transparent;touch-action:manipulation;">
<div style="display:flex;justify-content:space-between;align-items:flex-start;">
<div style="flex:1;">
......@@ -612,14 +627,31 @@ document.addEventListener('DOMContentLoaded', function() {
});
}
// Discipline chip filtering
var activeDiscId = '';
document.querySelectorAll('.disc-chip').forEach(function(chip) {
chip.addEventListener('click', function() {
document.querySelectorAll('.disc-chip').forEach(function(c) {
c.style.background = 'white'; c.style.color = '#374151';
});
this.style.background = '#2563EB'; this.style.color = 'white';
activeDiscId = this.dataset.disc;
filterGroups();
});
});
// Group search
var groupSearch = document.getElementById('groupSearch');
if (groupSearch) {
groupSearch.addEventListener('input', function() {
var q = this.value.toLowerCase();
groupSearch.addEventListener('input', filterGroups);
}
function filterGroups() {
var q = (groupSearch ? groupSearch.value : '').toLowerCase();
document.querySelectorAll('.group-option').forEach(function(el) {
el.style.display = el.dataset.name.toLowerCase().indexOf(q) >= 0 ? '' : 'none';
});
var matchText = !q || el.dataset.name.toLowerCase().indexOf(q) >= 0;
var matchDisc = !activeDiscId || el.dataset.discId === activeDiscId;
el.style.display = (matchText && matchDisc) ? '' : 'none';
});
}
......
......@@ -21,13 +21,30 @@
<!-- 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:180px;">
<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>
<input type="month" name="month" value="<?= e($filters['month'] ?? '') ?>" class="form-input">
</div>
<div style="min-width:150px;">
<label class="form-label" style="font-size:12px;">النشاط الرياضي</label>
<select name="discipline_id" class="form-select" id="subFilterDiscipline">
<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>
<div style="min-width:180px;">
<label class="form-label" style="font-size:12px;">المجموعة</label>
<select name="group_id" class="form-select">
<select name="group_id" class="form-select"
data-searchable="true"
data-cascade-from="discipline_id"
data-cascade-url="/api/sa/groups/search?active_only=1&discipline_id={value}"
data-placeholder="-- الكل --">
<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>
......
......@@ -99,6 +99,8 @@ $currentPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
</div>
<script src="<?= url('assets/js/app.js') ?>?v=<?= @filemtime(dirname(__DIR__, 3) . '/public/assets/js/app.js') ?: time() ?>"></script>
<script src="<?= url('assets/js/searchable-select.js') ?>?v=<?= @filemtime(dirname(__DIR__, 3) . '/public/assets/js/searchable-select.js') ?: time() ?>"></script>
<script src="<?= url('assets/js/forms-engine.js') ?>?v=<?= @filemtime(dirname(__DIR__, 3) . '/public/assets/js/forms-engine.js') ?: time() ?>"></script>
<script>
// Initialize Lucide icons
lucide.createIcons();
......
This diff is collapsed.
This diff is collapsed.
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