Fix all frontend TypeScript compilation errors

- Add _jiraUpdatedAt to ApplicationDetails type
- Fix bia type in ComplexityDynamicsBubbleChart (null to empty string)
- Fix source type in GovernanceModelHelper (explicit union type)
- Add vite-env.d.ts for import.meta.env types
- Add node.d.ts for NodeJS namespace types
- Fix hostingType vs applicationManagementHosting in EffortCalculationConfig
- Fix rule.result type errors with proper type guards
- Remove unused variables and imports
- Fix all req.query and req.params type errors
This commit is contained in:
2026-01-14 16:57:01 +01:00
parent f51e9b8574
commit aba16f68de
12 changed files with 58 additions and 43 deletions

View File

@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { ScatterChart, Scatter, XAxis, YAxis, ZAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, Cell } from 'recharts'; import { ScatterChart, Scatter, XAxis, YAxis, ZAxis, CartesianGrid, Tooltip, ResponsiveContainer, Cell } from 'recharts';
import { searchApplications } from '../services/api'; import { searchApplications } from '../services/api';
import type { ApplicationListItem, ReferenceValue, ApplicationStatus } from '../types'; import type { ReferenceValue, ApplicationStatus } from '../types';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
const ALL_STATUSES: ApplicationStatus[] = [ const ALL_STATUSES: ApplicationStatus[] = [
@@ -116,7 +116,7 @@ export default function ComplexityDynamicsBubbleChart() {
x: complexity, x: complexity,
y: dynamics, y: dynamics,
z: Math.max(0.1, fte), // Minimum size for visibility z: Math.max(0.1, fte), // Minimum size for visibility
bia: app.businessImpactAnalyse?.name || null, bia: app.businessImpactAnalyse?.name || '',
biaId: app.businessImpactAnalyse?.objectId || 'none', biaId: app.businessImpactAnalyse?.objectId || 'none',
name: app.name, name: app.name,
id: app.id, id: app.id,

View File

@@ -176,7 +176,7 @@ export default function Configuration() {
<div className="space-y-4"> <div className="space-y-4">
{[...config.governanceModelRules] {[...config.governanceModelRules]
.sort((a, b) => a.governanceModel.localeCompare(b.governanceModel)) .sort((a, b) => a.governanceModel.localeCompare(b.governanceModel))
.map((rule, originalIndex) => { .map((rule) => {
// Find the original index in the unsorted array // Find the original index in the unsorted array
const index = config.governanceModelRules.findIndex(r => r === rule); const index = config.governanceModelRules.findIndex(r => r === rule);
return ( return (
@@ -329,8 +329,9 @@ function ApplicationTypeRuleEditor({ ruleKey, rule, applicationManagementHosting
// Check if rule is a simple EffortRule or ApplicationTypeRule // Check if rule is a simple EffortRule or ApplicationTypeRule
const isSimpleRule = 'result' in rule && !('applicationTypes' in rule); const isSimpleRule = 'result' in rule && !('applicationTypes' in rule);
if (isSimpleRule) { if (isSimpleRule && typeof rule === 'object' && 'result' in rule) {
// Simple EffortRule // Simple EffortRule
const simpleRule = rule as { result: number };
return ( return (
<div className="border border-gray-200 rounded-md p-3 bg-gray-50"> <div className="border border-gray-200 rounded-md p-3 bg-gray-50">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@@ -339,8 +340,8 @@ function ApplicationTypeRuleEditor({ ruleKey, rule, applicationManagementHosting
<input <input
type="number" type="number"
step="0.01" step="0.01"
value={rule.result} value={simpleRule.result}
onChange={(e) => onUpdate({ result: parseFloat(e.target.value) || 0 })} onChange={(e) => onUpdate({ result: parseFloat(e.target.value) || 0 } as any)}
className="px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" className="px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/> />
<span className="text-sm text-gray-500">FTE</span> <span className="text-sm text-gray-500">FTE</span>
@@ -485,7 +486,7 @@ function ApplicationTypeRuleEditor({ ruleKey, rule, applicationManagementHosting
newRules[index] = { ...newRules[index], ...updates }; newRules[index] = { ...newRules[index], ...updates };
updateDefaultRule(newRules); updateDefaultRule(newRules);
}} }}
onRemove={rule.default.length > 1 ? () => { onRemove={rule.default && Array.isArray(rule.default) && rule.default.length > 1 ? () => {
const newRules = (rule.default as any[]).filter((_, i) => i !== index); const newRules = (rule.default as any[]).filter((_, i) => i !== index);
updateDefaultRule(newRules.length === 1 ? newRules[0] : newRules); updateDefaultRule(newRules.length === 1 ? newRules[0] : newRules);
} : undefined} } : undefined}

View File

@@ -10,7 +10,6 @@ import {
type GovernanceModelConfigV25, type GovernanceModelConfigV25,
type ApplicationTypeConfigV25, type ApplicationTypeConfigV25,
type BIALevelConfig, type BIALevelConfig,
type FTERange,
} from '../services/api'; } from '../services/api';
import type { ReferenceValue } from '../types'; import type { ReferenceValue } from '../types';
@@ -25,7 +24,7 @@ export default function ConfigurationV25() {
const [hostingOptions, setHostingOptions] = useState<ReferenceValue[]>([]); const [hostingOptions, setHostingOptions] = useState<ReferenceValue[]>([]);
const [applicationTypeOptions, setApplicationTypeOptions] = useState<ReferenceValue[]>([]); const [applicationTypeOptions, setApplicationTypeOptions] = useState<ReferenceValue[]>([]);
const [biaOptions, setBiaOptions] = useState<ReferenceValue[]>([]); const [biaOptions, setBiaOptions] = useState<ReferenceValue[]>([]);
const [governanceOptions, setGovernanceOptions] = useState<ReferenceValue[]>([]); const [, setGovernanceOptions] = useState<ReferenceValue[]>([]);
useEffect(() => { useEffect(() => {
loadConfig(); loadConfig();
@@ -241,7 +240,7 @@ function RegieModelEditor({
code, code,
model, model,
hostingOptions, hostingOptions,
applicationTypeOptions, applicationTypeOptions: _appTypes,
biaOptions, biaOptions,
onUpdate, onUpdate,
onUpdateAppType onUpdateAppType
@@ -325,7 +324,7 @@ interface ApplicationTypeEditorProps {
onUpdate: (updates: Partial<ApplicationTypeConfigV25>) => void; onUpdate: (updates: Partial<ApplicationTypeConfigV25>) => void;
} }
function ApplicationTypeEditor({ appType, config, hostingOptions, biaOptions, onUpdate }: ApplicationTypeEditorProps) { function ApplicationTypeEditor({ appType, config, hostingOptions, biaOptions: _bia, onUpdate }: ApplicationTypeEditorProps) {
const [expanded, setExpanded] = useState(false); const [expanded, setExpanded] = useState(false);
return ( return (
@@ -445,7 +444,7 @@ interface BIALevelEditorProps {
onUpdate: (updates: Partial<BIALevelConfig>) => void; onUpdate: (updates: Partial<BIALevelConfig>) => void;
} }
function BIALevelEditor({ biaLevel, config, hostingOptions, onUpdate }: BIALevelEditorProps) { function BIALevelEditor({ biaLevel, config, hostingOptions: _hosting, onUpdate }: BIALevelEditorProps) {
const [expanded, setExpanded] = useState(false); const [expanded, setExpanded] = useState(false);
return ( return (

View File

@@ -51,7 +51,7 @@ function generateId(): string {
export default function DataCompletenessConfig() { export default function DataCompletenessConfig() {
const [config, setConfig] = useState<DataCompletenessConfig | null>(null); const [config, setConfig] = useState<DataCompletenessConfig | null>(null);
const [schema, setSchema] = useState<SchemaResponse | null>(null); const [, setSchema] = useState<SchemaResponse | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);

View File

@@ -44,7 +44,7 @@ export function EffortDisplay({
onOverrideChange, onOverrideChange,
}: EffortDisplayProps) { }: EffortDisplayProps) {
const hasOverride = overrideFte !== null && overrideFte !== undefined; const hasOverride = overrideFte !== null && overrideFte !== undefined;
const hasBreakdown = breakdown !== null && breakdown !== undefined; // const hasBreakdown = breakdown !== null && breakdown !== undefined; // Unused
// Extract breakdown values // Extract breakdown values
const baseEffort = breakdown?.baseEffort ?? null; const baseEffort = breakdown?.baseEffort ?? null;

View File

@@ -64,7 +64,7 @@ export default function GovernanceModelHelper() {
const [governanceModels, setGovernanceModels] = useState<ReferenceValue[]>([]); const [governanceModels, setGovernanceModels] = useState<ReferenceValue[]>([]);
const [applicationFunctions, setApplicationFunctions] = useState<ReferenceValue[]>([]); const [applicationFunctions, setApplicationFunctions] = useState<ReferenceValue[]>([]);
const [applicationSubteams, setApplicationSubteams] = useState<ReferenceValue[]>([]); const [applicationSubteams, setApplicationSubteams] = useState<ReferenceValue[]>([]);
const [applicationTeams, setApplicationTeams] = useState<ReferenceValue[]>([]); const [, setApplicationTeams] = useState<ReferenceValue[]>([]);
const [subteamToTeamMapping, setSubteamToTeamMapping] = useState<Record<string, ReferenceValue | null>>({}); const [subteamToTeamMapping, setSubteamToTeamMapping] = useState<Record<string, ReferenceValue | null>>({});
const [applicationTypes, setApplicationTypes] = useState<ReferenceValue[]>([]); const [applicationTypes, setApplicationTypes] = useState<ReferenceValue[]>([]);
const [hostingTypes, setHostingTypes] = useState<ReferenceValue[]>([]); const [hostingTypes, setHostingTypes] = useState<ReferenceValue[]>([]);
@@ -723,7 +723,7 @@ export default function GovernanceModelHelper() {
const selectedHasPrimary = aiPrimaryCode && selectedFunctions.some( const selectedHasPrimary = aiPrimaryCode && selectedFunctions.some(
(f) => f.key === aiPrimaryCode (f) => f.key === aiPrimaryCode
); );
const source = aiSuggestion const source: 'AI_ACCEPTED' | 'AI_MODIFIED' | 'MANUAL' = aiSuggestion
? selectedHasPrimary ? selectedHasPrimary
? 'AI_ACCEPTED' ? 'AI_ACCEPTED'
: 'AI_MODIFIED' : 'AI_MODIFIED'
@@ -809,7 +809,7 @@ export default function GovernanceModelHelper() {
const selectedHasPrimary = aiPrimaryCode && selectedFunctions.some( const selectedHasPrimary = aiPrimaryCode && selectedFunctions.some(
(f) => f.key === aiPrimaryCode (f) => f.key === aiPrimaryCode
); );
const source = aiSuggestion const source: 'AI_ACCEPTED' | 'AI_MODIFIED' | 'MANUAL' = aiSuggestion
? selectedHasPrimary ? selectedHasPrimary
? 'AI_ACCEPTED' ? 'AI_ACCEPTED'
: 'AI_MODIFIED' : 'AI_MODIFIED'

View File

@@ -40,25 +40,26 @@ const STATUS_LABELS: Record<string, string> = {
}; };
// Risk calculation: High BIA (F, E, D) + EOL/EOS/Deprecated = critical risk // Risk calculation: High BIA (F, E, D) + EOL/EOS/Deprecated = critical risk
function calculateRiskLevel(status: string | null, bia: string | null): 'critical' | 'high' | 'medium' | 'low' { // Unused function - kept for reference
if (!status || !bia) return 'low'; // function calculateRiskLevel(status: string | null, bia: string | null): 'critical' | 'high' | 'medium' | 'low' {
// if (!status || !bia) return 'low';
const isHighBIA = ['F', 'E', 'D'].includes(bia); //
const isEOL = status === 'End of life'; // const isHighBIA = ['F', 'E', 'D'].includes(bia);
const isEOS = status === 'End of support'; // const isEOL = status === 'End of life';
const isDeprecated = status === 'Deprecated'; // const isEOS = status === 'End of support';
// const isDeprecated = status === 'Deprecated';
if (isHighBIA && (isEOL || isEOS || isDeprecated)) { //
if (isEOL && ['F', 'E'].includes(bia)) return 'critical'; // if (isHighBIA && (isEOL || isEOS || isDeprecated)) {
if (isEOL || (isEOS && bia === 'F')) return 'critical'; // if (isEOL && ['F', 'E'].includes(bia)) return 'critical';
if (isHighBIA) return 'high'; // if (isEOL || (isEOS && bia === 'F')) return 'critical';
} // if (isHighBIA) return 'high';
// }
if (isEOL || isEOS) return 'high'; //
if (isDeprecated) return 'medium'; // if (isEOL || isEOS) return 'high';
// if (isDeprecated) return 'medium';
return 'low'; //
} // return 'low';
// }
function getRiskColor(riskLevel: string): string { function getRiskColor(riskLevel: string): string {
switch (riskLevel) { switch (riskLevel) {

View File

@@ -466,24 +466,24 @@ export interface EffortCalculationConfig {
[key: string]: { [key: string]: {
result: number; result: number;
conditions?: { conditions?: {
hostingType?: string | string[]; applicationManagementHosting?: string | string[];
}; };
} | Array<{ } | Array<{
result: number; result: number;
conditions?: { conditions?: {
hostingType?: string | string[]; applicationManagementHosting?: string | string[];
}; };
}>; }>;
}; };
default?: { default?: {
result: number; result: number;
conditions?: { conditions?: {
hostingType?: string | string[]; applicationManagementHosting?: string | string[];
}; };
} | Array<{ } | Array<{
result: number; result: number;
conditions?: { conditions?: {
hostingType?: string | string[]; applicationManagementHosting?: string | string[];
}; };
}>; }>;
}; };
@@ -491,7 +491,7 @@ export interface EffortCalculationConfig {
default?: { default?: {
result: number; result: number;
conditions?: { conditions?: {
hostingType?: string | string[]; applicationManagementHosting?: string | string[];
}; };
}; };
}>; }>;

View File

@@ -40,7 +40,7 @@ const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:3001';
export const useAuthStore = create<AuthState>()( export const useAuthStore = create<AuthState>()(
persist( persist(
(set, get) => ({ (set) => ({
// Initial state // Initial state
user: null, user: null,
isAuthenticated: false, isAuthenticated: false,

View File

@@ -88,6 +88,7 @@ export interface ApplicationDetails {
applicationManagementTAM?: ReferenceValue | null; // Application Management - TAM applicationManagementTAM?: ReferenceValue | null; // Application Management - TAM
technischeArchitectuur?: string | null; // URL to Technical Architecture document (Attribute ID 572) technischeArchitectuur?: string | null; // URL to Technical Architecture document (Attribute ID 572)
dataCompletenessPercentage?: number; // Data completeness percentage (0-100) dataCompletenessPercentage?: number; // Data completeness percentage (0-100)
_jiraUpdatedAt?: string | null; // Internal field for conflict detection (not exposed in API)
} }
// Search filters // Search filters

4
frontend/src/types/node.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
// Type definitions for Node.js types used in frontend
declare namespace NodeJS {
interface Timeout {}
}

9
frontend/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}