Add USB checkout system and SSO profile page
New Features: - USB Device checkout/check-in system with barcode scanning - displayusb.asp: List all USB devices with status - addusb.asp: Add new USB devices via barcode scan - checkout_usb.asp/savecheckout_usb.asp: Check out USB to SSO - checkin_usb.asp/savecheckin_usb.asp: Check in with wipe confirmation - usb_history.asp: Full checkout history with filters - api_usb.asp: JSON API for AJAX lookups - displayprofile.asp: SSO profile page showing user info and USB history - Date/time format changed to 12-hour (MM/DD/YYYY h:mm AM/PM) - SSO links in USB history now link to profile page via search Database: - New machinetypeid 44 for USB devices - New usb_checkouts table for tracking checkouts Cleanup: - Removed v2 folder (duplicate/old files) - Removed old debug/test files - Removed completed migration documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
137
tv-dashboard/slideshow.html
Normal file
137
tv-dashboard/slideshow.html
Normal file
@@ -0,0 +1,137 @@
|
||||
<!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>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
background: #000;
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
.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 {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
.progress-bar {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0;
|
||||
height: 4px;
|
||||
background: #4181ff;
|
||||
z-index: 100;
|
||||
}
|
||||
.status {
|
||||
position: fixed;
|
||||
top: 50%; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: #fff;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="slideshow"><div class="status">Loading slides...</div></div>
|
||||
<div class="progress-bar" id="progressBar"></div>
|
||||
|
||||
<!-- Slides list generated by update_slides.bat -->
|
||||
<script>var SLIDES = [];</script>
|
||||
<script src="slides.js"></script>
|
||||
|
||||
<script>
|
||||
const INTERVAL = 10;
|
||||
|
||||
let slides = [];
|
||||
let currentSlide = 0;
|
||||
let slideTimer = null;
|
||||
|
||||
function loadSlides() {
|
||||
if (typeof SLIDES === 'undefined' || SLIDES.length === 0) {
|
||||
document.getElementById('slideshow').innerHTML =
|
||||
'<div class="status">No slides found.<br><br><span style="font-size:16px;color:#888;">Run update_slides.bat to generate slides.js</span></div>';
|
||||
return;
|
||||
}
|
||||
buildSlides(SLIDES);
|
||||
}
|
||||
|
||||
function buildSlides(filenames) {
|
||||
const container = document.getElementById('slideshow');
|
||||
container.innerHTML = '';
|
||||
slides = [];
|
||||
|
||||
filenames.forEach((filename, i) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'slide' + (i === 0 ? ' active' : '');
|
||||
const img = document.createElement('img');
|
||||
img.src = filename;
|
||||
div.appendChild(img);
|
||||
container.appendChild(div);
|
||||
slides.push({ element: div, filename: filename });
|
||||
});
|
||||
|
||||
currentSlide = 0;
|
||||
if (slides.length > 1) startSlideshow();
|
||||
}
|
||||
|
||||
function startSlideshow() {
|
||||
if (slideTimer) clearTimeout(slideTimer);
|
||||
scheduleNext();
|
||||
}
|
||||
|
||||
function scheduleNext() {
|
||||
const bar = document.getElementById('progressBar');
|
||||
bar.style.transition = 'none';
|
||||
bar.style.width = '0%';
|
||||
setTimeout(() => {
|
||||
bar.style.transition = 'width ' + INTERVAL + 's linear';
|
||||
bar.style.width = '100%';
|
||||
}, 50);
|
||||
|
||||
slideTimer = setTimeout(() => {
|
||||
nextSlide();
|
||||
scheduleNext();
|
||||
}, 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');
|
||||
}
|
||||
|
||||
let lastSlideList = JSON.stringify(SLIDES);
|
||||
loadSlides();
|
||||
|
||||
// Check for new slides every 60 seconds
|
||||
setInterval(function() {
|
||||
const script = document.createElement('script');
|
||||
script.src = 'slides.js?t=' + Date.now();
|
||||
script.onload = function() {
|
||||
const newList = JSON.stringify(SLIDES);
|
||||
if (newList !== lastSlideList) {
|
||||
lastSlideList = newList;
|
||||
loadSlides();
|
||||
}
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
}, 60000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user