Files
narrow_casting_system/admin/index.html
Alvin-Zilverstand 8e446a1339 🎿 Complete SnowWorld Narrowcasting System - MBO Challenge 18
 Full-stack narrowcasting platform implementation
 Real-time WebSocket communication for instant updates
 Zone-specific content distribution (reception, restaurant, skislope, lockers, shop)
 Professional admin dashboard with content management interface
 Beautiful client display with winter/snow theme matching SnowWorld branding
 Comprehensive technical documentation and test suite
 Docker deployment support with CI/CD pipeline
 All system tests passing successfully

🏗️ Technical Implementation:
- Backend: Node.js/Express with SQLite database
- Frontend: Vanilla HTML/CSS/JavaScript (no frameworks)
- Real-time: Socket.io WebSocket communication
- Database: Complete schema with content, schedule, zones, logs tables
- Security: File validation, input sanitization, CORS protection
- Performance: Optimized for fast loading and real-time updates

🚀 Features Delivered:
- Content upload (images, videos) with drag-and-drop interface
- Content scheduling and planning system
- Weather widget with real-time snow information
- Responsive design for all screen sizes
- Comprehensive error handling and fallback mechanisms
- Professional winter theme with snow animations
- Keyboard shortcuts and accessibility features

📁 Project Structure:
- /backend: Complete Node.js server with API and WebSocket
- /admin: Professional admin dashboard interface
- /client: Beautiful client display application
- /deployment: Docker and deployment configurations
- /docs: Comprehensive technical documentation
- /test_system.js: Complete test suite (all tests passing)

🧪 Testing Results:
- Server health:  Online and responsive
- API endpoints:  All endpoints functional
- Database operations:  All operations successful
- WebSocket communication:  Real-time updates working
- Zone distribution:  6 zones correctly loaded
- Weather integration:  Weather data available

Ready for production deployment at SnowWorld! 🎿❄️
2026-01-19 10:02:11 +01:00

253 lines
10 KiB
HTML

