Commit 858cd3f6 authored by Fares's avatar Fares

fix(sa): improve group player search with status info and treasury button

- Player search now shows type (member/non-member), medical status, and
  serial number in dropdown results for accurate identification
- Group player table adds columns for player type and medical status
- Pending payment players now show a "الخزينة" button linking to cashier
- Enrollment form stays visible when group is full (uses force-enroll)
- SearchableSelect prioritizes label field for richer display text
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 8d0f495b
...@@ -46,7 +46,8 @@ class PlayerSearchApiController extends Controller ...@@ -46,7 +46,8 @@ class PlayerSearchApiController extends Controller
} }
$results = $db->select( $results = $db->select(
"SELECT DISTINCT p.id, p.full_name_ar, p.registration_serial, p.national_id, p.player_type, p.medical_status "SELECT DISTINCT p.id, p.full_name_ar, p.registration_serial, p.national_id,
p.player_type, p.medical_status, p.phone, p.date_of_birth
FROM sa_players p FROM sa_players p
{$joins} {$joins}
WHERE {$where} WHERE {$where}
...@@ -55,6 +56,31 @@ class PlayerSearchApiController extends Controller ...@@ -55,6 +56,31 @@ class PlayerSearchApiController extends Controller
$params $params
); );
$medicalLabels = [
'fit' => 'لائق',
'conditional' => 'لائق مشروط',
'pending' => 'في الانتظار',
'unfit' => 'غير لائق',
];
foreach ($results as &$row) {
$type = $row['player_type'] === 'member' ? 'عضو' : 'غير عضو';
$medical = $medicalLabels[$row['medical_status'] ?? ''] ?? 'بدون شهادة';
$serial = $row['registration_serial'] ?? '';
$label = $row['full_name_ar'];
$parts = [];
if ($serial) {
$parts[] = $serial;
}
$parts[] = $type;
$parts[] = $medical;
$label .= ' (' . implode(' • ', $parts) . ')';
$row['label'] = $label;
}
unset($row);
return $this->json(['results' => $results]); return $this->json(['results' => $results]);
} }
} }
...@@ -47,7 +47,9 @@ class Group extends Model ...@@ -47,7 +47,9 @@ class Group extends Model
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
return $db->select( return $db->select(
"SELECT gp.*, p.full_name_ar as player_name, p.registration_serial as player_code, p.phone, p.medical_status, "SELECT gp.*, p.full_name_ar as player_name, p.registration_serial as player_code,
p.phone, p.medical_status, p.player_type,
pr.request_number, pr.status as payment_status_req, pr.amount as payment_amount,
(SELECT COUNT(*) FROM sa_subscriptions s (SELECT COUNT(*) FROM sa_subscriptions s
WHERE s.player_id = gp.player_id AND s.group_id = gp.group_id WHERE s.player_id = gp.player_id AND s.group_id = gp.group_id
AND s.payment_status IN ('unpaid','overdue','partial')) as unpaid_count, AND s.payment_status IN ('unpaid','overdue','partial')) as unpaid_count,
...@@ -56,6 +58,7 @@ class Group extends Model ...@@ -56,6 +58,7 @@ class Group extends Model
AND s2.payment_status IN ('unpaid','overdue','partial')) as unpaid_total AND s2.payment_status IN ('unpaid','overdue','partial')) as unpaid_total
FROM sa_group_players gp FROM sa_group_players gp
JOIN sa_players p ON p.id = gp.player_id JOIN sa_players p ON p.id = gp.player_id
LEFT JOIN payment_requests pr ON pr.id = gp.payment_request_id
WHERE gp.group_id = ? AND gp.status IN ('active','pending_payment') WHERE gp.group_id = ? AND gp.status IN ('active','pending_payment')
ORDER BY FIELD(gp.status, 'pending_payment', 'active'), p.full_name_ar ASC", ORDER BY FIELD(gp.status, 'pending_payment', 'active'), p.full_name_ar ASC",
[$groupId] [$groupId]
......
...@@ -81,9 +81,19 @@ $st = $group['status'] ?? 'active'; ...@@ -81,9 +81,19 @@ $st = $group['status'] ?? 'active';
</div> </div>
<!-- Enroll Form --> <!-- Enroll Form -->
<?php if ((int) $group['current_count'] < (int) $group['max_capacity'] && can('sa.group.enroll')): ?> <?php
$isFull = (int) $group['current_count'] >= (int) $group['max_capacity'];
$canForce = can('sa.group.force_enroll');
if (can('sa.group.enroll') && (!$isFull || $canForce)):
?>
<div style="padding:15px 20px;background:#F9FAFB;border-bottom:1px solid #E5E7EB;"> <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;" id="enrollForm"> <?php if ($isFull): ?>
<div style="background:#FEF3C7;border:1px solid #F59E0B;border-radius:6px;padding:8px 12px;margin-bottom:10px;font-size:12px;color:#92400E;">
<i data-lucide="alert-triangle" style="width:13px;height:13px;vertical-align:middle;margin-left:4px;"></i>
المجموعة ممتلئة (<?= (int) $group['current_count'] ?>/<?= (int) $group['max_capacity'] ?>) — سيتم تسجيل اللاعب بتجاوز السعة
</div>
<?php endif; ?>
<form method="POST" action="/sa/groups/<?= (int) $group['id'] ?>/<?= $isFull ? 'force-enroll' : 'enroll' ?>" style="display:flex;gap:10px;align-items:end;" id="enrollForm">
<?= csrf_field() ?> <?= csrf_field() ?>
<div style="flex:1;"> <div style="flex:1;">
<label class="form-label" style="font-size:12px;">تسجيل لاعب جديد</label> <label class="form-label" style="font-size:12px;">تسجيل لاعب جديد</label>
...@@ -108,7 +118,8 @@ $st = $group['status'] ?? 'active'; ...@@ -108,7 +118,8 @@ $st = $group['status'] ?? 'active';
<tr> <tr>
<th>الكود</th> <th>الكود</th>
<th>الاسم</th> <th>الاسم</th>
<th>الهاتف</th> <th>النوع</th>
<th>الحالة الطبية</th>
<th>المستحقات</th> <th>المستحقات</th>
<th>تاريخ التسجيل</th> <th>تاريخ التسجيل</th>
<th>إجراءات</th> <th>إجراءات</th>
...@@ -121,19 +132,40 @@ $st = $group['status'] ?? 'active'; ...@@ -121,19 +132,40 @@ $st = $group['status'] ?? 'active';
<td><code style="font-size:11px;background:#F3F4F6;padding:2px 6px;border-radius:4px;"><?= e($pl['player_code'] ?? '') ?></code></td> <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;"> <td style="font-weight:600;">
<a href="/sa/players/<?= (int) $pl['player_id'] ?>" style="color:inherit;text-decoration:none;"><?= e($pl['player_name']) ?></a> <a href="/sa/players/<?= (int) $pl['player_id'] ?>" style="color:inherit;text-decoration:none;"><?= e($pl['player_name']) ?></a>
<?php if (!in_array($pl['medical_status'] ?? 'pending', ['fit', 'conditional'])): ?>
<i data-lucide="alert-triangle" style="width:13px;height:13px;color:#D97706;vertical-align:middle;margin-right:4px;" title="بدون شهادة طبية سارية"></i>
<?php endif; ?>
<?php if (($pl['status'] ?? '') === 'pending_payment'): ?> <?php if (($pl['status'] ?? '') === 'pending_payment'): ?>
<span style="padding:2px 6px;border-radius:8px;font-size:10px;font-weight:700;background:#FEF3C7;color:#D97706;margin-right:5px;">في انتظار الدفع</span> <span style="padding:2px 6px;border-radius:8px;font-size:10px;font-weight:700;background:#FEF3C7;color:#D97706;margin-right:5px;">في انتظار الدفع</span>
<?php endif; ?> <?php endif; ?>
</td> </td>
<td style="direction:ltr;text-align:right;"><?= e($pl['phone'] ?? '—') ?></td> <td>
<?php if (($pl['player_type'] ?? '') === 'member'): ?>
<span style="background:#ECFDF5;color:#059669;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;">عضو</span>
<?php else: ?>
<span style="background:#FEF2F2;color:#DC2626;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;">غير عضو</span>
<?php endif; ?>
</td>
<td>
<?php
$medStatus = $pl['medical_status'] ?? '';
if ($medStatus === 'fit'): ?>
<span style="background:#ECFDF5;color:#059669;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;">لائق</span>
<?php elseif ($medStatus === 'conditional'): ?>
<span style="background:#FEF3C7;color:#D97706;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;">لائق مشروط</span>
<?php elseif ($medStatus === 'pending'): ?>
<span style="background:#FEF3C7;color:#D97706;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;">في الانتظار</span>
<?php else: ?>
<span style="background:#FEE2E2;color:#DC2626;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;">بدون شهادة</span>
<?php endif; ?>
</td>
<td> <td>
<?php if (($pl['status'] ?? '') === 'pending_payment'): ?> <?php if (($pl['status'] ?? '') === 'pending_payment'): ?>
<span style="padding:3px 8px;border-radius:8px;font-size:11px;font-weight:700;background:#FEF3C7;color:#D97706;"> <div style="font-size:11px;">
في انتظار التحصيل <span style="padding:3px 8px;border-radius:8px;font-weight:700;background:#FEF3C7;color:#D97706;">
</span> في انتظار التحصيل
</span>
<?php if (!empty($pl['payment_amount'])): ?>
<div style="margin-top:4px;font-weight:600;color:#374151;"><?= money((float) $pl['payment_amount']) ?></div>
<?php endif; ?>
</div>
<?php elseif ((int) ($pl['unpaid_count'] ?? 0) > 0): ?> <?php elseif ((int) ($pl['unpaid_count'] ?? 0) > 0): ?>
<span style="padding:3px 8px;border-radius:8px;font-size:11px;font-weight:700;background:#FEE2E2;color:#DC2626;"> <span style="padding:3px 8px;border-radius:8px;font-size:11px;font-weight:700;background:#FEE2E2;color:#DC2626;">
<?= (int) $pl['unpaid_count'] ?> غير مدفوع — <?= money((float) ($pl['unpaid_total'] ?? 0)) ?> <?= (int) $pl['unpaid_count'] ?> غير مدفوع — <?= money((float) ($pl['unpaid_total'] ?? 0)) ?>
...@@ -143,12 +175,17 @@ $st = $group['status'] ?? 'active'; ...@@ -143,12 +175,17 @@ $st = $group['status'] ?? 'active';
<?php endif; ?> <?php endif; ?>
</td> </td>
<td><?= e($pl['enrolled_at'] ?? '') ?></td> <td><?= e($pl['enrolled_at'] ?? '') ?></td>
<td> <td style="white-space:nowrap;">
<?php if (($pl['status'] ?? '') === 'pending_payment' && !empty($pl['request_number'])): ?>
<a href="/cashier?search=<?= e($pl['request_number']) ?>" class="btn btn-sm btn-primary" style="font-size:11px;padding:4px 10px;margin-left:4px;" title="عرض طلب الدفع في الخزينة">
<i data-lucide="banknote" style="width:13px;height:13px;vertical-align:middle;margin-left:3px;"></i> الخزينة
</a>
<?php endif; ?>
<?php if (can('sa.group.enroll')): ?> <?php if (can('sa.group.enroll')): ?>
<form method="POST" action="/sa/groups/<?= (int) $group['id'] ?>/remove-player" style="display:inline;" onsubmit="return confirm('هل تريد سحب هذا اللاعب من المجموعة؟');"> <form method="POST" action="/sa/groups/<?= (int) $group['id'] ?>/remove-player" style="display:inline;" onsubmit="return confirm('هل تريد سحب هذا اللاعب من المجموعة؟');">
<?= csrf_field() ?> <?= csrf_field() ?>
<input type="hidden" name="player_id" value="<?= (int) $pl['player_id'] ?>"> <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;"> <button type="submit" class="btn btn-sm btn-outline" style="font-size:11px;padding:4px 10px;color:#DC2626;border-color:#DC2626;">
<i data-lucide="user-minus" style="width:13px;height:13px;vertical-align:middle;"></i> سحب <i data-lucide="user-minus" style="width:13px;height:13px;vertical-align:middle;"></i> سحب
</button> </button>
</form> </form>
...@@ -158,7 +195,7 @@ $st = $group['status'] ?? 'active'; ...@@ -158,7 +195,7 @@ $st = $group['status'] ?? 'active';
<?php endforeach; ?> <?php endforeach; ?>
<?php else: ?> <?php else: ?>
<tr> <tr>
<td colspan="6" style="text-align:center;padding:30px;color:#6B7280;">لا يوجد لاعبون مسجلون بعد</td> <td colspan="7" style="text-align:center;padding:30px;color:#6B7280;">لا يوجد لاعبون مسجلون بعد</td>
</tr> </tr>
<?php endif; ?> <?php endif; ?>
</tbody> </tbody>
......
...@@ -223,7 +223,7 @@ var SearchableSelect = (function() { ...@@ -223,7 +223,7 @@ var SearchableSelect = (function() {
self.filteredOptions = results.map(function(r) { self.filteredOptions = results.map(function(r) {
return { return {
value: String(r.id || r.value || ''), value: String(r.id || r.value || ''),
text: r.full_name_ar || r.name_ar || r.name || r.label || r.text || '', text: r.label || r.full_name_ar || r.name_ar || r.name || r.text || '',
group: r.group || '' group: r.group || ''
}; };
}); });
......
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