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

135
backend/src/routes/cache.ts Normal file
View File

@@ -0,0 +1,135 @@
/**
* Cache management routes
*
* Provides endpoints for cache status and manual sync triggers.
*/
import { Router, Request, Response } from 'express';
import { cacheStore } from '../services/cacheStore.js';
import { syncEngine } from '../services/syncEngine.js';
import { logger } from '../services/logger.js';
import { OBJECT_TYPES } from '../generated/jira-schema.js';
import type { CMDBObjectTypeName } from '../generated/jira-types.js';
const router = Router();
// Get cache status
router.get('/status', (req: Request, res: Response) => {
try {
const cacheStats = cacheStore.getStats();
const syncStatus = syncEngine.getStatus();
res.json({
cache: cacheStats,
sync: syncStatus,
supportedTypes: Object.keys(OBJECT_TYPES),
});
} catch (error) {
logger.error('Failed to get cache status', error);
res.status(500).json({ error: 'Failed to get cache status' });
}
});
// Trigger full sync
router.post('/sync', async (req: Request, res: Response) => {
try {
logger.info('Manual full sync triggered');
// Don't wait for completion - return immediately
syncEngine.fullSync().catch(err => {
logger.error('Full sync failed', err);
});
res.json({
status: 'started',
message: 'Full sync started in background',
});
} catch (error) {
logger.error('Failed to trigger full sync', error);
res.status(500).json({ error: 'Failed to trigger sync' });
}
});
// Trigger sync for a specific object type
router.post('/sync/:objectType', async (req: Request, res: Response) => {
try {
const { objectType } = req.params;
// Validate object type
if (!OBJECT_TYPES[objectType]) {
res.status(400).json({
error: `Unknown object type: ${objectType}`,
supportedTypes: Object.keys(OBJECT_TYPES),
});
return;
}
logger.info(`Manual sync triggered for ${objectType}`);
const result = await syncEngine.syncType(objectType as CMDBObjectTypeName);
res.json({
status: 'completed',
objectType,
stats: result,
});
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Failed to sync object type';
logger.error(`Failed to sync object type ${req.params.objectType}`, error);
// Return 409 (Conflict) if sync is already in progress, otherwise 500
const statusCode = errorMessage.includes('already in progress') ? 409 : 500;
res.status(statusCode).json({
error: errorMessage,
objectType: req.params.objectType,
});
}
});
// Clear cache for a specific type
router.delete('/clear/:objectType', (req: Request, res: Response) => {
try {
const { objectType } = req.params;
if (!OBJECT_TYPES[objectType]) {
res.status(400).json({
error: `Unknown object type: ${objectType}`,
supportedTypes: Object.keys(OBJECT_TYPES),
});
return;
}
logger.info(`Clearing cache for ${objectType}`);
const deleted = cacheStore.clearObjectType(objectType as CMDBObjectTypeName);
res.json({
status: 'cleared',
objectType,
deletedCount: deleted,
});
} catch (error) {
logger.error(`Failed to clear cache for ${req.params.objectType}`, error);
res.status(500).json({ error: 'Failed to clear cache' });
}
});
// Clear entire cache
router.delete('/clear', (req: Request, res: Response) => {
try {
logger.info('Clearing entire cache');
cacheStore.clearAll();
res.json({
status: 'cleared',
message: 'Entire cache cleared',
});
} catch (error) {
logger.error('Failed to clear cache', error);
res.status(500).json({ error: 'Failed to clear cache' });
}
});
export default router;