feat(ai-act-kompass): internal and external regulatory templates (works council etc.)
- Company-wide templates view: internal AI policy (framework), AI literacy training plan & record (Art. 4) listing inventory systems, works agreement on AI (Sec. 87(1) No. 6 / Sec. 90 BetrVG draft) - System-specific forms in detail view (role/risk/country gated): works council information letter (Sec. 90 BetrVG, DE module), employee information on AI use (Art. 26(7) / Law 132/2025), FRIA working template (Art. 27), serious incident report form (Art. 73 incl. 15/10/2-day deadlines) - All templates bilingual (DE/EN), letterhead-printable, placeholder- based drafts with legal disclaimer - 8 new tests (52 total) Claude-Session: https://claude.ai/code/session_017YjohVNx1ZGNM4MX7tHpfv
This commit is contained in:
parent
cd31274738
commit
2ec13165c8
@ -12,6 +12,7 @@
|
|||||||
- **Pflichten-Checklisten**: rollenspezifisch (Anbieter/Betreiber/Einführer/Händler) abgeleitete EU-Pflichten (Art. 4–73) mit Rechtsgrundlage, Geltungsfrist und Umsetzungsstatus.
|
- **Pflichten-Checklisten**: rollenspezifisch (Anbieter/Betreiber/Einführer/Händler) abgeleitete EU-Pflichten (Art. 4–73) mit Rechtsgrundlage, Geltungsfrist und Umsetzungsstatus.
|
||||||
- **Länder-Module** (zuschaltbar): nationale Zusatzanforderungen für DE (KI-MIG/BNetzA, § 87 BetrVG, DSGVO/BDSG), AT (RTR-KI-Servicestelle), IT (Legge 132/2025: Arbeitgeber-Info, Deepfake-Strafnormen, Sektorregeln), ES (AESIA, Sandbox RD 817/2023), FR (CNIL), NL (Algorithmenregister). Bei grenzüberschreitender Zusammenarbeit mehrere Länder aktivieren.
|
- **Länder-Module** (zuschaltbar): nationale Zusatzanforderungen für DE (KI-MIG/BNetzA, § 87 BetrVG, DSGVO/BDSG), AT (RTR-KI-Servicestelle), IT (Legge 132/2025: Arbeitgeber-Info, Deepfake-Strafnormen, Sektorregeln), ES (AESIA, Sandbox RD 817/2023), FR (CNIL), NL (Algorithmenregister). Bei grenzüberschreitender Zusammenarbeit mehrere Länder aktivieren.
|
||||||
- **Dokumenten-Generator** (Markdown + Druck/PDF mit Firmen-Briefkopf): Risikobericht, Maßnahmenplan, Annex-IV-Grundgerüst, **Lastenheft KI-Beschaffung** (AI-Act-Anforderungen an Anbieter, MUSS/SOLL) und **EU-Konformitätserklärung** (Entwurf nach Art. 47/Annex V).
|
- **Dokumenten-Generator** (Markdown + Druck/PDF mit Firmen-Briefkopf): Risikobericht, Maßnahmenplan, Annex-IV-Grundgerüst, **Lastenheft KI-Beschaffung** (AI-Act-Anforderungen an Anbieter, MUSS/SOLL) und **EU-Konformitätserklärung** (Entwurf nach Art. 47/Annex V).
|
||||||
|
- **Vorlagen & Vordrucke** (zweisprachig, druckbar): unternehmensweit **KI-Richtlinie**, **Schulungsplan/Nachweis KI-Kompetenz** (Art. 4) und **Betriebsvereinbarung KI** (§ 87/§ 90 BetrVG); systembezogen **Betriebsrats-Information** (§ 90 BetrVG), **Mitarbeiterinformation** (Art. 26 Abs. 7 / Legge 132), **FRIA-Arbeitsvorlage** (Art. 27) und **Vorfallmeldung** (Art. 73 mit Fristen).
|
||||||
- **Firmenprofil**: Logo, Name, Kontaktdaten — erscheinen in Briefkopf und Fußzeile aller gedruckten Dokumente.
|
- **Firmenprofil**: Logo, Name, Kontaktdaten — erscheinen in Briefkopf und Fußzeile aller gedruckten Dokumente.
|
||||||
- **Fristen-Übersicht**: alle Geltungsfristen des AI Act inkl. der Digital-Omnibus-Verschiebung (Annex III → 02.12.2027).
|
- **Fristen-Übersicht**: alle Geltungsfristen des AI Act inkl. der Digital-Omnibus-Verschiebung (Annex III → 02.12.2027).
|
||||||
|
|
||||||
|
|||||||
104
ai-act-kompass/src/engine/templates.test.ts
Normal file
104
ai-act-kompass/src/engine/templates.test.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { CONTENT_PACK, getContentPack } from '../content/pack';
|
||||||
|
import { classify } from './classify';
|
||||||
|
import { aiPolicy, trainingPlan, worksAgreement } from './templatesCompany';
|
||||||
|
import { employeeInfo, friaTemplate, incidentReportForm, worksCouncilInfo } from './templatesSystem';
|
||||||
|
import type { AISystem, AssessmentAnswers, CompanyProfile } from './types';
|
||||||
|
|
||||||
|
const answers: AssessmentAnswers = {
|
||||||
|
isAISystem: true,
|
||||||
|
role: 'deployer',
|
||||||
|
isGPAIModel: false,
|
||||||
|
prohibitedPractices: [],
|
||||||
|
annexIIICategories: ['a3-employment'],
|
||||||
|
art63Exception: false,
|
||||||
|
involvesProfiling: false,
|
||||||
|
isAnnexISafetyComponent: false,
|
||||||
|
transparencyTriggers: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const system: AISystem = {
|
||||||
|
id: 'sys-1',
|
||||||
|
name: 'Bewerber-Screening',
|
||||||
|
purpose: 'Vorauswahl',
|
||||||
|
vendor: 'ACME',
|
||||||
|
owner: 'Jane',
|
||||||
|
createdAt: '2026-07-01T00:00:00.000Z',
|
||||||
|
updatedAt: '2026-07-01T00:00:00.000Z',
|
||||||
|
answers,
|
||||||
|
classification: classify(answers, CONTENT_PACK),
|
||||||
|
obligationStatus: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const profile: CompanyProfile = {
|
||||||
|
name: 'Muster GmbH',
|
||||||
|
street: '',
|
||||||
|
zipCity: '',
|
||||||
|
contactPerson: '',
|
||||||
|
email: '',
|
||||||
|
phone: '',
|
||||||
|
website: '',
|
||||||
|
logoDataUrl: '',
|
||||||
|
countries: ['DE'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const TODAY = '2026-07-04';
|
||||||
|
|
||||||
|
describe('systembezogene Vordrucke', () => {
|
||||||
|
it('Betriebsrats-Information nennt § 90 und § 87 BetrVG', () => {
|
||||||
|
const md = worksCouncilInfo(system, CONTENT_PACK, TODAY, profile);
|
||||||
|
expect(md).toContain('§ 90');
|
||||||
|
expect(md).toContain('§ 87 Abs. 1 Nr. 6');
|
||||||
|
expect(md).toContain('Muster GmbH');
|
||||||
|
expect(md).toContain('keine Rechtsberatung');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Mitarbeiterinformation verweist auf Art. 26 Abs. 7', () => {
|
||||||
|
const md = employeeInfo(system, CONTENT_PACK, TODAY, profile);
|
||||||
|
expect(md).toContain('Art. 26 Abs. 7');
|
||||||
|
expect(md).toContain('Bewerber-Screening');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('FRIA-Vorlage enthält die Art.-27-Prüfschritte', () => {
|
||||||
|
const md = friaTemplate(system, CONTENT_PACK, TODAY, profile);
|
||||||
|
expect(md).toContain('Art. 27');
|
||||||
|
expect(md).toContain('Grundrechte');
|
||||||
|
expect(md).toContain('_[AUSFÜLLEN]_');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Vorfallmeldung nennt die Art.-73-Fristen (15/10/2 Tage)', () => {
|
||||||
|
const md = incidentReportForm(system, CONTENT_PACK, TODAY, profile);
|
||||||
|
expect(md).toContain('Art. 73');
|
||||||
|
expect(md).toContain('15 Tage');
|
||||||
|
expect(md).toContain('2 Tage');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Vordrucke sind auch auf Englisch verfügbar', () => {
|
||||||
|
const md = worksCouncilInfo(system, getContentPack('en'), TODAY, profile, 'en');
|
||||||
|
expect(md).toContain('works council');
|
||||||
|
expect(md).toContain('Sec. 90 BetrVG');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('unternehmensweite Vorlagen', () => {
|
||||||
|
it('KI-Richtlinie enthält Kernabschnitte', () => {
|
||||||
|
const md = aiPolicy(CONTENT_PACK, TODAY, profile);
|
||||||
|
expect(md).toContain('KI-Richtlinie');
|
||||||
|
expect(md).toContain('Verbotene Nutzungen');
|
||||||
|
expect(md).toContain('Art. 50');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Schulungsplan listet betroffene Systeme', () => {
|
||||||
|
const md = trainingPlan([system], CONTENT_PACK, TODAY, profile);
|
||||||
|
expect(md).toContain('Art. 4');
|
||||||
|
expect(md).toContain('Schulungsmatrix');
|
||||||
|
expect(md).toContain('Bewerber-Screening');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Betriebsvereinbarung enthält Paragrafen und Unterschriftszeile', () => {
|
||||||
|
const md = worksAgreement(CONTENT_PACK, TODAY, profile);
|
||||||
|
expect(md).toContain('§ 1 Geltungsbereich');
|
||||||
|
expect(md).toContain('§ 3 Keine vollautomatisierten Personalentscheidungen');
|
||||||
|
expect(md).toContain('Betriebsrat: _______________');
|
||||||
|
});
|
||||||
|
});
|
||||||
176
ai-act-kompass/src/engine/templatesCompany.ts
Normal file
176
ai-act-kompass/src/engine/templatesCompany.ts
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
import type { Locale } from '../i18n/types';
|
||||||
|
import { docStrings, formatDocDate } from './docStrings';
|
||||||
|
import type { AISystem, CompanyProfile, ContentPack } from './types';
|
||||||
|
|
||||||
|
/** Gemeinsamer Kopf für unternehmensweite Vordrucke. */
|
||||||
|
function head(
|
||||||
|
title: string,
|
||||||
|
pack: ContentPack,
|
||||||
|
today: string,
|
||||||
|
locale: Locale,
|
||||||
|
profile?: CompanyProfile,
|
||||||
|
): string {
|
||||||
|
const s = docStrings(locale);
|
||||||
|
const company = profile && profile.name.trim() !== '' ? profile.name : '_______________';
|
||||||
|
return [
|
||||||
|
`# ${title}`,
|
||||||
|
'',
|
||||||
|
`**${s.meta.company}:** ${company} · **${s.meta.createdAt}:** ${formatDocDate(today, locale)} · Content-Pack v${pack.version}`,
|
||||||
|
'',
|
||||||
|
s.disclaimer,
|
||||||
|
'',
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Vordruck: Interne KI-Richtlinie (AI Policy) — Rahmenentwurf. */
|
||||||
|
export function aiPolicy(
|
||||||
|
pack: ContentPack,
|
||||||
|
today: string,
|
||||||
|
profile?: CompanyProfile,
|
||||||
|
locale: Locale = 'de',
|
||||||
|
): string {
|
||||||
|
const s = docStrings(locale);
|
||||||
|
const T = locale === 'en'
|
||||||
|
? {
|
||||||
|
title: 'Internal AI policy (draft framework)',
|
||||||
|
sections: [
|
||||||
|
['Scope', 'This policy applies to all employees and governs the use of AI systems and AI-generated content in the company.'],
|
||||||
|
['Approved AI systems', 'Only AI systems listed in the company AI inventory may be used for business purposes. New tools require prior approval by [responsible unit].'],
|
||||||
|
['Prohibited uses', 'Entering trade secrets, personal data or customer data into non-approved AI services is prohibited. Practices prohibited under Art. 5 EU AI Act must not be implemented or commissioned.'],
|
||||||
|
['Human oversight', 'AI output supporting decisions affecting persons must be reviewed by a competent human before taking effect.'],
|
||||||
|
['Transparency', 'AI interaction with customers and AI-generated content is disclosed and marked in accordance with Art. 50 EU AI Act.'],
|
||||||
|
['Data protection', 'GDPR requirements (legal basis, information duties, DPIA where required) are checked before any deployment.'],
|
||||||
|
['Training', 'All employees using AI receive training appropriate to their role (Art. 4 EU AI Act); participation is documented.'],
|
||||||
|
['Incidents', 'Malfunctions and suspected serious incidents must be reported immediately to [contact]; the Art. 73 reporting process applies.'],
|
||||||
|
['Responsibilities', 'AI officer: [name]. Data protection officer: [name]. Works council involvement per BetrVG/works agreement.'],
|
||||||
|
['Entry into force and review', 'This policy enters into force on [date] and is reviewed at least annually.'],
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
title: 'Interne KI-Richtlinie (Rahmenentwurf)',
|
||||||
|
sections: [
|
||||||
|
['Geltungsbereich', 'Diese Richtlinie gilt für alle Beschäftigten und regelt die Nutzung von KI-Systemen und KI-generierten Inhalten im Unternehmen.'],
|
||||||
|
['Freigegebene KI-Systeme', 'Für dienstliche Zwecke dürfen nur KI-Systeme genutzt werden, die im KI-Inventar des Unternehmens gelistet sind. Neue Tools bedürfen der vorherigen Freigabe durch [zuständige Stelle].'],
|
||||||
|
['Verbotene Nutzungen', 'Die Eingabe von Geschäftsgeheimnissen, personenbezogenen Daten oder Kundendaten in nicht freigegebene KI-Dienste ist untersagt. Nach Art. 5 EU-KI-VO verbotene Praktiken dürfen weder umgesetzt noch beauftragt werden.'],
|
||||||
|
['Menschliche Aufsicht', 'KI-Ausgaben, die personenwirksame Entscheidungen unterstützen, werden vor Wirksamwerden durch eine fachkundige Person geprüft.'],
|
||||||
|
['Transparenz', 'KI-Interaktion mit Kunden und KI-generierte Inhalte werden gemäß Art. 50 EU-KI-VO offengelegt bzw. gekennzeichnet.'],
|
||||||
|
['Datenschutz', 'DSGVO-Anforderungen (Rechtsgrundlage, Informationspflichten, ggf. DSFA) werden vor jedem Einsatz geprüft.'],
|
||||||
|
['Schulung', 'Alle Beschäftigten, die KI nutzen, erhalten rollengerechte Schulungen (Art. 4 EU-KI-VO); die Teilnahme wird dokumentiert.'],
|
||||||
|
['Vorfälle', 'Fehlfunktionen und mutmaßlich schwerwiegende Vorfälle sind unverzüglich an [Kontakt] zu melden; es gilt der Meldeprozess nach Art. 73.'],
|
||||||
|
['Verantwortlichkeiten', 'KI-Beauftragte/r: [Name]. Datenschutzbeauftragte/r: [Name]. Einbindung des Betriebsrats gemäß BetrVG/Betriebsvereinbarung.'],
|
||||||
|
['Inkrafttreten und Überprüfung', 'Diese Richtlinie tritt am [Datum] in Kraft und wird mindestens jährlich überprüft.'],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const lines = [head(T.title, pack, today, locale, profile)];
|
||||||
|
T.sections.forEach(([sec, body], i) => {
|
||||||
|
lines.push(`## ${i + 1}. ${sec}`, '', body ?? s.todo, '');
|
||||||
|
});
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Vordruck: Schulungsplan & Nachweis KI-Kompetenz (Art. 4). */
|
||||||
|
export function trainingPlan(
|
||||||
|
systems: readonly AISystem[],
|
||||||
|
pack: ContentPack,
|
||||||
|
today: string,
|
||||||
|
profile?: CompanyProfile,
|
||||||
|
locale: Locale = 'de',
|
||||||
|
): string {
|
||||||
|
const T = locale === 'en'
|
||||||
|
? {
|
||||||
|
title: 'AI literacy training plan & record (Art. 4)',
|
||||||
|
intro: 'Art. 4 EU AI Act requires a sufficient level of AI literacy for staff dealing with AI systems — in force since 2 February 2025. This document plans the measures and serves as evidence.',
|
||||||
|
matrix: 'Training matrix',
|
||||||
|
cols: '| Target group | Content | Format | Interval | Evidence |',
|
||||||
|
colsSep: '|---|---|---|---|---|',
|
||||||
|
rows: [
|
||||||
|
'| All employees | AI basics, opportunities/risks, internal AI policy | e-learning | annually | participation list |',
|
||||||
|
'| Users of the systems listed below | system-specific operation, limits, human oversight | workshop | before first use + on changes | certificate |',
|
||||||
|
'| Management / AI officer | AI Act obligations, incident process, liability | seminar | annually | certificate |',
|
||||||
|
],
|
||||||
|
record: 'Training record (to be completed)',
|
||||||
|
recordCols: '| Date | Employee | Training | Trainer | Signature |',
|
||||||
|
recordSep: '|---|---|---|---|---|',
|
||||||
|
systemsTitle: 'Systems in scope',
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
title: 'Schulungsplan & Nachweis KI-Kompetenz (Art. 4)',
|
||||||
|
intro: 'Art. 4 EU-KI-VO verlangt ein ausreichendes Maß an KI-Kompetenz für Personal, das mit KI-Systemen umgeht — in Kraft seit 2. Februar 2025. Dieses Dokument plant die Maßnahmen und dient als Nachweis.',
|
||||||
|
matrix: 'Schulungsmatrix',
|
||||||
|
cols: '| Zielgruppe | Inhalte | Format | Turnus | Nachweis |',
|
||||||
|
colsSep: '|---|---|---|---|---|',
|
||||||
|
rows: [
|
||||||
|
'| Alle Beschäftigten | KI-Grundlagen, Chancen/Risiken, interne KI-Richtlinie | E-Learning | jährlich | Teilnahmeliste |',
|
||||||
|
'| Nutzer der unten gelisteten Systeme | systemspezifische Bedienung, Grenzen, menschliche Aufsicht | Workshop | vor Erstnutzung + bei Änderungen | Zertifikat |',
|
||||||
|
'| Führungskräfte / KI-Beauftragte | AI-Act-Pflichten, Vorfallprozess, Haftung | Seminar | jährlich | Zertifikat |',
|
||||||
|
],
|
||||||
|
record: 'Schulungsnachweis (fortlaufend zu führen)',
|
||||||
|
recordCols: '| Datum | Beschäftigte/r | Schulung | Trainer | Unterschrift |',
|
||||||
|
recordSep: '|---|---|---|---|---|',
|
||||||
|
systemsTitle: 'Betroffene Systeme',
|
||||||
|
};
|
||||||
|
|
||||||
|
const lines = [head(T.title, pack, today, locale, profile)];
|
||||||
|
lines.push(T.intro, '', `## ${T.matrix}`, '', T.cols, T.colsSep, ...T.rows, '');
|
||||||
|
lines.push(`## ${T.systemsTitle}`, '');
|
||||||
|
if (systems.length === 0) {
|
||||||
|
lines.push('—', '');
|
||||||
|
} else {
|
||||||
|
for (const sys of systems) {
|
||||||
|
lines.push(`- ${sys.name}${sys.purpose ? ` — ${sys.purpose}` : ''}`);
|
||||||
|
}
|
||||||
|
lines.push('');
|
||||||
|
}
|
||||||
|
lines.push(`## ${T.record}`, '', T.recordCols, T.recordSep, '| | | | | |', '| | | | | |', '| | | | | |');
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Vordruck: Betriebsvereinbarung KI (Rahmenentwurf, Deutschland). */
|
||||||
|
export function worksAgreement(
|
||||||
|
pack: ContentPack,
|
||||||
|
today: string,
|
||||||
|
profile?: CompanyProfile,
|
||||||
|
locale: Locale = 'de',
|
||||||
|
): string {
|
||||||
|
const s = docStrings(locale);
|
||||||
|
const T = locale === 'en'
|
||||||
|
? {
|
||||||
|
title: 'Works agreement on the use of AI systems (draft framework, Germany)',
|
||||||
|
parties: 'Between the employer [company] and the works council of [site], the following works agreement is concluded pursuant to Sec. 87 (1) No. 6, Sec. 90 BetrVG:',
|
||||||
|
sections: [
|
||||||
|
['§ 1 Scope', 'This agreement applies to all employees of the site and to the AI systems listed in Annex 1 (AI inventory).'],
|
||||||
|
['§ 2 Purpose limitation', 'The systems are used exclusively for the purposes described in Annex 1. Performance and behaviour monitoring beyond that is not permitted.'],
|
||||||
|
['§ 3 No fully automated personnel decisions', 'Decisions with legal effect for employees (e.g. hiring, promotion, termination) are always taken by humans; AI output is decision support only.'],
|
||||||
|
['§ 4 Data protection', 'Employee data is processed only per Annex 2 (data catalogue). Evaluations for individual performance review require the works council’s consent.'],
|
||||||
|
['§ 5 Information and training', 'Affected employees are informed before commissioning (Art. 26(7) AI Act) and trained per Art. 4 AI Act.'],
|
||||||
|
['§ 6 Introduction of new systems / changes', 'New AI systems or substantial changes require timely information of the works council (Sec. 90 BetrVG) and an addendum to Annex 1.'],
|
||||||
|
['§ 7 Rights of the works council', 'The works council may inspect logging data relevant to employees and consult external expertise where required (Sec. 80 (3) BetrVG).'],
|
||||||
|
['§ 8 Term and termination', 'This agreement enters into force upon signature and may be terminated with [3] months’ notice; its after-effect applies.'],
|
||||||
|
],
|
||||||
|
signature: 'Place, date: _______________\n\nEmployer: _______________ Works council: _______________',
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
title: 'Betriebsvereinbarung über den Einsatz von KI-Systemen (Rahmenentwurf)',
|
||||||
|
parties: 'Zwischen dem Arbeitgeber [Firma] und dem Betriebsrat des Betriebs [Standort] wird gemäß § 87 Abs. 1 Nr. 6, § 90 BetrVG folgende Betriebsvereinbarung geschlossen:',
|
||||||
|
sections: [
|
||||||
|
['§ 1 Geltungsbereich', 'Diese Vereinbarung gilt für alle Beschäftigten des Betriebs und für die in Anlage 1 (KI-Inventar) aufgeführten KI-Systeme.'],
|
||||||
|
['§ 2 Zweckbindung', 'Die Systeme werden ausschließlich zu den in Anlage 1 beschriebenen Zwecken eingesetzt. Eine darüber hinausgehende Leistungs- und Verhaltenskontrolle findet nicht statt.'],
|
||||||
|
['§ 3 Keine vollautomatisierten Personalentscheidungen', 'Entscheidungen mit Rechtswirkung für Beschäftigte (z. B. Einstellung, Beförderung, Kündigung) trifft stets ein Mensch; KI-Ausgaben sind nur Entscheidungsunterstützung.'],
|
||||||
|
['§ 4 Datenschutz', 'Beschäftigtendaten werden nur gemäß Anlage 2 (Datenkatalog) verarbeitet. Auswertungen zur individuellen Leistungsbeurteilung bedürfen der Zustimmung des Betriebsrats.'],
|
||||||
|
['§ 5 Information und Schulung', 'Betroffene Beschäftigte werden vor Inbetriebnahme informiert (Art. 26 Abs. 7 KI-VO) und nach Art. 4 KI-VO geschult.'],
|
||||||
|
['§ 6 Einführung neuer Systeme / Änderungen', 'Neue KI-Systeme oder wesentliche Änderungen erfordern die rechtzeitige Unterrichtung des Betriebsrats (§ 90 BetrVG) und eine Ergänzung der Anlage 1.'],
|
||||||
|
['§ 7 Rechte des Betriebsrats', 'Der Betriebsrat kann beschäftigtenrelevante Protokolldaten einsehen und bei Bedarf externen Sachverstand hinzuziehen (§ 80 Abs. 3 BetrVG).'],
|
||||||
|
['§ 8 Laufzeit und Kündigung', 'Diese Vereinbarung tritt mit Unterzeichnung in Kraft und kann mit einer Frist von [3] Monaten gekündigt werden; sie wirkt nach.'],
|
||||||
|
],
|
||||||
|
signature: 'Ort, Datum: _______________\n\nArbeitgeber: _______________ Betriebsrat: _______________',
|
||||||
|
};
|
||||||
|
|
||||||
|
const lines = [head(T.title, pack, today, locale, profile)];
|
||||||
|
lines.push(T.parties, '');
|
||||||
|
for (const [sec, body] of T.sections) {
|
||||||
|
lines.push(`## ${sec}`, '', body ?? s.todo, '');
|
||||||
|
}
|
||||||
|
lines.push(T.signature);
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
220
ai-act-kompass/src/engine/templatesSystem.ts
Normal file
220
ai-act-kompass/src/engine/templatesSystem.ts
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
import type { Locale } from '../i18n/types';
|
||||||
|
import { docStrings, formatDocDate } from './docStrings';
|
||||||
|
import type { AISystem, CompanyProfile, ContentPack } from './types';
|
||||||
|
|
||||||
|
/** Gemeinsamer Kopf für systembezogene Vordrucke. */
|
||||||
|
function head(
|
||||||
|
title: string,
|
||||||
|
system: AISystem,
|
||||||
|
pack: ContentPack,
|
||||||
|
today: string,
|
||||||
|
locale: Locale,
|
||||||
|
profile?: CompanyProfile,
|
||||||
|
): string {
|
||||||
|
const s = docStrings(locale);
|
||||||
|
return [
|
||||||
|
`# ${title}`,
|
||||||
|
'',
|
||||||
|
`| | |`,
|
||||||
|
`|---|---|`,
|
||||||
|
...(profile && profile.name.trim() !== '' ? [`| **${s.meta.company}** | ${profile.name} |`] : []),
|
||||||
|
`| **${s.meta.system}** | ${system.name} |`,
|
||||||
|
`| **${s.meta.purpose}** | ${system.purpose || '—'} |`,
|
||||||
|
`| **${s.meta.riskClass}** | ${s.risk[system.classification.riskClass]} |`,
|
||||||
|
`| **${s.meta.createdAt}** | ${formatDocDate(today, locale)} |`,
|
||||||
|
`| **${s.meta.contentPack}** | v${pack.version} |`,
|
||||||
|
'',
|
||||||
|
s.disclaimer,
|
||||||
|
'',
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vordruck: Information des Betriebsrats nach § 90 BetrVG (Deutschland)
|
||||||
|
* über den geplanten Einsatz eines KI-Systems.
|
||||||
|
*/
|
||||||
|
export function worksCouncilInfo(
|
||||||
|
system: AISystem,
|
||||||
|
pack: ContentPack,
|
||||||
|
today: string,
|
||||||
|
profile?: CompanyProfile,
|
||||||
|
locale: Locale = 'de',
|
||||||
|
): string {
|
||||||
|
const s = docStrings(locale);
|
||||||
|
const T = locale === 'en'
|
||||||
|
? {
|
||||||
|
title: 'Information to the works council pursuant to Sec. 90 BetrVG (draft)',
|
||||||
|
to: 'To the works council of',
|
||||||
|
subject: 'Subject: Information on the planned use of an AI system',
|
||||||
|
body: 'Dear members of the works council,\n\npursuant to Sec. 90 (1) No. 3 BetrVG we inform you in due time about the planned use of the following AI system and offer consultation pursuant to Sec. 90 (2) BetrVG. If the system is capable of monitoring the behaviour or performance of employees, we will additionally seek your co-determination pursuant to Sec. 87 (1) No. 6 BetrVG (works agreement).',
|
||||||
|
sections: [
|
||||||
|
['Planned date of introduction', ''],
|
||||||
|
['Affected departments / groups of employees', ''],
|
||||||
|
['Purpose and functioning of the system (summary)', ''],
|
||||||
|
['Data processed relating to employees', ''],
|
||||||
|
['Effects on work processes and workplaces', ''],
|
||||||
|
['Planned training measures (Art. 4 AI Act)', ''],
|
||||||
|
['Contact person for consultation', ''],
|
||||||
|
],
|
||||||
|
closing: 'We look forward to the consultation and remain at your disposal for questions.\n\nPlace, date: _______________ Signature (employer): _______________',
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
title: 'Information des Betriebsrats nach § 90 BetrVG (Entwurf)',
|
||||||
|
to: 'An den Betriebsrat der',
|
||||||
|
subject: 'Betreff: Unterrichtung über den geplanten Einsatz eines KI-Systems',
|
||||||
|
body: 'Sehr geehrte Mitglieder des Betriebsrats,\n\ngemäß § 90 Abs. 1 Nr. 3 BetrVG unterrichten wir Sie rechtzeitig über den geplanten Einsatz des folgenden KI-Systems und bieten die Beratung nach § 90 Abs. 2 BetrVG an. Sofern das System geeignet ist, Verhalten oder Leistung der Beschäftigten zu überwachen, werden wir zusätzlich Ihre Mitbestimmung nach § 87 Abs. 1 Nr. 6 BetrVG einholen (Betriebsvereinbarung).',
|
||||||
|
sections: [
|
||||||
|
['Geplanter Einführungszeitpunkt', ''],
|
||||||
|
['Betroffene Abteilungen / Beschäftigtengruppen', ''],
|
||||||
|
['Zweck und Funktionsweise des Systems (Kurzbeschreibung)', ''],
|
||||||
|
['Verarbeitete Beschäftigtendaten', ''],
|
||||||
|
['Auswirkungen auf Arbeitsabläufe und Arbeitsplätze', ''],
|
||||||
|
['Geplante Schulungsmaßnahmen (Art. 4 AI Act)', ''],
|
||||||
|
['Ansprechpartner für die Beratung', ''],
|
||||||
|
],
|
||||||
|
closing: 'Wir freuen uns auf die Beratung und stehen für Rückfragen zur Verfügung.\n\nOrt, Datum: _______________ Unterschrift (Arbeitgeber): _______________',
|
||||||
|
};
|
||||||
|
|
||||||
|
const lines = [head(T.title, system, pack, today, locale, profile)];
|
||||||
|
lines.push(`${T.to} ${profile?.name ?? '_______________'}`, '', `**${T.subject}**`, '', T.body, '');
|
||||||
|
for (const [label] of T.sections) {
|
||||||
|
lines.push(`## ${label}`, '', s.todo, '');
|
||||||
|
}
|
||||||
|
lines.push(T.closing);
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vordruck: Information der Beschäftigten über den KI-Einsatz
|
||||||
|
* (Art. 26 Abs. 7 AI Act; in Italien zusätzlich Legge 132/2025).
|
||||||
|
*/
|
||||||
|
export function employeeInfo(
|
||||||
|
system: AISystem,
|
||||||
|
pack: ContentPack,
|
||||||
|
today: string,
|
||||||
|
profile?: CompanyProfile,
|
||||||
|
locale: Locale = 'de',
|
||||||
|
): string {
|
||||||
|
const s = docStrings(locale);
|
||||||
|
const T = locale === 'en'
|
||||||
|
? {
|
||||||
|
title: 'Employee information on the use of an AI system (draft)',
|
||||||
|
body: 'Dear colleagues,\n\nwe hereby inform you that the AI system described below will be used in our company. This information is provided pursuant to Art. 26(7) of the EU AI Act and applicable national law.',
|
||||||
|
sections: [
|
||||||
|
['What the system does and what it is used for', ''],
|
||||||
|
['Which decisions the system supports (final decisions are made by humans)', ''],
|
||||||
|
['Which of your data is processed', ''],
|
||||||
|
['Human oversight: who supervises the system and how you can raise objections', ''],
|
||||||
|
['Your contacts (specialist department, data protection officer, works council)', ''],
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
title: 'Mitarbeiterinformation zum Einsatz eines KI-Systems (Entwurf)',
|
||||||
|
body: 'Liebe Kolleginnen und Kollegen,\n\nhiermit informieren wir Sie, dass in unserem Unternehmen das nachfolgend beschriebene KI-System eingesetzt wird. Diese Information erfolgt nach Art. 26 Abs. 7 der EU-KI-Verordnung sowie einschlägigem nationalem Recht.',
|
||||||
|
sections: [
|
||||||
|
['Was das System tut und wofür es eingesetzt wird', ''],
|
||||||
|
['Welche Entscheidungen das System unterstützt (Letztentscheidung trifft der Mensch)', ''],
|
||||||
|
['Welche Ihrer Daten verarbeitet werden', ''],
|
||||||
|
['Menschliche Aufsicht: wer das System überwacht und wie Sie Einwände erheben können', ''],
|
||||||
|
['Ihre Ansprechpartner (Fachabteilung, Datenschutzbeauftragte/r, Betriebsrat)', ''],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const lines = [head(T.title, system, pack, today, locale, profile)];
|
||||||
|
lines.push(T.body, '');
|
||||||
|
for (const [label] of T.sections) {
|
||||||
|
lines.push(`## ${label}`, '', s.todo, '');
|
||||||
|
}
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Vordruck: Grundrechte-Folgenabschätzung (FRIA, Art. 27) — Arbeitsvorlage. */
|
||||||
|
export function friaTemplate(
|
||||||
|
system: AISystem,
|
||||||
|
pack: ContentPack,
|
||||||
|
today: string,
|
||||||
|
profile?: CompanyProfile,
|
||||||
|
locale: Locale = 'de',
|
||||||
|
): string {
|
||||||
|
const s = docStrings(locale);
|
||||||
|
const T = locale === 'en'
|
||||||
|
? {
|
||||||
|
title: 'Fundamental rights impact assessment (FRIA, Art. 27) — working template',
|
||||||
|
sections: [
|
||||||
|
'Description of the deployment process and intended use',
|
||||||
|
'Period and frequency of use',
|
||||||
|
'Categories of natural persons and groups likely to be affected',
|
||||||
|
'Specific risks of harm to fundamental rights (non-discrimination, privacy, freedom of expression, social rights)',
|
||||||
|
'Human oversight measures as per instructions for use',
|
||||||
|
'Measures in case risks materialise (internal governance, complaint mechanisms)',
|
||||||
|
'Result and decision (use / use with conditions / no use)',
|
||||||
|
'Notification to the market surveillance authority (where required)',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
title: 'Grundrechte-Folgenabschätzung (FRIA, Art. 27) — Arbeitsvorlage',
|
||||||
|
sections: [
|
||||||
|
'Beschreibung des Einsatzprozesses und der Zweckbestimmung',
|
||||||
|
'Zeitraum und Häufigkeit der Nutzung',
|
||||||
|
'Kategorien betroffener Personen und Gruppen',
|
||||||
|
'Spezifische Schadensrisiken für Grundrechte (Nichtdiskriminierung, Privatsphäre, Meinungsfreiheit, soziale Rechte)',
|
||||||
|
'Maßnahmen der menschlichen Aufsicht gemäß Betriebsanleitung',
|
||||||
|
'Maßnahmen bei Eintritt der Risiken (interne Governance, Beschwerdemechanismen)',
|
||||||
|
'Ergebnis und Entscheidung (Einsatz / Einsatz mit Auflagen / kein Einsatz)',
|
||||||
|
'Meldung an die Marktüberwachungsbehörde (soweit erforderlich)',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const lines = [head(T.title, system, pack, today, locale, profile)];
|
||||||
|
T.sections.forEach((sec, i) => {
|
||||||
|
lines.push(`## ${i + 1}. ${sec}`, '', s.todo, '');
|
||||||
|
});
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Vordruck: Meldung eines schwerwiegenden Vorfalls (Art. 73) — internes Formular. */
|
||||||
|
export function incidentReportForm(
|
||||||
|
system: AISystem,
|
||||||
|
pack: ContentPack,
|
||||||
|
today: string,
|
||||||
|
profile?: CompanyProfile,
|
||||||
|
locale: Locale = 'de',
|
||||||
|
): string {
|
||||||
|
const s = docStrings(locale);
|
||||||
|
const T = locale === 'en'
|
||||||
|
? {
|
||||||
|
title: 'Serious incident report (Art. 73) — internal form',
|
||||||
|
note: 'Deadlines: report to the market surveillance authority immediately after establishing the causal link, at the latest within 15 days; death of a person: 10 days; widespread infringement / critical infrastructure: 2 days.',
|
||||||
|
fields: [
|
||||||
|
'Date/time of the incident and of becoming aware',
|
||||||
|
'Description of the incident and immediate consequences',
|
||||||
|
'Persons affected / damage incurred (health, safety, fundamental rights)',
|
||||||
|
'Presumed cause / connection with the AI system',
|
||||||
|
'Immediate measures taken (containment, shutdown)',
|
||||||
|
'Reported to (authority, provider, internal bodies) with date',
|
||||||
|
'Corrective actions and lessons learned',
|
||||||
|
'Responsible person (name, function, signature)',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
title: 'Meldung eines schwerwiegenden Vorfalls (Art. 73) — internes Formular',
|
||||||
|
note: 'Fristen: Meldung an die Marktüberwachungsbehörde unverzüglich nach Feststellung des Kausalzusammenhangs, spätestens 15 Tage; Tod einer Person: 10 Tage; weitverbreiteter Verstoß / kritische Infrastruktur: 2 Tage.',
|
||||||
|
fields: [
|
||||||
|
'Datum/Uhrzeit des Vorfalls und der Kenntnisnahme',
|
||||||
|
'Beschreibung des Vorfalls und unmittelbare Folgen',
|
||||||
|
'Betroffene Personen / eingetretener Schaden (Gesundheit, Sicherheit, Grundrechte)',
|
||||||
|
'Vermutete Ursache / Zusammenhang mit dem KI-System',
|
||||||
|
'Ergriffene Sofortmaßnahmen (Eindämmung, Abschaltung)',
|
||||||
|
'Gemeldet an (Behörde, Anbieter, interne Stellen) mit Datum',
|
||||||
|
'Korrekturmaßnahmen und Lessons Learned',
|
||||||
|
'Verantwortliche Person (Name, Funktion, Unterschrift)',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const lines = [head(T.title, system, pack, today, locale, profile)];
|
||||||
|
lines.push(`> ${T.note}`, '');
|
||||||
|
T.fields.forEach((f, i) => {
|
||||||
|
lines.push(`## ${i + 1}. ${f}`, '', s.todo, '');
|
||||||
|
});
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
@ -150,6 +150,21 @@ const DE = {
|
|||||||
legalNoticeTitle: 'Rechtlicher Hinweis (keine Rechtsberatung)',
|
legalNoticeTitle: 'Rechtlicher Hinweis (keine Rechtsberatung)',
|
||||||
resultDisclaimer:
|
resultDisclaimer:
|
||||||
'Diese Einstufung ist eine unverbindliche, automatisierte Ersteinschätzung auf Basis Ihrer Angaben — keine Rechtsberatung. Lassen Sie das Ergebnis vor weitreichenden Entscheidungen rechtlich prüfen.',
|
'Diese Einstufung ist eine unverbindliche, automatisierte Ersteinschätzung auf Basis Ihrer Angaben — keine Rechtsberatung. Lassen Sie das Ergebnis vor weitreichenden Entscheidungen rechtlich prüfen.',
|
||||||
|
|
||||||
|
navTemplates: 'Vorlagen & Vordrucke',
|
||||||
|
templatesTitle: 'Vorlagen & Vordrucke',
|
||||||
|
templatesSub: 'Unternehmensweite Entwürfe für die interne und externe Umsetzung — mit Firmen-Briefkopf druckbar. Systembezogene Vordrucke (Betriebsrat, Mitarbeiterinfo, FRIA, Vorfallmeldung) finden Sie in der jeweiligen Systemansicht.',
|
||||||
|
templatesHint: 'Alle Vorlagen sind Entwürfe mit Platzhaltern — vor Verwendung anpassen und rechtlich prüfen lassen.',
|
||||||
|
tplPolicy: 'Interne KI-Richtlinie',
|
||||||
|
tplPolicyDesc: 'Nutzungsregeln für KI im Unternehmen: Freigaben, Verbote, Aufsicht, Transparenz, Vorfälle.',
|
||||||
|
tplTraining: 'Schulungsplan KI-Kompetenz (Art. 4)',
|
||||||
|
tplTrainingDesc: 'Schulungsmatrix und fortlaufender Nachweis — Pflicht seit 02.02.2025.',
|
||||||
|
tplWorksAgreement: 'Betriebsvereinbarung KI (Rahmen)',
|
||||||
|
tplWorksAgreementDesc: 'Entwurf nach § 87 Abs. 1 Nr. 6, § 90 BetrVG: Zweckbindung, keine automatisierten Personalentscheidungen, Rechte des Betriebsrats.',
|
||||||
|
docWorksCouncil: 'Betriebsrats-Information (§ 90 BetrVG)',
|
||||||
|
docEmployeeInfo: 'Mitarbeiterinformation KI-Einsatz',
|
||||||
|
docFria: 'FRIA-Vorlage (Art. 27)',
|
||||||
|
docIncident: 'Vorfallmeldung (Art. 73)',
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UIStrings = typeof DE;
|
export type UIStrings = typeof DE;
|
||||||
@ -302,6 +317,21 @@ const EN: UIStrings = {
|
|||||||
legalNoticeTitle: 'Legal notice (not legal advice)',
|
legalNoticeTitle: 'Legal notice (not legal advice)',
|
||||||
resultDisclaimer:
|
resultDisclaimer:
|
||||||
'This classification is a non-binding, automated preliminary assessment based on your input — not legal advice. Have the result legally reviewed before far-reaching decisions.',
|
'This classification is a non-binding, automated preliminary assessment based on your input — not legal advice. Have the result legally reviewed before far-reaching decisions.',
|
||||||
|
|
||||||
|
navTemplates: 'Templates & forms',
|
||||||
|
templatesTitle: 'Templates & forms',
|
||||||
|
templatesSub: 'Company-wide drafts for internal and external implementation — printable with company letterhead. System-specific forms (works council, employee info, FRIA, incident report) are in the respective system view.',
|
||||||
|
templatesHint: 'All templates are drafts with placeholders — adapt and have them legally reviewed before use.',
|
||||||
|
tplPolicy: 'Internal AI policy',
|
||||||
|
tplPolicyDesc: 'Usage rules for AI in the company: approvals, prohibitions, oversight, transparency, incidents.',
|
||||||
|
tplTraining: 'AI literacy training plan (Art. 4)',
|
||||||
|
tplTrainingDesc: 'Training matrix and continuous record — mandatory since 2 Feb 2025.',
|
||||||
|
tplWorksAgreement: 'Works agreement on AI (framework)',
|
||||||
|
tplWorksAgreementDesc: 'Draft per Sec. 87(1) No. 6, Sec. 90 BetrVG: purpose limitation, no automated personnel decisions, works council rights.',
|
||||||
|
docWorksCouncil: 'Works council information (Sec. 90 BetrVG)',
|
||||||
|
docEmployeeInfo: 'Employee information on AI use',
|
||||||
|
docFria: 'FRIA template (Art. 27)',
|
||||||
|
docIncident: 'Incident report (Art. 73)',
|
||||||
};
|
};
|
||||||
|
|
||||||
const DICTS: Readonly<Record<Locale, UIStrings>> = { de: DE, en: EN };
|
const DICTS: Readonly<Record<Locale, UIStrings>> = { de: DE, en: EN };
|
||||||
|
|||||||
@ -9,11 +9,13 @@ import { Dashboard } from './Dashboard';
|
|||||||
import { DisclaimerGate } from './DisclaimerGate';
|
import { DisclaimerGate } from './DisclaimerGate';
|
||||||
import { SettingsView } from './SettingsView';
|
import { SettingsView } from './SettingsView';
|
||||||
import { SystemDetail } from './SystemDetail';
|
import { SystemDetail } from './SystemDetail';
|
||||||
|
import { TemplatesView } from './TemplatesView';
|
||||||
import { Wizard } from './Wizard';
|
import { Wizard } from './Wizard';
|
||||||
|
|
||||||
type View =
|
type View =
|
||||||
| { readonly kind: 'dashboard' }
|
| { readonly kind: 'dashboard' }
|
||||||
| { readonly kind: 'tasks' }
|
| { readonly kind: 'tasks' }
|
||||||
|
| { readonly kind: 'templates' }
|
||||||
| { readonly kind: 'wizard'; readonly systemId: string | null }
|
| { readonly kind: 'wizard'; readonly systemId: string | null }
|
||||||
| { readonly kind: 'detail'; readonly systemId: string }
|
| { readonly kind: 'detail'; readonly systemId: string }
|
||||||
| { readonly kind: 'settings' };
|
| { readonly kind: 'settings' };
|
||||||
@ -88,6 +90,12 @@ export function App(): JSX.Element {
|
|||||||
>
|
>
|
||||||
{t.navNew}
|
{t.navNew}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
className={view.kind === 'templates' ? 'active' : ''}
|
||||||
|
onClick={() => setView({ kind: 'templates' })}
|
||||||
|
>
|
||||||
|
{t.navTemplates}
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
className={view.kind === 'settings' ? 'active' : ''}
|
className={view.kind === 'settings' ? 'active' : ''}
|
||||||
onClick={() => setView({ kind: 'settings' })}
|
onClick={() => setView({ kind: 'settings' })}
|
||||||
@ -138,6 +146,8 @@ export function App(): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{view.kind === 'templates' && <TemplatesView systems={systems} profile={profile} />}
|
||||||
|
|
||||||
{view.kind === 'wizard' && (
|
{view.kind === 'wizard' && (
|
||||||
<Wizard
|
<Wizard
|
||||||
existing={view.systemId ? (findSystem(view.systemId) ?? null) : null}
|
existing={view.systemId ? (findSystem(view.systemId) ?? null) : null}
|
||||||
|
|||||||
@ -3,6 +3,12 @@ import { docStrings } from '../engine/docStrings';
|
|||||||
import { actionPlan, annexIVSkeleton, riskReport } from '../engine/documents';
|
import { actionPlan, annexIVSkeleton, riskReport } from '../engine/documents';
|
||||||
import { conformityDeclaration, procurementSpec } from '../engine/extraDocs';
|
import { conformityDeclaration, procurementSpec } from '../engine/extraDocs';
|
||||||
import { deriveObligations } from '../engine/obligations';
|
import { deriveObligations } from '../engine/obligations';
|
||||||
|
import {
|
||||||
|
employeeInfo,
|
||||||
|
friaTemplate,
|
||||||
|
incidentReportForm,
|
||||||
|
worksCouncilInfo,
|
||||||
|
} from '../engine/templatesSystem';
|
||||||
import type { AISystem, CompanyProfile, ObligationStatus } from '../engine/types';
|
import type { AISystem, CompanyProfile, ObligationStatus } from '../engine/types';
|
||||||
import { useI18n } from '../i18n/context';
|
import { useI18n } from '../i18n/context';
|
||||||
import { fmt } from '../i18n/ui';
|
import { fmt } from '../i18n/ui';
|
||||||
@ -18,7 +24,16 @@ interface SystemDetailProps {
|
|||||||
readonly onBack: () => void;
|
readonly onBack: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type DocKind = 'report' | 'plan' | 'annexIV' | 'procurement' | 'conformity';
|
type DocKind =
|
||||||
|
| 'report'
|
||||||
|
| 'plan'
|
||||||
|
| 'annexIV'
|
||||||
|
| 'procurement'
|
||||||
|
| 'conformity'
|
||||||
|
| 'worksCouncil'
|
||||||
|
| 'employeeInfo'
|
||||||
|
| 'fria'
|
||||||
|
| 'incident';
|
||||||
|
|
||||||
/** Detailansicht eines Systems: Klassifizierung, Pflichten-Checkliste, Dokumente. */
|
/** Detailansicht eines Systems: Klassifizierung, Pflichten-Checkliste, Dokumente. */
|
||||||
export function SystemDetail({
|
export function SystemDetail({
|
||||||
@ -79,13 +94,38 @@ export function SystemDetail({
|
|||||||
file: `konformitaetserklaerung-${system.name}-${today}.md`,
|
file: `konformitaetserklaerung-${system.name}-${today}.md`,
|
||||||
render: () => conformityDeclaration(system, pack, today, profile, locale),
|
render: () => conformityDeclaration(system, pack, today, profile, locale),
|
||||||
},
|
},
|
||||||
|
worksCouncil: {
|
||||||
|
label: t.docWorksCouncil,
|
||||||
|
file: `betriebsrat-info-${system.name}-${today}.md`,
|
||||||
|
render: () => worksCouncilInfo(system, pack, today, profile, locale),
|
||||||
|
},
|
||||||
|
employeeInfo: {
|
||||||
|
label: t.docEmployeeInfo,
|
||||||
|
file: `mitarbeiterinfo-${system.name}-${today}.md`,
|
||||||
|
render: () => employeeInfo(system, pack, today, profile, locale),
|
||||||
|
},
|
||||||
|
fria: {
|
||||||
|
label: t.docFria,
|
||||||
|
file: `fria-${system.name}-${today}.md`,
|
||||||
|
render: () => friaTemplate(system, pack, today, profile, locale),
|
||||||
|
},
|
||||||
|
incident: {
|
||||||
|
label: t.docIncident,
|
||||||
|
file: `vorfallmeldung-${system.name}-${today}.md`,
|
||||||
|
render: () => incidentReportForm(system, pack, today, profile, locale),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const isProvider = system.answers.role === 'provider';
|
const isProvider = system.answers.role === 'provider';
|
||||||
|
const isDeployer = system.answers.role === 'deployer';
|
||||||
const isHigh = system.classification.riskClass === 'high';
|
const isHigh = system.classification.riskClass === 'high';
|
||||||
const visibleDocs = (Object.keys(docs) as DocKind[]).filter((kind) => {
|
const visibleDocs = (Object.keys(docs) as DocKind[]).filter((kind) => {
|
||||||
if (kind === 'annexIV' || kind === 'conformity') return isHigh && isProvider;
|
if (kind === 'annexIV' || kind === 'conformity') return isHigh && isProvider;
|
||||||
if (kind === 'procurement') return system.answers.role === 'deployer';
|
if (kind === 'procurement') return isDeployer;
|
||||||
|
if (kind === 'worksCouncil') return isDeployer && profile.countries.includes('DE');
|
||||||
|
if (kind === 'employeeInfo') return isDeployer;
|
||||||
|
if (kind === 'fria') return isDeployer && isHigh;
|
||||||
|
if (kind === 'incident') return isHigh;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
86
ai-act-kompass/src/ui/TemplatesView.tsx
Normal file
86
ai-act-kompass/src/ui/TemplatesView.tsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { aiPolicy, trainingPlan, worksAgreement } from '../engine/templatesCompany';
|
||||||
|
import type { AISystem, CompanyProfile } from '../engine/types';
|
||||||
|
import { useI18n } from '../i18n/context';
|
||||||
|
import { printDocument } from '../print/print';
|
||||||
|
import { downloadText, todayISO } from './common';
|
||||||
|
|
||||||
|
interface TemplatesViewProps {
|
||||||
|
readonly systems: readonly AISystem[];
|
||||||
|
readonly profile: CompanyProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TplKind = 'policy' | 'training' | 'worksAgreement';
|
||||||
|
|
||||||
|
/** Unternehmensweite Vorlagen: KI-Richtlinie, Schulungsplan, Betriebsvereinbarung. */
|
||||||
|
export function TemplatesView({ systems, profile }: TemplatesViewProps): JSX.Element {
|
||||||
|
const { t, locale, pack } = useI18n();
|
||||||
|
const today = todayISO();
|
||||||
|
const [preview, setPreview] = useState<TplKind | null>(null);
|
||||||
|
|
||||||
|
const templates: Record<
|
||||||
|
TplKind,
|
||||||
|
{ label: string; desc: string; file: string; render: () => string }
|
||||||
|
> = {
|
||||||
|
policy: {
|
||||||
|
label: t.tplPolicy,
|
||||||
|
desc: t.tplPolicyDesc,
|
||||||
|
file: `ki-richtlinie-${today}.md`,
|
||||||
|
render: () => aiPolicy(pack, today, profile, locale),
|
||||||
|
},
|
||||||
|
training: {
|
||||||
|
label: t.tplTraining,
|
||||||
|
desc: t.tplTrainingDesc,
|
||||||
|
file: `schulungsplan-${today}.md`,
|
||||||
|
render: () => trainingPlan(systems, pack, today, profile, locale),
|
||||||
|
},
|
||||||
|
worksAgreement: {
|
||||||
|
label: t.tplWorksAgreement,
|
||||||
|
desc: t.tplWorksAgreementDesc,
|
||||||
|
file: `betriebsvereinbarung-ki-${today}.md`,
|
||||||
|
render: () => worksAgreement(pack, today, profile, locale),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{t.templatesTitle}</h1>
|
||||||
|
<p className="sub">{t.templatesSub}</p>
|
||||||
|
|
||||||
|
{(Object.keys(templates) as TplKind[]).map((kind) => (
|
||||||
|
<div key={kind} className="panel">
|
||||||
|
<div className="panel-head">
|
||||||
|
<div>
|
||||||
|
<h2 style={{ margin: 0 }}>{templates[kind].label}</h2>
|
||||||
|
<p className="hint" style={{ margin: '4px 0 0' }}>
|
||||||
|
{templates[kind].desc}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="toolbar" style={{ margin: 0 }}>
|
||||||
|
<button className="ghost" onClick={() => setPreview(preview === kind ? null : kind)}>
|
||||||
|
{preview === kind ? t.docHide : t.docShow}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="primary"
|
||||||
|
onClick={() =>
|
||||||
|
printDocument(templates[kind].file.replace(/\.md$/, ''), templates[kind].render(), profile)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t.docPrint}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="ghost"
|
||||||
|
onClick={() => downloadText(templates[kind].file, templates[kind].render())}
|
||||||
|
>
|
||||||
|
.md
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{preview === kind && <pre className="doc-preview">{templates[kind].render()}</pre>}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<p className="disclaimer">{t.templatesHint}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user