Spaces:
Sleeping
Sleeping
Commit ·
bff40f8
1
Parent(s): a4d589b
Sticky nav, avatar dropdown menu, mobile responsive layout
Browse files- static/css/style.css +103 -13
- static/js/app.js +13 -0
- templates/index.html +11 -5
static/css/style.css
CHANGED
|
@@ -339,6 +339,9 @@ input:focus, select:focus, textarea:focus {
|
|
| 339 |
|
| 340 |
/* ---- Nav ---- */
|
| 341 |
.nav {
|
|
|
|
|
|
|
|
|
|
| 342 |
display: flex;
|
| 343 |
align-items: center;
|
| 344 |
justify-content: space-between;
|
|
@@ -362,25 +365,75 @@ input:focus, select:focus, textarea:focus {
|
|
| 362 |
}
|
| 363 |
.nav-brand svg { stroke: #fff !important; }
|
| 364 |
.nav-user {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
display: flex;
|
| 366 |
align-items: center;
|
| 367 |
-
|
|
|
|
|
|
|
| 368 |
}
|
| 369 |
-
.
|
| 370 |
-
|
| 371 |
-
color: rgba(255,255,255,0.
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 376 |
}
|
| 377 |
-
.nav
|
| 378 |
-
|
|
|
|
|
|
|
|
|
|
| 379 |
}
|
| 380 |
-
.nav
|
| 381 |
-
|
| 382 |
-
background:
|
|
|
|
| 383 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 384 |
|
| 385 |
/* ---- Page header ---- */
|
| 386 |
.page-header { margin-bottom: 1.75rem; }
|
|
@@ -778,3 +831,40 @@ input:focus, select:focus, textarea:focus {
|
|
| 778 |
.mt-1 { margin-top: 0.5rem; }
|
| 779 |
.mt-2 { margin-top: 1.25rem; }
|
| 780 |
.mb-2 { margin-bottom: 1rem; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
|
| 340 |
/* ---- Nav ---- */
|
| 341 |
.nav {
|
| 342 |
+
position: sticky;
|
| 343 |
+
top: 0;
|
| 344 |
+
z-index: 100;
|
| 345 |
display: flex;
|
| 346 |
align-items: center;
|
| 347 |
justify-content: space-between;
|
|
|
|
| 365 |
}
|
| 366 |
.nav-brand svg { stroke: #fff !important; }
|
| 367 |
.nav-user {
|
| 368 |
+
position: relative;
|
| 369 |
+
}
|
| 370 |
+
|
| 371 |
+
/* Avatar button */
|
| 372 |
+
.nav-avatar {
|
| 373 |
+
width: 36px;
|
| 374 |
+
height: 36px;
|
| 375 |
+
border-radius: 50%;
|
| 376 |
+
border: 2px solid rgba(255,255,255,0.35);
|
| 377 |
+
background: rgba(255,255,255,0.15);
|
| 378 |
+
color: #fff;
|
| 379 |
display: flex;
|
| 380 |
align-items: center;
|
| 381 |
+
justify-content: center;
|
| 382 |
+
cursor: pointer;
|
| 383 |
+
transition: all var(--transition);
|
| 384 |
}
|
| 385 |
+
.nav-avatar:hover {
|
| 386 |
+
background: rgba(255,255,255,0.25);
|
| 387 |
+
border-color: rgba(255,255,255,0.5);
|
| 388 |
+
}
|
| 389 |
+
|
| 390 |
+
/* Dropdown */
|
| 391 |
+
.nav-dropdown {
|
| 392 |
+
position: absolute;
|
| 393 |
+
top: calc(100% + 0.5rem);
|
| 394 |
+
right: 0;
|
| 395 |
+
min-width: 220px;
|
| 396 |
+
background: var(--bg-card);
|
| 397 |
+
border: 1px solid var(--border);
|
| 398 |
+
border-radius: var(--radius-sm);
|
| 399 |
+
box-shadow: 0 8px 32px rgba(0,0,0,0.12);
|
| 400 |
+
padding: 0.5rem 0;
|
| 401 |
+
animation: dropIn 0.15s ease;
|
| 402 |
+
z-index: 200;
|
| 403 |
+
}
|
| 404 |
+
@keyframes dropIn {
|
| 405 |
+
from { opacity: 0; transform: translateY(-6px); }
|
| 406 |
+
to { opacity: 1; transform: translateY(0); }
|
| 407 |
}
|
| 408 |
+
.nav-dropdown-email {
|
| 409 |
+
padding: 0.6rem 1rem;
|
| 410 |
+
font-size: 0.82rem;
|
| 411 |
+
color: var(--text-muted);
|
| 412 |
+
word-break: break-all;
|
| 413 |
}
|
| 414 |
+
.nav-dropdown-divider {
|
| 415 |
+
height: 1px;
|
| 416 |
+
background: var(--border);
|
| 417 |
+
margin: 0.25rem 0;
|
| 418 |
}
|
| 419 |
+
.nav-dropdown-item {
|
| 420 |
+
display: flex;
|
| 421 |
+
align-items: center;
|
| 422 |
+
gap: 0.5rem;
|
| 423 |
+
width: 100%;
|
| 424 |
+
padding: 0.55rem 1rem;
|
| 425 |
+
border: none;
|
| 426 |
+
background: none;
|
| 427 |
+
color: var(--text);
|
| 428 |
+
font-family: var(--font-sans);
|
| 429 |
+
font-size: 0.85rem;
|
| 430 |
+
cursor: pointer;
|
| 431 |
+
transition: background var(--transition);
|
| 432 |
+
}
|
| 433 |
+
.nav-dropdown-item:hover {
|
| 434 |
+
background: var(--bg-elevated);
|
| 435 |
+
}
|
| 436 |
+
.nav-dropdown-item svg { color: var(--text-dim); }
|
| 437 |
|
| 438 |
/* ---- Page header ---- */
|
| 439 |
.page-header { margin-bottom: 1.75rem; }
|
|
|
|
| 831 |
.mt-1 { margin-top: 0.5rem; }
|
| 832 |
.mt-2 { margin-top: 1.25rem; }
|
| 833 |
.mb-2 { margin-bottom: 1rem; }
|
| 834 |
+
|
| 835 |
+
/* ---- Mobile responsive ---- */
|
| 836 |
+
@media (max-width: 768px) {
|
| 837 |
+
.app { padding: 1rem; }
|
| 838 |
+
|
| 839 |
+
.nav {
|
| 840 |
+
margin-top: -1rem;
|
| 841 |
+
padding: 0.75rem 1rem;
|
| 842 |
+
}
|
| 843 |
+
|
| 844 |
+
.page-header h1 { font-size: 1.3rem; }
|
| 845 |
+
.page-header p { font-size: 0.82rem; }
|
| 846 |
+
|
| 847 |
+
.auth-container { margin: 2rem auto; }
|
| 848 |
+
.auth-container .card { padding: 1.5rem; }
|
| 849 |
+
|
| 850 |
+
.options-row { flex-direction: column; }
|
| 851 |
+
.options-row .form-group { min-width: 100%; }
|
| 852 |
+
|
| 853 |
+
.result-stats { grid-template-columns: repeat(2, 1fr); }
|
| 854 |
+
|
| 855 |
+
.history-item { flex-direction: column; align-items: flex-start; gap: 0.5rem; }
|
| 856 |
+
.history-actions { align-self: flex-end; }
|
| 857 |
+
|
| 858 |
+
.app-footer {
|
| 859 |
+
margin-bottom: -1rem;
|
| 860 |
+
padding: 1rem;
|
| 861 |
+
}
|
| 862 |
+
|
| 863 |
+
.card { padding: 1.15rem; }
|
| 864 |
+
.card-header { margin-bottom: 1rem; }
|
| 865 |
+
|
| 866 |
+
.regions-table { font-size: 0.78rem; }
|
| 867 |
+
.regions-table th, .regions-table td { padding: 0.45rem 0.5rem; }
|
| 868 |
+
|
| 869 |
+
.nav-dropdown { right: -0.5rem; min-width: 200px; }
|
| 870 |
+
}
|
static/js/app.js
CHANGED
|
@@ -114,9 +114,22 @@ document.querySelectorAll('[data-view]').forEach((a) => {
|
|
| 114 |
document.getElementById('btn-logout')?.addEventListener('click', async () => {
|
| 115 |
try { await fetch(API_BASE + '/api/auth/logout', { method: 'POST', credentials: 'include' }); } catch (_) {}
|
| 116 |
setToken(null);
|
|
|
|
| 117 |
showView('login');
|
| 118 |
});
|
| 119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
async function init() {
|
| 121 |
const token = getToken();
|
| 122 |
if (!token) { showView('login'); return; }
|
|
|
|
| 114 |
document.getElementById('btn-logout')?.addEventListener('click', async () => {
|
| 115 |
try { await fetch(API_BASE + '/api/auth/logout', { method: 'POST', credentials: 'include' }); } catch (_) {}
|
| 116 |
setToken(null);
|
| 117 |
+
document.getElementById('nav-dropdown')?.classList.add('hidden');
|
| 118 |
showView('login');
|
| 119 |
});
|
| 120 |
|
| 121 |
+
// ---- Avatar dropdown toggle ----
|
| 122 |
+
document.getElementById('btn-avatar')?.addEventListener('click', (e) => {
|
| 123 |
+
e.stopPropagation();
|
| 124 |
+
document.getElementById('nav-dropdown')?.classList.toggle('hidden');
|
| 125 |
+
});
|
| 126 |
+
document.addEventListener('click', (e) => {
|
| 127 |
+
const dd = document.getElementById('nav-dropdown');
|
| 128 |
+
if (dd && !dd.classList.contains('hidden') && !e.target.closest('.nav-user')) {
|
| 129 |
+
dd.classList.add('hidden');
|
| 130 |
+
}
|
| 131 |
+
});
|
| 132 |
+
|
| 133 |
async function init() {
|
| 134 |
const token = getToken();
|
| 135 |
if (!token) { showView('login'); return; }
|
templates/index.html
CHANGED
|
@@ -138,11 +138,17 @@
|
|
| 138 |
<span>SatDetect</span>
|
| 139 |
</div>
|
| 140 |
<div class="nav-user">
|
| 141 |
-
<
|
| 142 |
-
|
| 143 |
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
| 144 |
-
Log out
|
| 145 |
</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
</div>
|
| 147 |
</nav>
|
| 148 |
|
|
@@ -282,6 +288,6 @@
|
|
| 282 |
</div>
|
| 283 |
</div>
|
| 284 |
|
| 285 |
-
<script src="/static/js/app.js?v=
|
| 286 |
</body>
|
| 287 |
</html>
|
|
|
|
| 138 |
<span>SatDetect</span>
|
| 139 |
</div>
|
| 140 |
<div class="nav-user">
|
| 141 |
+
<button type="button" class="nav-avatar" id="btn-avatar" aria-label="Account menu">
|
| 142 |
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
|
|
|
|
|
|
| 143 |
</button>
|
| 144 |
+
<div class="nav-dropdown hidden" id="nav-dropdown">
|
| 145 |
+
<div class="nav-dropdown-email" id="user-email"></div>
|
| 146 |
+
<div class="nav-dropdown-divider"></div>
|
| 147 |
+
<button type="button" class="nav-dropdown-item" id="btn-logout">
|
| 148 |
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
| 149 |
+
Log out
|
| 150 |
+
</button>
|
| 151 |
+
</div>
|
| 152 |
</div>
|
| 153 |
</nav>
|
| 154 |
|
|
|
|
| 288 |
</div>
|
| 289 |
</div>
|
| 290 |
|
| 291 |
+
<script src="/static/js/app.js?v=10"></script>
|
| 292 |
</body>
|
| 293 |
</html>
|