/** * 🔷 IT-PRAXISPORTAL – UML DIAGRAMM STUDIO (KLASSEN, SEQUENZ, USE CASE) * Standard für IHK FIAE & FISI: * - Klassendiagramme (Attribute +, -, #, Methoden, Vererbung, Assoziation, Komposition) * - Sequenzdiagramme (Lebenslinien, synchrone/asynchrone Aufrufe, Return) * - Use Case Diagramme (Akteure, Systemgrenze, <>, <>) * Synchronisierte Mermaid-Live-Vorschau mit interaktiver Generierung! */ class UMLEditor { constructor(containerId, options = {}) { this.container = typeof containerId === 'string' ? document.getElementById(containerId) : containerId; this.options = options; this.diagramType = 'class'; // 'class' | 'sequence' | 'usecase' this.mermaidCode = ''; this.init(); } init() { if (!this.container) return; this.container.innerHTML = `

UML Quellcode (Mermaid / PlantUML Standard)

💡 Tipp: Bearbeite den Diagrammcode direkt links in der Textbox oder lade eine IHK-Vorlage. Klicke auf Neu Rendern zur Aktualisierung.
`; this.bindEvents(); this.loadTemplate('class_ecommerce'); } bindEvents() { document.getElementById('uml-select-type').addEventListener('change', (e) => { this.diagramType = e.target.value; if (this.diagramType === 'class') this.loadTemplate('class_ecommerce'); else if (this.diagramType === 'sequence') this.loadTemplate('seq_login'); else if (this.diagramType === 'usecase') this.loadTemplate('uc_hotel'); }); document.getElementById('uml-select-template').addEventListener('change', (e) => { if (e.target.value) this.loadTemplate(e.target.value); }); document.getElementById('uml-btn-render').addEventListener('click', () => { this.mermaidCode = document.getElementById('uml-code-input').value; this.render(); }); document.getElementById('uml-btn-clear').addEventListener('click', () => { if (confirm('Möchtest du das aktuelle UML-Diagramm leeren?')) { document.getElementById('uml-code-input').value = 'classDiagram\n class MeineKlasse {\n +int id\n }'; this.mermaidCode = document.getElementById('uml-code-input').value; this.render(); } }); document.getElementById('uml-btn-export-svg').addEventListener('click', () => this.exportSvg()); } loadTemplate(tpl) { let code = ''; if (tpl === 'class_ecommerce') { this.diagramType = 'class'; document.getElementById('uml-select-type').value = 'class'; code = `classDiagram direction TB class Benutzer { <> #int id #String email #String passwortHash +anmelden() boolean +abmelden() void } class Kunde { -String vorname -String nachname -String lieferadresse +bestellen(Warenkorb w) Bestellung } class Administrator { -int rechteStufe +artikelAnlegen(Artikel a) void +sperreBenutzer(int userId) void } class Bestellung { -int bestellId -Date datum -String status +berechneGesamtsumme() double } class Bestellposition { -int menge -double einzelpreis } class Artikel { -int artikelId -String bezeichnung -double preis -int bestand } class IPaymentMethod { <> +processPayment(double amount) boolean } Benutzer <|-- Kunde : Generalisierung Benutzer <|-- Administrator : Generalisierung Kunde "1" --> "*" Bestellung : gibt auf Bestellung "1" *-- "1..*" Bestellposition : Komposition Bestellposition "*" --> "1" Artikel : Assoziation Bestellung ..> IPaymentMethod : Realisierung`; } else if (tpl === 'class_ticket') { this.diagramType = 'class'; document.getElementById('uml-select-type').value = 'class'; code = `classDiagram class Ticket { -int ticketNummer -String titel -String prioritaet -Date erstelltAm +statusAendern(String status) void +zuweisen(Mitarbeiter m) void } class Mitarbeiter { -int mitarbeiterId -String name -String abteilung } class HardwareAsset { -String seriennummer -String hostname -String standort } Ticket "*" --> "1" Mitarbeiter : Melder Ticket "*" --> "0..1" Mitarbeiter : Bearbeiter Ticket "*" --> "1" HardwareAsset : Betrifft`; } else if (tpl === 'seq_login') { this.diagramType = 'sequence'; document.getElementById('uml-select-type').value = 'sequence'; code = `sequenceDiagram autonumber actor User as Benutzer participant Browser as Client / Browser participant AuthAPI as Auth-Service participant DB as Datenbank participant Mail as 2FA-Mail-Service User->>Browser: Login-Formular ausfüllen (Email, Passwort) Browser->>AuthAPI: POST /api/v1/auth/login activate AuthAPI AuthAPI->>DB: SELECT * FROM users WHERE email = ? activate DB DB-->>AuthAPI: User-Record mit Password-Hash deactivate DB AuthAPI->>AuthAPI: Passwort-Hash validieren (Argon2id) alt Passwort korrekt AuthAPI->>Mail: 2FA-Einmalcode senden (SMS/Email) AuthAPI-->>Browser: 200 OK (Requires 2FA) User->>Browser: 2FA-Code eingeben Browser->>AuthAPI: POST /api/v1/auth/verify-otp AuthAPI-->>Browser: 200 OK (JWT Access Token & Cookie) else Passwort falsch AuthAPI-->>Browser: 401 Unauthorized (Ungültige Anmeldedaten) end deactivate AuthAPI`; } else if (tpl === 'seq_order') { this.diagramType = 'sequence'; document.getElementById('uml-select-type').value = 'sequence'; code = `sequenceDiagram autonumber actor Kunde participant Webshop participant Lager as ERP / Lager participant Bank as Payment-Gateway Kunde->>Webshop: "Jetzt kaufen" klicken activate Webshop Webshop->>Lager: Bestand prüfen & reservieren activate Lager Lager-->>Webshop: Bestand bestätigt deactivate Lager Webshop->>Bank: Kreditkarte autorisieren (129.90 EUR) activate Bank Bank-->>Webshop: Zahlung genehmigt (Auth-Token) deactivate Bank Webshop->>Lager: Kommissionierauftrag erstellen Webshop-->>Kunde: Bestellbestätigung per E-Mail deactivate Webshop`; } else if (tpl === 'uc_hotel') { this.diagramType = 'usecase'; document.getElementById('uml-select-type').value = 'usecase'; code = `flowchart LR subgraph Hotelverwaltungssystem [Systemgrenze: Hotel-Reservierung] UC1((Zimmer suchen)) UC2((Zimmer reservieren)) UC3((Zahlung durchführen)) UC4((Reservierung stornieren)) UC5((Zimmerrechnung erstellen)) end Gast[Gast / Kunde] --> UC1 Gast --> UC2 Gast --> UC4 Rezeption[Rezeptionist] --> UC2 Rezeption --> UC4 Rezeption --> UC5 UC2 -.->|<>| UC3 UC4 -.->|<>| UC5`; } document.getElementById('uml-code-input').value = code; this.mermaidCode = code; this.render(); } async render() { const target = document.getElementById('uml-render-target'); if (!target) return; // Load mermaid if not already on window if (!window.mermaid) { target.innerHTML = `
Lade Diagramm-Renderer...
`; await this.loadMermaidScript(); } try { window.mermaid.initialize({ startOnLoad: false, theme: 'dark', themeVariables: { darkMode: true, background: '#090d16', primaryColor: '#1e293b', primaryTextColor: '#f8fafc', primaryBorderColor: '#38bdf8', lineColor: '#38bdf8', secondaryColor: '#0f172a', tertiaryColor: '#1e1b4b' } }); const id = 'mermaid_svg_' + Date.now(); const { svg } = await window.mermaid.render(id, this.mermaidCode); target.innerHTML = svg; } catch (err) { target.innerHTML = `
⚠️ Syntaxfehler im UML-Code:
${err.message || err}
`; } } loadMermaidScript() { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js'; script.onload = () => resolve(); script.onerror = () => reject(new Error('Mermaid CDN konnte nicht geladen werden.')); document.head.appendChild(script); }); } exportSvg() { const svgEl = document.querySelector('#uml-render-target svg'); if (!svgEl) { alert('Kein gerendertes Diagramm zum Exportieren vorhanden.'); return; } const serializer = new XMLSerializer(); const source = serializer.serializeToString(svgEl); const blob = new Blob([source], { type: 'image/svg+xml;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `uml_${this.diagramType}_ihk.svg`; a.click(); URL.revokeObjectURL(url); } } window.UMLEditor = UMLEditor;