<?php require_once __DIR__ . '/../../app/core/bootstrap.php'; require_once __DIR__ . '/../../app/core/project_permissions.php'; require_once __DIR__ . '/../../app/core/i18n.php'; auth_require_login(); $page_title = 'Projects'; $pdo = crm_pdo(); $_lang = gp_lang($pdo); $role = gp_user_role($pdo); $userId = gp_current_user_id(); $projectModel = new ProjectModel($pdo); $filterYear = isset($_GET['year']) ? (int)$_GET['year'] : null; $filterStatus = isset($_GET['status']) ? (string)$_GET['status'] : null; $rows = $projectModel->getListForRole($role, $userId, $filterYear, $filterStatus); // Assistant filters $assistantFilter = isset($_GET['assistant_filter']) ? $_GET['assistant_filter'] : ''; if ($assistantFilter === 'unpaid') { $stmt = $pdo->query(" SELECT id FROM projects p WHERE status = 'completed' AND COALESCE(budget, 0) > ( SELECT COALESCE(SUM(amount), 0) FROM project_payments WHERE project_id = p.id ) "); $unpaidIds = $stmt->fetchAll(PDO::FETCH_COLUMN); $rows = array_filter($rows, function($r) use ($unpaidIds) { return in_array($r['id'], $unpaidIds); }); } elseif ($assistantFilter === 'overdue') { $stmt = $pdo->query(" SELECT id FROM projects WHERE status != 'completed' AND status != 'cancelled' AND created_at < DATE_SUB(NOW(), INTERVAL 1 DAY) "); $overdueIds = $stmt->fetchAll(PDO::FETCH_COLUMN); $rows = array_filter($rows, function($r) use ($overdueIds) { return in_array($r['id'], $overdueIds); }); } // Get unique years for the dropdown $stmtY = $pdo->query("SELECT DISTINCT YEAR(start_date) as yr FROM projects WHERE start_date IS NOT NULL ORDER BY yr DESC"); $years = $stmtY->fetchAll(PDO::FETCH_COLUMN); // Available statuses $statuses = gp_project_statuses($pdo); include __DIR__ . '/../includes/header.php'; ?> <style> .projects-card-modern{padding:0; border-radius: var(--radius); background: var(--card); backdrop-filter: var(--glass-blur); border: 1px solid var(--border);} .projects-toolbar{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:24px 24px 8px 24px} .projects-toolbar-left{display:flex;flex-direction:column;gap:4px} .projects-toolbar-left h2{margin:0;font-size:1.2rem; font-weight:700; color:var(--text);} .projects-toolbar-left span{opacity:.8;font-size:.9rem; color:var(--muted);} .filter-nav-bar { display: flex; align-items: center; gap: 8px; padding: 0 24px 16px 24px; overflow-x: auto; scrollbar-width: none; } .filter-nav-bar::-webkit-scrollbar { display: none; } .filter-btn { padding: 6px 14px; border-radius: 999px; background: rgba(0,0,0,0.03); color: var(--gp-muted); font-size: 0.8rem; font-weight: 600; text-decoration: none; transition: all 0.2s; white-space: nowrap; border: 1px solid transparent; } :root[data-theme='dark'] .filter-btn { background: rgba(255,255,255,0.03); } .filter-btn:hover { background: rgba(0,0,0,0.06); color: var(--gp-text); } [data-theme='dark'] .filter-btn:hover { background: rgba(255,255,255,0.06); } .filter-btn.active { background: var(--gp-primary); color: #fff; border-color: var(--gp-primary); box-shadow: 0 4px 10px rgba(37,99,235,0.2); } .year-select { padding: 8px 12px; border-radius: 12px; border: 1px solid var(--border); background: var(--gp-card-bg); color: var(--gp-text); font-size: 0.85rem; font-weight: 600; outline: none; cursor: pointer; } .projects-table-wrap{padding:0 24px 24px 24px;} .projects-table-modern{width:100%;border-collapse:separate;border-spacing:0 10px;table-layout:fixed} .projects-table-modern thead th{font-size:.8rem;font-weight:700;color:var(--text-dark);text-transform:uppercase;letter-spacing:.03em;padding:0 12px 8px 12px;text-align:left;vertical-align:bottom} .projects-table-modern tbody tr{background:rgba(0,0,0,0.02); transition: transform 0.2s, box-shadow 0.2s;} :root[data-theme='dark'] .projects-table-modern tbody tr{background:rgba(255,255,255,0.02);} .projects-table-modern tbody tr:hover{transform:translateY(-2px); box-shadow:0 4px 16px rgba(0,0,0,0.08); position:relative; z-index:99;} .projects-table-modern tbody td{padding:16px 12px;vertical-align:middle;border-top:1px solid var(--border);border-bottom:1px solid var(--border)} .projects-table-modern tbody td:first-child{border-left:1px solid var(--border);border-top-left-radius:14px;border-bottom-left-radius:14px} .projects-table-modern tbody td:last-child{border-right:1px solid var(--border);border-top-right-radius:14px;border-bottom-right-radius:14px; text-align:right;} .project-id-col{width:64px} .project-main-col{width:25%} .project-customer-col{width:18%} .project-status-col{width:14%} .project-date-col{width:120px} .project-actions-col{width:160px; text-align:right;} .project-title-strong{display:block;font-weight:700;font-size:1rem;line-height:1.25; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:260px;} .project-code-sub{display:block;margin-top:4px;font-size:.85rem;color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:260px;} .project-customer-cell{font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:180px;} .project-date-cell{white-space:nowrap; font-size:.9rem; color:var(--text-dark);} .project-actions-row{display:flex;flex-wrap:wrap;gap:8px;align-items:center; justify-content:flex-end;} .project-actions-row .btn{white-space:nowrap} /* Customer filter highlight */ .customer-filter-wrap { position: relative; } .customer-filter-wrap .search-icon { position:absolute; left:10px; top:50%; transform:translateY(-50%); pointer-events:none; font-size:.85rem; } .customer-filter-input { padding: 7px 10px 7px 32px; border-radius: 12px; border: 1px solid var(--border); background: var(--gp-card-bg); color: var(--gp-text); font-size: 0.85rem; font-weight: 600; outline: none; width: 180px; transition: border-color .2s; } .customer-filter-input:focus { border-color: var(--gp-primary); } /* Table Dropdown */ .table-dropdown { position: relative; display: inline-block; text-align: left; } .table-dropdown-content { display: none; position: absolute; right: 0; top: 100%; margin-top: 4px; background: var(--gp-card-bg, #fff); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 6px; flex-direction: column; gap: 4px; z-index: 1000; box-shadow: var(--shadow-lg); min-width: 150px; backdrop-filter: blur(16px); } :root[data-theme='dark'] .table-dropdown-content { background: var(--gp-card-bg, #111827); } .table-dropdown:hover .table-dropdown-content, .table-dropdown:focus-within .table-dropdown-content { display: flex; animation: slideUp .2s cubic-bezier(0.4,0,0.2,1); } .table-dropdown-item { padding: 10px 14px; text-decoration: none; color: var(--text); font-size: 0.85rem; font-weight: 600; border-radius: 8px; transition: all .2s; } .table-dropdown-item:hover { background: rgba(0,0,0,0.05); color: var(--primary); } :root[data-theme='dark'] .table-dropdown-item:hover { background: rgba(255,255,255,0.05); } @media (max-width: 980px){ .projects-table-modern, .projects-table-modern thead, .projects-table-modern tbody, .projects-table-modern th, .projects-table-modern td, .projects-table-modern tr{display:block;width:100%} .projects-table-modern thead{display:none} .projects-table-modern tbody tr{margin-bottom:12px;border-radius:14px} .projects-table-modern tbody td{border-left:1px solid rgba(15,23,42,.06)!important;border-right:1px solid rgba(15,23,42,.06)!important;border-radius:0!important} .projects-table-modern tbody td:first-child{border-top-left-radius:14px!important;border-top-right-radius:14px!important} .projects-table-modern tbody td:last-child{border-bottom-left-radius:14px!important;border-bottom-right-radius:14px!important} } </style> <div class="page-header"> <div class="page-header-title"> <h1><?php echo gp_tr($pdo,'projects'); ?></h1> <span><?php echo gp_tr($pdo,'all_projects'); ?></span> </div> <div class="page-header-actions" style="display:flex; gap:12px; align-items:center;"> <div class="view-toggle" style="display:flex; background:var(--gp-nav-bg-2); border-radius:8px; padding:4px; border:1px solid var(--gp-card-border);"> <a href="index.php" style="padding:6px 12px; text-decoration:none; color:var(--gp-text); background:var(--gp-card-bg); font-size:0.85rem; font-weight:600; border-radius:6px; box-shadow:0 2px 4px rgba(0,0,0,0.05);"><?php echo $_lang==='el'?'Λίστα':'List'; ?></a> <a href="board.php" style="padding:6px 12px; text-decoration:none; color:var(--gp-muted); font-size:0.85rem; font-weight:600; border-radius:6px;"><?php echo $_lang==='el'?'Πίνακας':'Board'; ?></a> <a href="gantt.php" style="padding:6px 12px; text-decoration:none; color:var(--gp-muted); font-size:0.85rem; font-weight:600; border-radius:6px;">📊 Gantt</a> </div> <?php if (gp_can_create_project($pdo)): ?> <a class="btn btn-primary btn-sm" href="edit.php">+ <?php echo gp_tr($pdo,'new_project'); ?></a> <?php endif; ?> </div> </div> <?php include __DIR__ . '/../includes/assistant.php'; ?> <div class="card projects-card-modern"> <div class="projects-toolbar"> <div class="projects-toolbar-left"> <h2> <?php if ($assistantFilter === 'unpaid') echo gp_tr($pdo, 'unpaid_projects'); elseif ($assistantFilter === 'overdue') echo gp_tr($pdo, 'overdue_projects'); else echo gp_tr($pdo, 'all_projects'); ?> </h2> <span><?php echo count($rows); ?> <?php echo $_lang==='el'?'έργα βρέθηκαν':'projects found'; ?></span> </div> <div style="display:flex; align-items:center; gap:10px; flex-wrap:wrap;"> <select class="year-select" id="year-select" onchange="applyFilters()"> <option value=""><?php echo $_lang==='el'?'Όλα τα έτη':'All years'; ?></option> <?php foreach ($years as $y): ?> <option value="<?php echo $y; ?>" <?php echo $filterYear === (int)$y ? 'selected' : ''; ?>><?php echo $y; ?></option> <?php endforeach; ?> </select> <div class="customer-filter-wrap"> <span class="search-icon">🏢</span> <input type="text" id="customer-filter" class="customer-filter-input" placeholder="<?php echo $_lang==='el'?'Πελάτης...':'Customer...'; ?>" autocomplete="off"> </div> <div class="list-search-wrap" style="max-width:240px"> <span class="search-icon">🔍</span> <input type="text" id="title-search" class="list-search-input" placeholder="<?php echo gp_tr($pdo,'search').'...'; ?>" autocomplete="off"> </div> </div> </div> <div class="filter-nav-bar"> <a href="index.php?year=<?php echo $filterYear; ?>&status=all" class="filter-btn <?php echo (!$filterStatus || $filterStatus === 'all') ? 'active' : ''; ?>"><?php echo $_lang==='el'?'Όλες οι Καταστάσεις':'All Statuses'; ?></a> <?php foreach ($statuses as $k => $v): ?> <a href="index.php?year=<?php echo $filterYear; ?>&status=<?php echo urlencode($k); ?>" class="filter-btn <?php echo $filterStatus === $k ? 'active' : ''; ?>"> <?php echo h($v); ?> </a> <?php endforeach; ?> </div> <?php if (!$rows): ?> <div class="projects-empty"> <p class="small"><?php echo $_lang==='el'?'Δεν βρέθηκαν προσβάσιμα έργα.':'No accessible projects found.'; ?></p> </div> <?php else: ?> <div class="projects-table-wrap"> <table class="projects-table-modern" id="projects-table-main"> <thead> <tr> <th class="project-id-col">ID</th> <th class="project-main-col"><?php echo gp_tr($pdo,'project'); ?></th> <th class="project-customer-col"><?php echo gp_tr($pdo,'customer'); ?></th> <th class="project-status-col"><?php echo gp_tr($pdo,'status'); ?></th> <th class="project-date-col"><?php echo gp_tr($pdo,'start_date'); ?></th> <th class="project-date-col"><?php echo $_lang==='el'?'Καταχώρηση':'Added by'; ?></th> <th class="project-actions-col"><?php echo gp_tr($pdo,'actions'); ?></th> </tr> </thead> <tbody> <?php foreach ($rows as $row): ?> <tr> <td>#<?php echo (int)$row['id']; ?></td> <td> <span class="project-title-strong"><?php echo h($row['title']); ?></span> <span class="project-code-sub"><?php echo h($row['code']); ?></span> </td> <td class="project-customer-cell"><?php echo h($row['customer_name']); ?></td> <td><span class="badge"><?php echo h(gp_project_status_label($pdo, (string)$row['status'])); ?></span></td> <td class="project-date-cell"><?php echo h(gp_date((string)$row['start_date'])); ?></td> <td> <?php if (!empty($row['created_by_username'])): ?> <div style="display:flex;align-items:center;gap:6px;"> <span style="width:24px;height:24px;border-radius:50%;background:linear-gradient(135deg,var(--gp-primary),#6366f1);color:#fff;display:inline-flex;align-items:center;justify-content:center;font-size:.65rem;font-weight:800;flex-shrink:0;"><?php echo strtoupper(substr($row['created_by_username'],0,1)); ?></span> <div> <div style="font-size:.8rem;font-weight:700;line-height:1.2;"><?php echo h($row['created_by_username']); ?></div> <?php if (!empty($row['created_at'])): ?> <div style="font-size:.68rem;color:var(--gp-muted);"><?php echo gp_date(substr($row['created_at'],0,10)); ?></div> <?php endif; ?> </div> </div> <?php else: ?> <span style="color:var(--gp-muted);font-size:.78rem;">—</span> <?php endif; ?> </td> <td> <div class="project-actions-row"> <a class="btn btn-sm btn-primary" href="view.php?id=<?php echo (int)$row['id']; ?>" style="box-shadow:none;"><?php echo gp_tr($pdo,'open'); ?></a> <div class="table-dropdown"> <a href="javascript:void(0)" class="btn btn-sm btn-secondary" style="padding:9px 12px;">•••</a> <div class="table-dropdown-content"> <a class="table-dropdown-item" href="equipment.php?project_id=<?php echo (int)$row['id']; ?>"><?php echo gp_tr($pdo,'equipment'); ?></a> <a class="table-dropdown-item" href="payments.php?project_id=<?php echo (int)$row['id']; ?>"><?php echo gp_tr($pdo,'payments'); ?></a> <a class="table-dropdown-item" href="dispatch.php?project_id=<?php echo (int)$row['id']; ?>"><?php echo gp_tr($pdo,'dispatch'); ?></a> <?php if (gp_can_manage_project_access($pdo)): ?> <a class="table-dropdown-item" href="access.php?project_id=<?php echo (int)$row['id']; ?>"><?php echo $_lang==='el'?'Διαχ. Πρόσβασης':'Manage Access'; ?></a> <a class="table-dropdown-item" href="javascript:void(0)" onclick="deleteProject(<?php echo (int)$row['id']; ?>)" style="color: #ef4444; border-top: 1px solid rgba(0,0,0,0.05); margin-top: 4px; padding-top: 10px;"><?php echo $_lang==='el'?'🗑 Διαγραφή':'🗑 Delete'; ?></a> <?php endif; ?> </div> </div> </div> </td> </tr> <?php endforeach; ?> </tbody> </table> </div> <?php endif; ?> </div> <?php include __DIR__ . '/../includes/footer.php'; ?> <script> function deleteProject(id) { if (!confirm('<?php echo $_lang==='el'?'Είστε σίγουροι ότι θέλετε να διαγράψετε οριστικά αυτό το έργο; Όλα τα σχετικά δεδομένα θα χαθούν!':'Are you sure you want to permanently delete this project? All related data will be lost!'; ?>')) return; var fd = new FormData(); fd.append('project_id', id); fetch('/gopools/public/api/project_delete.php', { method: 'POST', body: fd }) .then(r => r.json()) .then(res => { if (res.ok) { alert('<?php echo $_lang==='el'?'Το έργο διαγράφηκε επιτυχώς.':'Project deleted successfully.'; ?>'); window.location.reload(); } else { alert('<?php echo $_lang==='el'?'Σφάλμα: ':'Error: '; ?>' + (res.error || 'Unknown error')); } }) .catch(err => alert('Network error')); } // Live filtering: title + customer both work together (function() { const titleInput = document.getElementById('title-search'); const customerInput = document.getElementById('customer-filter'); const table = document.getElementById('projects-table-main'); function filterRows() { const titleQ = (titleInput ? titleInput.value.toLowerCase().trim() : ''); const customerQ = (customerInput ? customerInput.value.toLowerCase().trim() : ''); const rows = table ? table.querySelectorAll('tbody tr') : []; rows.forEach(row => { const text = row.textContent.toLowerCase(); // col 2 = project title, col 3 = customer name const customerCell = row.cells[2] ? row.cells[2].textContent.toLowerCase() : ''; const titleMatch = !titleQ || text.includes(titleQ); const customerMatch = !customerQ || customerCell.includes(customerQ); row.style.display = (titleMatch && customerMatch) ? '' : 'none'; }); // Update count const visibleCount = table ? table.querySelectorAll('tbody tr:not([style*="display: none"])').length : 0; const countEl = document.querySelector('.projects-toolbar-left span'); if (countEl) countEl.textContent = visibleCount + ' <?php echo $_lang==='el'?'έργα βρέθηκαν':'projects found'; ?>'; } if (titleInput) titleInput.addEventListener('input', filterRows); if (customerInput) customerInput.addEventListener('input', filterRows); })(); function applyFilters() { const year = document.getElementById('year-select').value; const status = '<?php echo h($filterStatus); ?>'; let url = 'index.php?'; if (year) url += 'year=' + year + '&'; if (status) url += 'status=' + encodeURIComponent(status); window.location.href = url; } </script> <?php require_once __DIR__ . '/i18n.php'; function gp_render_project_menu(PDO $pdo, int $projectId): void { if ($projectId <= 0) return; $currentFile = basename($_SERVER['PHP_SELF']); $financialFiles = ['expenses.php','payments.php','employee_expenses.php']; $materialFiles = ['equipment.php','sold_equipment.php']; $teamFiles = ['assign_send.php','partners.php']; $mediaFiles = ['photos.php','map.php']; $tabs = [ [ 'file' => 'view.php', 'label' => 'Overview', 'icon' => '📁', 'url' => "/gopools/public/projects/view.php?id=$projectId", ], [ 'file' => 'edit.php', 'label' => gp_tr($pdo, 'edit'), 'icon' => '✏️', 'url' => "/gopools/public/projects/edit.php?id=$projectId", ], [ 'file' => 'tasks.php', 'label' => gp_tr($pdo, 'tasks'), 'icon' => '✅', 'url' => "/gopools/public/projects/tasks.php?project_id=$projectId", ], // ── Financials group ────────────────────────────────────────────── [ 'file' => 'payments.php', 'label' => 'Οικονομικά', 'icon' => '💰', 'url' => "/gopools/public/projects/payments.php?project_id=$projectId", 'groupFiles' => $financialFiles, 'sub' => [ ['file' => 'payments.php', 'label' => 'Πληρωμές', 'icon' => '💳', 'url' => "/gopools/public/projects/payments.php?project_id=$projectId"], ['file' => 'expenses.php', 'label' => 'Έξοδα Έργου', 'icon' => '💸', 'url' => "/gopools/public/projects/expenses.php?project_id=$projectId"], ['file' => 'employee_expenses.php','label' => 'Έξοδα Υπαλλήλων','icon' => '👤', 'url' => "/gopools/public/projects/employee_expenses.php?project_id=$projectId"], ], ], // ── Materials group ─────────────────────────────────────────────── [ 'file' => 'equipment.php', 'label' => 'Υλικά', 'icon' => '🛠️', 'url' => "/gopools/public/projects/equipment.php?project_id=$projectId", 'groupFiles' => $materialFiles, 'sub' => [ ['file' => 'equipment.php', 'label' => 'Υλικά Έργου', 'icon' => '🔩', 'url' => "/gopools/public/projects/equipment.php?project_id=$projectId"], ['file' => 'sold_equipment.php','label' => 'Εγγυήσεις', 'icon' => '📜', 'url' => "/gopools/public/projects/sold_equipment.php?project_id=$projectId"], ], ], // ── Team group ──────────────────────────────────────────────────── [ 'file' => 'assign_send.php', 'label' => 'Ομάδα', 'icon' => '👥', 'url' => "/gopools/public/projects/assign_send.php?project_id=$projectId", 'groupFiles' => $teamFiles, 'sub' => [ ['file' => 'assign_send.php','label' => 'Ανάθεση & Αποστολή','icon' => '🚀', 'url' => "/gopools/public/projects/assign_send.php?project_id=$projectId"], ['file' => 'partners.php', 'label' => 'Συνεργάτες', 'icon' => '🤝', 'url' => "/gopools/public/projects/partners.php?project_id=$projectId"], ], ], // ── Media group ─────────────────────────────────────────────────── [ 'file' => 'photos.php', 'label' => 'Media', 'icon' => '📸', 'url' => "/gopools/public/projects/photos.php?project_id=$projectId", 'groupFiles' => $mediaFiles, 'sub' => [ ['file' => 'photos.php','label' => 'Φωτογραφίες','icon' => '📷', 'url' => "/gopools/public/projects/photos.php?project_id=$projectId"], ['file' => 'map.php', 'label' => 'Χάρτης', 'icon' => '📍', 'url' => "/gopools/public/projects/map.php?project_id=$projectId"], ], ], ]; if (function_exists('gp_can_manage_project_access') && gp_can_manage_project_access($pdo)) { $tabs[] = ['file' => 'access.php', 'label' => gp_tr($pdo, 'access'), 'icon' => '🔑', 'url' => "/gopools/public/projects/access.php?project_id=$projectId"]; } ?> <style> .project-nav-container { margin-bottom: 24px; position: sticky; top: 56px; z-index: 990; background: var(--gp-nav-bg-1); border-bottom: 1px solid var(--gp-card-border); margin-left: -24px; margin-right: -24px; margin-top: -24px; padding: 0 24px; backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); } [data-theme='dark'] .project-nav-container { background: rgba(15,23,42,0.85); } .project-nav-tabs { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; } .nav-item-wrapper { position: relative; flex-shrink: 0; } .project-nav-link { display: flex; align-items: center; gap: 6px; padding: 14px 11px; font-size: 0.83rem; font-weight: 600; color: var(--gp-muted); text-decoration: none; white-space: nowrap; transition: all 0.2s; position: relative; border-radius: 6px 6px 0 0; } .project-nav-link:hover { color: var(--gp-text); background: rgba(0,0,0,0.02); } [data-theme='dark'] .project-nav-link:hover { background: rgba(255,255,255,0.03); } .project-nav-link.active { color: var(--gp-primary); } .project-nav-link.active::after { content: ""; position: absolute; bottom: 0; left: 8px; right: 8px; height: 3px; background: var(--gp-primary); border-radius: 3px 3px 0 0; box-shadow: 0 -2px 10px rgba(37,99,235,0.3); } .project-nav-link .nav-icon { font-size: 1rem; opacity: 0.85; } .project-nav-link .nav-arrow { font-size: 0.55rem; margin-left: 1px; opacity: 0.45; } /* Dropdown */ .nav-dropdown { position: absolute; top: calc(100% + 4px); left: 0; min-width: 210px; background: var(--gp-card-bg); border: 1px solid var(--gp-card-border); border-radius: 12px; box-shadow: 0 15px 35px rgba(0,0,0,0.12); padding: 6px; display: none; flex-direction: column; gap: 2px; z-index: 1000; animation: navSlideDown 0.18s ease; backdrop-filter: blur(24px); } @keyframes navSlideDown { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } } .nav-item-wrapper:hover .nav-dropdown { display: flex; } .nav-dropdown-item { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 8px; font-size: 0.83rem; font-weight: 600; text-decoration: none; color: var(--gp-text); transition: all 0.18s; } .nav-dropdown-item:hover { background: rgba(0,0,0,0.04); padding-left: 16px; } [data-theme='dark'] .nav-dropdown-item:hover { background: rgba(255,255,255,0.05); } .nav-dropdown-item.active { background: rgba(37,99,235,0.08); color: var(--gp-primary); } .nav-dropdown-divider { height: 1px; background: var(--gp-card-border); margin: 3px 6px; } @media (max-width: 1024px) { .project-nav-container { margin-left: -20px; margin-right: -20px; padding: 0 16px; } .project-nav-link { padding: 12px 9px; font-size: 0.79rem; } } </style> <div class="project-nav-container"> <div class="project-nav-tabs"> <?php foreach ($tabs as $tab): $hasSub = !empty($tab['sub']); $groupFiles = $tab['groupFiles'] ?? []; $isActive = ($currentFile === $tab['file']) || (!empty($groupFiles) && in_array($currentFile, $groupFiles, true)); ?> <div class="nav-item-wrapper"> <a href="<?php echo h($tab['url']); ?>" class="project-nav-link <?php echo $isActive ? 'active' : ''; ?>"> <span class="nav-icon"><?php echo $tab['icon']; ?></span> <span><?php echo h($tab['label']); ?></span> <?php if ($hasSub): ?><span class="nav-arrow">▼</span><?php endif; ?> </a> <?php if ($hasSub): ?> <div class="nav-dropdown"> <?php foreach ($tab['sub'] as $sub): ?> <a href="<?php echo h($sub['url']); ?>" class="nav-dropdown-item <?php echo $currentFile === $sub['file'] ? 'active' : ''; ?>"> <span><?php echo $sub['icon']; ?></span> <span><?php echo h($sub['label']); ?></span> </a> <?php endforeach; ?> </div> <?php endif; ?> </div> <?php endforeach; ?> </div> </div> <?php }