Improve Team-indeling dashboard UI and cache invalidation

- Replace 'TEAM' label with Type attribute (Business/Enabling/Staf) in team blocks
- Make Type labels larger (text-sm) and brighter colors
- Make SUBTEAM label less bright (indigo-300) and smaller (text-[10px])
- Add 'FTE' suffix to bandbreedte values in header and application blocks
- Add Platform and Connected Device labels to application blocks
- Show Platform FTE and Workloads FTE separately in Platform blocks
- Add spacing between Regiemodel letter and count value
- Add cache invalidation for Team Dashboard when applications are updated
- Enrich team references with Type attribute in getSubteamToTeamMapping
This commit is contained in:
2026-01-10 02:16:55 +01:00
parent ea1c84262c
commit ca21b9538d
54 changed files with 13444 additions and 1789 deletions

View File

@@ -6,13 +6,18 @@ import cookieParser from 'cookie-parser';
import { config, validateConfig } from './config/env.js';
import { logger } from './services/logger.js';
import { dataService } from './services/dataService.js';
import { syncEngine } from './services/syncEngine.js';
import { cmdbService } from './services/cmdbService.js';
import applicationsRouter from './routes/applications.js';
import classificationsRouter from './routes/classifications.js';
import referenceDataRouter from './routes/referenceData.js';
import dashboardRouter from './routes/dashboard.js';
import configurationRouter from './routes/configuration.js';
import authRouter, { authMiddleware } from './routes/auth.js';
import { jiraAssetsService } from './services/jiraAssets.js';
import searchRouter from './routes/search.js';
import cacheRouter from './routes/cache.js';
import objectsRouter from './routes/objects.js';
import schemaRouter from './routes/schema.js';
// Validate configuration
validateConfig();
@@ -50,16 +55,16 @@ app.use((req, res, next) => {
// Auth middleware - extract session info for all requests
app.use(authMiddleware);
// Set user token on JiraAssets service for each request
// Set user token on CMDBService for each request (for user-specific OAuth)
app.use((req, res, next) => {
// Set user's OAuth token if available
if (req.accessToken) {
jiraAssetsService.setRequestToken(req.accessToken);
cmdbService.setUserToken(req.accessToken);
}
// Clear token after response is sent
res.on('finish', () => {
jiraAssetsService.clearRequestToken();
cmdbService.clearUserToken();
});
next();
@@ -68,12 +73,19 @@ app.use((req, res, next) => {
// Health check
app.get('/health', async (req, res) => {
const jiraConnected = await dataService.testConnection();
const cacheStatus = dataService.getCacheStatus();
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
dataSource: dataService.isUsingJiraAssets() ? 'jira-assets' : 'mock-data',
dataSource: dataService.isUsingJiraAssets() ? 'jira-assets-cached' : 'mock-data',
jiraConnected: dataService.isUsingJiraAssets() ? jiraConnected : null,
aiConfigured: !!config.anthropicApiKey,
cache: {
isWarm: cacheStatus.isWarm,
objectCount: cacheStatus.totalObjects,
lastSync: cacheStatus.lastIncrementalSync,
},
});
});
@@ -91,6 +103,10 @@ app.use('/api/classifications', classificationsRouter);
app.use('/api/reference-data', referenceDataRouter);
app.use('/api/dashboard', dashboardRouter);
app.use('/api/configuration', configurationRouter);
app.use('/api/search', searchRouter);
app.use('/api/cache', cacheRouter);
app.use('/api/objects', objectsRouter);
app.use('/api/schema', schemaRouter);
// Error handling
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
@@ -108,20 +124,33 @@ app.use((req, res) => {
// Start server
const PORT = config.port;
app.listen(PORT, () => {
app.listen(PORT, async () => {
logger.info(`Server running on http://localhost:${PORT}`);
logger.info(`Environment: ${config.nodeEnv}`);
logger.info(`AI Classification: ${config.anthropicApiKey ? 'Configured' : 'Not configured'}`);
logger.info(`Jira Assets: ${config.jiraPat ? 'Configured' : 'Using mock data'}`);
logger.info(`Jira Assets: ${config.jiraPat ? 'Configured with caching' : 'Using mock data'}`);
// Initialize sync engine if using Jira Assets
if (config.jiraPat && config.jiraSchemaId) {
try {
await syncEngine.initialize();
logger.info('Sync Engine: Initialized and running');
} catch (error) {
logger.error('Failed to initialize sync engine', error);
}
}
});
// Graceful shutdown
process.on('SIGTERM', () => {
logger.info('SIGTERM signal received: closing HTTP server');
const shutdown = () => {
logger.info('Shutdown signal received: stopping services...');
// Stop sync engine
syncEngine.stop();
logger.info('Services stopped, exiting');
process.exit(0);
});
};
process.on('SIGINT', () => {
logger.info('SIGINT signal received: closing HTTP server');
process.exit(0);
});
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);