Add navigation controls, keyboard arrows, and API data loading
This commit is contained in:
parent
a2b6ab0546
commit
2835d375dd
1 changed files with 124 additions and 12 deletions
136
index.html
136
index.html
|
|
@ -178,6 +178,40 @@ body {
|
||||||
/* Color classes for special characters */
|
/* Color classes for special characters */
|
||||||
.flap-green .flap-char { color: #4caf50 !important; }
|
.flap-green .flap-char { color: #4caf50 !important; }
|
||||||
.flap-red .flap-char { color: #f44336 !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>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -189,6 +223,14 @@ body {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="sections"></div>
|
<div id="sections"></div>
|
||||||
|
|
||||||
|
<div id="nav">
|
||||||
|
<span id="btn-prev">< PREV</span>
|
||||||
|
<span class="sep">|</span>
|
||||||
|
<span id="btn-today">TODAY</span>
|
||||||
|
<span class="sep">|</span>
|
||||||
|
<span id="btn-next">NEXT ></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -447,32 +489,102 @@ body {
|
||||||
return rows;
|
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 ─────────────────────────────────────────────────────────
|
// ─── Init ─────────────────────────────────────────────────────────
|
||||||
function init() {
|
function init() {
|
||||||
const sections = document.getElementById('sections');
|
const sections = document.getElementById('sections');
|
||||||
|
|
||||||
const p1Rows = buildSection(sections, 'P1 PRISMA');
|
p1Rows = buildSection(sections, 'P1 PRISMA');
|
||||||
|
|
||||||
const spacer = document.createElement('div');
|
const spacer = document.createElement('div');
|
||||||
spacer.style.marginTop = '24px';
|
spacer.style.marginTop = '24px';
|
||||||
sections.appendChild(spacer);
|
sections.appendChild(spacer);
|
||||||
|
|
||||||
const p2Rows = buildSection(sections, 'P2 PRISMA FIT');
|
p2Rows = buildSection(sections, 'P2 PRISMA FIT');
|
||||||
|
|
||||||
// Demo: static content
|
// Navigation
|
||||||
setDateText('SATURDAY, MARCH 01, 2025');
|
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 = [
|
document.addEventListener('keydown', (e) => {
|
||||||
{ time: '8:00A', type: 'MRI', title: 'BRAIN W/O CONTRAST', src: 'OP', body_part: 'BRAIN', pt: 'J.D.', scr: true, ins: true },
|
if (e.key === 'ArrowLeft') { e.preventDefault(); goToDate(addDays(currentDate, -1)); }
|
||||||
{ time: '8:30A', type: 'CT', title: 'CHEST W/ CONTRAST', src: 'ER', body_part: 'CHEST', pt: 'M.S.', scr: true, ins: false },
|
else if (e.key === 'ArrowRight') { e.preventDefault(); goToDate(addDays(currentDate, 1)); }
|
||||||
{ time: '9:00A', type: 'XRAY', title: 'LEFT HAND 3 VIEW', src: 'OP', body_part: 'LEFT HAND', pt: 'A.B.', scr: false, ins: true },
|
});
|
||||||
];
|
|
||||||
|
|
||||||
for (let r = 0; r < MAX_ROWS; r++) {
|
// Auto-scale board to fit viewport
|
||||||
const text = r < demoData.length ? formatRow(demoData[r]) : ''.padEnd(TOTAL_CHARS);
|
function scaleBoard() {
|
||||||
setRowText(p1Rows[r], text);
|
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') {
|
if (document.readyState === 'loading') {
|
||||||
document.addEventListener('DOMContentLoaded', init);
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue