feat: Enhance frontend with theme support and offline capabilities
Some checks failed
Integration Tests / integration-tests (push) Failing after 24s
Integration Tests / performance-tests (push) Has been skipped
API Docs (Node.js Express) / test (20) (push) Failing after 42s
API Docs (Node.js Express) / build (push) Has been skipped
Service Adapters (Python FastAPI) / test (3.11) (push) Successful in 1m8s
Service Adapters (Python FastAPI) / test (3.12) (push) Successful in 1m13s
Frontend (React) / test (20) (push) Successful in 1m46s
Frontend (React) / build (push) Failing after 52s
Frontend (React) / lighthouse (push) Has been skipped
Service Adapters (Python FastAPI) / test (3.13) (push) Successful in 2m4s
Service Adapters (Python FastAPI) / build (push) Failing after 17s

### Summary of Changes
- Introduced theme-aware CSS variables for consistent styling across light and dark modes.
- Updated `App.jsx` to manage theme settings and improve layout responsiveness.
- Refactored `OfflineMode` component to provide detailed connection status and quick actions for users.
- Enhanced `Dashboard`, `Settings`, and `SystemMetrics` components to utilize new theme variables and improve UI consistency.
- Updated service URLs in constants and API documentation to reflect new configurations.

### Expected Results
- Improved user experience with a cohesive design that adapts to user preferences.
- Enhanced offline functionality, providing users with better feedback and control during service outages.
- Streamlined codebase with consistent styling practices, making future updates easier.
This commit is contained in:
GSRN
2025-09-18 02:37:58 +02:00
parent 4b2ef7e246
commit 48c755dff3
17 changed files with 1754 additions and 194 deletions

View File

@@ -43,7 +43,7 @@ const SERVICES = {
},
'service-adapters': {
name: 'Service Adapters',
url: process.env.SERVICE_ADAPTERS_URL || 'http://localhost:8000',
url: process.env.SERVICE_ADAPTERS_URL || 'http://localhost:8001',
openapiPath: '/openapi.json',
description: 'Integration adapters for Home Assistant, Frigate, Immich, and other services'
},
@@ -84,7 +84,8 @@ async function fetchServiceSpec (serviceKey, service) {
}
const response = await axios.get(`${service.url}${service.openapiPath}`, {
timeout: 5000
timeout: 5000,
rejectUnauthorized: false
})
return response.data
} catch (error) {
@@ -126,7 +127,7 @@ async function generateUnifiedSpec () {
description: 'API Gateway (Production)'
},
{
url: 'http://localhost:8000',
url: 'http://localhost:8001',
description: 'Service Adapters (Production)'
},
{
@@ -155,12 +156,45 @@ async function generateUnifiedSpec () {
// Fetch specs from all services
for (const [serviceKey, service] of Object.entries(SERVICES)) {
const spec = await fetchServiceSpec(serviceKey, service)
// Collect original tags before modifying them
const subCategories = new Set()
if (spec.paths) {
for (const [path, methods] of Object.entries(spec.paths)) {
for (const [method, operation] of Object.entries(methods)) {
if (operation.tags) {
operation.tags.forEach(tag => {
subCategories.add(tag)
})
}
}
}
}
// Merge paths with service prefix
if (spec.paths) {
for (const [path, methods] of Object.entries(spec.paths)) {
const prefixedPath = `/${serviceKey}${path}`
unifiedSpec.paths[prefixedPath] = methods
const updatedMethods = {}
for (const [method, operation] of Object.entries(methods)) {
// Use only the main service name as the primary tag
// Store original category in metadata for internal organization
const originalTags = operation.tags || ['General']
const category = originalTags[0] || 'General'
updatedMethods[method] = {
...operation,
tags: [service.name], // Only main service tag for top-level grouping
summary: `[${category}] ${operation.summary || `${method.toUpperCase()} ${path}`}`,
'x-service': serviceKey,
'x-service-url': service.url,
'x-original-tags': originalTags,
'x-category': category
}
}
unifiedSpec.paths[prefixedPath] = updatedMethods
}
}
@@ -176,7 +210,9 @@ async function generateUnifiedSpec () {
name: service.name,
description: service.description,
'x-service-url': service.url,
'x-service-status': service.status || 'active'
'x-service-status': service.status || 'active',
'x-service-key': serviceKey,
'x-categories': Array.from(subCategories) // Store available categories for reference
})
}
@@ -314,12 +350,42 @@ app.get('/', swaggerUi.setup(null, {
displayRequestDuration: true,
filter: true,
showExtensions: true,
showCommonExtensions: true
showCommonExtensions: true,
operationsSorter: function(a, b) {
// Sort by summary (which includes category tags)
const summaryA = a.get('summary') || '';
const summaryB = b.get('summary') || '';
return summaryA.localeCompare(summaryB);
},
tagsSorter: 'alpha'
},
customCss: `
.swagger-ui .topbar { display: none; }
.swagger-ui .info { margin: 20px 0; }
.swagger-ui .info .title { color: #1890ff; }
/* Style service tags */
.swagger-ui .opblock-tag {
margin: 20px 0 10px 0;
padding: 10px 0;
border-bottom: 2px solid #1890ff;
}
/* Style operation blocks */
.swagger-ui .opblock {
margin: 10px 0;
border-radius: 4px;
}
/* Style operation summaries with category badges */
.swagger-ui .opblock-summary-description {
font-weight: 500;
}
/* Add some spacing between operations */
.swagger-ui .opblock-tag-section .opblock {
margin-bottom: 15px;
}
`,
customSiteTitle: 'LabFusion API Documentation'
}))

View File

@@ -11,7 +11,7 @@ app = FastAPI(
version="1.0.0",
license_info={"name": "MIT License", "url": "https://opensource.org/licenses/MIT"},
servers=[
{"url": "http://localhost:8000", "description": "Development Server"},
{"url": "http://localhost:8001", "description": "Development Server"},
{"url": "https://adapters.labfusion.dev", "description": "Production Server"},
],
)
@@ -35,4 +35,4 @@ app.include_router(events.router)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
uvicorn.run(app, host="127.0.0.1", port=8001)