521 lines
19 KiB
HTML
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>
|