UI config
This commit is contained in:
@@ -24,6 +24,11 @@ MAIL_FROM_NAME=your_mail_from_name_here
|
||||
# Application settings
|
||||
BASE_URL=your_base_url_here
|
||||
|
||||
# UI Configuration
|
||||
TAG=
|
||||
TOP_BAR_BASE_COLOR=#2c3e50
|
||||
ENVIRONMENT=development
|
||||
|
||||
# Redis (optional)
|
||||
REDIS_URL=
|
||||
|
||||
|
||||
@@ -11,10 +11,35 @@ from app.models.local_flight import LocalFlightStatus
|
||||
from app.models.departure import DepartureStatus
|
||||
from app.models.arrival import ArrivalStatus
|
||||
from datetime import date, datetime, timedelta
|
||||
import re
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def lighten_color(hex_color, factor=0.3):
|
||||
"""Lighten a hex color by a factor (0-1)"""
|
||||
hex_color = hex_color.lstrip('#')
|
||||
if len(hex_color) != 6:
|
||||
return hex_color # Invalid, return as is
|
||||
r, g, b = int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16)
|
||||
r = min(255, int(r + (255 - r) * factor))
|
||||
g = min(255, int(g + (255 - g) * factor))
|
||||
b = min(255, int(b + (255 - b) * factor))
|
||||
return f"#{r:02x}{g:02x}{b:02x}"
|
||||
|
||||
|
||||
def darken_color(hex_color, factor=0.3):
|
||||
"""Darken a hex color by a factor (0-1)"""
|
||||
hex_color = hex_color.lstrip('#')
|
||||
if len(hex_color) != 6:
|
||||
return hex_color # Invalid, return as is
|
||||
r, g, b = int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16)
|
||||
r = max(0, int(r * (1 - factor)))
|
||||
g = max(0, int(g * (1 - factor)))
|
||||
b = max(0, int(b * (1 - factor)))
|
||||
return f"#{r:02x}{g:02x}{b:02x}"
|
||||
|
||||
|
||||
@router.get("/arrivals")
|
||||
async def get_public_arrivals(db: Session = Depends(get_db)):
|
||||
"""Get today's arrivals for public display (PPR and local flights)"""
|
||||
@@ -200,4 +225,18 @@ async def get_public_departures(db: Session = Depends(get_db)):
|
||||
'isDeparture': True
|
||||
})
|
||||
|
||||
return departures_list
|
||||
return departures_list
|
||||
|
||||
|
||||
@router.get("/config")
|
||||
async def get_ui_config():
|
||||
"""Get UI configuration for client-side rendering"""
|
||||
from app.core.config import settings
|
||||
base_color = settings.top_bar_base_color
|
||||
return {
|
||||
"tag": settings.tag,
|
||||
"top_bar_gradient_start": base_color,
|
||||
"top_bar_gradient_end": lighten_color(base_color, 0.4), # Lighten for gradient end
|
||||
"footer_color": darken_color(base_color, 0.2), # Darken for footer
|
||||
"environment": settings.environment
|
||||
}
|
||||
@@ -28,6 +28,11 @@ class Settings(BaseSettings):
|
||||
project_name: str = "Airfield PPR API"
|
||||
base_url: str
|
||||
|
||||
# UI Configuration
|
||||
tag: str = ""
|
||||
top_bar_base_color: str = "#2c3e50"
|
||||
environment: str = "production" # production, development, staging, etc.
|
||||
|
||||
# Redis settings (for future use)
|
||||
redis_url: Optional[str] = None
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ services:
|
||||
MAIL_FROM_NAME: ${MAIL_FROM_NAME}
|
||||
BASE_URL: ${BASE_URL}
|
||||
REDIS_URL: ${REDIS_URL}
|
||||
TAG: ${TAG}
|
||||
TOP_BAR_BASE_COLOR: ${TOP_BAR_BASE_COLOR}
|
||||
ENVIRONMENT: production
|
||||
WORKERS: "4"
|
||||
ports:
|
||||
|
||||
@@ -38,6 +38,9 @@ services:
|
||||
MAIL_FROM_NAME: ${MAIL_FROM_NAME}
|
||||
BASE_URL: ${BASE_URL}
|
||||
REDIS_URL: ${REDIS_URL}
|
||||
TOWER_NAME: ${TOWER_NAME}
|
||||
TOP_BAR_BASE_COLOR: ${TOP_BAR_BASE_COLOR}
|
||||
ENVIRONMENT: ${ENVIRONMENT}
|
||||
ports:
|
||||
- "${API_PORT_EXTERNAL}:8000" # Use different port to avoid conflicts with existing system
|
||||
depends_on:
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<body>
|
||||
<div class="top-bar">
|
||||
<div class="title">
|
||||
<h1>✈️ Swansea Tower</h1>
|
||||
<h1 id="tower-title">✈️ Swansea Tower</h1>
|
||||
</div>
|
||||
<div class="menu-buttons">
|
||||
<div class="dropdown">
|
||||
@@ -1129,11 +1129,41 @@
|
||||
let currentUserId = null;
|
||||
let currentChangePasswordUserId = null;
|
||||
|
||||
// ==================== GENERIC MODAL HELPER ====================
|
||||
function closeModal(modalId, additionalCleanup = null) {
|
||||
document.getElementById(modalId).style.display = 'none';
|
||||
if (additionalCleanup) {
|
||||
additionalCleanup();
|
||||
// Load UI configuration from API
|
||||
async function loadUIConfig() {
|
||||
try {
|
||||
const response = await fetch('/api/v1/public/config');
|
||||
if (response.ok) {
|
||||
const config = await response.json();
|
||||
|
||||
// Update tower title
|
||||
const titleElement = document.getElementById('tower-title');
|
||||
if (titleElement && config.tag) {
|
||||
titleElement.innerHTML = `✈️ Tower Ops ${config.tag}`;
|
||||
}
|
||||
|
||||
// Update top bar gradient
|
||||
const topBar = document.querySelector('.top-bar');
|
||||
if (topBar && config.top_bar_gradient_start && config.top_bar_gradient_end) {
|
||||
topBar.style.background = `linear-gradient(135deg, ${config.top_bar_gradient_start}, ${config.top_bar_gradient_end})`;
|
||||
}
|
||||
|
||||
// Update footer color
|
||||
const footerBar = document.querySelector('.footer-bar');
|
||||
if (footerBar && config.footer_color) {
|
||||
footerBar.style.background = config.footer_color;
|
||||
}
|
||||
|
||||
// Optionally indicate environment (e.g., add to title if not production)
|
||||
if (config.environment && config.environment !== 'production') {
|
||||
const envIndicator = ` (${config.environment.toUpperCase()})`;
|
||||
if (titleElement) {
|
||||
titleElement.innerHTML += envIndicator;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to load UI config:', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4748,6 +4778,7 @@
|
||||
|
||||
// Initialize the page when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadUIConfig(); // Load UI configuration first
|
||||
setupLoginForm();
|
||||
setupKeyboardShortcuts();
|
||||
initializeTimeDropdowns(); // Initialize time dropdowns
|
||||
|
||||
@@ -367,7 +367,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="title">
|
||||
<h1>📊 PPR Reports</h1>
|
||||
<h1 id="tower-title">📊 PPR Reports</h1>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
Logged in as: <span id="current-user">Loading...</span> |
|
||||
@@ -591,8 +591,49 @@
|
||||
let currentPPRs = []; // Store current results for export
|
||||
let currentOtherFlights = []; // Store other flights for export
|
||||
|
||||
// Load UI configuration from API
|
||||
async function loadUIConfig() {
|
||||
try {
|
||||
const response = await fetch('/api/v1/public/config');
|
||||
if (response.ok) {
|
||||
const config = await response.json();
|
||||
|
||||
// Update tower title
|
||||
const titleElement = document.getElementById('tower-title');
|
||||
if (titleElement && config.tag) {
|
||||
titleElement.innerHTML = `📊 Reports ${config.tag}`;
|
||||
}
|
||||
|
||||
// Update top bar gradient
|
||||
const topBar = document.querySelector('.top-bar');
|
||||
if (topBar && config.top_bar_gradient_start && config.top_bar_gradient_end) {
|
||||
topBar.style.background = `linear-gradient(135deg, ${config.top_bar_gradient_start}, ${config.top_bar_gradient_end})`;
|
||||
}
|
||||
|
||||
// Update page title
|
||||
if (config.tag) {
|
||||
document.title = `PPR Reports - ${config.tag}`;
|
||||
}
|
||||
|
||||
// Optionally indicate environment (e.g., add to title if not production)
|
||||
if (config.environment && config.environment !== 'production') {
|
||||
const envIndicator = ` (${config.environment.toUpperCase()})`;
|
||||
if (titleElement) {
|
||||
titleElement.innerHTML += envIndicator;
|
||||
}
|
||||
if (document.title) {
|
||||
document.title += envIndicator;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to load UI config:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the page
|
||||
async function initializePage() {
|
||||
loadUIConfig(); // Load UI configuration first
|
||||
await initializeAuth();
|
||||
setupDefaultDateRange();
|
||||
await loadReports();
|
||||
|
||||
Reference in New Issue
Block a user