feat(dashboard+api): card-based dashboard, camelCase API, UTC fixes

Dashboard: new Syncfusion card layout, global stats, filters, health bars, active event display, client details, bulk restart, 15s auto-refresh, manual refresh toasts
API: standardized responses to camelCase; added serializers.py and updated events endpoints
Time: ensured UTC storage; frontend appends 'Z' for parsing and displays local time
Docs: updated copilot-instructions.md, README.md, TECH-CHANGELOG.md
Program Info: bumped to 2025.1.0-alpha.12 with user-facing changelog
BREAKING: external API consumers must migrate field names from PascalCase to camelCase.
This commit is contained in:
RobbStarkAustria
2025-11-27 20:30:00 +00:00
parent 452ba3033b
commit 6dcf93f0dd
13 changed files with 1282 additions and 350 deletions

74
server/serializers.py Normal file
View File

@@ -0,0 +1,74 @@
"""
Serialization helpers for converting between Python snake_case and JavaScript camelCase.
"""
import re
from typing import Any, Dict, List, Union
def to_camel_case(snake_str: str) -> str:
"""
Convert snake_case string to camelCase.
Examples:
event_type -> eventType
start_time -> startTime
is_active -> isActive
"""
components = snake_str.split('_')
# Keep the first component as-is, capitalize the rest
return components[0] + ''.join(word.capitalize() for word in components[1:])
def to_snake_case(camel_str: str) -> str:
"""
Convert camelCase string to snake_case.
Examples:
eventType -> event_type
startTime -> start_time
isActive -> is_active
"""
# Insert underscore before uppercase letters and convert to lowercase
snake = re.sub('([A-Z])', r'_\1', camel_str).lower()
# Remove leading underscore if present
return snake.lstrip('_')
def dict_to_camel_case(data: Union[Dict, List, Any]) -> Union[Dict, List, Any]:
"""
Recursively convert dictionary keys from snake_case to camelCase.
Also handles lists of dictionaries.
Args:
data: Dictionary, list, or primitive value to convert
Returns:
Converted data structure with camelCase keys
"""
if isinstance(data, dict):
return {to_camel_case(key): dict_to_camel_case(value)
for key, value in data.items()}
elif isinstance(data, list):
return [dict_to_camel_case(item) for item in data]
else:
return data
def dict_to_snake_case(data: Union[Dict, List, Any]) -> Union[Dict, List, Any]:
"""
Recursively convert dictionary keys from camelCase to snake_case.
Also handles lists of dictionaries.
Args:
data: Dictionary, list, or primitive value to convert
Returns:
Converted data structure with snake_case keys
"""
if isinstance(data, dict):
return {to_snake_case(key): dict_to_snake_case(value)
for key, value in data.items()}
elif isinstance(data, list):
return [dict_to_snake_case(item) for item in data]
else:
return data