diff --git a/ui/app.js b/ui/app.js index a42e6cb..f23760b 100644 --- a/ui/app.js +++ b/ui/app.js @@ -43,7 +43,7 @@ const SECTION_META = [ { id:'suppliers', labelKey:'nav_suppliers', label:'Supply Chain', icon:'ph-truck', minRole:'contentowner', functions:['ciso','revision'] }, { id:'bcm', labelKey:'nav_bcm', label:'Business Continuity',icon:'ph-heartbeat', minRole:'contentowner', functions:['ciso','revision'] }, { id:'governance', labelKey:'nav_governance', label:'Governance', icon:'ph-chalkboard-teacher', minRole:'contentowner', functions:['ciso','dso','revision','qmb'] }, - { id:'policy-acks', labelKey:'nav_policyAcks', label:'Richtlinien-Bestätigungen', icon:'ph-check-circle', minRole:'contentowner', functions:['ciso','revision','qmb'] }, + { id:'policy-acks', labelKey:'nav_policyAcks', label:'Policy Acknowledgements', icon:'ph-check-circle', minRole:'contentowner', functions:['ciso','revision','qmb'] }, { id:'reports', labelKey:'nav_reports', label:'Reports', icon:'ph-chart-line', minRole:'contentowner', functions:['ciso','dso','revision','qmb'] }, { id:'settings', labelKey:'nav_settings', label:'Settings', icon:'ph-gear', minRole:'contentowner', functions:['ciso','dso','revision','qmb'] }, // ── Nur Admin ──────────────────────────────────────────────────────────── @@ -137,7 +137,7 @@ function renderLifecycleActions(template) { if (available.length === 0) { bar.style.display = 'none'; return } bar.style.display = 'flex' - bar.innerHTML = `Aktion:` + bar.innerHTML = `${t('common_action')}:` available.forEach(tr => { const btn = document.createElement('button') btn.className = `btn-lifecycle ${tr.cls}` @@ -206,20 +206,20 @@ function _initSemanticSearch() { } async function _doSearch(q) { - _open(`
Suche…
`) + _open(`
${t('search')}
`) try { const res = await fetch(`/api/ai/search?q=${encodeURIComponent(q)}`, { credentials: 'include' }) if (!res.ok) throw new Error('API error') const data = await res.json() _results = data.results || [] if (!_results.length) { - _open(`
No results for ${q}
`) + _open(`
${t('search_noResultsFor')} ${q}
`) return } const mode = data.mode || 'keyword' const modeBadge = mode === 'semantic' - ? 'KI' - : 'Keyword' + ? `${t('search_ai')}` + : `${t('search_keyword')}` const rows = _results.map((r, i) => { const icon = TYPE_ICONS[r.type] || 'ph-magnifying-glass' return `
@@ -233,10 +233,10 @@ function _initSemanticSearch() {
` }).join('') - _open(`
Suchergebnisse (${_results.length})${modeBadge}
${rows}`) + _open(`
${t('search_results')} (${_results.length})${modeBadge}
${rows}`) _activeIdx = -1 } catch { - _open(`
AI search unavailable
`) + _open(`
${t('search_aiUnavailable')}
`) } } @@ -938,9 +938,9 @@ function removeAllDynamicPanels() { // ── Reports ───────────────────────────────────────────────────────── // ── Findings: Severity- und Status-Labels ───────────────────────────────────── -const FINDING_SEVERITY_LABELS = { critical:'Kritisch', high:'Hoch', medium:'Mittel', low:'Niedrig', observation:'Hinweis' } -const FINDING_STATUS_LABELS = { open:'Offen', in_progress:'In Bearbeitung', resolved:'Behoben', accepted:'Akzeptiert' } -const FINDING_ACT_STATUS_LABELS = { open:'Offen', in_progress:'In Bearbeitung', done:'Erledigt' } +const FINDING_SEVERITY_LABELS = { critical:t('findings_critical'), high:t('findings_high'), medium:t('findings_medium'), low:t('findings_low'), observation:t('findings_observation') } +const FINDING_STATUS_LABELS = { open:t('findings_statusOpen'), in_progress:t('findings_statusInProgress'), resolved:t('findings_statusResolved'), accepted:t('findings_statusAccepted') } +const FINDING_ACT_STATUS_LABELS = { open:t('findings_statusOpen'), in_progress:t('findings_statusInProgress'), done:t('findings_statusDone') } const FINDING_SEVERITY_COLOR = { critical:'#f87171', high:'#fb923c', medium:'#fbbf24', low:'#4ade80', observation:'#60a5fa' } const FINDING_STATUS_COLOR = { open:'#f87171', in_progress:'#fbbf24', resolved:'#4ade80', accepted:'#60a5fa' } @@ -949,15 +949,15 @@ let _findingsTab = 'list' // 'list' | 'open' | 'resolved' let _findingFormBack = 'list' const REPORT_TYPES = [ - { id: 'compliance', label: 'Compliance', icon: 'ph-shield-check', desc: 'Implementation rate per entity and framework', needsEntity: true }, - { id: 'framework', label: 'Framework Coverage', icon: 'ph-chart-bar', desc: 'Controls per framework: applicable / implemented / gap', needsEntity: false }, - { id: 'gap', label: 'Gap Analysis', icon: 'ph-warning-circle', desc: 'Controls without linked policy templates', needsEntity: true }, - { id: 'templates', label: 'Template Overview', icon: 'ph-files', desc: 'All templates of an entity by status', needsEntity: true }, - { id: 'reviews', label: 'Due Reviews', icon: 'ph-calendar-x', desc: 'Templates with overdue or upcoming review date', needsEntity: false }, - { id: 'matrix', label: 'Compliance Matrix', icon: 'ph-table', desc: 'Control × Entity traffic light overview', needsEntity: false }, - { id: 'audit', label: 'Audit Trail', icon: 'ph-clock-counter-clockwise', desc: 'Status changes on templates in a given period', needsEntity: false }, - { id: 'findings', label: 'Audit Findings', icon: 'ph-magnifying-glass', desc: 'Audit findings with action plans (IST→SOLL→Risk→Recommendation)', needsEntity: false }, - { id: 'risks', label: 'Risk Register', icon: 'ph-warning', desc: 'All approved risks incl. CVSS scores from scan imports', needsEntity: true }, + { id: 'compliance', labelKey: 'reports_compliance', icon: 'ph-shield-check', descKey: 'reports_descCompliance', needsEntity: true }, + { id: 'framework', labelKey: 'reports_fw', icon: 'ph-chart-bar', descKey: 'reports_descFramework', needsEntity: false }, + { id: 'gap', labelKey: 'reports_gap', icon: 'ph-warning-circle', descKey: 'reports_descGap', needsEntity: true }, + { id: 'templates', labelKey: 'reports_templates', icon: 'ph-files', descKey: 'reports_descTemplates', needsEntity: true }, + { id: 'reviews', labelKey: 'reports_reviews', icon: 'ph-calendar-x', descKey: 'reports_descReviews', needsEntity: false }, + { id: 'matrix', labelKey: 'reports_matrix', icon: 'ph-table', descKey: 'reports_descMatrix', needsEntity: false }, + { id: 'audit', labelKey: 'reports_audit', icon: 'ph-clock-counter-clockwise', descKey: 'reports_descAudit', needsEntity: false }, + { id: 'findings', labelKey: 'findings_title', icon: 'ph-magnifying-glass', descKey: 'reports_descFindings', needsEntity: false }, + { id: 'risks', labelKey: 'risk_register', icon: 'ph-warning', descKey: 'reports_descRisks', needsEntity: true }, ] let _reportEntities = [] @@ -972,14 +972,14 @@ async function renderReports() { container.innerHTML = `
-

Reports & Compliance

+

${t('reports_title')}

- -
@@ -994,7 +994,7 @@ async function renderReports() { async function switchReportsMainTab(tab) { _reportsMainTab = tab document.querySelectorAll('#reportsContainer .training-tab').forEach(b => - b.classList.toggle('active', b.textContent.trim().toLowerCase().includes(tab === 'reports' ? 'bericht' : 'feststellung')) + b.classList.toggle('active', b.dataset.tab === tab) ) const content = dom('reportsMainContent') if (!content) return @@ -1014,25 +1014,25 @@ function renderReportsTabContent(container) { ${REPORT_TYPES.map(rt => `
-

${rt.label}

-

${rt.desc}

+

${t(rt.labelKey)}

+

${t(rt.descKey)}

`).join('')}