Commit 4372c4e4 authored by Mahmoud Aglan's avatar Mahmoud Aglan

dfgjdfhjh

parent de5cfc33
{
"permissions": {
"allow": [
"Bash(wc -l /Users/mahmoudaglan/clubphp/app/Core/*.php)"
"Bash(wc -l /Users/mahmoudaglan/clubphp/app/Core/*.php)",
"Bash(grep -r \"icon\" /Users/mahmoudaglan/clubphp/app/Modules/*/bootstrap.php)",
"Bash(grep -h \"'icon'\" /Users/mahmoudaglan/clubphp/app/Modules/*/bootstrap.php)"
]
}
}
......@@ -10,7 +10,7 @@ use App\Core\Registries\MenuRegistry;
$currentPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
$employee = App::getInstance()->currentEmployee();
// Icon name → Lucide icon mapping
// Icon name → Lucide icon mapping (includes legacy names, short names, and emoji fallbacks)
$iconMap = [
'dashboard' => 'layout-dashboard', 'tachometer-alt' => 'gauge', 'home' => 'home',
'users' => 'users', 'user' => 'user', 'user-plus' => 'user-plus', 'user-clock' => 'user-cog',
......@@ -21,14 +21,15 @@ $iconMap = [
'credit-card' => 'credit-card', 'receipt' => 'receipt', 'coins' => 'coins',
'file-invoice-dollar' => 'file-text', 'hand-holding-usd' => 'hand-coins',
'exchange-alt' => 'arrow-left-right', 'random' => 'shuffle', 'transfer' => 'arrow-left-right',
'swap' => 'arrow-left-right',
'gavel' => 'gavel', 'balance-scale' => 'scale', 'exclamation-triangle' => 'alert-triangle',
'alert' => 'alert-triangle', 'warning' => 'alert-triangle', 'ban' => 'ban',
'trophy' => 'trophy', 'medal' => 'medal', 'award' => 'award', 'star' => 'star',
'globe' => 'globe', 'globe-americas' => 'globe-2', 'flag' => 'flag',
'id-card' => 'id-card', 'address-card' => 'contact', 'qrcode' => 'qr-code',
'file' => 'folder', 'folder' => 'folder', 'folder-open' => 'folder-open',
'file' => 'file-text', 'folder' => 'folder', 'folder-open' => 'folder-open',
'sms' => 'smartphone', 'envelope' => 'mail', 'bell' => 'bell', 'comment' => 'message-circle',
'chart-bar' => 'bar-chart-3', 'chart-line' => 'trending-up', 'chart-pie' => 'pie-chart',
'chart' => 'bar-chart-3', 'chart-bar' => 'bar-chart-3', 'chart-line' => 'trending-up', 'chart-pie' => 'pie-chart',
'cog' => 'settings', 'cogs' => 'settings-2', 'wrench' => 'wrench', 'tools' => 'wrench',
'sliders-h' => 'sliders-horizontal', 'settings' => 'settings',
'shield-alt' => 'shield', 'lock' => 'lock', 'key' => 'key-round',
......@@ -43,15 +44,44 @@ $iconMap = [
'dollar-sign' => 'circle-dollar-sign', 'percentage' => 'percent',
'swimming-pool' => 'waves', 'running' => 'activity', 'futbol' => 'trophy',
'sun' => 'sun', 'umbrella-beach' => 'umbrella',
// Emoji → Lucide fallbacks (some modules still register emojis)
'📊' => 'layout-dashboard', '🏠' => 'home',
'👥' => 'users', '👤' => 'user', '⏰' => 'clock',
'👔' => 'briefcase', '🛡️' => 'shield-check',
'📋' => 'clipboard-list', '📄' => 'file-text',
'📅' => 'calendar', '✅' => 'check-circle',
'💰' => 'banknote', '💳' => 'wallet', '🧾' => 'receipt', '🪙' => 'coins',
'💲' => 'circle-dollar-sign',
'🔀' => 'arrow-left-right',
'⚖️' => 'scale', '⚠️' => 'alert-triangle', '🚫' => 'ban',
'🏆' => 'trophy', '🏅' => 'medal', '🎖️' => 'award', '⭐' => 'star',
'🌍' => 'globe', '🌎' => 'globe', '🏳️' => 'flag',
'🪪' => 'id-card', '📱' => 'smartphone',
'📁' => 'folder', '📂' => 'folder-open',
'✉️' => 'mail', '🔔' => 'bell', '💬' => 'message-circle',
'📈' => 'trending-up',
'⚙️' => 'settings', '🔧' => 'wrench', '🛠️' => 'wrench',
'🔐' => 'shield', '🔒' => 'lock', '🔑' => 'key-round',
'🏢' => 'building-2', '🏙️' => 'building', '🏪' => 'store',
'📖' => 'book-open', '📜' => 'history', '🗄️' => 'archive',
'🔄' => 'refresh-cw',
'❤️' => 'heart', '💓' => 'heart-pulse', '✝️' => 'cross',
'💍' => 'gem', '👶' => 'baby', '👦' => 'user', '👨‍👩‍👧‍👦' => 'users',
'🖨️' => 'printer', '🔍' => 'search', '➕' => 'plus', '✏️' => 'pencil',
'🗑️' => 'trash-2', '❌' => 'x',
'💹' => 'percent',
'🏊' => 'waves', '🏃' => 'activity', '⚽' => 'trophy',
'☀️' => 'sun', '🏖️' => 'umbrella',
'📌' => 'circle',
];
$getIcon = function(?string $icon) use ($iconMap): string {
if ($icon === null || $icon === '') return 'circle';
// If it's already a lucide icon name (contains hyphen or is a known name)
// Direct lookup (handles both text keys and emoji keys)
if (isset($iconMap[$icon])) return $iconMap[$icon];
$lower = strtolower($icon);
if (isset($iconMap[$lower])) return $iconMap[$lower];
// Check if it looks like it might already be a lucide icon name
// Check if it looks like a valid Lucide icon name already
if (preg_match('/^[a-z][a-z0-9-]+$/', $lower)) return $lower;
return 'circle';
};
......@@ -161,7 +191,7 @@ if (empty($menuItems)) {
<span class="sidebar-text"><?= e($item['label_ar']) ?></span>
<span class="sidebar-arrow"><i data-lucide="chevron-down"></i></span>
</a>
<ul class="sidebar-submenu" style="display:<?= $isOpen ? 'block' : 'none' ?>;<?= $isOpen ? '' : 'max-height:0;overflow:hidden;' ?>">
<ul class="sidebar-submenu"<?= $isOpen ? '' : ' style="display:none;"' ?>>
<?php foreach ($visibleChildren as $child): ?>
<?php $childRoute = $child['route'] ?? '#'; ?>
<li>
......
......@@ -127,20 +127,46 @@ function closeSidebar() {
function toggleSubmenu(el) {
var parent = el.parentElement;
var submenu = parent.querySelector('.sidebar-submenu');
if (submenu) {
if (!submenu) return;
var isOpen = parent.classList.contains('open');
parent.classList.toggle('open', !isOpen);
if (!isOpen) {
submenu.style.maxHeight = submenu.scrollHeight + 'px';
// Opening: show first, then animate height
submenu.style.display = 'block';
submenu.style.overflow = 'hidden';
submenu.style.maxHeight = '0px';
// Force reflow so the browser registers the 0 height
submenu.offsetHeight;
submenu.style.transition = 'max-height 0.3s cubic-bezier(0.16, 1, 0.3, 1)';
submenu.style.maxHeight = submenu.scrollHeight + 'px';
parent.classList.add('open');
// Clean up after transition
submenu.addEventListener('transitionend', function handler() {
submenu.removeEventListener('transitionend', handler);
if (parent.classList.contains('open')) {
submenu.style.maxHeight = '';
submenu.style.overflow = '';
submenu.style.transition = '';
}
});
} else {
submenu.style.maxHeight = '0';
setTimeout(function() {
// Closing: set explicit height first, then animate to 0
submenu.style.overflow = 'hidden';
submenu.style.maxHeight = submenu.scrollHeight + 'px';
submenu.offsetHeight;
submenu.style.transition = 'max-height 0.25s ease';
submenu.style.maxHeight = '0px';
parent.classList.remove('open');
submenu.addEventListener('transitionend', function handler() {
submenu.removeEventListener('transitionend', handler);
if (!parent.classList.contains('open')) {
submenu.style.display = 'none';
submenu.style.maxHeight = '';
submenu.style.overflow = '';
submenu.style.transition = '';
}
}, 300);
}
});
}
}
......
......@@ -157,33 +157,8 @@ function confirmModal(title, message, onConfirm) {
}
// ── Sidebar Toggle ──
function toggleSidebar() {
document.getElementById('sidebar').classList.toggle('open');
}
function toggleSubmenu(el) {
const parent = el.closest('.sidebar-item');
const submenu = parent.querySelector('.sidebar-submenu');
if (submenu) {
const isOpen = parent.classList.contains('open');
parent.classList.toggle('open', !isOpen);
if (!isOpen) {
submenu.style.display = 'block';
submenu.style.maxHeight = submenu.scrollHeight + 'px';
submenu.style.overflow = 'hidden';
submenu.style.transition = 'max-height 0.3s cubic-bezier(0.16, 1, 0.3, 1)';
} else {
submenu.style.maxHeight = '0';
submenu.style.overflow = 'hidden';
submenu.style.transition = 'max-height 0.25s ease';
setTimeout(function() {
if (!parent.classList.contains('open')) {
submenu.style.display = 'none';
}
}, 250);
}
}
}
// Note: toggleSidebar() and toggleSubmenu() are defined in main.php layout
// so they have access to sidebar-specific DOM elements and run after the sidebar renders.
// ── National ID Parser ──
function initNationalIdParser(inputId, config = {}) {
......
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