ux: format activity feed JSON, remove default creds hint, add login loading state
This commit is contained in:
+59
-3
@@ -136,6 +136,7 @@
|
||||
flex: 1;
|
||||
padding: 24px;
|
||||
max-width: calc(100vw - var(--sidebar-w));
|
||||
position: relative;
|
||||
}
|
||||
.page-title {
|
||||
font-size: 22px;
|
||||
@@ -716,6 +717,21 @@
|
||||
.cs-repl-status.approved { background: rgba(72,199,142,0.15); color: var(--green); }
|
||||
.cs-repl-status.rejected { background: rgba(240,61,62,0.15); color: var(--red); }
|
||||
.cs-repl-resolve-all { margin-top: 8px; }
|
||||
|
||||
/* ── Route Planner iframe ── */
|
||||
#pageRoute {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
margin-top: 0; padding: 0;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
#pageRoute.hidden { display: none !important; }
|
||||
#routeFrame {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
border: none;
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/exifr@7/dist/lite.umd.js"></script>
|
||||
@@ -731,7 +747,6 @@
|
||||
<input id="loginUser" class="input-field" type="text" placeholder="Username" autocomplete="username">
|
||||
<input id="loginPass" class="input-field" type="password" placeholder="Password" autocomplete="current-password">
|
||||
<button id="loginBtn" class="btn btn-primary btn-block" onclick="doLogin()">Sign In</button>
|
||||
<div style="text-align:center;font-size:12px;color:var(--text3);margin-top:10px;">Default: admin / changeme</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -779,6 +794,9 @@
|
||||
<button class="nav-item" data-page="exifscanner" onclick="switchPage('exifscanner')">
|
||||
<span class="ni-icon">📸</span><span>EXIF Scanner</span>
|
||||
</button>
|
||||
<button class="nav-item" data-page="route" onclick="switchPage('route')">
|
||||
<span class="ni-icon">🗺️</span><span>Route</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="sidebar-footer">
|
||||
<button id="logoutBtn" class="logout-btn" onclick="doLogout()">🚪 Sign out</button>
|
||||
@@ -1116,6 +1134,11 @@
|
||||
</div>
|
||||
<div id="exifPrevResults"></div>
|
||||
</div>
|
||||
</div> <!-- /pageExifscanner -->
|
||||
|
||||
<!-- ── ROUTE PLANNER (iframe) ── -->
|
||||
<div id="pageRoute" class="page hidden">
|
||||
<iframe id="routeFrame" src="https://route.ourpad.casa" frameborder="0"></iframe>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -1201,7 +1224,7 @@ function showToast(msg, isError = false) {
|
||||
t.textContent = msg;
|
||||
t.className = 'toast' + (isError ? ' error' : '');
|
||||
t.classList.add('show');
|
||||
setTimeout(() => t.classList.remove('show'), 3000);
|
||||
setTimeout(() => t.classList.remove('show'), 4000);
|
||||
}
|
||||
|
||||
let modalResolve = null;
|
||||
@@ -1245,6 +1268,7 @@ function switchPage(page) {
|
||||
else if (page === 'activity') loadActivity();
|
||||
else if (page === 'cantaloupe') loadCantaloupeSync();
|
||||
else if (page === 'exifscanner') loadExifScanner();
|
||||
else if (page === 'route') initRoutePage();
|
||||
}
|
||||
function closeDetail() {
|
||||
if (AppState._prevPage) switchPage(AppState._prevPage);
|
||||
@@ -1257,10 +1281,13 @@ function closeDetail() {
|
||||
async function doLogin() {
|
||||
const username = document.getElementById('loginUser').value.trim();
|
||||
const password = document.getElementById('loginPass').value;
|
||||
const loginBtn = document.getElementById('loginBtn');
|
||||
if (!username || !password) {
|
||||
showLoginError('Please enter username and password');
|
||||
return;
|
||||
}
|
||||
loginBtn.disabled = true;
|
||||
loginBtn.textContent = 'Signing in...';
|
||||
try {
|
||||
const data = await api('/api/auth/login', {
|
||||
method: 'POST',
|
||||
@@ -1276,6 +1303,9 @@ async function doLogin() {
|
||||
switchPage('dashboard');
|
||||
} catch (e) {
|
||||
showLoginError(e.message);
|
||||
} finally {
|
||||
loginBtn.disabled = false;
|
||||
loginBtn.textContent = 'Sign In';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1400,10 +1430,23 @@ function renderDashActivity(items) {
|
||||
}
|
||||
el.innerHTML = items.map(a => {
|
||||
const icon = getActIcon(a.action);
|
||||
let details = a.details || a.action || '';
|
||||
// Try to pretty-print JSON details
|
||||
try {
|
||||
const parsed = JSON.parse(details);
|
||||
if (typeof parsed === 'object' && parsed !== null) {
|
||||
const parts = [];
|
||||
if (parsed.assets_updated !== undefined) parts.push(`${parsed.assets_updated} asset(s) updated`);
|
||||
if (parsed.fields_applied !== undefined) parts.push(`${parsed.fields_applied} field(s) applied`);
|
||||
if (parsed.assets_created !== undefined) parts.push(`${parsed.assets_created} created`);
|
||||
if (parts.length > 0) details = parts.join(', ');
|
||||
else details = JSON.stringify(parsed).replace(/[{}"]/g, '').replace(/,/g, ', ');
|
||||
}
|
||||
} catch(e) { /* not JSON, show as-is */ }
|
||||
return `<div class="activity-item">
|
||||
<div class="act-icon ${icon}">${getActEmoji(a.action)}</div>
|
||||
<div class="act-info">
|
||||
<div class="act-text">${esc(a.details || a.action || '')} <span class="act-user">${esc(a.user_name || '')}</span></div>
|
||||
<div class="act-text">${esc(details)} <span class="act-user">${esc(a.user_name || '')}</span></div>
|
||||
<div class="act-time">${timeAgo(a.created_at)}</div>
|
||||
</div>
|
||||
</div>`;
|
||||
@@ -4022,6 +4065,19 @@ function openExifLightbox(src) {
|
||||
function closeExifLightbox() {
|
||||
if (exifLightboxEl) exifLightboxEl.style.display = 'none';
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════════
|
||||
// ROUTE PLANNER
|
||||
// ═════════════════════════════════════════════════════════════════════════════
|
||||
function initRoutePage() {
|
||||
const iframe = document.getElementById('routeFrame');
|
||||
if (!iframe) return;
|
||||
// Force-reload on each visit so the route data is always fresh
|
||||
if (iframe.dataset.loaded) {
|
||||
iframe.src = iframe.src;
|
||||
}
|
||||
iframe.dataset.loaded = '1';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user