refactor(quick-260921-bi2): maschinelle Lint-Fixe und toten Code abbauen

- Aufgabe 2: vier sichere Biome-Regeln (useImportType pfadgebunden auf
  apps/web+packages, noUselessEscapeInRegex, useConst,
  useExponentiationOperator) sowie fuenf ungesicherte Regeln
  (useNodejsImportProtocol, useLiteralKeys, useOptionalChain, useTemplate,
  useParseIntRadix) angewendet und den gesamten Diff von Hand gelesen
  (ldap.service.ts zeichenweise gegen Gross-/Kleinschreibung der
  AD-Merkmale, auth.service.ts/jwt.strategy.ts gegen Durchwinken bei
  fehlender Sitzung geprueft)
- noUselessSwitchCase bleibt bewusst stehen (tender-normalizer.service.ts:60,
  die Fallmarke dokumentiert Absicht)
- Toter Code (D-03): fuenf folgenlose Auffangvariablen entfernt, eine
  nicht benutzte Funktion (forSystemQuery, Pruefskript) entfernt, ein
  positionsgebundener Dekoratorparameter umbenannt (current-user.decorator.ts),
  fuenf Symptomfunde entfernt und als Folgeaufgaben zu melden (siehe unten)
- Sechs weitere, im Plan nicht namentlich gelistete aber
  gleich-kategorische Dead-Code-Fundstellen in Testdateien zusaetzlich
  bereinigt (groups.service.spec.ts, cert-manager.test.tsx,
  ldap.service.spec.ts, prisma-tenant.extension.spec.ts x3) — noetig, um
  die vom Plan selbst verlangten Nullstaende bei noUnusedVariables/
  noUnusedImports/noUnusedFunctionParameters zu erreichen

Dekoratordaten aus apps/api unveraendert (593 Zeilen, sha256 6e1583f1...).
Endstand 620 Befunde (541 echt, 79 Test) statt der im Plan geschaetzten
621/542 — eine Differenz von 1, weil das Streichen des Namens aus
`catch (e: any)` in calendar.service.ts (Symptom-Fix) den dort ebenfalls
gemeldeten noExplicitAny-Befund miteliminiert; das ist eine erwuenschte
Nebenwirkung, keine Regression. Fehlerstufe 0, beide Testlaeufe
punktgleich gruen (69/1124, 66/459), pnpm type-check 4/4, pnpm lint
--force 5/5.

Folgeaufgaben aus D-03 (nicht in diesem Vorgang behoben):
- force-password-change.interceptor.ts: Freigabeliste prueft nur den Pfad,
  nicht die HTTP-Methode
- change-password/page.tsx: nach erzwungenem Wechsel bleibt die Person auf
  der Seite stehen (keine Weiterleitung, keine Aktualisierung der
  Benutzerablage)
- VehicleTable.tsx: Loeschschaltflaeche hat keinen Besetztzustand, laesst
  sich doppelt ausloesen
- SplitTab.tsx: downloadAllAsZip erhielt eine ungenutzte
  Uebersetzungsfunktion, Hinweis auf fest verdrahtete Texte im Zip-Pfad

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
2026-09-21 08:58:32 +02:00
parent 8d1c8f320b
commit 636fe0df8f
56 changed files with 126 additions and 147 deletions
-2
View File
@@ -2,7 +2,6 @@
import { useState, useTransition } from 'react';
import { useTranslations } from 'next-intl';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import { login } from '@/lib/auth-actions';
import { sanitizeNextPath } from '@/lib/safe-next';
@@ -18,7 +17,6 @@ import { DesktopDownloadLinks } from '@/components/desktop/desktop-download-link
*/
export default function LoginPage() {
const t = useTranslations('auth');
const router = useRouter();
const [isPending, startTransition] = useTransition();
const [error, setError] = useState<string | null>(null);
@@ -2,14 +2,10 @@
import { useState, useTransition, useEffect } from 'react';
import { useTranslations } from 'next-intl';
import { useRouter } from 'next/navigation';
import { fetchCurrentUser, changePasswordAction } from '@/lib/auth-actions';
import { useAuthStore } from '@/lib/stores/auth-store';
export default function ChangePasswordPage() {
const t = useTranslations('auth');
const router = useRouter();
const { user, setUser } = useAuthStore();
const [isPending, startTransition] = useTransition();
const [error, setError] = useState<string | null>(null);
const [passwordMismatch, setPasswordMismatch] = useState(false);
@@ -1,4 +1,4 @@
import { cleanup, render, screen, fireEvent, waitFor, act } from '@testing-library/react';
import { cleanup, render, screen, fireEvent, waitFor } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
// Mock next-intl — provide certManager namespace keys
@@ -17,7 +17,7 @@ const ROLE_STYLES: Record<CertRole, string> = {
'end-entity': 'bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300',
};
function downloadAllAsZip(certs: SplitResponse['certs'], t: (k: string) => string) {
function downloadAllAsZip(certs: SplitResponse['certs']) {
const files: Record<string, Uint8Array> = {};
for (const cert of certs) {
const bytes = Uint8Array.from(atob(cert.content), (c) => c.charCodeAt(0));
@@ -79,7 +79,7 @@ export function SplitTab({ file, pemText: _pemText, password: _password }: Split
{result && result.certs.length > 1 && (
<button
onClick={() => downloadAllAsZip(result.certs, t)}
onClick={() => downloadAllAsZip(result.certs)}
className="border border-border px-4 py-2 rounded text-sm font-medium hover:bg-secondary transition-colors"
>
{t('actions.downloadZip')}
@@ -161,7 +161,7 @@ export function VehicleTable() {
// Delete confirm
const [deleteTarget, setDeleteTarget] = useState<DkvVehicle | null>(null);
const [isDeleting, setIsDeleting] = useState(false);
const [, setIsDeleting] = useState(false);
// Server error below table
const [tableError, setTableError] = useState<string | null>(null);
@@ -260,9 +260,9 @@ export function EmailAlertConfigForm() {
{/* Host / EWS-URL */}
<div>
<label htmlFor="email-alert-host" className={labelCls}>
{(form.protocol === 'exchange'
{`${form.protocol === 'exchange'
? t('emailAlerts.hostLabelExchange')
: t('emailAlerts.hostLabelImap')) + ' *'}
: t('emailAlerts.hostLabelImap')} *`}
</label>
<input
id="email-alert-host"
@@ -1,6 +1,6 @@
'use client';
import { FormEvent, useEffect, useMemo, useRef, useState } from 'react';
import { type FormEvent, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslations } from 'next-intl';
import { updateWidgetConfig } from '@/lib/dashboard-api';
import {
+1 -1
View File
@@ -139,7 +139,7 @@ export async function changePasswordAction(
const maxAgeMatch = setCookieHeader.match(/Max-Age=(\d+)/i);
if (sessionMatch) {
newSessionToken = sessionMatch[1];
newSessionMaxAge = maxAgeMatch ? parseInt(maxAgeMatch[1]) : undefined;
newSessionMaxAge = maxAgeMatch ? parseInt(maxAgeMatch[1], 10) : undefined;
}
}
} catch (err) {
+1 -1
View File
@@ -1,4 +1,4 @@
import { NextRequest, NextResponse } from 'next/server';
import { type NextRequest, NextResponse } from 'next/server';
import { jwtVerify } from 'jose';
import { buildNextParam } from '@/lib/safe-next';