feat: dashboard widgets, auth fix, profile names, dynamic groups

- Add VehicleDashboardCard: self-contained widget modelled after
  AtemschutzDashboardCard, shows einsatzbereit ratio and inspection
  warnings inline; replaces StatsCard + InspectionAlerts in Dashboard

- Add EquipmentDashboardCard: consolidated equipment status widget
  showing only aggregated counts (no per-item listing); replaces
  EquipmentAlerts component in Dashboard

- Fix auth race condition: add authInitialized flag to api.ts so 401
  responses during initial token validation no longer trigger a
  spurious redirect to /login; save intended destination before login
  redirect and restore it after successful auth callback

- Fix profile firstname/lastname: add extractNames() helper to
  auth.controller.ts that falls back to splitting userinfo.name when
  Authentik does not provide separate given_name/family_name fields;
  applied on both create and update paths

- Dynamic groups endpoint: replace hardcoded KNOWN_GROUPS array in
  events.controller.ts with a DB query (SELECT DISTINCT unnest
  (authentik_groups) FROM users); known slugs get German labels via
  lookup map, unknown slugs are humanized automatically

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Matthias Hochmeister
2026-03-03 10:28:31 +01:00
parent 831927ae90
commit 2306741c4d
11 changed files with 470 additions and 110 deletions

View File

@@ -2,6 +2,12 @@ import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } f
import { API_URL } from '../utils/config';
import { getToken, removeToken, removeUser } from '../utils/storage';
let authInitialized = false;
export function setAuthInitialized(value: boolean): void {
authInitialized = value;
}
export interface ApiError {
message: string;
status?: number;
@@ -40,11 +46,14 @@ class ApiService {
(response) => response,
async (error: AxiosError) => {
if (error.response?.status === 401) {
// Clear tokens and redirect to login
console.warn('Unauthorized request, redirecting to login');
removeToken();
removeUser();
window.location.href = '/login';
if (authInitialized) {
// Clear tokens and redirect to login
console.warn('Unauthorized request, redirecting to login');
removeToken();
removeUser();
window.location.href = '/login';
}
// During initialization, silently reject without redirecting
}
// Retry on 429 (Too Many Requests) with exponential backoff