feat(ai-act-kompass): include training register in JSON backup (schema v3)
Some checks failed
security-scan / secret-scan (push) Failing after 5s

The training register incl. scan attachments and checksums is now part
of the backup export/import — the Art. 4 evidence chain survives device
moves. Older backups (v1/v2) remain importable.

Claude-Session: https://claude.ai/code/session_017YjohVNx1ZGNM4MX7tHpfv
This commit is contained in:
Claude 2026-07-04 11:37:14 +00:00
parent dc09999472
commit 94b539ed24
No known key found for this signature in database
3 changed files with 22 additions and 7 deletions

View File

@ -25,17 +25,20 @@ export function saveTraining(records: readonly TrainingRecord[]): void {
/** Format der Export-/Import-Datei. */
export interface ExportFile {
readonly app: 'ai-act-kompass';
readonly schemaVersion: 1 | 2;
readonly schemaVersion: 1 | 2 | 3;
readonly exportedAt: string;
readonly systems: readonly AISystem[];
/** Ab schemaVersion 2 enthalten. */
readonly profile?: CompanyProfile;
/** Ab schemaVersion 3 enthalten: Schulungsregister inkl. Scan-Belegen. */
readonly training?: readonly TrainingRecord[];
}
/** Ergebnis eines Imports (Profil optional, ältere Backups haben keins). */
/** Ergebnis eines Imports (ältere Backups haben kein Profil/Register). */
export interface ImportResult {
readonly systems: readonly AISystem[];
readonly profile: CompanyProfile | null;
readonly training: readonly TrainingRecord[] | null;
}
/** Leeres Firmenprofil (Default). */
@ -106,13 +109,15 @@ export function exportData(
systems: readonly AISystem[],
profile: CompanyProfile,
exportedAt: string,
training: readonly TrainingRecord[] = [],
): string {
const file: ExportFile = {
app: 'ai-act-kompass',
schemaVersion: 2,
schemaVersion: 3,
exportedAt,
systems,
profile,
training,
};
return JSON.stringify(file, null, 2);
}
@ -139,6 +144,7 @@ export function importData(json: string): ImportResult {
return {
systems: file.systems,
profile: file.profile ? { ...emptyProfile(), ...file.profile } : null,
training: Array.isArray(file.training) ? file.training : null,
};
}

View File

@ -211,10 +211,12 @@ export function App(): JSX.Element {
<SettingsView
systems={systems}
profile={profile}
training={training}
onProfileChange={setProfile}
onImport={(imported, importedProfile) => {
onImport={(imported, importedProfile, importedTraining) => {
setSystems(imported);
if (importedProfile) setProfile(importedProfile);
if (importedTraining) setTraining(importedTraining);
}}
onClear={() => {
setSystems([]);

View File

@ -1,4 +1,5 @@
import { useRef, useState } from 'react';
import type { TrainingRecord } from '../engine/quiz';
import type { AISystem, CompanyProfile } from '../engine/types';
import { useI18n } from '../i18n/context';
import { LOCALE_NAMES, LOCALES } from '../i18n/types';
@ -9,8 +10,13 @@ import { downloadText, todayISO } from './common';
interface SettingsViewProps {
readonly systems: readonly AISystem[];
readonly profile: CompanyProfile;
readonly training: readonly TrainingRecord[];
readonly onProfileChange: (profile: CompanyProfile) => void;
readonly onImport: (systems: readonly AISystem[], profile: CompanyProfile | null) => void;
readonly onImport: (
systems: readonly AISystem[],
profile: CompanyProfile | null,
training: readonly TrainingRecord[] | null,
) => void;
readonly onClear: () => void;
}
@ -22,6 +28,7 @@ type ProfileTextKey = Exclude<keyof CompanyProfile, 'logoDataUrl' | 'countries'>
export function SettingsView({
systems,
profile,
training,
onProfileChange,
onImport,
onClear,
@ -45,7 +52,7 @@ export function SettingsView({
const handleImport = async (file: File): Promise<void> => {
try {
const result = importData(await file.text());
onImport(result.systems, result.profile);
onImport(result.systems, result.profile, result.training);
setMessage(
fmt(t.importOk, {
count: result.systems.length,
@ -190,7 +197,7 @@ export function SettingsView({
onClick={() =>
downloadText(
`ai-act-kompass-backup-${todayISO()}.json`,
exportData(systems, profile, new Date().toISOString()),
exportData(systems, profile, new Date().toISOString(), training),
'application/json',
)
}