Commit 45c184f0 authored by Mahmoud Aglan's avatar Mahmoud Aglan

fgnfgjn

parent 4f63ae92
...@@ -262,6 +262,7 @@ final class PaymentRequestService ...@@ -262,6 +262,7 @@ final class PaymentRequestService
'carnet_replacement' => 'بدل فاقد كارنيه', 'carnet_replacement' => 'بدل فاقد كارنيه',
'sports_registration' => 'تسجيل رياضي', 'sports_registration' => 'تسجيل رياضي',
'activity_subscription' => 'اشتراك نشاط', 'activity_subscription' => 'اشتراك نشاط',
'seasonal_fee' => 'رسوم عضوية موسمية',
default => $type, default => $type,
}; };
} }
......
...@@ -113,6 +113,39 @@ EventBus::listen('payment_request.completed', function (array $data) { ...@@ -113,6 +113,39 @@ EventBus::listen('payment_request.completed', function (array $data) {
EventBus::dispatch($cfg['event'], [$cfg['key'] => $entityId, 'payment_id' => $paymentId]); EventBus::dispatch($cfg['event'], [$cfg['key'] => $entityId, 'payment_id' => $paymentId]);
\App\Core\Logger::info("Life-event fee paid via cashier", ['type' => $paymentType, 'entity_id' => $entityId]); \App\Core\Logger::info("Life-event fee paid via cashier", ['type' => $paymentType, 'entity_id' => $entityId]);
} }
$receiptNumber = $data['receipt_number'] ?? '';
if ($paymentType === 'addition_fee' && $entityType === 'spouses') {
$db->update('spouses', [
'status' => 'active',
'join_date' => date('Y-m-d'),
'fee_receipt_number' => $receiptNumber ?: null,
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [$entityId]);
EventBus::dispatch('spouse.fee_paid', ['spouse_id' => $entityId, 'member_id' => $memberId, 'payment_id' => $paymentId]);
\App\Core\Logger::info("Spouse activated via cashier", ['spouse_id' => $entityId]);
}
if ($paymentType === 'addition_fee' && $entityType === 'children') {
$db->update('children', [
'status' => 'active',
'fee_receipt_number' => $receiptNumber ?: null,
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [$entityId]);
EventBus::dispatch('child.fee_paid', ['child_id' => $entityId, 'member_id' => $memberId, 'payment_id' => $paymentId]);
\App\Core\Logger::info("Child activated via cashier", ['child_id' => $entityId]);
}
if ($paymentType === 'addition_fee' && $entityType === 'temporary_members') {
$db->update('temporary_members', [
'status' => 'active',
'fee_receipt_number' => $receiptNumber ?: null,
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [$entityId]);
EventBus::dispatch('temporary.fee_paid', ['temp_id' => $entityId, 'member_id' => $memberId, 'payment_id' => $paymentId]);
\App\Core\Logger::info("Temporary member activated via cashier", ['temp_id' => $entityId]);
}
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
\App\Core\Logger::error("payment_request.completed listener failed: " . $e->getMessage(), ['data' => $data]); \App\Core\Logger::error("payment_request.completed listener failed: " . $e->getMessage(), ['data' => $data]);
......
...@@ -11,6 +11,7 @@ use App\Core\EventBus; ...@@ -11,6 +11,7 @@ use App\Core\EventBus;
use App\Modules\Children\Models\Child; use App\Modules\Children\Models\Child;
use App\Modules\Children\Services\ChildFeeCalculator; use App\Modules\Children\Services\ChildFeeCalculator;
use App\Modules\Members\Services\NationalIdParser; use App\Modules\Members\Services\NationalIdParser;
use App\Modules\Cashier\Services\PaymentRequestService;
class ChildController extends Controller class ChildController extends Controller
{ {
...@@ -127,6 +128,9 @@ class ChildController extends Controller ...@@ -127,6 +128,9 @@ class ChildController extends Controller
$childOrder = $feeCalc['child_order'] ?? Child::getNextOrder((int) $memberId); $childOrder = $feeCalc['child_order'] ?? Child::getNextOrder((int) $memberId);
$classification = $feeCalc['classification'] ?? 'included'; $classification = $feeCalc['classification'] ?? 'included';
$totalFee = $feeCalc['total_fee'] ?? $feeCalc['fee'] ?? '0.00';
$hasFee = bccomp($totalFee, '0', 2) > 0;
$child = Child::create([ $child = Child::create([
'member_id' => (int) $memberId, 'member_id' => (int) $memberId,
'child_order' => $childOrder, 'child_order' => $childOrder,
...@@ -142,8 +146,8 @@ class ChildController extends Controller ...@@ -142,8 +146,8 @@ class ChildController extends Controller
'school_faculty' => $data['school_faculty'] ?? null, 'school_faculty' => $data['school_faculty'] ?? null,
'nationality' => $data['nationality'] ?? 'مصري', 'nationality' => $data['nationality'] ?? 'مصري',
'classification' => $classification, 'classification' => $classification,
'addition_fee' => $feeCalc['fee'] ?? '0.00', 'addition_fee' => $totalFee,
'status' => 'active', 'status' => $hasFee ? 'pending_payment' : 'active',
'remarks' => $data['remarks'] ?? null, 'remarks' => $data['remarks'] ?? null,
]); ]);
...@@ -151,11 +155,31 @@ class ChildController extends Controller ...@@ -151,11 +155,31 @@ class ChildController extends Controller
'member_id' => (int) $memberId, 'member_id' => (int) $memberId,
'child_id' => (int) $child->id, 'child_id' => (int) $child->id,
'classification' => $classification, 'classification' => $classification,
'fee' => $feeCalc['fee'] ?? '0.00', 'fee' => $totalFee,
]); ]);
if ($hasFee) {
$childLabel = $data['gender'] === 'male' ? 'ابن' : 'ابنة';
$payResult = PaymentRequestService::createRequest([
'member_id' => (int) $memberId,
'amount' => $totalFee,
'payment_type' => 'addition_fee',
'related_entity_type' => 'children',
'related_entity_id' => (int) $child->id,
'description_ar' => 'رسوم إضافة ' . $childLabel . ' — ' . trim($data['full_name_ar']),
]);
if ($payResult['success']) {
return $this->redirect("/members/{$memberId}")
->withSuccess('تم إضافة الابن/الابنة وإرسال طلب الدفع للخزينة — التصنيف: ' . $child->getClassificationLabel() . ' — الرسوم: ' . money($totalFee) . ' — رقم الطلب: ' . $payResult['request_number']);
}
return $this->redirect("/members/{$memberId}")
->withError('تم إضافة الابن/الابنة لكن فشل إنشاء طلب الدفع: ' . ($payResult['error'] ?? ''));
}
return $this->redirect("/members/{$memberId}") return $this->redirect("/members/{$memberId}")
->withSuccess('تم إضافة الابن/الابنة — التصنيف: ' . ($child->getClassificationLabel()) . ' — الرسوم: ' . money($feeCalc['fee'] ?? '0.00')); ->withSuccess('تم إضافة الابن/الابنة — التصنيف: ' . $child->getClassificationLabel() . ' — مشمول بدون رسوم');
} }
public function show(Request $request, string $memberId, string $id): Response public function show(Request $request, string $memberId, string $id): Response
......
...@@ -119,9 +119,10 @@ class MemberController extends Controller ...@@ -119,9 +119,10 @@ class MemberController extends Controller
$branch = $db->selectOne("SELECT name_ar FROM branches WHERE id = ?", [(int) $member->branch_id]); $branch = $db->selectOne("SELECT name_ar FROM branches WHERE id = ?", [(int) $member->branch_id]);
$qualification = $member->qualification_id ? $db->selectOne("SELECT name_ar FROM qualifications WHERE id = ?", [(int) $member->qualification_id]) : null; $qualification = $member->qualification_id ? $db->selectOne("SELECT name_ar FROM qualifications WHERE id = ?", [(int) $member->qualification_id]) : null;
$spouses = []; $children = []; $spouses = []; $children = []; $temporaries = [];
try { $spouses = $db->select("SELECT * FROM spouses WHERE member_id = ? AND is_archived = 0 ORDER BY spouse_order", [(int) $id]); } catch (\Throwable $e) {} try { $spouses = $db->select("SELECT * FROM spouses WHERE member_id = ? AND is_archived = 0 ORDER BY spouse_order", [(int) $id]); } catch (\Throwable $e) {}
try { $children = $db->select("SELECT * FROM children WHERE member_id = ? AND is_archived = 0 ORDER BY child_order", [(int) $id]); } catch (\Throwable $e) {} try { $children = $db->select("SELECT * FROM children WHERE member_id = ? AND is_archived = 0 ORDER BY child_order", [(int) $id]); } catch (\Throwable $e) {}
try { $temporaries = $db->select("SELECT * FROM temporary_members WHERE member_id = ? AND is_archived = 0 ORDER BY id", [(int) $id]); } catch (\Throwable $e) {}
$bill = BillingService::getMemberBill((int) $id); $bill = BillingService::getMemberBill((int) $id);
$formFilled = ($member->qualification_id !== null && $member->qualification_id > 0); $formFilled = ($member->qualification_id !== null && $member->qualification_id > 0);
...@@ -147,6 +148,7 @@ class MemberController extends Controller ...@@ -147,6 +148,7 @@ class MemberController extends Controller
'qualificationName' => $qualification['name_ar'] ?? '—', 'qualificationName' => $qualification['name_ar'] ?? '—',
'spouses' => $spouses, 'spouses' => $spouses,
'children' => $children, 'children' => $children,
'temporaries' => $temporaries,
'bill' => $bill, 'bill' => $bill,
'formFee' => MemberNumberGenerator::getFormFee(), 'formFee' => MemberNumberGenerator::getFormFee(),
'formFilled' => $formFilled, 'formFilled' => $formFilled,
......
...@@ -23,12 +23,27 @@ final class BillingService ...@@ -23,12 +23,27 @@ final class BillingService
// ── 1. Form Fee (505) ── // ── 1. Form Fee (505) ──
$formFeePaid = false; $formFeePaid = false;
$formFeePending = false;
try { try {
$ff = $db->selectOne( $ff = $db->selectOne(
"SELECT id FROM payments WHERE member_id = ? AND payment_type = 'form_fee' AND is_voided = 0 LIMIT 1", "SELECT id FROM payments WHERE member_id = ? AND payment_type = 'form_fee' AND is_voided = 0 LIMIT 1",
[$memberId] [$memberId]
); );
$formFeePaid = ($ff !== null); $formFeePaid = ($ff !== null);
if (!$formFeePaid) {
$ffReq = $db->selectOne(
"SELECT id, status FROM payment_requests WHERE member_id = ? AND payment_type = 'form_fee' AND is_voided = 0 AND status IN ('pending','processing') LIMIT 1",
[$memberId]
);
$formFeePending = ($ffReq !== null);
if (!$formFeePending) {
$ffCompleted = $db->selectOne(
"SELECT id FROM payment_requests WHERE member_id = ? AND payment_type = 'form_fee' AND is_voided = 0 AND status = 'completed' LIMIT 1",
[$memberId]
);
$formFeePaid = ($ffCompleted !== null);
}
}
} catch (\Throwable $e) {} } catch (\Throwable $e) {}
$formFeeAmount = ServicePrice::getPrice('SVC_NEW_FORM', '505.00'); $formFeeAmount = ServicePrice::getPrice('SVC_NEW_FORM', '505.00');
...@@ -44,12 +59,27 @@ final class BillingService ...@@ -44,12 +59,27 @@ final class BillingService
// ── 2. Membership Value ── // ── 2. Membership Value ──
$membershipPaid = false; $membershipPaid = false;
$membershipPending = false;
try { try {
$mp = $db->selectOne( $mp = $db->selectOne(
"SELECT id FROM payments WHERE member_id = ? AND payment_type IN ('membership_fee','down_payment') AND is_voided = 0 LIMIT 1", "SELECT id FROM payments WHERE member_id = ? AND payment_type IN ('membership_fee','down_payment') AND is_voided = 0 LIMIT 1",
[$memberId] [$memberId]
); );
$membershipPaid = ($mp !== null); $membershipPaid = ($mp !== null);
if (!$membershipPaid) {
$mpReq = $db->selectOne(
"SELECT id FROM payment_requests WHERE member_id = ? AND payment_type IN ('membership_fee','down_payment') AND is_voided = 0 AND status IN ('pending','processing') LIMIT 1",
[$memberId]
);
$membershipPending = ($mpReq !== null);
if (!$membershipPending) {
$mpCompleted = $db->selectOne(
"SELECT id FROM payment_requests WHERE member_id = ? AND payment_type IN ('membership_fee','down_payment') AND is_voided = 0 AND status = 'completed' LIMIT 1",
[$memberId]
);
$membershipPaid = ($mpCompleted !== null);
}
}
} catch (\Throwable $e) {} } catch (\Throwable $e) {}
$qualName = '—'; $qualName = '—';
...@@ -187,6 +217,28 @@ final class BillingService ...@@ -187,6 +217,28 @@ final class BillingService
} }
} catch (\Throwable $e) {} } catch (\Throwable $e) {}
// ── Check payment_requests for completed or pending additions ──
try {
$additionRequests = $db->select(
"SELECT related_entity_type, related_entity_id, status FROM payment_requests
WHERE member_id = ? AND payment_type = 'addition_fee' AND is_voided = 0",
[$memberId]
);
foreach ($additionRequests as $ar) {
foreach ($items as &$item) {
if (($item['entity_type'] ?? '') === $ar['related_entity_type']
&& ($item['entity_id'] ?? 0) == $ar['related_entity_id']) {
if ($ar['status'] === 'completed') {
$item['paid'] = true;
} elseif (in_array($ar['status'], ['pending', 'processing'], true)) {
$item['in_queue'] = true;
}
}
}
unset($item);
}
} catch (\Throwable $e) {}
// ── Calculate totals ── // ── Calculate totals ──
$totalRequired = '0.00'; $totalRequired = '0.00';
$totalPaid = '0.00'; $totalPaid = '0.00';
...@@ -214,7 +266,9 @@ final class BillingService ...@@ -214,7 +266,9 @@ final class BillingService
'total_pending' => $totalPending, 'total_pending' => $totalPending,
'total_included' => $totalIncluded, 'total_included' => $totalIncluded,
'form_fee_paid' => $formFeePaid, 'form_fee_paid' => $formFeePaid,
'form_fee_pending' => $formFeePending ?? false,
'membership_paid' => $membershipPaid, 'membership_paid' => $membershipPaid,
'membership_pending' => $membershipPending ?? false,
'all_paid' => (bccomp($totalPending, '0', 2) <= 0), 'all_paid' => (bccomp($totalPending, '0', 2) <= 0),
]; ];
} }
......
...@@ -124,11 +124,13 @@ $isActive = ($member->status === 'active'); ...@@ -124,11 +124,13 @@ $isActive = ($member->status === 'active');
</td> </td>
<td style="padding:10px;text-align:center;"> <td style="padding:10px;text-align:center;">
<?php if ($item['paid']): ?> <?php if ($item['paid']): ?>
<span style="color:#059669;font-weight:700;">✅ مدفوع</span> <span style="color:#059669;font-weight:700;">&#x2705; مدفوع</span>
<?php elseif (!empty($item['in_queue'])): ?>
<span style="color:#D97706;font-weight:600;">&#x1f4b3; في الخزينة</span>
<?php elseif ($item['included']): ?> <?php elseif ($item['included']): ?>
<span style="color:#0284C7;font-size:12px;">مشمول</span> <span style="color:#0284C7;font-size:12px;">مشمول</span>
<?php else: ?> <?php else: ?>
<span style="color:#DC2626;font-weight:600;"> معلق</span> <span style="color:#DC2626;font-weight:600;">&#x23f3; معلق</span>
<?php endif; ?> <?php endif; ?>
</td> </td>
</tr> </tr>
...@@ -239,6 +241,66 @@ $isActive = ($member->status === 'active'); ...@@ -239,6 +241,66 @@ $isActive = ($member->status === 'active');
</div> </div>
<?php endif; ?> <?php endif; ?>
<!-- ═══════════════════════════════════════════════ -->
<!-- MISSING STEPS — shows what still needs to happen -->
<!-- ═══════════════════════════════════════════════ -->
<?php
$missingSteps = [];
if (!$bill['form_fee_paid'] && $member->status === 'potential') {
if (!empty($bill['form_fee_pending']) || !empty($pendingFormFee)) {
$missingSteps[] = ['icon' => '&#x1f4b3;', 'text' => 'رسوم الاستمارة في انتظار الخزينة', 'color' => '#D97706', 'done' => false];
} else {
$missingSteps[] = ['icon' => '&#x1f512;', 'text' => 'لم يتم دفع رسوم الاستمارة', 'color' => '#DC2626', 'done' => false];
}
} else {
$missingSteps[] = ['icon' => '&#x2705;', 'text' => 'رسوم الاستمارة', 'color' => '#059669', 'done' => true];
}
if ($bill['form_fee_paid'] && !$formFilled) {
$missingSteps[] = ['icon' => '&#x1f4dd;', 'text' => 'لم يتم ملء الاستمارة بعد', 'color' => '#DC2626', 'done' => false];
} elseif ($formFilled) {
$missingSteps[] = ['icon' => '&#x2705;', 'text' => 'الاستمارة مكتملة', 'color' => '#059669', 'done' => true];
}
if ($formFilled && !$bill['membership_paid'] && !in_array($member->status, ['active'], true)) {
if (!empty($bill['membership_pending']) || !empty($pendingMembership)) {
$missingSteps[] = ['icon' => '&#x1f4b3;', 'text' => 'رسوم العضوية في انتظار الخزينة', 'color' => '#D97706', 'done' => false];
} elseif (in_array($member->status, ['accepted', 'payment_pending'], true)) {
$missingSteps[] = ['icon' => '&#x23f3;', 'text' => 'لم يتم سداد رسوم العضوية', 'color' => '#DC2626', 'done' => false];
} elseif (in_array($member->status, ['under_review', 'interview_scheduled'], true)) {
$missingSteps[] = ['icon' => '&#x1f4cb;', 'text' => 'في انتظار قرار مجلس الأمناء', 'color' => '#3B82F6', 'done' => false];
}
} elseif ($bill['membership_paid'] || $member->status === 'active') {
$missingSteps[] = ['icon' => '&#x2705;', 'text' => 'رسوم العضوية', 'color' => '#059669', 'done' => true];
}
foreach ($bill['items'] as $item) {
if (in_array($item['type'] ?? '', ['spouse_fee', 'child_fee', 'temp_fee'], true) && !$item['included'] && !$item['paid']) {
$statusText = !empty($item['in_queue']) ? ' (في الخزينة)' : '';
$statusColor = !empty($item['in_queue']) ? '#D97706' : '#DC2626';
$missingSteps[] = ['icon' => '&#x26a0;', 'text' => 'لم يتم سداد: ' . $item['label'] . $statusText, 'color' => $statusColor, 'done' => false];
}
}
$hasIncomplete = !empty(array_filter($missingSteps, fn($s) => !$s['done']));
?>
<?php if ($hasIncomplete): ?>
<div class="card" style="margin-bottom:20px;border:2px solid #F59E0B;">
<div style="padding:15px 20px;background:#FFFBEB;border-bottom:1px solid #FDE68A;">
<h3 style="margin:0;color:#D97706;font-size:15px;">&#x1f4cb; الخطوات الناقصة</h3>
</div>
<div style="padding:15px 20px;">
<?php foreach ($missingSteps as $step): ?>
<div style="display:flex;align-items:center;gap:10px;padding:8px 0;<?= $step['done'] ? 'opacity:0.5;' : '' ?>border-bottom:1px solid #F9FAFB;">
<span style="font-size:18px;"><?= $step['icon'] ?></span>
<span style="color:<?= $step['color'] ?>;font-weight:<?= $step['done'] ? '400' : '600' ?>;font-size:14px;<?= $step['done'] ? 'text-decoration:line-through;' : '' ?>"><?= e($step['text']) ?></span>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<!-- Info Grid --> <!-- Info Grid -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:20px;"> <div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:20px;">
<div class="card"> <div class="card">
...@@ -327,7 +389,7 @@ $isActive = ($member->status === 'active'); ...@@ -327,7 +389,7 @@ $isActive = ($member->status === 'active');
<a href="/payments/member/<?= (int) $member->id ?>" class="btn btn-sm btn-outline">📜 سجل المدفوعات</a> <a href="/payments/member/<?= (int) $member->id ?>" class="btn btn-sm btn-outline">📜 سجل المدفوعات</a>
<?php if ($isActive): ?> <?php if ($isActive): ?>
<a href="/installments/create/<?= (int) $member->id ?>" class="btn btn-sm btn-outline">📅 خطة تقسيط</a> <a href="/installments/create/<?= (int) $member->id ?>" class="btn btn-sm btn-outline">📅 خطة تقسيط</a>
<a href="/subscriptions/member/<?= (int) $member->id ?>" class="btn btn-sm btn-outline">🔄 الاشتراكات</a> <a href="/members/<?= (int) $member->id ?>/subscriptions" class="btn btn-sm btn-outline">🔄 الاشتراكات</a>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </div>
...@@ -351,7 +413,7 @@ $isActive = ($member->status === 'active'); ...@@ -351,7 +413,7 @@ $isActive = ($member->status === 'active');
<div style="margin-bottom:15px;"> <div style="margin-bottom:15px;">
<div style="font-size:12px;color:#6B7280;font-weight:600;margin-bottom:8px;text-transform:uppercase;">🏅 عضويات خاصة</div> <div style="font-size:12px;color:#6B7280;font-weight:600;margin-bottom:8px;text-transform:uppercase;">🏅 عضويات خاصة</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;"> <div style="display:flex;gap:8px;flex-wrap:wrap;">
<a href="/seasonal/create/<?= (int) $member->id ?>" class="btn btn-sm btn-outline">☀️ عضوية موسمية</a> <a href="/members/<?= (int) $member->id ?>/seasonal/create" class="btn btn-sm btn-outline">☀️ عضوية موسمية</a>
<a href="/members/<?= (int) $member->id ?>/sports/create" class="btn btn-sm btn-outline">🏆 عضوية رياضية</a> <a href="/members/<?= (int) $member->id ?>/sports/create" class="btn btn-sm btn-outline">🏆 عضوية رياضية</a>
<a href="/members/<?= (int) $member->id ?>/honorary/create" class="btn btn-sm btn-outline">⭐ عضوية شرفية</a> <a href="/members/<?= (int) $member->id ?>/honorary/create" class="btn btn-sm btn-outline">⭐ عضوية شرفية</a>
<a href="/members/<?= (int) $member->id ?>/foreign/create" class="btn btn-sm btn-outline">🌍 عضوية أجنبية</a> <a href="/members/<?= (int) $member->id ?>/foreign/create" class="btn btn-sm btn-outline">🌍 عضوية أجنبية</a>
...@@ -377,7 +439,7 @@ $isActive = ($member->status === 'active'); ...@@ -377,7 +439,7 @@ $isActive = ($member->status === 'active');
<a href="/transfers/create/<?= (int) $member->id ?>" class="btn btn-sm btn-outline">🔀 طلب تحويل/فصل</a> <a href="/transfers/create/<?= (int) $member->id ?>" class="btn btn-sm btn-outline">🔀 طلب تحويل/فصل</a>
<a href="/divorce/create/<?= (int) $member->id ?>" class="btn btn-sm btn-outline">💔 تسجيل طلاق</a> <a href="/divorce/create/<?= (int) $member->id ?>" class="btn btn-sm btn-outline">💔 تسجيل طلاق</a>
<a href="/death/create/<?= (int) $member->id ?>" class="btn btn-sm btn-outline" style="color:#6B7280;">🕊️ تسجيل وفاة</a> <a href="/death/create/<?= (int) $member->id ?>" class="btn btn-sm btn-outline" style="color:#6B7280;">🕊️ تسجيل وفاة</a>
<a href="/waiver/create/<?= (int) $member->id ?>" class="btn btn-sm btn-outline">📤 طلب تنازل</a> <a href="/waivers/create/<?= (int) $member->id ?>" class="btn btn-sm btn-outline">📤 طلب تنازل</a>
</div> </div>
</div> </div>
<?php endif; ?> <?php endif; ?>
...@@ -396,57 +458,112 @@ $isActive = ($member->status === 'active'); ...@@ -396,57 +458,112 @@ $isActive = ($member->status === 'active');
</div> </div>
</div> </div>
<!-- Spouses --> <!-- ═══════════════════════════════════════════════ -->
<?php if (!empty($spouses)): ?> <!-- FAMILY TREE — unified display of all dependants -->
<!-- ═══════════════════════════════════════════════ -->
<?php
$totalDependants = count($spouses) + count($children) + count($temporaries ?? []);
$categoryLabels = [
'parent' => 'والد/ة', 'special_needs' => 'ذوي احتياجات خاصة', 'unmarried_daughter' => 'ابنة غير متزوجة',
'sister' => 'شقيقة', 'stepchild' => 'ابن/ة زوج', 'orphan' => 'يتيم', 'disabled_sibling' => 'شقيق معاق', 'nanny' => 'مربية',
];
?>
<div class="card" style="margin-bottom:20px;"> <div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center;"> <div style="padding:15px 20px;background:linear-gradient(135deg, #F0FDF4, #ECFDF5);border-bottom:2px solid #A7F3D0;display:flex;justify-content:space-between;align-items:center;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">💍 الزوجات (<?= count($spouses) ?>)</h3> <h3 style="margin:0;color:#065F46;font-size:16px;">&#x1f468;&#x200d;&#x1f469;&#x200d;&#x1f467;&#x200d;&#x1f466; الشجرة العائلية (<?= $totalDependants ?> تابع)</h3>
<a href="/members/<?= (int) $member->id ?>/spouses/create" class="btn btn-sm btn-outline">+ إضافة</a> <div style="display:flex;gap:8px;">
<a href="/members/<?= (int) $member->id ?>/spouses/create" class="btn btn-sm btn-outline">+ زوج/ة</a>
<a href="/members/<?= (int) $member->id ?>/children/create" class="btn btn-sm btn-outline">+ ابن/ة</a>
<a href="/members/<?= (int) $member->id ?>/temporary/create" class="btn btn-sm btn-outline">+ عضو مؤقت</a>
</div>
</div> </div>
<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 ($totalDependants === 0): ?>
<div style="padding:30px;text-align:center;color:#9CA3AF;">
<div style="font-size:36px;margin-bottom:10px;">&#x1f465;</div>
<p style="margin:0;">لا يوجد تابعون مسجلون — يمكنك إضافة زوج/ة، أبناء، أو أعضاء مؤقتين</p>
</div>
<?php else: ?>
<?php if (!empty($spouses)): ?>
<!-- Spouses -->
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<div style="font-size:12px;color:#0D7377;font-weight:700;margin-bottom:10px;text-transform:uppercase;">&#x1f48d; الزوجات (<?= count($spouses) ?>)</div>
<div class="table-responsive"><table class="data-table" style="margin:0;"><thead><tr><th>#</th><th>الاسم</th><th>الرقم القومي</th><th>تاريخ الالتحاق</th><th>الرسوم</th><th>الحالة</th><th></th></tr></thead><tbody>
<?php foreach ($spouses as $s): ?> <?php foreach ($spouses as $s): ?>
<tr> <tr>
<td><?= (int) $s['spouse_order'] ?></td> <td><?= (int) $s['spouse_order'] ?></td>
<td style="font-weight:600;"><?= e($s['full_name_ar']) ?></td> <td style="font-weight:600;"><?= e($s['full_name_ar']) ?></td>
<td style="direction:ltr;text-align:right;font-size:12px;"><?= e($s['national_id'] ?? '—') ?></td> <td style="direction:ltr;text-align:right;font-size:12px;"><?= e($s['national_id'] ?? '—') ?></td>
<td style="font-size:12px;"><?= $s['join_date'] ? e($s['join_date']) : '<span style="color:#D97706;">لم يُحدد بعد</span>' ?></td>
<td style="font-weight:600;"><?php <td style="font-weight:600;"><?php
$fee = $s['addition_fee'] ?? '0.00'; $fee = $s['addition_fee'] ?? '0.00';
if (bccomp($fee, '0', 2) <= 0 && (int) $s['spouse_order'] === 1) echo '<span style="color:#059669;">مشمولة</span>'; if (bccomp($fee, '0', 2) <= 0 && (int) $s['spouse_order'] === 1) echo '<span style="color:#059669;">مشمولة</span>';
else echo money($fee); else echo money($fee);
?></td> ?></td>
<td><span style="color:<?= $s['status'] === 'active' ? '#059669' : '#DC2626' ?>;font-weight:600;"><?= $s['status'] === 'active' ? 'نشط' : $s['status'] ?></span></td> <td><span style="color:<?= match($s['status']) { 'active' => '#059669', 'pending_payment' => '#D97706', default => '#DC2626' } ?>;font-weight:600;font-size:12px;">&#x25cf; <?= match($s['status']) { 'active' => 'نشط', 'pending_payment' => 'في انتظار الدفع', 'inactive' => 'غير نشط', default => $s['status'] } ?></span></td>
<td><a href="/members/<?= (int) $member->id ?>/spouses/<?= (int) $s['id'] ?>" class="btn btn-sm btn-outline">عرض</a></td> <td><a href="/members/<?= (int) $member->id ?>/spouses/<?= (int) $s['id'] ?>" class="btn btn-sm btn-outline">عرض</a></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</tbody></table></div> </tbody></table></div>
</div>
<?php endif; ?>
<!-- Children -->
<?php if (!empty($children)): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">👨‍👩‍👧‍👦 الأبناء (<?= count($children) ?>)</h3>
<a href="/members/<?= (int) $member->id ?>/children/create" class="btn btn-sm btn-outline">+ إضافة</a>
</div> </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 endif; ?>
<?php if (!empty($children)): ?>
<!-- Children -->
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<div style="font-size:12px;color:#0D7377;font-weight:700;margin-bottom:10px;text-transform:uppercase;">&#x1f476; الأبناء (<?= count($children) ?>)</div>
<div class="table-responsive"><table class="data-table" style="margin:0;"><thead><tr><th>#</th><th>الاسم</th><th>النوع</th><th>السن</th><th>التصنيف</th><th>الرسوم</th><th>الحالة</th><th></th></tr></thead><tbody>
<?php foreach ($children as $c): ?> <?php foreach ($children as $c): ?>
<tr> <tr>
<td><?= (int) $c['child_order'] ?></td> <td><?= (int) $c['child_order'] ?></td>
<td style="font-weight:600;"><?= e($c['full_name_ar']) ?></td> <td style="font-weight:600;"><?= e($c['full_name_ar']) ?></td>
<td><?= $c['gender'] === 'male' ? 'ذكر' : 'أنثى' ?></td> <td><?= $c['gender'] === 'male' ? 'ذكر' : 'أنثى' ?></td>
<td><?= (int) ($c['age_years'] ?? 0) ?></td> <td><?= (int) ($c['age_years'] ?? 0) ?></td>
<td style="font-size:12px;"><?= match($c['classification'] ?? '') { 'included' => '<span style="color:#059669;">مشمول</span>', 'dependent_with_fee' => '<span style="color:#D97706;">برسوم</span>', default => e($c['classification'] ?? '') } ?></td> <td style="font-size:12px;"><?= match($c['classification'] ?? '') {
'included' => '<span style="color:#059669;">مشمول</span>',
'dependent_with_fee' => '<span style="color:#D97706;">برسوم</span>',
'frozen' => '<span style="color:#6B7280;">مجمد</span>',
'temporary' => '<span style="color:#3B82F6;">مؤقت</span>',
default => e($c['classification'] ?? '—')
} ?></td>
<td style="font-weight:600;"><?php <td style="font-weight:600;"><?php
$fee = $c['addition_fee'] ?? '0.00'; $fee = $c['addition_fee'] ?? '0.00';
echo bccomp($fee, '0', 2) <= 0 ? '<span style="color:#059669;">مشمول</span>' : money($fee); echo bccomp($fee, '0', 2) <= 0 ? '<span style="color:#059669;">مشمول</span>' : money($fee);
?></td> ?></td>
<td><span style="color:<?= match($c['status'] ?? '') { 'active' => '#059669', 'pending_payment' => '#D97706', 'frozen' => '#6B7280', default => '#DC2626' } ?>;font-weight:600;font-size:12px;">&#x25cf; <?= match($c['status'] ?? '') { 'active' => 'نشط', 'pending_payment' => 'في انتظار الدفع', 'frozen' => 'مجمد', 'inactive' => 'غير نشط', default => $c['status'] ?? '—' } ?></span></td>
<td><a href="/members/<?= (int) $member->id ?>/children/<?= (int) $c['id'] ?>" class="btn btn-sm btn-outline">عرض</a></td> <td><a href="/members/<?= (int) $member->id ?>/children/<?= (int) $c['id'] ?>" class="btn btn-sm btn-outline">عرض</a></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</tbody></table></div> </tbody></table></div>
</div>
<?php endif; ?>
<?php if (!empty($temporaries)): ?>
<!-- Temporary Members -->
<div style="padding:15px 20px;">
<div style="font-size:12px;color:#0D7377;font-weight:700;margin-bottom:10px;text-transform:uppercase;">&#x1f464; الأعضاء المؤقتون (<?= count($temporaries) ?>)</div>
<div class="table-responsive"><table class="data-table" style="margin:0;"><thead><tr><th>الاسم</th><th>الصلة</th><th>النوع</th><th>السن</th><th>الرسوم</th><th>الحالة</th><th></th></tr></thead><tbody>
<?php foreach ($temporaries as $t): ?>
<tr>
<td style="font-weight:600;"><?= e($t['full_name_ar']) ?></td>
<td style="font-size:12px;"><?= $categoryLabels[$t['category']] ?? e($t['category']) ?></td>
<td><?= $t['gender'] === 'male' ? 'ذكر' : 'أنثى' ?></td>
<td><?= (int) ($t['age_years'] ?? 0) ?></td>
<td style="font-weight:600;"><?php
$fee = $t['addition_fee'] ?? '0.00';
echo bccomp($fee, '0', 2) <= 0 ? '<span style="color:#059669;">مشمول</span>' : money($fee);
?></td>
<td><span style="color:<?= match($t['status'] ?? '') { 'active' => '#059669', 'pending_payment' => '#D97706', default => '#DC2626' } ?>;font-weight:600;font-size:12px;">&#x25cf; <?= match($t['status'] ?? '') { 'active' => 'نشط', 'pending_payment' => 'في انتظار الدفع', 'inactive' => 'غير نشط', default => $t['status'] ?? '—' } ?></span></td>
<td><a href="/members/<?= (int) $member->id ?>/temporary/<?= (int) $t['id'] ?>" class="btn btn-sm btn-outline">عرض</a></td>
</tr>
<?php endforeach; ?>
</tbody></table></div>
</div>
<?php endif; ?>
<?php endif; ?>
</div> </div>
<?php endif; ?>
<!-- Status Change --> <!-- Status Change -->
<div class="card" style="margin-bottom:20px;"> <div class="card" style="margin-bottom:20px;">
......
...@@ -11,6 +11,7 @@ use App\Core\EventBus; ...@@ -11,6 +11,7 @@ use App\Core\EventBus;
use App\Modules\Seasonal\Models\SeasonalMembership; use App\Modules\Seasonal\Models\SeasonalMembership;
use App\Modules\Rules\Services\RuleEngine; use App\Modules\Rules\Services\RuleEngine;
use App\Modules\Payments\Services\PaymentService; use App\Modules\Payments\Services\PaymentService;
use App\Modules\Cashier\Services\PaymentRequestService;
class SeasonalController extends Controller class SeasonalController extends Controller
{ {
...@@ -110,21 +111,20 @@ class SeasonalController extends Controller ...@@ -110,21 +111,20 @@ class SeasonalController extends Controller
'notes' => $data['notes'] ?? null, 'notes' => $data['notes'] ?? null,
]); ]);
// Process payment through central PaymentService // Send payment to cashier queue
if (bccomp($fee, '0.00', 2) > 0) { if (bccomp($fee, '0.00', 2) > 0) {
$payResult = PaymentService::processPayment([ $payResult = PaymentRequestService::createRequest([
'member_id' => (int) $memberId, 'member_id' => (int) $memberId,
'amount' => $fee, 'amount' => $fee,
'payment_type' => 'seasonal_fee', 'payment_type' => 'seasonal_fee',
'payment_method' => $data['payment_method'] ?? 'cash',
'related_entity_type' => 'seasonal_memberships', 'related_entity_type' => 'seasonal_memberships',
'related_entity_id' => (int) $seasonal->id, 'related_entity_id' => (int) $seasonal->id,
'description' => 'رسوم عضوية موسمية — من ' . $startDate . ' إلى ' . $endDate, 'description_ar' => 'رسوم عضوية موسمية — من ' . $startDate . ' إلى ' . $endDate,
]); ]);
if (!$payResult['success']) { if (!$payResult['success']) {
return $this->redirect("/members/{$memberId}/seasonal/create") return $this->redirect("/members/{$memberId}/seasonal/create")
->withError('تم إنشاء العضوية الموسمية لكن فشل تسجيل الدفع: ' . ($payResult['error'] ?? '')); ->withError('تم إنشاء العضوية الموسمية لكن فشل إنشاء طلب الدفع: ' . ($payResult['error'] ?? ''));
} }
} }
...@@ -135,6 +135,6 @@ class SeasonalController extends Controller ...@@ -135,6 +135,6 @@ class SeasonalController extends Controller
]); ]);
return $this->redirect("/members/{$memberId}") return $this->redirect("/members/{$memberId}")
->withSuccess("تم إنشاء العضوية الموسمية — من {$startDate} إلى {$endDate} — الرسوم: " . money($fee)); ->withSuccess("تم إنشاء العضوية الموسمية وإرسال طلب الدفع للخزينة — من {$startDate} إلى {$endDate} — الرسوم: " . money($fee));
} }
} }
\ No newline at end of file
...@@ -11,6 +11,7 @@ use App\Core\EventBus; ...@@ -11,6 +11,7 @@ use App\Core\EventBus;
use App\Modules\Spouses\Models\Spouse; use App\Modules\Spouses\Models\Spouse;
use App\Modules\Spouses\Services\SpouseFeeCalculator; use App\Modules\Spouses\Services\SpouseFeeCalculator;
use App\Modules\Members\Services\NationalIdParser; use App\Modules\Members\Services\NationalIdParser;
use App\Modules\Cashier\Services\PaymentRequestService;
class SpouseController extends Controller class SpouseController extends Controller
{ {
...@@ -145,6 +146,9 @@ class SpouseController extends Controller ...@@ -145,6 +146,9 @@ class SpouseController extends Controller
$spouseOrder = $feeCalc['spouse_order'] ?? ($currentCount + 1); $spouseOrder = $feeCalc['spouse_order'] ?? ($currentCount + 1);
$totalFee = $feeCalc['total_fee'] ?? '0.00';
$hasFee = bccomp($totalFee, '0', 2) > 0;
$spouse = Spouse::create([ $spouse = Spouse::create([
'member_id' => (int) $memberId, 'member_id' => (int) $memberId,
'spouse_order' => $spouseOrder, 'spouse_order' => $spouseOrder,
...@@ -164,22 +168,43 @@ class SpouseController extends Controller ...@@ -164,22 +168,43 @@ class SpouseController extends Controller
'work_phone' => $data['work_phone'] ?? null, 'work_phone' => $data['work_phone'] ?? null,
'mobile' => $data['mobile'] ?? null, 'mobile' => $data['mobile'] ?? null,
'marriage_date' => $data['marriage_date'], 'marriage_date' => $data['marriage_date'],
'join_date' => date('Y-m-d'), 'join_date' => $hasFee ? null : date('Y-m-d'),
'classification' => 'working', 'classification' => 'working',
'addition_fee' => $feeCalc['total_fee'] ?? '0.00', 'addition_fee' => $totalFee,
'status' => 'active', 'status' => $hasFee ? 'pending_payment' : 'active',
]); ]);
EventBus::dispatch('spouse.added', [ EventBus::dispatch('spouse.added', [
'member_id' => (int) $memberId, 'member_id' => (int) $memberId,
'spouse_id' => (int) $spouse->id, 'spouse_id' => (int) $spouse->id,
'spouse_order'=> $spouseOrder, 'spouse_order'=> $spouseOrder,
'fee' => $feeCalc['total_fee'] ?? '0.00', 'fee' => $totalFee,
]); ]);
$genderWord = $requiredGender === 'male' ? 'الزوج' : 'الزوجة'; $genderWord = $requiredGender === 'male' ? 'الزوج' : 'الزوجة';
if ($hasFee) {
$payResult = PaymentRequestService::createRequest([
'member_id' => (int) $memberId,
'amount' => $totalFee,
'payment_type' => 'addition_fee',
'related_entity_type' => 'spouses',
'related_entity_id' => (int) $spouse->id,
'description_ar' => 'رسوم إضافة ' . $genderWord . ' — ' . trim($data['full_name_ar']),
'notes' => json_encode(['fee_breakdown' => $feeCalc['breakdown'] ?? []], JSON_UNESCAPED_UNICODE),
]);
if ($payResult['success']) {
return $this->redirect("/members/{$memberId}")
->withSuccess("تم إضافة {$genderWord} وإرسال طلب الدفع للخزينة — الرسوم: " . money($totalFee) . ' — رقم الطلب: ' . $payResult['request_number']);
}
return $this->redirect("/members/{$memberId}")
->withError("تم إضافة {$genderWord} لكن فشل إنشاء طلب الدفع: " . ($payResult['error'] ?? ''));
}
return $this->redirect("/members/{$memberId}") return $this->redirect("/members/{$memberId}")
->withSuccess("تم إضافة {$genderWord} — الترتيب: #{$spouseOrder}الرسوم: " . money($feeCalc['total_fee'] ?? '0.00')); ->withSuccess("تم إضافة {$genderWord} — الترتيب: #{$spouseOrder}مشمول/ة في قيمة العضوية");
} }
public function show(Request $request, string $memberId, string $id): Response public function show(Request $request, string $memberId, string $id): Response
......
...@@ -11,6 +11,7 @@ use App\Core\EventBus; ...@@ -11,6 +11,7 @@ use App\Core\EventBus;
use App\Modules\Temporary\Models\TemporaryMember; use App\Modules\Temporary\Models\TemporaryMember;
use App\Modules\Temporary\Services\TemporaryFeeCalculator; use App\Modules\Temporary\Services\TemporaryFeeCalculator;
use App\Modules\Members\Services\NationalIdParser; use App\Modules\Members\Services\NationalIdParser;
use App\Modules\Cashier\Services\PaymentRequestService;
class TemporaryController extends Controller class TemporaryController extends Controller
{ {
...@@ -129,6 +130,9 @@ class TemporaryController extends Controller ...@@ -129,6 +130,9 @@ class TemporaryController extends Controller
$feeCalc = TemporaryFeeCalculator::calculate((int) $memberId, $data); $feeCalc = TemporaryFeeCalculator::calculate((int) $memberId, $data);
$totalFee = $feeCalc['total_fee'] ?? $feeCalc['fee'] ?? '0.00';
$hasFee = bccomp($totalFee, '0', 2) > 0;
$temp = TemporaryMember::create([ $temp = TemporaryMember::create([
'member_id' => (int) $memberId, 'member_id' => (int) $memberId,
'category' => $category, 'category' => $category,
...@@ -144,10 +148,10 @@ class TemporaryController extends Controller ...@@ -144,10 +148,10 @@ class TemporaryController extends Controller
'relationship_to_member' => $data['relationship_to_member'] ?? null, 'relationship_to_member' => $data['relationship_to_member'] ?? null,
'has_championship' => !empty($data['has_championship']) ? 1 : 0, 'has_championship' => !empty($data['has_championship']) ? 1 : 0,
'disability_documentation' => !empty($data['disability_documentation']) ? 1 : 0, 'disability_documentation' => !empty($data['disability_documentation']) ? 1 : 0,
'addition_fee' => $feeCalc['fee'] ?? '0.00', 'addition_fee' => $totalFee,
'can_separate' => TemporaryFeeCalculator::canSeparate($category) ? 1 : 0, 'can_separate' => TemporaryFeeCalculator::canSeparate($category) ? 1 : 0,
'can_get_independent' => TemporaryFeeCalculator::canGetIndependent($category) ? 1 : 0, 'can_get_independent' => TemporaryFeeCalculator::canGetIndependent($category) ? 1 : 0,
'status' => 'active', 'status' => $hasFee ? 'pending_payment' : 'active',
'notes' => $data['notes'] ?? null, 'notes' => $data['notes'] ?? null,
]); ]);
...@@ -155,11 +159,31 @@ class TemporaryController extends Controller ...@@ -155,11 +159,31 @@ class TemporaryController extends Controller
'member_id' => (int) $memberId, 'member_id' => (int) $memberId,
'temporary_id' => (int) $temp->id, 'temporary_id' => (int) $temp->id,
'category' => $category, 'category' => $category,
'fee' => $feeCalc['fee'] ?? '0.00', 'fee' => $totalFee,
]); ]);
if ($hasFee) {
$catLabel = $temp->getCategoryLabel();
$payResult = PaymentRequestService::createRequest([
'member_id' => (int) $memberId,
'amount' => $totalFee,
'payment_type' => 'addition_fee',
'related_entity_type' => 'temporary_members',
'related_entity_id' => (int) $temp->id,
'description_ar' => 'رسوم إضافة عضو مؤقت (' . $catLabel . ') — ' . trim($data['full_name_ar']),
]);
if ($payResult['success']) {
return $this->redirect("/members/{$memberId}")
->withSuccess('تم إضافة العضو المؤقت وإرسال طلب الدفع للخزينة — الرسوم: ' . money($totalFee) . ' — رقم الطلب: ' . $payResult['request_number']);
}
return $this->redirect("/members/{$memberId}")
->withError('تم إضافة العضو المؤقت لكن فشل إنشاء طلب الدفع: ' . ($payResult['error'] ?? ''));
}
return $this->redirect("/members/{$memberId}") return $this->redirect("/members/{$memberId}")
->withSuccess('تم إضافة العضو المؤقت — الرسوم: ' . money($feeCalc['fee'] ?? '0.00')); ->withSuccess('تم إضافة العضو المؤقت — معفى من الرسوم');
} }
public function show(Request $request, string $memberId, string $id): Response public function show(Request $request, string $memberId, string $id): Response
......
...@@ -31,7 +31,7 @@ $currentPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH); ...@@ -31,7 +31,7 @@ $currentPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script> <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
<!-- Main Stylesheet --> <!-- Main Stylesheet -->
<link rel="stylesheet" href="<?= url('assets/css/main.css') ?>"> <link rel="stylesheet" href="<?= url('assets/css/main.css') ?>?v=<?= filemtime(dirname(__DIR__, 2) . '/public/assets/css/main.css') ?: time() ?>">
<?= $__template->yield('styles', '') ?> <?= $__template->yield('styles', '') ?>
</head> </head>
<body> <body>
...@@ -97,7 +97,7 @@ $currentPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH); ...@@ -97,7 +97,7 @@ $currentPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
</footer> </footer>
</div> </div>
<script src="<?= url('assets/js/app.js') ?>"></script> <script src="<?= url('assets/js/app.js') ?>?v=<?= filemtime(dirname(__DIR__, 2) . '/public/assets/js/app.js') ?: time() ?>"></script>
<script> <script>
// Initialize Lucide icons // Initialize Lucide icons
lucide.createIcons(); lucide.createIcons();
......
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