Add navigation controls, keyboard arrows, and API data loading

This commit is contained in:
Jared Miller 2026-03-01 12:00:02 -05:00
parent a2b6ab0546
commit 2835d375dd
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C

View file

@ -178,6 +178,40 @@ body {
/* Color classes for special characters */
.flap-green .flap-char { color: #4caf50 !important; }
.flap-red .flap-char { color: #f44336 !important; }
/* Navigation */
#nav {
text-align: center;
margin-top: 20px;
padding-top: 16px;
border-top: 2px solid #252525;
}
#nav span {
color: #ffb300;
font-family: 'Roboto Mono', 'Courier New', monospace;
font-size: 15px;
font-weight: 700;
cursor: pointer;
padding: 6px 14px;
user-select: none;
letter-spacing: 1px;
transition: color 0.15s;
}
#nav span:hover {
color: #ffd54f;
}
#nav .sep {
color: #555;
cursor: default;
padding: 0 4px;
}
#nav .sep:hover {
color: #555;
}
</style>
</head>
<body>
@ -189,6 +223,14 @@ body {
</div>
<div id="sections"></div>
<div id="nav">
<span id="btn-prev">&lt; PREV</span>
<span class="sep">|</span>
<span id="btn-today">TODAY</span>
<span class="sep">|</span>
<span id="btn-next">NEXT &gt;</span>
</div>
</div>
</div>
@ -447,32 +489,102 @@ body {
return rows;
}
// ─── Date Helpers ──────────────────────────────────────────────────
let currentDate = null;
function todayEastern() {
const now = new Date();
const eastern = new Date(now.toLocaleString('en-US', { timeZone: 'America/New_York' }));
return new Date(eastern.getFullYear(), eastern.getMonth(), eastern.getDate());
}
function formatDateStr(d) {
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${y}-${m}-${day}`;
}
function addDays(d, n) {
const result = new Date(d);
result.setDate(result.getDate() + n);
return result;
}
// ─── Load Day Data ─────────────────────────────────────────────────
let p1Rows = [];
let p2Rows = [];
async function loadDay(dateStr) {
try {
const resp = await fetch(`/api/day/${dateStr}`);
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const data = await resp.json();
setDateText(data.display_date);
const p1Items = data.p1 || [];
for (let r = 0; r < MAX_ROWS; r++) {
const text = r < p1Items.length ? formatRow(p1Items[r]) : ''.padEnd(TOTAL_CHARS);
setTimeout(() => setRowText(p1Rows[r], text), r * 20);
}
const p2Items = data.p2 || [];
for (let r = 0; r < MAX_ROWS; r++) {
const text = r < p2Items.length ? formatRow(p2Items[r]) : ''.padEnd(TOTAL_CHARS);
setTimeout(() => setRowText(p2Rows[r], text), 80 + r * 20);
}
} catch (err) {
console.error('Failed to load day:', err);
setDateText('ERROR LOADING DATA');
}
}
// ─── Navigation ────────────────────────────────────────────────────
function goToDate(d) {
currentDate = d;
loadDay(formatDateStr(d));
}
// ─── Init ─────────────────────────────────────────────────────────
function init() {
const sections = document.getElementById('sections');
const p1Rows = buildSection(sections, 'P1 PRISMA');
p1Rows = buildSection(sections, 'P1 PRISMA');
const spacer = document.createElement('div');
spacer.style.marginTop = '24px';
sections.appendChild(spacer);
const p2Rows = buildSection(sections, 'P2 PRISMA FIT');
p2Rows = buildSection(sections, 'P2 PRISMA FIT');
// Demo: static content
setDateText('SATURDAY, MARCH 01, 2025');
// Navigation
document.getElementById('btn-prev').addEventListener('click', () => goToDate(addDays(currentDate, -1)));
document.getElementById('btn-today').addEventListener('click', () => goToDate(todayEastern()));
document.getElementById('btn-next').addEventListener('click', () => goToDate(addDays(currentDate, 1)));
const demoData = [
{ time: '8:00A', type: 'MRI', title: 'BRAIN W/O CONTRAST', src: 'OP', body_part: 'BRAIN', pt: 'J.D.', scr: true, ins: true },
{ time: '8:30A', type: 'CT', title: 'CHEST W/ CONTRAST', src: 'ER', body_part: 'CHEST', pt: 'M.S.', scr: true, ins: false },
{ time: '9:00A', type: 'XRAY', title: 'LEFT HAND 3 VIEW', src: 'OP', body_part: 'LEFT HAND', pt: 'A.B.', scr: false, ins: true },
];
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft') { e.preventDefault(); goToDate(addDays(currentDate, -1)); }
else if (e.key === 'ArrowRight') { e.preventDefault(); goToDate(addDays(currentDate, 1)); }
});
for (let r = 0; r < MAX_ROWS; r++) {
const text = r < demoData.length ? formatRow(demoData[r]) : ''.padEnd(TOTAL_CHARS);
setRowText(p1Rows[r], text);
// Auto-scale board to fit viewport
function scaleBoard() {
const wrapper = document.getElementById('board-wrapper');
const board = document.getElementById('board');
wrapper.style.transform = 'none';
const boardWidth = board.offsetWidth + 60;
const viewportWidth = window.innerWidth;
if (boardWidth > viewportWidth) {
wrapper.style.transform = `scale(${viewportWidth / boardWidth})`;
}
}
scaleBoard();
window.addEventListener('resize', scaleBoard);
// Load today
goToDate(todayEastern());
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);