diff --git a/core/ui/static/modules/actions.js b/core/ui/static/modules/actions.js index 6edb7286..4ad3e55a 100644 --- a/core/ui/static/modules/actions.js +++ b/core/ui/static/modules/actions.js @@ -548,7 +548,7 @@ function renderReviewItem(item) { if (item.commit_sha) parts.push(`commit ${escapeHtml(item.commit_sha.substring(0, 8))}`); if (item.review_requested_at) { const d = new Date(item.review_requested_at); - const label = isNaN(d) ? item.review_requested_at : d.toLocaleString(); + const label = isNaN(d) ? item.review_requested_at : formatFriendlyDate(item.review_requested_at); parts.push(`${escapeHtml(label)}`); } return parts.length ? `
${parts.join('')}
` : ''; diff --git a/core/ui/static/modules/aether.js b/core/ui/static/modules/aether.js index 9e811bc0..76829614 100644 --- a/core/ui/static/modules/aether.js +++ b/core/ui/static/modules/aether.js @@ -836,7 +836,8 @@ const Aether = (function() { if (errorEl) errorEl.textContent = _stats.errors; if (lastEventEl && _lastEvent) { - const timeStr = _lastEvent.time.toLocaleTimeString(); + const _t = _lastEvent.time; + const timeStr = `${_t.getHours().toString().padStart(2,'0')}:${_t.getMinutes().toString().padStart(2,'0')}:${_t.getSeconds().toString().padStart(2,'0')}`; const colorVar = _lastEvent.type === 'START' ? '--color-success' : _lastEvent.type === 'COMPLETE' ? '--color-secondary' : '--color-primary'; lastEventEl.innerHTML = `${_lastEvent.type}${timeStr}`; diff --git a/core/ui/static/modules/decisions.js b/core/ui/static/modules/decisions.js index 5cc9ff02..41203595 100644 --- a/core/ui/static/modules/decisions.js +++ b/core/ui/static/modules/decisions.js @@ -190,7 +190,10 @@ function _renderList() { function _friendlyDate(iso) { if (!iso) return ''; try { - return new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }); + const d = new Date(iso); + if (isNaN(d.getTime())) return iso; + const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; + return `${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`; } catch { return iso; } } diff --git a/core/ui/static/modules/processes.js b/core/ui/static/modules/processes.js index 28d66079..6d2005aa 100644 --- a/core/ui/static/modules/processes.js +++ b/core/ui/static/modules/processes.js @@ -295,7 +295,7 @@ function toggleProcessExpand(processId) { function renderOutputHtml(events) { let html = ''; for (const evt of events) { - const ts = evt.timestamp ? new Date(evt.timestamp).toLocaleTimeString() : ''; + const ts = evt.timestamp ? formatCompactTime(evt.timestamp) : ''; const typeClass = evt.type === 'rate_limit' ? 'warning' : (evt.type === 'text' ? 'text' : 'tool'); const messageText = stripConsoleSequences(evt.message || ''); html += `
`; diff --git a/core/ui/static/modules/ui-updates.js b/core/ui/static/modules/ui-updates.js index d95ab2cb..7c7ec94d 100644 --- a/core/ui/static/modules/ui-updates.js +++ b/core/ui/static/modules/ui-updates.js @@ -64,7 +64,8 @@ function updateUI(state) { * Update timestamp display */ function updateTimestamp(instanceId) { - setElementText('last-update', new Date().toLocaleTimeString()); + const _now = new Date(); + setElementText('last-update', `${_now.getHours().toString().padStart(2,'0')}:${_now.getMinutes().toString().padStart(2,'0')}:${_now.getSeconds().toString().padStart(2,'0')}`); setElementText('instance-id', instanceId || '--'); // Update footer mission on each poll to ensure it's current updateFooterMission(); @@ -143,7 +144,7 @@ function updateSessionInfo(session) { if (session.started_at) { const started = new Date(session.started_at); - setElementText('session-started', started.toLocaleTimeString()); + setElementText('session-started', `${started.getHours().toString().padStart(2,'0')}:${started.getMinutes().toString().padStart(2,'0')}:${started.getSeconds().toString().padStart(2,'0')}`); sessionStartTime = started; } else { setElementText('session-started', '--'); diff --git a/core/ui/static/modules/utils.js b/core/ui/static/modules/utils.js index 8eed31ee..bf7ae9a2 100644 --- a/core/ui/static/modules/utils.js +++ b/core/ui/static/modules/utils.js @@ -90,20 +90,22 @@ function formatCompactDate(isoString) { /** * Format ISO date string to human-friendly format with day of week * @param {string} isoString - ISO date string - * @returns {string} Formatted date like "Fri Dec 15 14:30" + * @returns {string} Formatted date like "Fri, Dec 15 2026 14:30" */ function formatFriendlyDate(isoString) { if (!isoString) return ''; try { const date = new Date(isoString); + if (isNaN(date.getTime())) return ''; const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; const dayOfWeek = days[date.getDay()]; const month = months[date.getMonth()]; const dayNum = date.getDate(); + const year = date.getFullYear(); const hours = date.getHours().toString().padStart(2, '0'); const mins = date.getMinutes().toString().padStart(2, '0'); - return `${dayOfWeek} ${month} ${dayNum} ${hours}:${mins}`; + return `${dayOfWeek}, ${month} ${dayNum} ${year} ${hours}:${mins}`; } catch (e) { return ''; } diff --git a/server/src/Dotbot.Server/wwwroot/css/dashboard.css b/server/src/Dotbot.Server/wwwroot/css/dashboard.css index 9cdc0cb1..286be4ee 100644 --- a/server/src/Dotbot.Server/wwwroot/css/dashboard.css +++ b/server/src/Dotbot.Server/wwwroot/css/dashboard.css @@ -914,7 +914,7 @@ select.filter-input option { background: var(--bg-panel); border: 1px solid var(--bezel-edge); border-radius: 6px; - max-width: 900px; + max-width: 920px; width: 95%; max-height: 80vh; overflow: hidden; diff --git a/server/src/Dotbot.Server/wwwroot/js/dashboard.js b/server/src/Dotbot.Server/wwwroot/js/dashboard.js index 374f205d..5948633d 100644 --- a/server/src/Dotbot.Server/wwwroot/js/dashboard.js +++ b/server/src/Dotbot.Server/wwwroot/js/dashboard.js @@ -5,6 +5,15 @@ (function () { 'use strict'; + // --- Formatters --- + const _dtFormatter = new Intl.DateTimeFormat('en-US', { + weekday: 'short', month: 'short', day: 'numeric', + year: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false + }); + const _timeFormatter = new Intl.DateTimeFormat('en-US', { + hour: '2-digit', minute: '2-digit', hour12: false + }); + // --- State --- let allInstances = []; let byPerson = {}; // email -> [{ instance, recipient, response }] @@ -16,6 +25,26 @@ let nudgeCooldowns = {}; // key -> timestamp // --- Helpers --- + function formatDashboardDateTime(date) { + try { + const d = date instanceof Date ? date : new Date(date); + if (isNaN(d.getTime())) return String(date); + const parts = _dtFormatter.formatToParts(d); + const get = (t) => (parts.find(p => p.type === t) || {}).value || ''; + return `${get('weekday')}, ${get('month')} ${get('day')} ${get('year')} ${get('hour')}:${get('minute')}`; + } catch (e) { return String(date); } + } + + function formatDashboardTime(date) { + try { + const d = date instanceof Date ? date : new Date(date); + if (isNaN(d.getTime())) return ''; + const parts = _timeFormatter.formatToParts(d); + const get = (t) => (parts.find(p => p.type === t) || {}).value || ''; + return `${get('hour')}:${get('minute')}`; + } catch (e) { return ''; } + } + function buildRecipientPills(recipients, max) { if (!recipients || recipients.length === 0) return ''; // Sort: non-responders first, then responders @@ -286,7 +315,7 @@
Created - ${inst.createdAt ? new Date(inst.createdAt).toLocaleString() : '-'} + ${inst.createdAt ? formatDashboardDateTime(inst.createdAt) : '-'}
Created By @@ -814,6 +843,6 @@ function updateRefreshTime() { const el = document.getElementById('last-refresh'); - el.textContent = `Last refresh: ${new Date().toLocaleTimeString()}`; + el.textContent = `Last refresh: ${formatDashboardTime(new Date())}`; } })();