<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SnowWorld - Narrowcasting Admin Dashboard</title>
<link rel="stylesheet" href="styles.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<!-- Header -->
<header class="header">
<div class="header-content">
<div class="logo">
<i class="fas fa-snowflake"></i>
<h1>SnowWorld Narrowcasting</h1>
</div>
<div class="header-actions">
<button id="refreshBtn" class="btn btn-secondary">
<i class="fas fa-sync-alt"></i> Verversen
</button>
<div class="status-indicator">
<span id="connectionStatus" class="status-dot"></span>
<span id="connectionText">Verbonden</span>
</div>
</div>
</div>
</header>
<!-- Navigation -->
<nav class="nav-tabs">
<button class="nav-tab active" data-tab="content">
<i class="fas fa-photo-video"></i> Content Beheer
</button>
<button class="nav-tab" data-tab="schedule">
<i class="fas fa-calendar-alt"></i> Planning
</button>
<button class="nav-tab" data-tab="zones">
<i class="fas fa-map-marked-alt"></i> Zones
</button>
<button class="nav-tab" data-tab="analytics">
<i class="fas fa-chart-bar"></i> Analytics
</button>
</nav>
<!-- Main Content -->
<main class="main-content">
<!-- Content Management Tab -->
<div id="content-tab" class="tab-content active">
<div class="section-header">
<h2>Content Beheer</h2>
<button id="addContentBtn" class="btn btn-primary">
<i class="fas fa-plus"></i> Content Toevoegen
</button>
</div>
<!-- Filter Controls -->
<div class="filter-controls">
<select id="zoneFilter" class="form-select">
<option value="">Alle Zones</option>
</select>
<select id="typeFilter" class="form-select">
<option value="">Alle Types</option>
<option value="image">Afbeeldingen</option>
<option value="video">Video's</option>
<option value="livestream">Livestreams</option>
</select>
<button id="applyFilters" class="btn btn-secondary">Toepassen</button>
</div>
<!-- Content Grid -->
<div id="contentGrid" class="content-grid">
<!-- Content items will be dynamically loaded here -->
</div>
</div>
<!-- Schedule Tab -->
<div id="schedule-tab" class="tab-content">
<div class="section-header">
<h2>Content Planning</h2>
<button id="addScheduleBtn" class="btn btn-primary">
<i class="fas fa-plus"></i> Planning Toevoegen
</button>
</div>
<div class="schedule-container">
<div class="zone-selector">
<h3>Kies Zone:</h3>
<select id="scheduleZoneSelect" class="form-select">
<!-- Zones will be loaded dynamically -->
</select>
</div>
<div id="scheduleTimeline" class="schedule-timeline">
<!-- Schedule items will be displayed here -->
</div>
</div>
</div>
<!-- Zones Tab -->
<div id="zones-tab" class="tab-content">
<div class="section-header">
<h2>Zone Overzicht</h2>
</div>
<div id="zonesGrid" class="zones-grid">
<!-- Zone information will be displayed here -->
</div>
</div>
<!-- Analytics Tab -->
<div id="analytics-tab" class="tab-content">
<div class="section-header">
<h2>Analytics Dashboard</h2>
</div>
<div class="analytics-grid">
<div class="analytics-card">
<h3>Content Statistieken</h3>
<div id="contentStats" class="stats-container">
<!-- Content stats will be loaded here -->
</div>
</div>
<div class="analytics-card">
<h3>Planning Statistieken</h3>
<div id="scheduleStats" class="stats-container">
<!-- Schedule stats will be loaded here -->
</div>
</div>
<div class="analytics-card">
<h3>Zone Overzicht</h3>
<div id="zoneStats" class="stats-container">
<!-- Zone stats will be loaded here -->
</div>
</div>
</div>
</div>
</main>
</div>
<!-- Content Upload Modal -->
<div id="contentModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>Content Toevoegen</h3>
<button class="close-btn">&times;</button>
</div>
<form id="contentUploadForm" class="modal-body">
<div class="form-group">
<label for="contentTitle">Titel:</label>
<input type="text" id="contentTitle" class="form-control" required>
</div>
<div class="form-group">
<label for="contentType">Type:</label>
<select id="contentType" class="form-control" required>
<option value="">Kies type...</option>
<option value="image">Afbeelding</option>
<option value="video">Video</option>
<option value="livestream">Livestream</option>
</select>
</div>
<div class="form-group">
<label for="contentZone">Zone:</label>
<select id="contentZone" class="form-control" required>
<!-- Zones will be loaded dynamically -->
</select>
</div>
<div class="form-group">
<label for="contentDuration">Weergave Duur (seconden):</label>
<input type="number" id="contentDuration" class="form-control" min="5" max="300" value="10">
</div>
<div class="form-group">
<label for="contentFile">Bestand:</label>
<input type="file" id="contentFile" class="form-control" accept="image/*,video/*" required>
<div id="fileInfo" class="file-info"></div>
</div>
<div class="modal-actions">
<button type="button" class="btn btn-secondary" onclick="closeModal()">Annuleren</button>
<button type="submit" class="btn btn-primary">Uploaden</button>
</div>
</form>
</div>
</div>
<!-- Schedule Modal -->
<div id="scheduleModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>Planning Toevoegen</h3>
<button class="close-btn">&times;</button>
</div>
<form id="scheduleForm" class="modal-body">
<div class="form-group">
<label for="scheduleContent">Content:</label>
<select id="scheduleContent" class="form-control" required>
<!-- Available content will be loaded dynamically -->
</select>
</div>
<div class="form-group">
<label for="scheduleZone">Zone:</label>
<select id="scheduleZone" class="form-control" required>
<!-- Zones will be loaded dynamically -->
</select>
</div>
<div class="form-group">
<label for="scheduleStart">Start Tijd:</label>
<input type="datetime-local" id="scheduleStart" class="form-control" required>
</div>
<div class="form-group">
<label for="scheduleEnd">Eind Tijd:</label>
<input type="datetime-local" id="scheduleEnd" class="form-control" required>
</div>
<div class="form-group">
<label for="schedulePriority">Prioriteit:</label>
<select id="schedulePriority" class="form-control">
<option value="1">Laag</option>
<option value="2">Normaal</option>
<option value="3">Hoog</option>
</select>
</div>
<div class="modal-actions">
<button type="button" class="btn btn-secondary" onclick="closeScheduleModal()">Annuleren</button>
<button type="submit" class="btn btn-primary">Plannen</button>
</div>
</form>
</div>
</div>
<!-- Toast Notifications -->
<div id="toastContainer" class="toast-container">
<!-- Toast notifications will appear here -->
</div>
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
<script src="js/api.js"></script>
<script src="js/ui.js"></script>
<script src="js/websocket.js"></script>
<script src="js/app.js"></script>
</body>
</html>