Files
shopdb/tv-dashboard/index.html
cproudlock 1f1bd8ee02 Remove legacy pc tables, fix ASP issues, update dashboard APIs
Database changes (run sql/migration_drop_pc_tables.sql on prod):
- Drop pc, pc_backup_phase2, pc_to_machine_id_mapping tables
- Rename pcid columns to machineid in machineoverrides, dualpathassignments, networkinterfaces
- Recreate 9 views to use machines.machineid instead of pcid
- Clean orphaned records and add FK constraints to machines table

ASP fixes:
- editprinter.asp: Fix CLng type mismatch when no printerid provided
- includes/sql.asp: Remove AutoDeactivateExpiredNotifications (endtime handles expiry)
- includes/leftsidebar.asp: Update fiscal week banner styling, remove dead Information link
- charts/warrantychart.asp: Use vw_warranty_status instead of pc table

Dashboard API renames (naming convention):
- shopfloor-dashboard: Update to use apishopfloor.asp, apibusinessunits.asp
- tv-dashboard: Rename api_slides.asp to apislides.asp

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 09:05:06 -05:00

180 lines
5.4 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>West Jefferson - Display</title>
<link rel="icon" type="image/x-icon" href="favicon.ico">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #000;
margin: 0;
padding: 0;
overflow: hidden;
height: 100vh;
width: 100vw;
}
.slideshow-container {
position: relative;
width: 100vw;
height: 100vh;
background: #000;
}
.slide {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 1s ease-in-out;
display: flex;
align-items: center;
justify-content: center;
}
.slide.active {
opacity: 1;
}
.slide img {
width: 100%;
height: 100%;
object-fit: contain;
}
.progress-bar {
position: fixed;
bottom: 0;
left: 0;
height: 4px;
background: #4181ff;
transition: width linear;
z-index: 100;
}
.error-message {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-size: 24px;
}
.error-message h2 {
color: #ff4444;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="slideshow-container" id="slideshow"></div>
<div class="progress-bar" id="progressBar"></div>
<script>
const INTERVAL = 10; // seconds between slides
let slides = [];
let currentSlide = 0;
let slideTimer = null;
// Fetch slides from API
async function fetchSlides() {
try {
const response = await fetch('apislides.asp');
if (!response.ok) throw new Error('API error');
const data = await response.json();
if (data.success && data.slides && data.slides.length > 0) {
updateSlides(data.slides, data.basepath);
} else {
showError(data.message || 'No slides found');
}
} catch (error) {
console.error('Error fetching slides:', error);
showError('Unable to load slides');
}
}
// Update slides in DOM
function updateSlides(newSlides, basepath) {
const slideshow = document.getElementById('slideshow');
const currentSlideNames = slides.map(s => s.filename);
const newSlideNames = newSlides.map(s => s.filename);
// Check if slides changed
if (JSON.stringify(currentSlideNames) !== JSON.stringify(newSlideNames)) {
slideshow.innerHTML = '';
slides = [];
newSlides.forEach((slide, index) => {
const div = document.createElement('div');
div.className = 'slide' + (index === 0 ? ' active' : '');
const img = document.createElement('img');
img.src = basepath + encodeURIComponent(slide.filename);
div.appendChild(img);
slideshow.appendChild(div);
slides.push({ element: div, filename: slide.filename });
});
currentSlide = 0;
if (slides.length > 1) startSlideshow();
}
}
function showError(message) {
document.getElementById('slideshow').innerHTML =
'<div class="error-message"><h2>Display Error</h2><p>' + message + '</p></div>';
}
function startSlideshow() {
if (slideTimer) clearTimeout(slideTimer);
scheduleNextSlide();
}
function scheduleNextSlide() {
const progressBar = document.getElementById('progressBar');
progressBar.style.width = '0%';
progressBar.style.transition = 'none';
setTimeout(() => {
progressBar.style.transition = 'width ' + INTERVAL + 's linear';
progressBar.style.width = '100%';
}, 50);
slideTimer = setTimeout(() => {
nextSlide();
scheduleNextSlide();
}, INTERVAL * 1000);
}
function nextSlide() {
if (slides.length === 0) return;
slides[currentSlide].element.classList.remove('active');
currentSlide = (currentSlide + 1) % slides.length;
slides[currentSlide].element.classList.add('active');
}
// Start
fetchSlides();
// Refresh every 60 seconds to pick up new slides
setInterval(fetchSlides, 60000);
</script>
</body>
</html>