UI config
This commit is contained in:
@@ -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=
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user