fix(admin): resolve all 266 TypeScript errors, enable strict build

Eliminate the pre-existing TS errors that were masked by
next.config.js `typescript.ignoreBuildErrors: true`, then turn the flag
OFF so the compiler is a real safety net for future changes. `next build`
and `tsc --noEmit` now pass with 0 errors.

The errors were not cosmetic — several exposed real latent bugs hidden by
the flag, e.g. the drafting-engine ConstraintEnforcer read non-existent
fields (`t.rule.dsfaRequired`, `d.required`, `r.title`), so its DSFA hard
gate and risk-flag checks were silently no-ops; scopeDefaults read
snake_case CompanyProfile fields that never matched the camelCase type
(generator defaults never populated). Both fixed by aligning code to the
current types.

Highlights:
- Vitest globals: add vitest-globals.d.ts (config already had globals:true)
  so the test files type-check; exclude Playwright specs from vitest.
- Add a minimal ambient `pg` module declaration (no @types/pg installed).
- Fix Next 15 route handlers to await Promise params.
- Reconcile drifted types across loeschfristen, compliance-scope, document-
  generator, drafting-engine, vendor-compliance, agent and more.

Pre-existing (NOT caused here, proven by stashing the diff): 3 vitest
logic tests still fail — getNextStep (2) and buildDocumentScope priority (1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-11 00:42:44 +02:00
parent bb9aacc3d3
commit a28db8f8f0
76 changed files with 280 additions and 190 deletions
@@ -190,7 +190,7 @@ export default function GeneratorSection({
{ruleResult && (
<div className="flex gap-1.5 flex-wrap">
{flagPills.map(({ key, label, color }) =>
ruleResult.computedFlags[key] ? (
(ruleResult.computedFlags as unknown as Record<string, boolean>)[key] ? (
<span key={key} className={`px-2 py-0.5 text-[10px] font-medium rounded-full ${color}`}>
{label}
</span>
@@ -16,7 +16,7 @@ export default function RecommendedDocuments({ allTemplates, onUseTemplate }: Pr
const { state } = useSDK()
const [showOptional, setShowOptional] = useState(false)
const level = state?.complianceScope?.determinedLevel as ComplianceDepthLevel | undefined
const level = state?.complianceScope?.decision?.determinedLevel as ComplianceDepthLevel | undefined
const scopeAnswers = state?.complianceScope?.answers || []
const recommendations = useMemo(() => {
@@ -24,7 +24,7 @@ export default function RecommendedDocuments({ allTemplates, onUseTemplate }: Pr
return evaluateTemplateRecommendations(
scopeAnswers,
level,
(state?.companyProfile as Record<string, unknown>) || {},
(state?.companyProfile as unknown as Record<string, unknown>) || {},
)
}, [level, scopeAnswers, state?.companyProfile])
@@ -165,6 +165,44 @@ export interface FeaturesCtx {
HAS_WITHDRAWAL: boolean
CONSUMER_WITHDRAWAL_TEXT: string
SUPPORT_CHANNELS_TEXT: string
// ── Optionale Feature-Template-Variablen (per str() ausgegeben, daher string) ─
// Whistleblower (HinSchG)
WHISTLEBLOWER_CONTACT_NAME?: string
WHISTLEBLOWER_CONTACT_ROLE?: string
WHISTLEBLOWER_EMAIL?: string
WHISTLEBLOWER_PHONE?: string
WHISTLEBLOWER_URL?: string
// Videokonferenz
VIDEO_PROVIDER_NAME?: string
VIDEO_PROVIDER_COUNTRY?: string
VIDEO_PROVIDER_ROLE?: string
VIDEO_PROVIDER_PRIVACY_URL?: string
RECORDING_RETENTION_DAYS?: string
// KI / BYOD / Consent / Social Media
APPROVED_AI_SYSTEMS?: string
BYOD_COST_DETAILS?: string
NEWSLETTER_SIGNUP_URL?: string
SOCIAL_MEDIA_PLATFORMS_LIST?: string
EDITORIAL_EMAIL?: string
// Transfer / SCC (Empfänger im Drittland)
RECIPIENT_NAME?: string
RECIPIENT_COUNTRY?: string
RECIPIENT_ADDRESS?: string
RECIPIENT_CONTACT?: string
RECIPIENT_EMAIL?: string
RECIPIENT_ROLE?: string
TRANSFER_PURPOSE?: string
TRANSFER_MECHANISM?: string
TRANSFER_FREQUENCY?: string
DATA_CATEGORIES_TRANSFERRED?: string
DATA_SUBJECTS?: string
// DSI
DSI_TITLE?: string
SERVICE_SCOPE_DESCRIPTION?: string
FULFILLMENT_LOCATION?: string
GUIDELINES_URL?: string
PROCESSOR_LIST_URL?: string
}
export interface TOMCtx {
@@ -95,7 +95,7 @@ function DocumentGeneratorPageInner() {
// Pre-fill TOM/DPA context from Compliance Scope Engine
useEffect(() => {
const scopeLevel = state?.complianceScope?.determinedLevel
const scopeLevel = state?.complianceScope?.decision?.determinedLevel
if (scopeLevel) {
const defaults = getGeneratorDefaults(scopeLevel, state?.companyProfile as never)
setContext((prev) => ({
@@ -104,7 +104,7 @@ function DocumentGeneratorPageInner() {
DPA: { ...prev.DPA, ...defaults.dpa },
}))
}
}, [state?.complianceScope?.determinedLevel, state?.companyProfile])
}, [state?.complianceScope?.decision?.determinedLevel, state?.companyProfile])
// ── MODULE WIRING: Backend Banner-Config → CONSENT + FEATURES ────────────
useEffect(() => {
@@ -12,8 +12,8 @@
* L4 = Zertifizierungsbereit (≥250 MA oder regulierte Branche)
*/
import type { ComplianceDepthLevel } from '../../lib/sdk/compliance-scope-types/core-levels'
import type { CompanyProfile } from '../../lib/sdk/types'
import type { ComplianceDepthLevel } from '@/lib/sdk/compliance-scope-types/core-levels'
import type { CompanyProfile } from '@/lib/sdk/types'
import type { TOMCtx, DPACtx } from './contextBridge'
// ============================================================================
@@ -216,33 +216,29 @@ export function getGeneratorDefaults(
// CompanyProfile-Felder in TOM/DPA uebernehmen
if (profile) {
if (profile.company_name) {
dpaBase.AN_NAME = profile.company_name
if (profile.companyName) {
dpaBase.AN_NAME = profile.companyName
scopeSet.add('DPA.AN_NAME')
}
if (profile.address) {
dpaBase.AN_STRASSE = profile.address
if (profile.headquartersStreet) {
dpaBase.AN_STRASSE = profile.headquartersStreet
scopeSet.add('DPA.AN_STRASSE')
}
if (profile.city && profile.postal_code) {
dpaBase.AN_PLZ_ORT = `${profile.postal_code} ${profile.city}`
if (profile.headquartersCity && profile.headquartersZip) {
dpaBase.AN_PLZ_ORT = `${profile.headquartersZip} ${profile.headquartersCity}`
scopeSet.add('DPA.AN_PLZ_ORT')
}
if (profile.dpo_name) {
if (profile.dpoName) {
tomBase.ISB_NAME = tomBase.ISB_NAME || ''
dpaBase.AN_DSB_NAME = profile.dpo_name
dpaBase.AN_DSB_NAME = profile.dpoName
scopeSet.add('DPA.AN_DSB_NAME')
}
if (profile.dpo_email) {
dpaBase.AN_DSB_EMAIL = profile.dpo_email
if (profile.dpoEmail) {
dpaBase.AN_DSB_EMAIL = profile.dpoEmail
scopeSet.add('DPA.AN_DSB_EMAIL')
}
if (profile.ceo_name) {
dpaBase.AN_UNTERZEICHNER_NAME = profile.ceo_name
tomBase.GF_NAME = profile.ceo_name
scopeSet.add('DPA.AN_UNTERZEICHNER_NAME')
scopeSet.add('TOM.GF_NAME')
}
// Unterzeichner/GF werden NICHT aus dem CompanyProfile befuellt — es enthaelt
// keine Person; diese Felder kommen aus dem TOM/DPA-Generator selbst.
}
// Alle gesetzten TOM/DPA Felder als scope-set markieren
@@ -9,8 +9,8 @@
* the CompanyProfile and scope answers.
*/
import type { ComplianceDepthLevel } from '../../lib/sdk/compliance-scope-types/core-levels'
import type { ScopeProfilingAnswer } from '../../lib/sdk/compliance-scope-types/state'
import type { ComplianceDepthLevel } from '@/lib/sdk/compliance-scope-types/core-levels'
import type { ScopeProfilingAnswer } from '@/lib/sdk/compliance-scope-types/state'
// ============================================================================
// Template recommendation rules