Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>NTNU Crestron Control Panel</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --ntnu-blue: #00509e; | |
| --ntnu-blue-light: #e6f0fa; | |
| --active-green: #4CAF50; | |
| --muted-red: #F44336; | |
| --neutral-gray: #607D8B; | |
| } | |
| body { | |
| font-family: 'Open Sans', Arial, sans-serif; | |
| touch-action: manipulation; | |
| -webkit-tap-highlight-color: transparent; | |
| } | |
| .touch-target { | |
| min-width: 44px; | |
| min-height: 44px; | |
| } | |
| .drag-source { | |
| touch-action: none; | |
| user-select: none; | |
| } | |
| .drop-zone { | |
| transition: all 0.2s ease; | |
| } | |
| .drop-zone.active { | |
| background-color: var(--ntnu-blue-light); | |
| border: 2px dashed var(--ntnu-blue); | |
| } | |
| .volume-fader::-webkit-slider-thumb { | |
| -webkit-appearance: none; | |
| width: 24px; | |
| height: 24px; | |
| border-radius: 50%; | |
| background: var(--ntnu-blue); | |
| cursor: pointer; | |
| } | |
| .volume-fader::-moz-range-thumb { | |
| width: 24px; | |
| height: 24px; | |
| border-radius: 50%; | |
| background: var(--ntnu-blue); | |
| cursor: pointer; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-white h-screen w-screen overflow-hidden flex flex-col"> | |
| <!-- Top Bar --> | |
| <div class="bg-[#00509e] text-white p-2 flex items-center justify-between"> | |
| <!-- Language Toggle --> | |
| <div class="flex items-center space-x-2"> | |
| <button id="langToggle" class="bg-white text-[#00509e] px-3 py-1 rounded-full font-semibold text-sm"> | |
| EN | |
| </button> | |
| </div> | |
| <!-- NTNU Logo (Placeholder) --> | |
| <div class="flex-1 text-center"> | |
| <div class="font-bold text-xl">NTNU</div> | |
| </div> | |
| <!-- Help and Recording --> | |
| <div class="flex items-center space-x-4"> | |
| <button id="helpBtn" class="bg-white text-[#00509e] rounded-full w-8 h-8 flex items-center justify-center"> | |
| <i data-feather="help-circle"></i> | |
| </button> | |
| <div id="recordingIndicator" class="w-6 h-6 rounded-full bg-[#4CAF50]"></div> | |
| </div> | |
| </div> | |
| <!-- Main Content Area --> | |
| <div class="flex flex-1 overflow-hidden"> | |
| <!-- Side Menu --> | |
| <div class="w-24 bg-gray-100 flex flex-col items-center py-4 space-y-4 border-r border-gray-300"> | |
| <button class="menu-btn active bg-[#00509e] text-white" data-menu="home"> | |
| <i data-feather="home" class="w-6 h-6"></i> | |
| <span class="text-xs mt-1">Home</span> | |
| </button> | |
| <button class="menu-btn" data-menu="image"> | |
| <i data-feather="monitor" class="w-6 h-6"></i> | |
| <span class="text-xs mt-1">Image</span> | |
| </button> | |
| <button class="menu-btn" data-menu="audio"> | |
| <i data-feather="volume-2" class="w-6 h-6"></i> | |
| <span class="text-xs mt-1">Audio</span> | |
| </button> | |
| <button class="menu-btn" data-menu="lighting"> | |
| <i data-feather="sun" class="w-6 h-6"></i> | |
| <span class="text-xs mt-1">Lighting</span> | |
| </button> | |
| <button class="menu-btn" data-menu="blinds"> | |
| <i data-feather="layers" class="w-6 h-6"></i> | |
| <span class="text-xs mt-1">Blinds</span> | |
| </button> | |
| <button class="menu-btn" data-menu="camera"> | |
| <i data-feather="camera" class="w-6 h-6"></i> | |
| <span class="text-xs mt-1">Camera</span> | |
| </button> | |
| <button class="menu-btn hidden" id="techBtn" data-menu="technician"> | |
| <i data-feather="settings" class="w-6 h-6"></i> | |
| <span class="text-xs mt-1">Technician</span> | |
| </button> | |
| <button class="menu-btn" data-menu="shutdown"> | |
| <i data-feather="power" class="w-6 h-6"></i> | |
| <span class="text-xs mt-1">Exit</span> | |
| </button> | |
| </div> | |
| <!-- Content Panels --> | |
| <div class="flex-1 overflow-auto p-4"> | |
| <!-- Home Panel --> | |
| <div id="homePanel" class="menu-panel"> | |
| <div class="flex flex-col items-center justify-center h-full"> | |
| <h2 class="text-2xl font-bold text-[#00509e] mb-8">NTNU Classroom Control</h2> | |
| <button id="startRoomBtn" class="bg-[#00509e] hover:bg-[#003d7a] text-white font-bold py-4 px-8 rounded-lg text-xl transition-colors"> | |
| Start Room | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Image Panel --> | |
| <div id="imagePanel" class="menu-panel hidden"> | |
| <h2 class="text-xl font-bold text-[#00509e] mb-4">Image Routing</h2> | |
| <div class="mb-8"> | |
| <h3 class="text-lg font-semibold mb-2">Sources</h3> | |
| <div class="grid grid-cols-4 gap-4"> | |
| <div class="drag-source bg-[#00509e] text-white p-3 rounded-lg text-center cursor-move touch-target" draggable="true" data-source="ntnu-pc"> | |
| <i data-feather="cpu" class="w-8 h-8 mx-auto"></i> | |
| <div class="font-semibold mt-1">NTNU PC</div> | |
| </div> | |
| <div class="drag-source bg-[#00509e] text-white p-3 rounded-lg text-center cursor-move touch-target" draggable="true" data-source="laptop-hdmi"> | |
| <i data-feather="laptop" class="w-8 h-8 mx-auto"></i> | |
| <div class="font-semibold mt-1">Laptop HDMI</div> | |
| </div> | |
| <div class="drag-source bg-[#00509e] text-white p-3 rounded-lg text-center cursor-move touch-target" draggable="true" data-source="doc-cam"> | |
| <i data-feather="image" class="w-8 h-8 mx-auto"></i> | |
| <div class="font-semibold mt-1">Document Camera</div> | |
| </div> | |
| <div class="drag-source bg-gray-500 text-white p-3 rounded-lg text-center cursor-move touch-target" draggable="true" data-source="off"> | |
| <i data-feather="x" class="w-8 h-8 mx-auto"></i> | |
| <div class="font-semibold mt-1">Image Off</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <h3 class="text-lg font-semibold mb-2">Destinations</h3> | |
| <div class="grid grid-cols-3 gap-4"> | |
| <div class="drop-zone bg-gray-100 p-4 rounded-lg min-h-32 flex flex-col items-center justify-center touch-target" data-destination="projector1"> | |
| <div class="destination-content text-center"> | |
| <i data-feather="monitor" class="w-8 h-8 mx-auto text-gray-500"></i> | |
| <div class="font-semibold mt-1">Projector 1</div> | |
| <div class="text-xs text-gray-500">Drag source here</div> | |
| </div> | |
| <div class="audio-indicator mt-2 w-4 h-4 rounded-full bg-gray-300"></div> | |
| </div> | |
| <div class="drop-zone bg-gray-100 p-4 rounded-lg min-h-32 flex flex-col items-center justify-center touch-target" data-destination="projector2"> | |
| <div class="destination-content text-center"> | |
| <i data-feather="monitor" class="w-8 h-8 mx-auto text-gray-500"></i> | |
| <div class="font-semibold mt-1">Projector 2</div> | |
| <div class="text-xs text-gray-500">Drag source here</div> | |
| </div> | |
| <div class="audio-indicator mt-2 w-4 h-4 rounded-full bg-gray-300"></div> | |
| </div> | |
| <div class="drop-zone bg-gray-100 p-4 rounded-lg min-h-32 flex flex-col items-center justify-center touch-target hidden" id="projector3Zone" data-destination="projector3"> | |
| <div class="destination-content text-center"> | |
| <i data-feather="monitor" class="w-8 h-8 mx-auto text-gray-500"></i> | |
| <div class="font-semibold mt-1">Projector 3</div> | |
| <div class="text-xs text-gray-500">Drag source here</div> | |
| </div> | |
| <div class="audio-indicator mt-2 w-4 h-4 rounded-full bg-gray-300"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Audio Panel --> | |
| <div id="audioPanel" class="menu-panel hidden"> | |
| <h2 class="text-xl font-bold text-[#00509e] mb-4">Audio Controls</h2> | |
| <div class="grid grid-cols-4 gap-6 mb-8"> | |
| <div class="mic-control"> | |
| <h3 class="text-lg font-semibold mb-2">Mic 1</h3> | |
| <input type="range" min="0" max="100" value="50" class="volume-fader w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> | |
| <div class="flex justify-between text-xs text-gray-600 mt-1"> | |
| <span>0%</span> | |
| <span>100%</span> | |
| </div> | |
| <button class="mic-mute-btn mt-2 bg-[#4CAF50] text-white px-4 py-2 rounded-lg w-full touch-target"> | |
| <i data-feather="mic" class="w-4 h-4 inline mr-1"></i> | |
| <span>Mute</span> | |
| </button> | |
| </div> | |
| <div class="mic-control"> | |
| <h3 class="text-lg font-semibold mb-2">Mic 2</h3> | |
| <input type="range" min="0" max="100" value="50" class="volume-fader w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> | |
| <div class="flex justify-between text-xs text-gray-600 mt-1"> | |
| <span>0%</span> | |
| <span>100%</span> | |
| </div> | |
| <button class="mic-mute-btn mt-2 bg-[#4CAF50] text-white px-4 py-2 rounded-lg w-full touch-target"> | |
| <i data-feather="mic" class="w-4 h-4 inline mr-1"></i> | |
| <span>Mute</span> | |
| </button> | |
| </div> | |
| <div class="mic-control"> | |
| <h3 class="text-lg font-semibold mb-2">Mic 3</h3> | |
| <input type="range" min="0" max="100" value="50" class="volume-fader w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> | |
| <div class="flex justify-between text-xs text-gray-600 mt-1"> | |
| <span>0%</span> | |
| <span>100%</span> | |
| </div> | |
| <button class="mic-mute-btn mt-2 bg-[#4CAF50] text-white px-4 py-2 rounded-lg w-full touch-target"> | |
| <i data-feather="mic" class="w-4 h-4 inline mr-1"></i> | |
| <span>Mute</span> | |
| </button> | |
| </div> | |
| <div class="mic-control"> | |
| <h3 class="text-lg font-semibold mb-2">Mic 4</h3> | |
| <input type="range" min="0" max="100" value="50" class="volume-fader w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> | |
| <div class="flex justify-between text-xs text-gray-600 mt-1"> | |
| <span>0%</span> | |
| <span>100%</span> | |
| </div> | |
| <button class="mic-mute-btn mt-2 bg-[#4CAF50] text-white px-4 py-2 rounded-lg w-full touch-target"> | |
| <i data-feather="mic" class="w-4 h-4 inline mr-1"></i> | |
| <span>Mute</span> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="bg-gray-100 p-4 rounded-lg"> | |
| <h3 class="text-lg font-semibold mb-2">Program Audio</h3> | |
| <div class="flex items-center space-x-4"> | |
| <div class="flex-1"> | |
| <input type="range" min="0" max="100" value="70" class="volume-fader w-full h-2 bg-gray-300 rounded-lg appearance-none cursor-pointer"> | |
| <div class="flex justify-between text-xs text-gray-600 mt-1"> | |
| <span>0%</span> | |
| <span>100%</span> | |
| </div> | |
| </div> | |
| <button class="program-mute-btn bg-[#4CAF50] text-white px-4 py-2 rounded-lg touch-target"> | |
| <i data-feather="volume-2" class="w-4 h-4 inline mr-1"></i> | |
| <span>Mute</span> | |
| </button> | |
| </div> | |
| <div class="mt-4"> | |
| <h4 class="font-semibold mb-2">Audio Source Override</h4> | |
| <div class="grid grid-cols-4 gap-2"> | |
| <button class="audio-source-btn bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-lg text-sm touch-target">NTNU PC</button> | |
| <button class="audio-source-btn bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-lg text-sm touch-target">Laptop HDMI</button> | |
| <button class="audio-source-btn bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-lg text-sm touch-target">Doc Camera</button> | |
| <button class="audio-source-btn bg-gray-200 hover:bg-gray-300 px-3 py-2 rounded-lg text-sm touch-target">None</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Lighting Panel --> | |
| <div id="lightingPanel" class="menu-panel hidden"> | |
| <h2 class="text-xl font-bold text-[#00509e] mb-4">Lighting Controls</h2> | |
| <div class="mb-6"> | |
| <h3 class="text-lg font-semibold mb-2">Presets</h3> | |
| <div class="grid grid-cols-4 gap-4"> | |
| <button class="light-preset-btn bg-gray-200 hover:bg-gray-300 p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="sun" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">All Lights</div> | |
| </button> | |
| <button class="light-preset-btn bg-gray-200 hover:bg-gray-300 p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="user" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">Podium</div> | |
| </button> | |
| <button class="light-preset-btn bg-gray-200 hover:bg-gray-300 p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="edit-3" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">Whiteboard</div> | |
| </button> | |
| <button class="light-preset-btn bg-gray-200 hover:bg-gray-300 p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="video" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">Video</div> | |
| </button> | |
| </div> | |
| </div> | |
| <div> | |
| <h3 class="text-lg font-semibold mb-2">Adjustable Lights</h3> | |
| <div class="grid grid-cols-2 gap-6"> | |
| <div class="light-control"> | |
| <h4 class="font-semibold mb-2">Room Lights</h4> | |
| <input type="range" min="0" max="100" value="70" class="volume-fader w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer" orient="vertical" style="height: 120px;"> | |
| <div class="flex justify-between text-xs text-gray-600 mt-1"> | |
| <span>0%</span> | |
| <span>100%</span> | |
| </div> | |
| </div> | |
| <div class="light-control"> | |
| <h4 class="font-semibold mb-2">Podium Lights</h4> | |
| <input type="range" min="0" max="100" value="70" class="volume-fader w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer" orient="vertical" style="height: 120px;"> | |
| <div class="flex justify-between text-xs text-gray-600 mt-1"> | |
| <span>0%</span> | |
| <span>100%</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Blinds Panel --> | |
| <div id="blindsPanel" class="menu-panel hidden"> | |
| <h2 class="text-xl font-bold text-[#00509e] mb-4">Blinds & Screens</h2> | |
| <div class="grid grid-cols-2 gap-6"> | |
| <div class="screen-control"> | |
| <h3 class="text-lg font-semibold mb-2">Projection Screen</h3> | |
| <div class="grid grid-cols-3 gap-2"> | |
| <button class="screen-btn bg-[#00509e] text-white p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="chevron-up" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">Up</div> | |
| </button> | |
| <button class="screen-btn bg-gray-500 text-white p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="square" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">Stop</div> | |
| </button> | |
| <button class="screen-btn bg-[#00509e] text-white p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="chevron-down" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">Down</div> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="blinds-control"> | |
| <h3 class="text-lg font-semibold mb-2">Window Blinds</h3> | |
| <div class="grid grid-cols-3 gap-2"> | |
| <button class="blinds-btn bg-[#00509e] text-white p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="chevron-up" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">Up</div> | |
| </button> | |
| <button class="blinds-btn bg-gray-500 text-white p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="square" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">Stop</div> | |
| </button> | |
| <button class="blinds-btn bg-[#00509e] text-white p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="chevron-down" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">Down</div> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Camera Panel --> | |
| <div id="cameraPanel" class="menu-panel hidden"> | |
| <h2 class="text-xl font-bold text-[#00509e] mb-4">Teaching Camera</h2> | |
| <div class="mb-6"> | |
| <h3 class="text-lg font-semibold mb-2">Presets</h3> | |
| <div class="grid grid-cols-3 gap-4"> | |
| <button class="camera-preset-btn bg-[#00509e] text-white p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="edit-3" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">Whiteboard</div> | |
| </button> | |
| <button class="camera-preset-btn bg-[#00509e] text-white p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="user" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">Lecturer Desk</div> | |
| </button> | |
| <button class="camera-preset-btn bg-[#00509e] text-white p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="mic" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">Podium</div> | |
| </button> | |
| <button class="camera-preset-btn bg-[#00509e] text-white p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="arrow-left" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">WB Left</div> | |
| </button> | |
| <button class="camera-preset-btn bg-[#00509e] text-white p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="align-center" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">WB Center</div> | |
| </button> | |
| <button class="camera-preset-btn bg-[#00509e] text-white p-3 rounded-lg text-center touch-target"> | |
| <i data-feather="arrow-right" class="w-6 h-6 mx-auto"></i> | |
| <div class="font-semibold mt-1">WB Right</div> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="bg-gray-100 p-4 rounded-lg"> | |
| <h3 class="text-lg font-semibold mb-2">Tracking Control</h3> | |
| <button id="trackingToggle" class="bg-[#4CAF50] hover:bg-[#3e8e41] text-white font-bold py-3 px-6 rounded-lg text-lg transition-colors w-full touch-target"> | |
| <i data-feather="user-check" class="w-5 h-5 inline mr-2"></i> | |
| <span>Tracking: ON</span> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Shutdown Panel --> | |
| <div id="shutdownPanel" class="menu-panel hidden"> | |
| <h2 class="text-xl font-bold text-[#00509e] mb-4">System Shutdown</h2> | |
| <div class="bg-gray-100 p-6 rounded-lg max-w-md mx-auto"> | |
| <p class="text-lg mb-6">Are you sure you want to shut down the system?</p> | |
| <div class="grid grid-cols-2 gap-4 mb-6"> | |
| <button id="shutdownConfirm" class="bg-[#00509e] hover:bg-[#003d7a] text-white font-bold py-3 px-4 rounded-lg transition-colors touch-target"> | |
| Shut Down | |
| </button> | |
| <button id="shutdownCancel" class="bg-gray-500 hover:bg-gray-600 text-white font-bold py-3 px-4 rounded-lg transition-colors touch-target"> | |
| Cancel | |
| </button> | |
| </div> | |
| <div class="border-t border-gray-300 pt-4"> | |
| <p class="text-sm font-semibold mb-2">How did the room work today?</p> | |
| <div class="flex justify-around"> | |
| <button class="feedback-btn text-[#4CAF50] hover:text-[#3e8e41] text-4xl touch-target" data-rating="good"> | |
| 😊 | |
| </button> | |
| <button class="feedback-btn text-[#FFC107] hover:text-[#FFA000] text-4xl touch-target" data-rating="neutral"> | |
| 😐 | |
| </button> | |
| <button class="feedback-btn text-[#F44336] hover:text-[#D32F2F] text-4xl touch-target" data-rating="poor"> | |
| 😞 | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Technician Panel --> | |
| <div id="technicianPanel" class="menu-panel hidden"> | |
| <div class="flex items-center justify-between mb-6"> | |
| <h2 class="text-xl font-bold text-[#00509e]">Technician Menu</h2> | |
| <button id="logoutTechBtn" class="bg-gray-500 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded-lg text-sm transition-colors touch-target"> | |
| Logout | |
| </button> | |
| </div> | |
| <div class="bg-gray-100 rounded-lg overflow-hidden"> | |
| <div class="flex border-b border-gray-300"> | |
| <button class="tech-tab-btn active py-3 px-6 font-semibold text-[#00509e] border-b-2 border-[#00509e] touch-target">System</button> | |
| <button class="tech-tab-btn py-3 px-6 font-semibold text-gray-600 hover:text-[#00509e] touch-target">Audio Mixer</button> | |
| <button class="tech-tab-btn py-3 px-6 font-semibold text-gray-600 hover:text-[#00509e] touch-target">Video Mixer</button> | |
| <button class="tech-tab-btn py-3 px-6 font-semibold text-gray-600 hover:text-[#00509e] touch-target">Microphones</button> | |
| <button class="tech-tab-btn py-3 px-6 font-semibold text-gray-600 hover:text-[#00509e] touch-target">Camera</button> | |
| <button class="tech-tab-btn py-3 px-6 font-semibold text-gray-600 hover:text-[#00509e] touch-target">Feedback</button> | |
| <button class="tech-tab-btn py-3 px-6 font-semibold text-gray-600 hover:text-[#00509e] touch-target">Diagnostics</button> | |
| </div> | |
| <div class="p-6"> | |
| <!-- System Tab Content --> | |
| <div id="systemTab" class="tech-tab-content"> | |
| <h3 class="text-lg font-semibold mb-4">System Information</h3> | |
| <div class="grid grid-cols-2 gap-4 mb-6"> | |
| <div> | |
| <p class="text-sm text-gray-600">Crestron Version:</p> | |
| <p class="font-semibold">3.12.456.0</p> | |
| </div> | |
| <div> | |
| <p class="text-sm text-gray-600">Last Updated:</p> | |
| <p class="font-semibold">2023-11-15</p> | |
| </div> | |
| <div> | |
| <p class="text-sm text-gray-600">Programmer:</p> | |
| <p class="font-semibold">NTNU AV Services</p> | |
| </div> | |
| <div> | |
| <p class="text-sm text-gray-600">Vendor:</p> | |
| <p class="font-semibold">Crestron Electronics</p> | |
| </div> | |
| </div> | |
| <div class="border-t border-gray-300 pt-4"> | |
| <h3 class="text-lg font-semibold mb-2">Change Technician PIN</h3> | |
| <div class="flex items-center space-x-4"> | |
| <input type="password" class="border border-gray-300 rounded-lg px-3 py-2 w-32" placeholder="New PIN" maxlength="5"> | |
| <button class="bg-[#00509e] hover:bg-[#003d7a] text-white font-bold py-2 px-4 rounded-lg transition-colors touch-target"> | |
| Update | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Other tabs would be implemented similarly --> | |
| <div id="audioMixerTab" class="tech-tab-content hidden"> | |
| <!-- Audio mixer controls would go here --> | |
| </div> | |
| <div id="videoMixerTab" class="tech-tab-content hidden"> | |
| <!-- Video mixer controls would go here --> | |
| </div> | |
| <div id="microphonesTab" class="tech-tab-content hidden"> | |
| <!-- Microphone pairing controls would go here --> | |
| </div> | |
| <div id="cameraTab" class="tech-tab-content hidden"> | |
| <!-- Camera PTZ controls would go here --> | |
| </div> | |
| <div id="feedbackTab" class="tech-tab-content hidden"> | |
| <!-- Feedback statistics would go here --> | |
| </div> | |
| <div id="diagnosticsTab" class="tech-tab-content hidden"> | |
| <!-- Diagnostics information would go here --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- PIN Entry Modal --> | |
| <div id="pinModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> | |
| <div class="bg-white p-6 rounded-lg max-w-sm w-full"> | |
| <h3 class="text-xl font-bold text-[#00509e] mb-4">Enter Technician PIN</h3> | |
| <div class="mb-4"> | |
| <input type="password" id="pinInput" class="border border-gray-300 rounded-lg px-4 py-3 w-full text-center text-xl tracking-widest" placeholder="00000" maxlength="5"> | |
| </div> | |
| <div class="grid grid-cols-3 gap-2 mb-4"> | |
| <button class="pin-btn bg-gray-200 hover:bg-gray-300 py-3 rounded-lg font-semibold touch-target">1</button> | |
| <button class="pin-btn bg-gray-200 hover:bg-gray-300 py-3 rounded-lg font-semibold touch-target">2</button> | |
| <button class="pin-btn bg-gray-200 hover:bg-gray-300 py-3 rounded-lg font-semibold touch-target">3</button> | |
| <button class="pin-btn bg-gray-200 hover:bg-gray-300 py-3 rounded-lg font-semibold touch-target">4</button> | |
| <button class="pin-btn bg-gray-200 hover:bg-gray-300 py-3 rounded-lg font-semibold touch-target">5</button> | |
| <button class="pin-btn bg-gray-200 hover:bg-gray-300 py-3 rounded-lg font-semibold touch-target">6</button> | |
| <button class="pin-btn bg-gray-200 hover:bg-gray-300 py-3 rounded-lg font-semibold touch-target">7</button> | |
| <button class="pin-btn bg-gray-200 hover:bg-gray-300 py-3 rounded-lg font-semibold touch-target">8</button> | |
| <button class="pin-btn bg-gray-200 hover:bg-gray-300 py-3 rounded-lg font-semibold touch-target">9</button> | |
| <button class="pin-btn bg-gray-200 hover:bg-gray-300 py-3 rounded-lg font-semibold touch-target">0</button> | |
| <button class="pin-btn bg-gray-200 hover:bg-gray-300 py-3 rounded-lg font-semibold touch-target"> | |
| <i data-feather="delete" class="w-5 h-5 mx-auto"></i> | |
| </button> | |
| </div> | |
| <div class="flex space-x-4"> | |
| <button id="pinSubmit" class="flex-1 bg-[#00509e] hover:bg-[#003d7a] text-white font-bold py-2 px-4 rounded-lg transition-colors touch-target"> | |
| Submit | |
| </button> | |
| <button id="pinCancel" class="flex-1 bg-gray-500 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded-lg transition-colors touch-target"> | |
| Cancel | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Help Modal --> | |
| <div id="helpModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> | |
| <div class="bg-white p-6 rounded-lg max-w-md w-full"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h3 class="text-xl font-bold text-[#00509e]">Help</h3> | |
| <button id="helpClose" class="text-gray-500 hover:text-gray-700"> | |
| <i data-feather="x" class="w-6 h-6"></i> | |
| </button> | |
| </div> | |
| <div class="prose"> | |
| <p>For assistance with the classroom AV system:</p> | |
| <ul class="list-disc pl-5"> | |
| <li>Press the "Start Room" button to power on all equipment</li> | |
| <li>Drag video sources to projectors to route content</li> | |
| <li>Adjust microphone and audio levels as needed</li> | |
| <li>Use presets for quick lighting adjustments</li> | |
| </ul> | |
| <p class="mt-4">For technical support, contact NTNU AV Services:</p> | |
| <p class="font-semibold">Phone: 123 456 78</p> | |
| <p class="font-semibold">Email: av-support@ntnu.no</p> | |
| </div> | |
| <div class="mt-6 text-center"> | |
| <img src="http://static.photos/technology/320x240/1" alt="Support QR Code" class="w-32 h-32 mx-auto"> | |
| <p class="text-sm mt-2">Scan for more help resources</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Initialize Feather Icons | |
| feather.replace(); | |
| // Current state variables | |
| let currentLanguage = 'en'; | |
| let isRecording = false; | |
| let isTrackingOn = true; | |
| let isRoomStarted = false; | |
| let isTechMode = false; | |
| // DOM Elements | |
| const langToggle = document.getElementById('langToggle'); | |
| const helpBtn = document.getElementById('helpBtn'); | |
| const helpModal = document.getElementById('helpModal'); | |
| const helpClose = document.getElementById('helpClose'); | |
| const recordingIndicator = document.getElementById('recordingIndicator'); | |
| const startRoomBtn = document.getElementById('startRoomBtn'); | |
| const techBtn = document.getElementById('techBtn'); | |
| const pinModal = document.getElementById('pinModal'); | |
| const pinInput = document.getElementById('pinInput'); | |
| const pinSubmit = document.getElementById('pinSubmit'); | |
| const pinCancel = document.getElementById('pinCancel'); | |
| const logoutTechBtn = document.getElementById('logoutTechBtn'); | |
| const trackingToggle = document.getElementById('trackingToggle'); | |
| const shutdownConfirm = document.getElementById('shutdownConfirm'); | |
| const shutdownCancel = document.getElementById('shutdownCancel'); | |
| // Menu buttons and panels | |
| const menuButtons = document.querySelectorAll('.menu-btn'); | |
| const menuPanels = document.querySelectorAll('.menu-panel'); | |
| // Technician tab buttons and content | |
| const techTabButtons = document.querySelectorAll('.tech-tab-btn'); | |
| const techTabContents = document.querySelectorAll('.tech-tab-content'); | |
| // PIN buttons | |
| const pinButtons = document.querySelectorAll('.pin-btn'); | |
| // Feedback buttons | |
| const feedbackButtons = document.querySelectorAll('.feedback-btn'); | |
| // Drag and drop elements | |
| const dragSources = document.querySelectorAll('.drag-source'); | |
| const dropZones = document.querySelectorAll('.drop-zone'); | |
| // Mute buttons | |
| const muteButtons = document.querySelectorAll('.mic-mute-btn, .program-mute-btn'); | |
| // Event Listeners | |
| langToggle.addEventListener('click', toggleLanguage); | |
| helpBtn.addEventListener('click', () => helpModal.classList.remove('hidden')); | |
| helpClose.addEventListener('click', () => helpModal.classList.add('hidden')); | |
| startRoomBtn.addEventListener('click', startRoom); | |
| techBtn.addEventListener('click', () => pinModal.classList.remove('hidden')); | |
| pinSubmit.addEventListener('click', checkPin); | |
| pinCancel.addEventListener('click', () => pinModal.classList.add('hidden')); | |
| logoutTechBtn.addEventListener('click', logoutTechnician); | |
| trackingToggle.addEventListener('click', toggleTracking); | |
| shutdownConfirm.addEventListener('click', shutdownSystem); | |
| shutdownCancel.addEventListener('click', () => showPanel('home')); | |
| // Menu navigation | |
| menuButtons.forEach(button => { | |
| button.addEventListener('click', () => { | |
| const menu = button.dataset.menu; | |
| showPanel(menu); | |
| // Update active state | |
| menuButtons.forEach(btn => btn.classList.remove('active', 'bg-[#00509e]', 'text-white')); | |
| button.classList.add('active', 'bg-[#00509e]', 'text-white'); | |
| }); | |
| }); | |
| // Technician tabs | |
| techTabButtons.forEach((button, index) => { | |
| button.addEventListener('click', () => { | |
| techTabButtons.forEach(btn => { | |
| btn.classList.remove('active', 'text-[#00509e]', 'border-[#00509e]'); | |
| btn.classList.add('text-gray-600'); | |
| }); | |
| button.classList.add('active', 'text-[#00509e]', 'border-[#00509e]'); | |
| button.classList.remove('text-gray-600'); | |
| techTabContents.forEach(content => content.classList.add('hidden')); | |
| techTabContents[index].classList.remove('hidden'); | |
| }); | |
| }); | |
| // PIN pad | |
| pinButtons.forEach(button => { | |
| button.addEventListener('click', () => { | |
| if (button.querySelector('i')) { | |
| // Delete button | |
| pinInput.value = pinInput.value.slice(0, -1); | |
| } else { | |
| // Number button | |
| if (pinInput.value.length < 5) { | |
| pinInput.value += button.textContent.trim(); | |
| } | |
| } | |
| }); | |
| }); | |
| // Feedback buttons | |
| feedbackButtons.forEach(button => { | |
| button.addEventListener('click', () => { | |
| const rating = button.dataset.rating; | |
| // In a real implementation, this would send the feedback to a server | |
| alert(`Thank you for your feedback (${rating}). The system will now shut down.`); | |
| shutdownSystem(); | |
| }); | |
| }); | |
| // Drag and drop functionality | |
| dragSources.forEach(source => { | |
| source.addEventListener('dragstart', dragStart); | |
| }); | |
| dropZones.forEach(zone => { | |
| zone.addEventListener('dragover', dragOver); | |
| zone.addEventListener('dragenter', dragEnter); | |
| zone.addEventListener('dragleave', dragLeave); | |
| zone.addEventListener('drop', drop); | |
| zone.addEventListener('click', dropZoneClick); | |
| }); | |
| // Mute buttons | |
| muteButtons.forEach(button => { | |
| button.addEventListener('click', toggleMute); | |
| }); | |
| // Functions | |
| function toggleLanguage() { | |
| currentLanguage = currentLanguage === 'en' ? 'no' : 'en'; | |
| langToggle.textContent = currentLanguage.toUpperCase(); | |
| // In a real implementation, this would switch all text in the UI | |
| } | |
| function startRoom() { | |
| isRoomStarted = true; | |
| startRoomBtn.textContent = 'Room Started'; | |
| startRoomBtn.classList.remove('bg-[#00509e]', 'hover:bg-[#003d7a]'); | |
| startRoomBtn.classList.add('bg-gray-500', 'hover:bg-gray-600'); | |
| startRoomBtn.disabled = true; | |
| // Show projector 3 if it exists in this room | |
| document.getElementById('projector3Zone').classList.remove('hidden'); | |
| // Auto-switch to Image panel | |
| showPanel('image'); | |
| } | |
| function showPanel(panelName) { | |
| menuPanels.forEach(panel => panel.classList.add('hidden')); | |
| document.getElementById(`${panelName}Panel`).classList.remove('hidden'); | |
| } | |
| function checkPin() { | |
| if (pinInput.value === '00000') { | |
| isTechMode = true; | |
| techBtn.classList.remove('hidden'); | |
| pinModal.classList.add('hidden'); | |
| showPanel('technician'); | |
| } else { | |
| alert('Incorrect PIN'); | |
| pinInput.value = ''; | |
| } | |
| } | |
| function logoutTechnician() { | |
| isTechMode = false; | |
| techBtn.classList.add('hidden'); | |
| showPanel('home'); | |
| } | |
| function toggleTracking() { | |
| isTrackingOn = !isTrackingOn; | |
| trackingToggle.innerHTML = ` | |
| <i data-feather="${isTrackingOn ? 'user-check' : 'user-x'}" class="w-5 h-5 inline mr-2"></i> | |
| <span>Tracking: ${isTrackingOn ? 'ON' : 'OFF'}</span> | |
| `; | |
| trackingToggle.classList.toggle('bg-[#4CAF50]', isTrackingOn); | |
| trackingToggle.classList.toggle('bg-[#F44336]', !isTrackingOn); | |
| trackingToggle.classList.toggle('hover:bg-[#3e8e41]', isTrackingOn); | |
| trackingToggle.classList.toggle('hover:bg-[#D32F2F]', !isTrackingOn); | |
| feather.replace(); | |
| } | |
| function shutdownSystem() { | |
| // In a real implementation, this would send a shutdown command to the Crestron system | |
| alert('System is shutting down...'); | |
| isRoomStarted = false; | |
| showPanel('home'); | |
| // Reset start button | |
| startRoomBtn.textContent = 'Start Room'; | |
| startRoomBtn.classList.add('bg-[#00509e]', 'hover:bg-[#003d7a]'); | |
| startRoomBtn.classList.remove('bg-gray-500', 'hover:bg-gray-600'); | |
| startRoomBtn.disabled = false; | |
| } | |
| function toggleMute(e) { | |
| const button = e.currentTarget; | |
| const isMuted = button.classList.contains('bg-[#F44336]'); | |
| button.classList.toggle('bg-[#4CAF50]', isMuted); | |
| button.classList.toggle('bg-[#F44336]', !isMuted); | |
| button.classList.toggle('hover:bg-[#3e8e41]', isMuted); | |
| button.classList.toggle('hover:bg-[#D32F2F]', !isMuted); | |
| const icon = button.querySelector('i'); | |
| const text = button.querySelector('span'); | |
| if (isMuted) { | |
| icon.setAttribute('data-feather', 'mic'); | |
| text.textContent = 'Mute'; | |
| } else { | |
| icon.setAttribute('data-feather', 'mic-off'); | |
| text.textContent = 'Unmute'; | |
| } | |
| feather.replace(); | |
| } | |
| // Drag and drop functions | |
| let draggedSource = null; | |
| function dragStart(e) { | |
| draggedSource = e.target; | |
| e.dataTransfer.setData('text/plain', e.target.dataset.source); | |
| setTimeout(() => { | |
| e.target.classList.add('opacity-50'); | |
| }, 0); | |
| } | |
| function dragOver(e) { | |
| e.preventDefault(); | |
| } | |
| function dragEnter(e) { | |
| e.preventDefault(); | |
| e.currentTarget.classList.add('active'); | |
| } | |
| function dragLeave(e) { | |
| e.currentTarget.classList.remove('active'); | |
| } | |
| function drop(e) { | |
| e.preventDefault(); | |
| e.currentTarget.classList.remove('active'); | |
| const sourceType = e.dataTransfer.getData('text/plain'); | |
| const dropZone = e.currentTarget; | |
| updateDropZone(dropZone, sourceType); | |
| if (draggedSource) { | |
| draggedSource.classList.remove('opacity-50'); | |
| draggedSource = null; | |
| } | |
| } | |
| function dropZoneClick(e) { | |
| const dropZone = e.currentTarget.closest('.drop-zone'); | |
| if (!dropZone) return; | |
| // Create a simple source selector for touch devices | |
| const currentSource = dropZone.dataset.currentSource; | |
| const sources = ['ntnu-pc', 'laptop-hdmi', 'doc-cam', 'off']; | |
| const currentIndex = sources.indexOf(currentSource); | |
| const nextIndex = (currentIndex + 1) % sources.length; | |
| updateDropZone(dropZone, sources[nextIndex]); | |
| } | |
| function updateDropZone(dropZone, sourceType) { | |
| const content = dropZone.querySelector('.destination-content'); | |
| const audioIndicator = dropZone.querySelector('.audio-indicator'); | |
| dropZone.dataset.currentSource = sourceType; | |
| if (sourceType === 'off') { | |
| content.innerHTML = ` | |
| <i data-feather="monitor" class="w-8 h-8 mx-auto text-gray-500"></i> | |
| <div class="font-semibold mt-1">${dropZone.dataset.destination.replace('projector', 'Projector ')}</div> | |
| <div class="text-xs text-gray-500">Drag source here</div> | |
| `; | |
| audioIndicator.classList.remove('bg-[#4CAF50]'); | |
| audioIndicator.classList.add('bg-gray-300'); | |
| } else { | |
| let icon, label; | |
| switch(sourceType) { | |
| case 'ntnu-pc': | |
| icon = 'cpu'; | |
| label = 'NTNU PC'; | |
| break; | |
| case 'laptop-hdmi': | |
| icon = 'laptop'; | |
| label = 'Laptop HDMI'; | |
| break; | |
| case 'doc-cam': | |
| icon = 'image'; | |
| label = 'Document Camera'; | |
| break; | |
| } | |
| content.innerHTML = ` | |
| <i data-feather="${icon}" class="w-8 h-8 mx-auto text-[#00509e]"></i> | |
| <div class="font-semibold mt-1">${label}</div> | |
| `; | |
| // Set audio indicator to active (green) if this is the audio source | |
| const isAudioSource = true; // In a real app, this would check the actual state | |
| audioIndicator.classList.toggle('bg-[#4CAF50]', isAudioSource); | |
| audioIndicator.classList.toggle('bg-gray-300', !isAudioSource); | |
| } | |
| feather.replace(); | |
| } | |
| </script> | |
| </body> | |
| </html> | |