2026-04-16 18:19:15 -04:00

521 lines
19 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hotel Pi - Settings</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: white;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
max-width: 600px;
width: 100%;
padding: 40px;
}
h1 {
color: #333;
margin-bottom: 10px;
font-size: 28px;
}
.subtitle {
color: #666;
margin-bottom: 30px;
font-size: 14px;
}
.form-group {
margin-bottom: 25px;
}
label {
display: block;
color: #333;
font-weight: 500;
margin-bottom: 8px;
font-size: 14px;
}
input[type="text"],
input[type="number"],
input[type="color"],
select {
width: 100%;
padding: 10px 12px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 14px;
font-family: inherit;
transition: border-color 0.2s;
}
input[type="text"]:focus,
input[type="number"]:focus,
input[type="color"]:focus,
select:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.checkbox-group {
display: flex;
align-items: center;
gap: 10px;
}
input[type="checkbox"] {
width: 20px;
height: 20px;
cursor: pointer;
}
.checkbox-group label {
margin: 0;
cursor: pointer;
user-select: none;
}
.section {
margin-bottom: 30px;
padding-bottom: 30px;
border-bottom: 1px solid #eee;
}
.section:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.section h2 {
font-size: 16px;
color: #667eea;
margin-bottom: 20px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.buttons {
display: flex;
gap: 12px;
margin-top: 30px;
}
button {
flex: 1;
padding: 12px 24px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.btn-save {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-save:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
}
.btn-save:active {
transform: translateY(0);
}
.btn-reset {
background: #f0f0f0;
color: #333;
}
.btn-reset:hover {
background: #e0e0e0;
}
.message {
padding: 12px 16px;
border-radius: 6px;
margin-bottom: 20px;
display: none;
font-size: 14px;
}
.message.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
display: block;
}
.message.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
display: block;
}
.info-text {
font-size: 12px;
color: #888;
margin-top: 4px;
}
.location-mode {
display: flex;
gap: 20px;
align-items: center;
margin-top: 15px;
}
.location-mode label {
margin: 0;
}
#manualLocation {
margin-top: 10px;
}
@media (max-width: 600px) {
.container {
padding: 20px;
}
h1 {
font-size: 24px;
}
.buttons {
flex-direction: column;
}
}
</style>
</head>
<body>
<div class="container">
<h1>🎛️ Hotel Pi Settings</h1>
<p class="subtitle">Configure your kiosk display</p>
<div class="message" id="message"></div>
<form id="settingsForm">
<!-- Display Settings -->
<div class="section">
<h2>Display Settings</h2>
<div class="form-group">
<label for="title">Hero Name (Top Right)</label>
<input type="text" id="title" name="title" placeholder="e.g., Guest" required>
<p class="info-text">Shown as the guest/hero name (e.g., "Guest's Family")</p>
</div>
<div class="form-group">
<label for="welcomePrefix">Welcome Prefix</label>
<input type="text" id="welcomePrefix" name="welcomePrefix" placeholder="e.g., WELCOME" required>
<p class="info-text">First line above the hero name</p>
</div>
<div class="form-group">
<label for="welcomeSuffix">Welcome Suffix</label>
<input type="text" id="welcomeSuffix" name="welcomeSuffix" placeholder="e.g., 's Family">
<p class="info-text">Shown after the guest name on idle screen</p>
</div>
<div class="form-group">
<label for="resortName">Resort Name</label>
<input type="text" id="resortName" name="resortName" placeholder="e.g., Mojo Dojo Casa House" required>
<p class="info-text">Displayed on both welcome and hero pages</p>
</div>
<div class="form-group">
<label for="roomNumber">Room Number</label>
<input type="text" id="roomNumber" name="roomNumber" placeholder="e.g., ROOM 201" required>
<p class="info-text">Displayed on hero page top bar</p>
</div>
<div class="form-group">
<label for="brandColor">Brand Color</label>
<input type="color" id="brandColor" name="brand_color">
<p class="info-text">Primary accent color for UI</p>
</div>
<div class="form-group">
<label for="idleTimeout">Idle Timeout (seconds)</label>
<input type="number" id="idleTimeout" name="idle_timeout_seconds" min="30" max="600" required>
<p class="info-text">Time before returning to idle screen</p>
</div>
</div>
<!-- Hero Page Settings -->
<div class="section">
<h2>Hero Page</h2>
<div class="form-group">
<label for="backgroundVideoPath">Background Video Path</label>
<input type="text" id="backgroundVideoPath" name="backgroundVideoPath" placeholder="e.g., /media/background.mp4" required>
<p class="info-text">Path to video file (e.g., /media/background.mp4)</p>
</div>
<div class="form-group">
<label for="heroWelcomeText">Welcome Text</label>
<input type="text" id="heroWelcomeText" name="heroWelcomeText" placeholder="e.g., WELCOME" required>
<p class="info-text">Large text displayed on hero page (e.g., "WELCOME")</p>
</div>
<div class="form-group">
<label for="heroGuestText">Guest Name Text</label>
<input type="text" id="heroGuestText" name="heroGuestText" placeholder="e.g., Guest" required>
<p class="info-text">Guest name displayed below welcome text</p>
</div>
</div>
<!-- Themes -->
<div class="section">
<h2>Color Themes</h2>
<div class="form-group">
<label>Hero Page Colors</label>
</div>
<div class="form-group">
<label for="heroStatusBarBg">Status Bar Background</label>
<input type="color" id="heroStatusBarBg" name="heroStatusBarBg">
<p class="info-text">Top bar background color</p>
</div>
<div class="form-group">
<label for="heroHeaderBg">Header Background</label>
<input type="color" id="heroHeaderBg" name="heroHeaderBg">
<p class="info-text">Main header gradient base color</p>
</div>
<div class="form-group">
<label for="heroAccentLineColor">Accent Line Color</label>
<input type="color" id="heroAccentLineColor" name="heroAccentLineColor">
<p class="info-text">Divider line between status and header</p>
</div>
<div class="form-group">
<label for="heroWelcomeTextColor">Welcome Text Color</label>
<input type="color" id="heroWelcomeTextColor" name="heroWelcomeTextColor">
<p class="info-text">Color of "WELCOME" text</p>
</div>
<div class="form-group">
<label for="heroGuestTextColor">Guest Name Text Color</label>
<input type="color" id="heroGuestTextColor" name="heroGuestTextColor">
<p class="info-text">Color of guest name text</p>
</div>
<div class="form-group">
<label>Idle Page Colors</label>
</div>
<div class="form-group">
<label for="idleBgColor">Background Color</label>
<input type="color" id="idleBgColor" name="idleBgColor">
<p class="info-text">Idle screen background</p>
</div>
<div class="form-group">
<label for="idleTitleColor">Title Color</label>
<input type="color" id="idleTitleColor" name="idleTitleColor">
<p class="info-text">Guest name color on idle screen</p>
</div>
<div class="form-group">
<label for="idleSuffixColor">Suffix Color</label>
<input type="color" id="idleSuffixColor" name="idleSuffixColor">
<p class="info-text">Family suffix color on idle screen</p>
</div>
</div>
<!-- Location Settings -->
<div class="section">
<h2>Location Settings</h2>
<div class="location-mode">
<label>
<input type="radio" name="location_mode" value="ip" id="locationIP">
Use IP Geolocation
</label>
<label>
<input type="radio" name="location_mode" value="manual" id="locationManual">
Manual Location
</label>
</div>
<div class="form-group">
<label for="manualLocation">Location String</label>
<input type="text" id="manualLocation" name="manual_location" placeholder="e.g., Disneyland, Anaheim, CA">
<p class="info-text">Used when manual location is selected</p>
</div>
</div>
<!-- Features -->
<div class="section">
<h2>Features</h2>
<div class="form-group">
<div class="checkbox-group">
<input type="checkbox" id="plexEnabled" name="plex_enabled">
<label for="plexEnabled">Enable Plex Integration</label>
</div>
</div>
<div class="form-group">
<div class="checkbox-group">
<input type="checkbox" id="restaurantsEnabled" name="restaurants_enabled">
<label for="restaurantsEnabled">Show Restaurants Section</label>
</div>
</div>
<div class="form-group">
<div class="checkbox-group">
<input type="checkbox" id="attractionsEnabled" name="attractions_enabled">
<label for="attractionsEnabled">Show Attractions Section</label>
</div>
</div>
</div>
<!-- Buttons -->
<div class="buttons">
<button type="submit" class="btn-save">💾 Save Settings</button>
<button type="reset" class="btn-reset">↻ Reset</button>
</div>
</form>
</div>
<script>
const API_URL = '/api/settings';
const form = document.getElementById('settingsForm');
const message = document.getElementById('message');
// Load settings on page load
loadSettings();
async function loadSettings() {
try {
const response = await fetch(API_URL);
const settings = await response.json();
// Display Settings
document.getElementById('title').value = settings.title;
document.getElementById('welcomePrefix').value = settings.welcomePrefix || 'WELCOME';
document.getElementById('welcomeSuffix').value = settings.welcomeSuffix || "'s Family";
document.getElementById('resortName').value = settings.resortName || 'Mojo Dojo Casa House';
document.getElementById('roomNumber').value = settings.roomNumber || 'ROOM 201';
document.getElementById('brandColor').value = settings.brand_color;
document.getElementById('idleTimeout').value = settings.idle_timeout_seconds;
// Hero page
document.getElementById('backgroundVideoPath').value = settings.backgroundVideoPath || '/media/background.mp4';
document.getElementById('heroWelcomeText').value = settings.heroWelcomeText || 'WELCOME';
document.getElementById('heroGuestText').value = settings.heroGuestText || 'Guest';
// Color themes
document.getElementById('heroStatusBarBg').value = settings.heroStatusBarBg || '#1B4965';
document.getElementById('heroHeaderBg').value = settings.heroHeaderBg || '#1E8E9F';
document.getElementById('heroAccentLineColor').value = settings.heroAccentLineColor || '#FF6F61';
document.getElementById('heroWelcomeTextColor').value = settings.heroWelcomeTextColor || '#FF6F61';
document.getElementById('heroGuestTextColor').value = settings.heroGuestTextColor || '#ffffff';
document.getElementById('idleBgColor').value = settings.idleBgColor || '#1a1a1a';
document.getElementById('idleTitleColor').value = settings.idleTitleColor || '#ffffff';
document.getElementById('idleSuffixColor').value = settings.idleSuffixColor || '#d4af37';
document.getElementById('plexEnabled').checked = settings.plex_enabled;
document.getElementById('restaurantsEnabled').checked = settings.restaurants_enabled;
document.getElementById('attractionsEnabled').checked = settings.attractions_enabled;
// Set location mode
if (settings.use_ip_location) {
document.getElementById('locationIP').checked = true;
} else {
document.getElementById('locationManual').checked = true;
}
} catch (error) {
showMessage('Failed to load settings', 'error');
}
}
// Handle form submission
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
const settings = {
title: formData.get('title'),
welcomePrefix: formData.get('welcomePrefix'),
welcomeSuffix: formData.get('welcomeSuffix'),
resortName: formData.get('resortName'),
roomNumber: formData.get('roomNumber'),
backgroundVideoPath: formData.get('backgroundVideoPath'),
heroWelcomeText: formData.get('heroWelcomeText'),
heroGuestText: formData.get('heroGuestText'),
use_ip_location: document.getElementById('locationIP').checked,
manual_location: formData.get('manual_location'),
idle_timeout_seconds: parseInt(formData.get('idle_timeout_seconds')),
plex_enabled: formData.get('plex_enabled') === 'on',
restaurants_enabled: formData.get('restaurants_enabled') === 'on',
attractions_enabled: formData.get('attractions_enabled') === 'on',
brand_color: formData.get('brand_color'),
heroStatusBarBg: formData.get('heroStatusBarBg'),
heroHeaderBg: formData.get('heroHeaderBg'),
heroAccentLineColor: formData.get('heroAccentLineColor'),
heroWelcomeTextColor: formData.get('heroWelcomeTextColor'),
heroGuestTextColor: formData.get('heroGuestTextColor'),
idleBgColor: formData.get('idleBgColor'),
idleTitleColor: formData.get('idleTitleColor'),
idleSuffixColor: formData.get('idleSuffixColor'),
};
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(settings),
});
if (response.ok) {
showMessage('✓ Settings saved successfully!', 'success');
} else {
showMessage('Failed to save settings', 'error');
}
} catch (error) {
showMessage('Error saving settings: ' + error.message, 'error');
}
});
function showMessage(text, type) {
message.textContent = text;
message.className = `message ${type}`;
setTimeout(() => {
message.className = 'message';
}, 4000);
}
</script>
</body>
</html>