UI config

This commit is contained in:
2025-12-19 08:33:42 -05:00
parent 0149f45893
commit ac29b6e929
7 changed files with 134 additions and 8 deletions

View File

@@ -24,6 +24,11 @@ MAIL_FROM_NAME=your_mail_from_name_here
# Application settings # Application settings
BASE_URL=your_base_url_here BASE_URL=your_base_url_here
# UI Configuration
TAG=
TOP_BAR_BASE_COLOR=#2c3e50
ENVIRONMENT=development
# Redis (optional) # Redis (optional)
REDIS_URL= REDIS_URL=

View File

@@ -11,10 +11,35 @@ from app.models.local_flight import LocalFlightStatus
from app.models.departure import DepartureStatus from app.models.departure import DepartureStatus
from app.models.arrival import ArrivalStatus from app.models.arrival import ArrivalStatus
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
import re
router = APIRouter() 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") @router.get("/arrivals")
async def get_public_arrivals(db: Session = Depends(get_db)): async def get_public_arrivals(db: Session = Depends(get_db)):
"""Get today's arrivals for public display (PPR and local flights)""" """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 '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
}

View File

@@ -28,6 +28,11 @@ class Settings(BaseSettings):
project_name: str = "Airfield PPR API" project_name: str = "Airfield PPR API"
base_url: str 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 settings (for future use)
redis_url: Optional[str] = None redis_url: Optional[str] = None

View File

@@ -26,6 +26,8 @@ services:
MAIL_FROM_NAME: ${MAIL_FROM_NAME} MAIL_FROM_NAME: ${MAIL_FROM_NAME}
BASE_URL: ${BASE_URL} BASE_URL: ${BASE_URL}
REDIS_URL: ${REDIS_URL} REDIS_URL: ${REDIS_URL}
TAG: ${TAG}
TOP_BAR_BASE_COLOR: ${TOP_BAR_BASE_COLOR}
ENVIRONMENT: production ENVIRONMENT: production
WORKERS: "4" WORKERS: "4"
ports: ports:

View File

@@ -38,6 +38,9 @@ services:
MAIL_FROM_NAME: ${MAIL_FROM_NAME} MAIL_FROM_NAME: ${MAIL_FROM_NAME}
BASE_URL: ${BASE_URL} BASE_URL: ${BASE_URL}
REDIS_URL: ${REDIS_URL} REDIS_URL: ${REDIS_URL}
TOWER_NAME: ${TOWER_NAME}
TOP_BAR_BASE_COLOR: ${TOP_BAR_BASE_COLOR}
ENVIRONMENT: ${ENVIRONMENT}
ports: ports:
- "${API_PORT_EXTERNAL}:8000" # Use different port to avoid conflicts with existing system - "${API_PORT_EXTERNAL}:8000" # Use different port to avoid conflicts with existing system
depends_on: depends_on:

View File

@@ -10,7 +10,7 @@
<body> <body>
<div class="top-bar"> <div class="top-bar">
<div class="title"> <div class="title">
<h1>✈️ Swansea Tower</h1> <h1 id="tower-title">✈️ Swansea Tower</h1>
</div> </div>
<div class="menu-buttons"> <div class="menu-buttons">
<div class="dropdown"> <div class="dropdown">
@@ -1129,11 +1129,41 @@
let currentUserId = null; let currentUserId = null;
let currentChangePasswordUserId = null; let currentChangePasswordUserId = null;
// ==================== GENERIC MODAL HELPER ==================== // Load UI configuration from API
function closeModal(modalId, additionalCleanup = null) { async function loadUIConfig() {
document.getElementById(modalId).style.display = 'none'; try {
if (additionalCleanup) { const response = await fetch('/api/v1/public/config');
additionalCleanup(); 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 // Initialize the page when DOM is loaded
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
loadUIConfig(); // Load UI configuration first
setupLoginForm(); setupLoginForm();
setupKeyboardShortcuts(); setupKeyboardShortcuts();
initializeTimeDropdowns(); // Initialize time dropdowns initializeTimeDropdowns(); // Initialize time dropdowns

View File

@@ -367,7 +367,7 @@
</button> </button>
</div> </div>
<div class="title"> <div class="title">
<h1>📊 PPR Reports</h1> <h1 id="tower-title">📊 PPR Reports</h1>
</div> </div>
<div class="user-info"> <div class="user-info">
Logged in as: <span id="current-user">Loading...</span> | Logged in as: <span id="current-user">Loading...</span> |
@@ -591,8 +591,49 @@
let currentPPRs = []; // Store current results for export let currentPPRs = []; // Store current results for export
let currentOtherFlights = []; // Store other flights 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 // Initialize the page
async function initializePage() { async function initializePage() {
loadUIConfig(); // Load UI configuration first
await initializeAuth(); await initializeAuth();
setupDefaultDateRange(); setupDefaultDateRange();
await loadReports(); await loadReports();