| .. | .. |
|---|
| 13 | 13 | #main { flex: 1; display: flex; flex-direction: column; overflow-x: hidden; } |
|---|
| 14 | 14 | #topbar { background: #111827; border-bottom: 1px solid #1f2937; padding: 0.75rem 1.5rem; display: flex; align-items: center; gap: 1rem; } |
|---|
| 15 | 15 | #page-content { flex: 1; padding: 1.5rem; overflow-y: auto; } |
|---|
| 16 | | - .breadcrumb { display: flex; align-items: center; gap: 0.5rem; font-size: 0.875rem; color: #9ca3af; } |
|---|
| 16 | + .breadcrumb { display: flex; align-items: center; gap: 0.5rem; font-size: 0.875rem; color: #9ca3af; flex-wrap: wrap; } |
|---|
| 17 | 17 | .breadcrumb a { color: #60a5fa; cursor: pointer; text-decoration: none; } |
|---|
| 18 | 18 | .breadcrumb a:hover { text-decoration: underline; } |
|---|
| 19 | 19 | .breadcrumb .sep { color: #4b5563; } |
|---|
| .. | .. |
|---|
| 22 | 22 | .sidebar-logo { padding: 1.25rem 1rem; font-size: 1.125rem; font-weight: 700; color: #f3f4f6; border-bottom: 1px solid #1f2937; display: flex; align-items: center; gap: 0.5rem; } |
|---|
| 23 | 23 | .sidebar-nav { padding: 0.75rem 0.5rem; flex: 1; } |
|---|
| 24 | 24 | .sidebar-footer { padding: 0.75rem 1rem; border-top: 1px solid #1f2937; font-size: 0.75rem; color: #6b7280; } |
|---|
| 25 | | - .project-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1rem; } |
|---|
| 26 | | - .env-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1rem; } |
|---|
| 27 | | - .service-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1rem; } |
|---|
| 28 | | - .stat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1rem; } |
|---|
| 25 | + .grid-auto { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1rem; } |
|---|
| 26 | + .grid-stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 0.75rem; } |
|---|
| 27 | + .grid-metrics { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1rem; } |
|---|
| 29 | 28 | .card-clickable { cursor: pointer; transition: border-color 0.2s, transform 0.15s; } |
|---|
| 30 | 29 | .card-clickable:hover { border-color: #60a5fa; transform: translateY(-1px); } |
|---|
| 30 | + .stat-tile { cursor: pointer; transition: border-color 0.2s, transform 0.1s; } |
|---|
| 31 | + .stat-tile:hover { border-color: #60a5fa; transform: translateY(-2px); } |
|---|
| 32 | + .filter-badge { display: inline-flex; align-items: center; gap: 0.375rem; padding: 0.25rem 0.625rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 600; background: rgba(59,130,246,0.15); color: #60a5fa; border: 1px solid rgba(59,130,246,0.3); } |
|---|
| 33 | + .filter-badge button { background: none; border: none; color: #60a5fa; cursor: pointer; font-size: 0.875rem; padding: 0; line-height: 1; } |
|---|
| 34 | + .filter-badge button:hover { color: #f87171; } |
|---|
| 35 | + .view-toggle { display: flex; background: #1f2937; border-radius: 0.375rem; overflow: hidden; border: 1px solid #374151; } |
|---|
| 36 | + .view-toggle button { background: none; border: none; color: #6b7280; padding: 0.25rem 0.5rem; font-size: 0.75rem; cursor: pointer; display: flex; align-items: center; gap: 0.25rem; } |
|---|
| 37 | + .view-toggle button.active { background: rgba(59,130,246,0.2); color: #60a5fa; } |
|---|
| 38 | + .view-toggle button:hover:not(.active) { color: #d1d5db; } |
|---|
| 31 | 39 | .mobile-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.6); z-index: 40; } |
|---|
| 32 | 40 | @media (max-width: 768px) { |
|---|
| 33 | 41 | #sidebar { position: fixed; left: -240px; top: 0; bottom: 0; z-index: 50; transition: left 0.2s; } |
|---|
| 34 | 42 | #sidebar.open { left: 0; } |
|---|
| 35 | 43 | .mobile-overlay.open { display: block; } |
|---|
| 36 | 44 | .hamburger { display: block; } |
|---|
| 37 | | - .project-grid, .env-grid, .service-grid { grid-template-columns: 1fr; } |
|---|
| 45 | + .grid-auto { grid-template-columns: 1fr; } |
|---|
| 46 | + .grid-stats { grid-template-columns: repeat(2, 1fr); } |
|---|
| 38 | 47 | } |
|---|
| 39 | 48 | </style> |
|---|
| 40 | 49 | </head> |
|---|
| .. | .. |
|---|
| 53 | 62 | |
|---|
| 54 | 63 | <!-- App Shell --> |
|---|
| 55 | 64 | <div id="app" style="display:none;"> |
|---|
| 56 | | - <!-- Mobile overlay --> |
|---|
| 57 | 65 | <div id="mobile-overlay" class="mobile-overlay" onclick="toggleSidebar()"></div> |
|---|
| 58 | 66 | |
|---|
| 59 | 67 | <!-- Sidebar --> |
|---|
| .. | .. |
|---|
| 66 | 74 | <a class="sidebar-link active" data-page="dashboard" onclick="showPage('dashboard')"> |
|---|
| 67 | 75 | <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg> |
|---|
| 68 | 76 | Dashboard |
|---|
| 69 | | - </a> |
|---|
| 70 | | - <a class="sidebar-link" data-page="services" onclick="showPage('services')"> |
|---|
| 71 | | - <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg> |
|---|
| 72 | | - Services |
|---|
| 73 | 77 | </a> |
|---|
| 74 | 78 | <a class="sidebar-link" data-page="backups" onclick="showPage('backups')"> |
|---|
| 75 | 79 | <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> |
|---|
| .. | .. |
|---|
| 91 | 95 | |
|---|
| 92 | 96 | <!-- Main Content --> |
|---|
| 93 | 97 | <div id="main"> |
|---|
| 94 | | - <!-- Top bar --> |
|---|
| 95 | 98 | <div id="topbar"> |
|---|
| 96 | 99 | <button class="hamburger" onclick="toggleSidebar()">☰</button> |
|---|
| 97 | 100 | <div id="breadcrumbs" class="breadcrumb" style="flex:1;"></div> |
|---|
| 101 | + <div id="view-toggle-wrap" style="display:none;"> |
|---|
| 102 | + <div class="view-toggle"> |
|---|
| 103 | + <button id="btn-view-cards" class="active" onclick="setViewMode('cards')" title="Card view"> |
|---|
| 104 | + <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg> |
|---|
| 105 | + Cards |
|---|
| 106 | + </button> |
|---|
| 107 | + <button id="btn-view-table" onclick="setViewMode('table')" title="Table view"> |
|---|
| 108 | + <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg> |
|---|
| 109 | + Table |
|---|
| 110 | + </button> |
|---|
| 111 | + </div> |
|---|
| 112 | + </div> |
|---|
| 98 | 113 | <div style="display:flex;align-items:center;gap:0.75rem;"> |
|---|
| 99 | 114 | <div id="refresh-indicator" class="refresh-ring paused" title="Auto-refresh"></div> |
|---|
| 100 | 115 | <button class="btn btn-ghost btn-xs" onclick="refreshCurrentPage()" title="Refresh now">Refresh</button> |
|---|
| 101 | 116 | </div> |
|---|
| 102 | 117 | </div> |
|---|
| 103 | | - |
|---|
| 104 | | - <!-- Page content --> |
|---|
| 105 | 118 | <div id="page-content"></div> |
|---|
| 106 | 119 | </div> |
|---|
| 107 | 120 | </div> |
|---|
| .. | .. |
|---|
| 121 | 134 | </div> |
|---|
| 122 | 135 | <div class="modal-footer"> |
|---|
| 123 | 136 | <button class="btn btn-ghost btn-sm" onclick="closeLogModal()">Close</button> |
|---|
| 124 | | - <button class="btn btn-primary btn-sm" id="log-refresh-btn" onclick="refreshLogs()">Refresh</button> |
|---|
| 137 | + <button class="btn btn-primary btn-sm" onclick="refreshLogs()">Refresh</button> |
|---|
| 125 | 138 | </div> |
|---|
| 126 | 139 | </div> |
|---|
| 127 | 140 | </div> |
|---|