diff --git a/frontend/src/components/layout/AdminLayout.css b/frontend/src/components/layout/AdminLayout.css index 6a568c1..6c98818 100644 --- a/frontend/src/components/layout/AdminLayout.css +++ b/frontend/src/components/layout/AdminLayout.css @@ -1,6 +1,5 @@ -/* ===== Admin Layout V4 - Dark Theme (R2 Refactored) ===== */ +/* ===== Admin Layout - Light Theme V5 ===== */ -/* CSS Variables scoped to admin layout */ :root { --admin-sidebar-width: 240px; --sidebar-collapsed: 64px; @@ -20,141 +19,143 @@ .admin-sidebar { width: var(--admin-sidebar-width); min-width: var(--admin-sidebar-width); - background: linear-gradient(180deg, #FFFFFF 0%, #FAFBFE 100%); + background: #FFFFFF; border-right: 1px solid rgba(0, 0, 0, 0.08); display: flex; flex-direction: column; - transition: width 0.25s ease, min-width 0.25s ease; overflow: hidden; z-index: 100; } -.admin-sidebar.collapsed { - width: var(--sidebar-collapsed); - min-width: var(--sidebar-collapsed); -} - /* Sidebar Header */ .admin-sidebar-header { display: flex; - align-items: center; - justify-content: space-between; - padding: 16px 16px 16px 20px; - height: 60px; - border-bottom: 1px solid rgba(0, 0, 0, 0.08); + flex-direction: column; + padding: 14px 16px 10px; + border-bottom: 1px solid rgba(0, 0, 0, 0.06); flex-shrink: 0; } -.sidebar-logo { - display: flex; - align-items: center; - gap: 10px; -} - -.admin-brand { - font-size: 15px; - font-weight: 700; +.admin-logo { + font-size: 18px; + font-weight: 800; color: #1A1F36; + margin: 0; letter-spacing: -0.02em; - white-space: nowrap; - background: linear-gradient(135deg, #6C63FF, #A78BFA); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; } -.collapse-btn { +.admin-subtitle { + font-size: 11px; + color: #8892B0; + margin-top: 2px; +} + +/* Frontend link button */ +.admin-frontend-link { display: flex; align-items: center; justify-content: center; - width: 28px; - height: 28px; - border-radius: 6px; - border: none; - background: transparent; - color: #8892B0; - transition: all 0.15s ease; + gap: 6px; + padding: 8px 16px; + margin: 8px 12px; + border-radius: 8px; + font-size: 13px; + font-weight: 600; + color: #6C63FF; + background: rgba(108, 99, 255, 0.08); + border: 1px solid rgba(108, 99, 255, 0.15); cursor: pointer; - flex-shrink: 0; + transition: all 0.2s; + width: calc(100% - 24px); } -.collapse-btn:hover { - background: rgba(255, 255, 255, 0.06); - color: #A78BFA; +.admin-frontend-link:hover { + background: rgba(108, 99, 255, 0.15); + border-color: rgba(108, 99, 255, 0.3); + color: #5B5FED; } -.admin-sidebar.collapsed .admin-sidebar-header { - padding: 16px 12px; - justify-content: center; -} - -.admin-sidebar.collapsed .admin-brand { - display: none; -} - -/* Admin Navigation */ -.admin-nav { +/* Sidebar Nav */ +.sidebar-nav { flex: 1; - padding: 8px; overflow-y: auto; overflow-x: hidden; + padding: 4px 8px; } -.admin-nav::-webkit-scrollbar { +.sidebar-nav::-webkit-scrollbar { width: 4px; } -.admin-nav::-webkit-scrollbar-track { +.sidebar-nav::-webkit-scrollbar-track { background: transparent; } -.admin-nav::-webkit-scrollbar-thumb { - background: rgba(108, 99, 255, 0.10); +.sidebar-nav::-webkit-scrollbar-thumb { + background: rgba(108, 99, 255, 0.15); border-radius: 2px; } /* Nav Group */ .nav-group { - margin-bottom: 2px; + margin-bottom: 0; } .nav-group-header { display: flex; align-items: center; - gap: 6px; - padding: 8px 12px; + gap: 4px; + padding: 7px 10px; cursor: pointer; - border-radius: 8px; - transition: all 0.15s ease; + border-radius: 6px; + transition: background 0.15s ease; user-select: none; } .nav-group-header:hover { - background: rgba(255, 255, 255, 0.04); + background: rgba(0, 0, 0, 0.04); } -.nav-group-label { - font-size: 11px; - font-weight: 700; - color: #4A5578; - text-transform: uppercase; - letter-spacing: 0.1em; - flex: 1; +.nav-group-header.expanded { + background: rgba(0, 0, 0, 0.02); } -.active-group .nav-group-label, -.nav-group:hover .nav-group-label { - color: #6B7280; -} - -.nav-group-chevron { - color: #4A5578; - transition: transform 0.2s ease; +.nav-group-chevron-wrap { + display: flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; flex-shrink: 0; } -.nav-group-chevron.open { - transform: rotate(90deg); +.nav-group-chevron { + color: #8892B0; + transition: transform 0.2s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.nav-group-chevron.chevron-expanded { + transform: rotate(0deg); +} + +.nav-group-header:not(.expanded) .nav-group-chevron { + transform: rotate(-90deg); +} + +.nav-group-title { + font-size: 11px; + font-weight: 700; + color: #5A6480; + text-transform: uppercase; + letter-spacing: 0.08em; + flex: 1; +} + +.nav-group-active .nav-group-title { + color: #6C63FF; } /* Nav Group Items */ @@ -162,231 +163,128 @@ display: flex; flex-direction: column; gap: 1px; + padding: 2px 0 4px 0; overflow: hidden; - transition: max-height 0.3s ease; -} - -.nav-group-items.collapsed { - max-height: 0 !important; } /* Nav Item */ -.admin-nav-item { +.nav-item { display: flex; align-items: center; - gap: 10px; - padding: 9px 12px; - border-radius: 8px; - color: #8892B0; + gap: 8px; + padding: 7px 10px 7px 30px; + border-radius: 6px; + color: #5A6480; text-decoration: none; font-size: 13px; font-weight: 500; transition: all 0.15s ease; white-space: nowrap; position: relative; - border: none; - background: none; - width: 100%; - cursor: pointer; - text-align: left; } -.admin-nav-item:hover { +.nav-item:hover { background: rgba(108, 99, 255, 0.06); - color: #CCD6F6; + color: #1A1F36; } -.admin-nav-item.active { - background: rgba(108, 99, 255, 0.1); - color: #8B85FF; +.nav-item-active { + background: rgba(108, 99, 255, 0.10); + color: #6C63FF; + font-weight: 600; } -.nav-icon { +.nav-item-active::before { + content: ''; + position: absolute; + left: 14px; + top: 50%; + transform: translateY(-50%); + width: 3px; + height: 16px; + border-radius: 2px; + background: #6C63FF; +} + +.nav-item-icon { display: flex; align-items: center; justify-content: center; - width: 20px; - height: 20px; flex-shrink: 0; + opacity: 0.7; } -.nav-label { +.nav-item-active .nav-item-icon { + opacity: 1; +} + +.nav-item-label { overflow: hidden; text-overflow: ellipsis; } -/* Active indicator dot */ -.nav-active-dot { - position: absolute; - right: 8px; - width: 6px; - height: 6px; - border-radius: 50%; - background: #6C63FF; - box-shadow: 0 0 8px rgba(108, 99, 255, 0.5); -} - -/* Logout button special style */ -.logout-btn:hover { - background: rgba(239, 68, 68, 0.12) !important; - color: #EF4444 !important; -} - -/* ========== Collapsed Mode ========== */ -.admin-sidebar.collapsed .nav-group-header { - justify-content: center; - padding: 8px; -} - -.admin-sidebar.collapsed .nav-group-label, -.admin-sidebar.collapsed .nav-group-chevron, -.admin-sidebar.collapsed .nav-group-items { - display: none; -} - -.admin-sidebar.collapsed .admin-nav-item { - justify-content: center; - padding: 10px; -} - -.admin-sidebar.collapsed .nav-label, -.admin-sidebar.collapsed .nav-active-dot { - display: none; -} - /* Sidebar Footer */ .admin-sidebar-footer { - padding: 8px; - border-top: 1px solid rgba(0, 0, 0, 0.08); + padding: 8px 12px; + border-top: 1px solid rgba(0, 0, 0, 0.06); flex-shrink: 0; } -/* Version Info */ -.admin-version-info { - padding: 8px 16px 12px; - font-size: 11px; - color: #3A4460; - text-align: center; - border-top: 1px solid rgba(255, 255, 255, 0.04); -} - -/* ========== Top Bar ========== */ -.admin-topbar { +.admin-user-info { display: flex; align-items: center; - justify-content: space-between; - padding: 0 32px; - height: 60px; - border-bottom: 1px solid rgba(0, 0, 0, 0.08); - background: rgba(10, 14, 26, 0.85); - backdrop-filter: blur(12px); - flex-shrink: 0; + gap: 10px; } -.admin-topbar-left { - display: flex; - align-items: center; - gap: 24px; -} - -.admin-page-title { - font-size: 17px; - font-weight: 600; - color: #1A1F36; - margin: 0; - white-space: nowrap; -} - -/* Breadcrumb */ -.admin-breadcrumb { - display: flex; - align-items: center; - gap: 6px; - font-size: 13px; -} - -.breadcrumb-item { - color: #8892B0; -} - -.breadcrumb-item.current { - color: #8892B0; - font-weight: 500; -} - -.breadcrumb-sep { - color: #3A4460; - margin: 0 4px; -} - -.admin-topbar-right { - display: flex; - align-items: center; - gap: 16px; -} - -.admin-welcome { - font-size: 13px; - color: #6B7280; -} - -.admin-topbar-action { - display: flex; - align-items: center; - gap: 6px; - padding: 6px 12px; - border-radius: 8px; - color: #6B7280; - text-decoration: none; - font-size: 13px; - transition: all 0.15s ease; -} - -.admin-topbar-action:hover { - background: rgba(108, 99, 255, 0.06); - color: #A78BFA; -} - -/* User tag in topbar */ -.admin-user-tag { - display: flex; - align-items: center; - gap: 8px; - padding: 2px 12px 2px 4px; - border-radius: 20px; - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(0, 0, 0, 0.10); - font-size: 13px; - color: #CCD6F6; -} - -.admin-avatar { - width: 28px; - height: 28px; +.admin-user-avatar { + width: 32px; + height: 32px; border-radius: 50%; background: linear-gradient(135deg, #6C63FF, #A78BFA); color: #fff; display: flex; align-items: center; justify-content: center; - font-size: 12px; + font-size: 13px; font-weight: 700; + flex-shrink: 0; +} + +.admin-user-detail { + flex: 1; + min-width: 0; +} + +.admin-user-name { + font-size: 13px; + font-weight: 600; + color: #1A1F36; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.admin-logout-btn { + font-size: 11px; + color: #8892B0; + background: none; + border: none; + cursor: pointer; + padding: 0; + margin-top: 2px; +} + +.admin-logout-btn:hover { + color: #EF4444; } /* ========== Main Content ========== */ -.admin-main { - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; - background: #F5F7FC; -} - .admin-content { flex: 1; overflow-y: auto; overflow-x: hidden; - padding: 32px; + padding: 24px 32px; + background: #F5F7FC; } .admin-content::-webkit-scrollbar { @@ -398,67 +296,66 @@ } .admin-content::-webkit-scrollbar-thumb { - background: rgba(108, 99, 255, 0.10); + background: rgba(108, 99, 255, 0.15); border-radius: 3px; } /* ========== Shared Admin Page Components ========== */ -/* Stat cards grid */ -.admin-stats-grid { +/* Admin page title */ +.admin-page-title { + font-size: 20px; + font-weight: 700; + color: #1A1F36; + margin-bottom: 20px; +} + +/* Stats grid */ +.admin-stats { display: grid; - grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 24px; } +/* Stat card */ .admin-stat-card { - background: linear-gradient(135deg, rgba(17, 22, 39, 0.8), rgba(17, 22, 39, 0.6)); + background: #FFFFFF; border: 1px solid rgba(0, 0, 0, 0.08); border-radius: 12px; padding: 20px; transition: all 0.2s ease; + box-shadow: 0 1px 3px rgba(0, 0,0,0.04); } .admin-stat-card:hover { - border-color: rgba(108, 99, 255, 0.3); - box-shadow: 0 4px 20px rgba(108, 99, 255, 0.1); + border-color: rgba(108, 99, 255, 0.2); + box-shadow: 0 4px 16px rgba(0,0,0,0.06); transform: translateY(-1px); } -.admin-stat-card .stat-label { - font-size: 13px; - color: #6B7280; - margin-bottom: 8px; +.admin-stat-label { + font-size: 12px; + color: #8892B0; + margin-bottom: 4px; + font-weight: 500; } -.admin-stat-card .stat-value { - font-size: 28px; +.admin-stat-value { + font-size: 24px; font-weight: 700; color: #1A1F36; - line-height: 1.2; -} - -.admin-stat-card .stat-change { - font-size: 12px; - margin-top: 4px; -} - -.admin-stat-card .stat-change.up { - color: #EF4444; -} - -.admin-stat-card .stat-change.down { - color: #10B981; + line-height: 1.3; } /* Page section card */ .admin-section { - background: rgba(17, 22, 39, 0.5); + background: #FFFFFF; border: 1px solid rgba(0, 0, 0, 0.08); border-radius: 12px; padding: 24px; margin-bottom: 20px; + box-shadow: 0 1px 3px rgba(0,0,0,0.04); } .admin-section-header { @@ -467,7 +364,7 @@ justify-content: space-between; margin-bottom: 20px; padding-bottom: 16px; - border-bottom: 1px solid rgba(0, 0, 0, 0.08); + border-bottom: 1px solid rgba(0, 0, 0, 0.06); } .admin-section-header h3 { @@ -477,12 +374,38 @@ margin: 0; } +/* Admin card (generic) */ +.admin-card { + background: #FFFFFF; + border: 1px solid rgba(0, 0, 0, 0.08); + border-radius: 12px; + box-shadow: 0 1px 3px rgba(0,0,0,0.04); + overflow: hidden; +} + +.admin-card-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px 20px; + border-bottom: 1px solid rgba(0, 0, 0, 0.06); +} + +.admin-card-title { + font-size: 15px; + font-weight: 600; + color: #1A1F36; + display: flex; + align-items: center; + gap: 6px; +} + /* Tab bar */ .admin-tabs { display: flex; gap: 4px; padding: 4px; - background: rgba(255, 255, 255, 0.03); + background: #F0F2F8; border-radius: 10px; margin-bottom: 20px; } @@ -492,7 +415,7 @@ border-radius: 8px; font-size: 13px; font-weight: 500; - color: #6B7280; + color: #5A6480; cursor: pointer; transition: all 0.15s ease; border: none; @@ -500,12 +423,14 @@ } .admin-tab:hover { - color: #A78BFA; + color: #6C63FF; + background: rgba(108, 99, 255, 0.06); } .admin-tab.active { - background: rgba(108, 99, 255, 0.15); - color: #A78BFA; + background: #FFFFFF; + color: #6C63FF; + box-shadow: 0 1px 3px rgba(0,0,0,0.08); } /* Data tables */ @@ -518,23 +443,23 @@ padding: 12px 16px; font-size: 12px; font-weight: 600; - color: #6B7280; + color: #5A6480; text-transform: uppercase; letter-spacing: 0.05em; text-align: left; border-bottom: 1px solid rgba(0, 0, 0, 0.08); - background: rgba(255, 255, 255, 0.02); + background: #F8F9FD; } .admin-table td { padding: 12px 16px; font-size: 13px; - color: #8892B0; - border-bottom: 1px solid rgba(255, 255, 255, 0.04); + color: #1A1F36; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); } .admin-table tr:hover td { - background: rgba(255, 255, 255, 0.03); + background: rgba(108, 99, 255, 0.03); } /* Action buttons */ @@ -553,44 +478,44 @@ } .admin-btn-primary { - background: linear-gradient(135deg, #6C63FF, #8B85FF); + background: #6C63FF; color: #fff; } .admin-btn-primary:hover { - background: linear-gradient(135deg, #7B73FF, #9B95FF); - box-shadow: 0 2px 12px rgba(108, 99, 255, 0.3); + background: #5B5FED; + box-shadow: 0 2px 8px rgba(108, 99, 255, 0.3); } .admin-btn-secondary { - background: rgba(255, 255, 255, 0.06); - color: #8892B0; - border: 1px solid rgba(0, 0, 0, 0.10); + background: #F0F2F8; + color: #5A6480; + border: 1px solid rgba(0, 0, 0, 0.08); } .admin-btn-secondary:hover { - background: rgba(255, 255, 255, 0.1); - color: #CCD6F6; + background: #E8EBF5; + color: #1A1F36; } .admin-btn-danger { - background: rgba(239, 68, 68, 0.1); + background: rgba(239, 68, 68, 0.08); color: #EF4444; border: 1px solid rgba(239, 68, 68, 0.2); } .admin-btn-danger:hover { - background: rgba(239, 68, 68, 0.2); + background: rgba(239, 68, 68, 0.15); } .admin-btn-success { - background: rgba(16, 185, 129, 0.1); + background: rgba(16, 185, 129, 0.08); color: #10B981; border: 1px solid rgba(16, 185, 129, 0.2); } .admin-btn-success:hover { - background: rgba(16, 185, 129, 0.2); + background: rgba(16, 185, 129, 0.15); } .admin-btn-sm { @@ -613,10 +538,10 @@ align-items: center; gap: 8px; padding: 8px 14px; - background: rgba(255, 255, 255, 0.04); + background: #FFFFFF; border: 1px solid rgba(0, 0, 0, 0.10); border-radius: 8px; - color: #8892B0; + color: #5A6480; flex: 1; min-width: 200px; max-width: 360px; @@ -625,6 +550,7 @@ .admin-search:focus-within { border-color: rgba(108, 99, 255, 0.4); + box-shadow: 0 0 0 3px rgba(108, 99, 255, 0.08); } .admin-search input { @@ -637,7 +563,7 @@ } .admin-search input::placeholder { - color: #4A5578; + color: #8892B0; } /* Badge / Tag */ @@ -651,28 +577,28 @@ } .admin-badge-success { - background: rgba(16, 185, 129, 0.12); + background: rgba(16, 185, 129, 0.10); color: #10B981; } .admin-badge-warning { - background: rgba(245, 158, 11, 0.12); + background: rgba(245, 158, 11, 0.10); color: #F59E0B; } .admin-badge-danger { - background: rgba(239, 68, 68, 0.12); + background: rgba(239, 68, 68, 0.10); color: #EF4444; } .admin-badge-info { - background: rgba(108, 99, 255, 0.12); - color: #A78BFA; + background: rgba(59, 130, 246, 0.10); + color: #3B82F6; } .admin-badge-neutral { - background: rgba(255, 255, 255, 0.06); - color: #6B7280; + background: #F0F2F8; + color: #5A6480; } /* Empty state */ @@ -682,7 +608,7 @@ align-items: center; justify-content: center; padding: 60px 20px; - color: #4A5578; + color: #8892B0; } .admin-empty svg { @@ -695,11 +621,11 @@ margin: 0; } -/* Modal / Dialog overrides */ +/* Modal / Dialog */ .admin-modal-overlay { position: fixed; inset: 0; - background: rgba(0, 0, 0, 0.6); + background: rgba(0, 0, 0, 0.3); backdrop-filter: blur(4px); display: flex; align-items: center; @@ -716,7 +642,7 @@ max-width: 640px; max-height: 80vh; overflow-y: auto; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15); } .admin-modal h3 { @@ -726,7 +652,50 @@ margin: 0 0 20px 0; } -/* Form elements in dark theme */ +.admin-modal-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 20px; + padding-bottom: 16px; + border-bottom: 1px solid rgba(0, 0, 0, 0.06); +} + +.admin-modal-header h3 { + margin: 0 !important; +} + +.admin-modal-close { + background: none; + border: none; + color: #8892B0; + cursor: pointer; + padding: 4px; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; +} + +.admin-modal-close:hover { + color: #1A1F36; + background: rgba(0, 0, 0, 0.06); +} + +.admin-modal-body { + margin-bottom: 20px; +} + +.admin-modal-footer { + display: flex; + gap: 8px; + justify-content: flex-end; + padding-top: 16px; + border-top: 1px solid rgba(0, 0, 0, 0.06); +} + +/* Form elements */ .admin-form-group { margin-bottom: 16px; } @@ -735,7 +704,7 @@ display: block; font-size: 13px; font-weight: 500; - color: #8892B0; + color: #1A1F36; margin-bottom: 6px; } @@ -744,12 +713,12 @@ .admin-form-group textarea { width: 100%; padding: 10px 14px; - background: rgba(255, 255, 255, 0.04); + background: #FFFFFF; border: 1px solid rgba(0, 0, 0, 0.10); border-radius: 8px; color: #1A1F36; font-size: 13px; - transition: border-color 0.15s ease; + transition: border-color 0.15s ease, box-shadow 0.15s ease; outline: none; resize: vertical; } @@ -758,7 +727,7 @@ .admin-form-group select:focus, .admin-form-group textarea:focus { border-color: rgba(108, 99, 255, 0.4); - background: rgba(255, 255, 255, 0.06); + box-shadow: 0 0 0 3px rgba(108, 99, 255, 0.08); } .admin-form-group select { @@ -788,7 +757,7 @@ position: absolute; cursor: pointer; inset: 0; - background: #3A4460; + background: #D1D5DB; border-radius: 12px; transition: 0.2s; } @@ -803,6 +772,7 @@ background: #fff; border-radius: 50%; transition: 0.2s; + box-shadow: 0 1px 3px rgba(0,0,0,0.2); } .admin-toggle input:checked + .toggle-slider { @@ -813,11 +783,11 @@ transform: translateX(20px); } -/* Loading spinner in admin area */ +/* Loading spinner */ .admin-spinner { width: 24px; height: 24px; - border: 2px solid rgba(255, 255, 255, 0.1); + border: 2px solid rgba(108, 99, 255, 0.15); border-top-color: #6C63FF; border-radius: 50%; animation: admin-spin 0.8s linear infinite; @@ -841,21 +811,22 @@ height: 32px; border-radius: 6px; border: 1px solid rgba(0, 0, 0, 0.10); - background: transparent; - color: #8892B0; + background: #FFFFFF; + color: #5A6480; font-size: 13px; cursor: pointer; transition: all 0.15s ease; } .admin-pagination button:hover { - background: rgba(255, 255, 255, 0.06); + background: #F0F2F8; + color: #1A1F36; } .admin-pagination button.active { - background: rgba(108, 99, 255, 0.15); - border-color: rgba(108, 99, 255, 0.3); - color: #A78BFA; + background: #6C63FF; + border-color: #6C63FF; + color: #FFFFFF; } /* Toast notifications */ @@ -870,24 +841,25 @@ z-index: 2000; animation: toastSlideIn 0.3s ease; backdrop-filter: blur(12px); + box-shadow: 0 4px 16px rgba(0,0,0,0.12); } .admin-toast-success { - background: rgba(16, 185, 129, 0.15); + background: #FFFFFF; border: 1px solid rgba(16, 185, 129, 0.3); - color: #34D399; + color: #10B981; } .admin-toast-error { - background: rgba(239, 68, 68, 0.15); + background: #FFFFFF; border: 1px solid rgba(239, 68, 68, 0.3); - color: #F87171; + color: #EF4444; } .admin-toast-info { - background: rgba(108, 99, 255, 0.15); + background: #FFFFFF; border: 1px solid rgba(108, 99, 255, 0.3); - color: #A78BFA; + color: #6C63FF; } @keyframes toastSlideIn { @@ -895,58 +867,11 @@ to { opacity: 1; transform: translateX(0); } } -/* ========== Responsive ========== */ - - -/* Modal sub-components */ -.admin-modal-header { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 20px; - padding-bottom: 16px; - border-bottom: 1px solid rgba(0, 0, 0, 0.08); -} - -.admin-modal-header h3 { - margin: 0 !important; -} - -.admin-modal-close { - background: none; - border: none; - color: #8892B0; - cursor: pointer; - padding: 4px; - border-radius: 6px; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s; -} - -.admin-modal-close:hover { - color: #1A1F36; - background: rgba(255, 255, 255, 0.06); -} - -.admin-modal-body { - margin-bottom: 20px; -} - -.admin-modal-footer { - display: flex; - gap: 8px; - justify-content: flex-end; - padding-top: 16px; - border-top: 1px solid rgba(0, 0, 0, 0.08); -} - -/* Spinner animation for logo upload */ +/* Spinner */ .spinner { width: 32px; height: 32px; - border: 3px solid rgba(108, 99, 255, 0.2); + border: 3px solid rgba(108, 99, 255, 0.15); border-top-color: #6C63FF; border-radius: 50%; animation: spin 0.8s linear infinite; @@ -956,6 +881,7 @@ to { transform: rotate(360deg); } } +/* ========== Responsive ========== */ @media (max-width: 768px) { .admin-sidebar { position: fixed; @@ -973,11 +899,7 @@ padding: 16px; } - .admin-topbar { - padding: 0 16px; - } - - .admin-stats-grid { + .admin-stats { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); } @@ -987,31 +909,3 @@ max-width: 90vw; } } - - -/* Frontend access link */ -.admin-frontend-link { - display: flex; - align-items: center; - gap: 6px; - padding: 8px 16px; - margin: 8px 12px; - border-radius: 8px; - font-size: 13px; - font-weight: 500; - color: #6C63FF; - background: rgba(108, 99, 255, 0.08); - text-decoration: none; - transition: all 0.2s; - cursor: pointer; -} - -.admin-frontend-link:hover { - background: rgba(108, 99, 255, 0.15); - color: #5B5FED; -} - -/* Nav link cursor pointer */ -.landing-nav-v2 a { - cursor: pointer; -} diff --git a/frontend/src/components/layout/AdminLayout.tsx b/frontend/src/components/layout/AdminLayout.tsx index 34ea83a..7bd721d 100644 --- a/frontend/src/components/layout/AdminLayout.tsx +++ b/frontend/src/components/layout/AdminLayout.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { Outlet, NavLink, useNavigate } from 'react-router-dom'; import { useAuth } from '@/contexts/AuthContext'; import './AdminLayout.css'; @@ -18,10 +18,10 @@ interface NavGroup { const adminNavGroups: NavGroup[] = [ { - title: '驾驶舰', + title: '驾驶舱', icon: 'dashboard', items: [ - { path: '/admin', icon: 'dashboard', label: '平台驾驶舰', exact: true }, + { path: '/admin', icon: 'dashboard', label: '平台驾驶舱', exact: true }, { path: '/admin/analytics', icon: 'analytics', label: '数据分析' }, ], }, @@ -92,37 +92,61 @@ const adminNavGroups: NavGroup[] = [ ]; const adminIconPaths: Record = { - dashboard: , - users: , - models: , - settings: , - payment: , - services: , - membership: , - ads: , - market: , - logs: , - agents: , - skills: , - code: , - distro: , - privacy: , - promotion: , - achievement: , - tenant: , - billing: , - analytics: , - imbot: , - chevron: , + dashboard: , + users: , + models: , + settings: , + payment: , + services: , + membership: , + ads: , + market: , + logs: , + agents: , + skills: , + code: , + distro: , + privacy: , + promotion: , + achievement: , + tenant: , + billing: , + analytics: , + imbot: , + storage: , + automation: , + developer: , + devtools: , + knowledge: , + workflow: , + mcp: , + apps: , + assets: , + chevron: , }; const AdminLayout: React.FC = () => { const { user, logout } = useAuth(); const navigate = useNavigate(); - const [collapsedGroups, setCollapsedGroups] = useState>(new Set()); + + // Default: all groups collapsed. Auto-expand the group containing the active route. + const [expandedGroups, setExpandedGroups] = useState>(() => { + const initial = new Set(); + const currentHash = window.location.hash; + for (const group of adminNavGroups) { + const isActive = group.items.some(item => + item.exact ? currentHash === `#${item.path}` : currentHash.startsWith(`#${item.path}`) + ); + if (isActive) { + initial.add(group.title); + break; + } + } + return initial; + }); const toggleGroup = (title: string) => { - setCollapsedGroups(prev => { + setExpandedGroups(prev => { const next = new Set(prev); if (next.has(title)) next.delete(title); else next.add(title); @@ -135,6 +159,11 @@ const AdminLayout: React.FC = () => { navigate('/'); }; + const handleVisitFrontend = () => { + const url = window.location.origin + window.location.pathname + '#/portal'; + window.open(url, '_blank'); + }; + return (
- +