feat: Implement dashboard with risk overview, controls, and incidents; add dark mode support
This commit is contained in:
parent
bf0a3c22c0
commit
8afa7363d0
7 changed files with 275 additions and 44 deletions
Binary file not shown.
|
@ -2,7 +2,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: wira-risk-management\n"
|
"Project-Id-Version: wira-risk-management\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-09-09 14:20+0200\n"
|
"POT-Creation-Date: 2025-09-09 21:18+0200\n"
|
||||||
"PO-Revision-Date: 2025-09-09 13:45+0200\n"
|
"PO-Revision-Date: 2025-09-09 13:45+0200\n"
|
||||||
"Last-Translator: Kevin Heyer <kevin@example.com>\n"
|
"Last-Translator: Kevin Heyer <kevin@example.com>\n"
|
||||||
"Language-Team: German\n"
|
"Language-Team: German\n"
|
||||||
|
@ -40,73 +40,55 @@ msgstr "Risikomanagement"
|
||||||
msgid "Risk"
|
msgid "Risk"
|
||||||
msgstr "Risiko"
|
msgstr "Risiko"
|
||||||
|
|
||||||
#: risks/models.py:36
|
#: risks/models.py:36 templates/risks/dashboard.html:85
|
||||||
msgid "Risks"
|
msgid "Risks"
|
||||||
msgstr "Risiken"
|
msgstr "Risiken"
|
||||||
|
|
||||||
#: risks/models.py:39
|
#: risks/models.py:39
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Very low occurs less than once every 5 years"
|
|
||||||
msgid "Very low – occurs less than once every 5 years"
|
msgid "Very low – occurs less than once every 5 years"
|
||||||
msgstr "Sehr niedrig – tritt seltener als einmal in fünf Jahren auf"
|
msgstr "Sehr niedrig – tritt seltener als einmal in fünf Jahren auf"
|
||||||
|
|
||||||
#: risks/models.py:40
|
#: risks/models.py:40
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Low once every 15 years"
|
|
||||||
msgid "Low – once every 1–5 years"
|
msgid "Low – once every 1–5 years"
|
||||||
msgstr "Niedrig – einmal in 1–5 Jahren"
|
msgstr "Niedrig – einmal in 1–5 Jahren"
|
||||||
|
|
||||||
#: risks/models.py:41
|
#: risks/models.py:41
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Likely once per year or more"
|
|
||||||
msgid "Likely – once per year or more"
|
msgid "Likely – once per year or more"
|
||||||
msgstr "Wahrscheinlich – einmal pro Jahr oder öfter"
|
msgstr "Wahrscheinlich – einmal pro Jahr oder öfter"
|
||||||
|
|
||||||
#: risks/models.py:42
|
#: risks/models.py:42
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Very likely multiple times per year/monthly"
|
|
||||||
msgid "Very likely – multiple times per year/monthly"
|
msgid "Very likely – multiple times per year/monthly"
|
||||||
msgstr "Sehr wahrscheinlich – mehrmals pro Jahr/monatlich"
|
msgstr "Sehr wahrscheinlich – mehrmals pro Jahr/monatlich"
|
||||||
|
|
||||||
#: risks/models.py:45
|
#: risks/models.py:45
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Low (< 1,000 minor operational impact)"
|
|
||||||
msgid "Very Low (< 1,000 € – minor operational impact)"
|
msgid "Very Low (< 1,000 € – minor operational impact)"
|
||||||
msgstr "Sehr Gering (< 1.000 € – geringe betriebliche Auswirkungen)"
|
msgstr "Sehr Gering (< 1.000 € – geringe betriebliche Auswirkungen)"
|
||||||
|
|
||||||
#: risks/models.py:46
|
#: risks/models.py:46
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Medium (1,0005,000 local impact)"
|
|
||||||
msgid "Low (1,000–5,000 € – local impact)"
|
msgid "Low (1,000–5,000 € – local impact)"
|
||||||
msgstr "Gering (1.000–5.000 € – lokale Auswirkungen)"
|
msgstr "Gering (1.000–5.000 € – lokale Auswirkungen)"
|
||||||
|
|
||||||
#: risks/models.py:47
|
#: risks/models.py:47
|
||||||
#, fuzzy
|
|
||||||
#| msgid "High (5,00015,000 team-level impact)"
|
|
||||||
msgid "High (5,000–15,000 € – team-level impact)"
|
msgid "High (5,000–15,000 € – team-level impact)"
|
||||||
msgstr "Hoch (5.000–15.000 € – Auswirkungen auf Teamebene)"
|
msgstr "Hoch (5.000–15.000 € – Auswirkungen auf Teamebene)"
|
||||||
|
|
||||||
#: risks/models.py:48
|
#: risks/models.py:48
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Severe (50,000100,000 regional impact)"
|
|
||||||
msgid "Severe (50,000–100,000 € – regional impact)"
|
msgid "Severe (50,000–100,000 € – regional impact)"
|
||||||
msgstr "Schwerwiegend (50.000–100.000 € – regionale Auswirkungen)"
|
msgstr "Schwerwiegend (50.000–100.000 € – regionale Auswirkungen)"
|
||||||
|
|
||||||
#: risks/models.py:49
|
#: risks/models.py:49
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Critical (> 100,000 existential threat)"
|
|
||||||
msgid "Critical (> 100,000 € – existential threat)"
|
msgid "Critical (> 100,000 € – existential threat)"
|
||||||
msgstr "Kritisch (> 100.000 € – existenzielle Bedrohung)"
|
msgstr "Kritisch (> 100.000 € – existenzielle Bedrohung)"
|
||||||
|
|
||||||
#: risks/models.py:52
|
#: risks/models.py:52 templates/risks/dashboard.html:74
|
||||||
msgid "Confidentiality"
|
msgid "Confidentiality"
|
||||||
msgstr "Vertraulichkeit"
|
msgstr "Vertraulichkeit"
|
||||||
|
|
||||||
#: risks/models.py:53
|
#: risks/models.py:53 templates/risks/dashboard.html:79
|
||||||
msgid "Integrity"
|
msgid "Integrity"
|
||||||
msgstr "Integrität"
|
msgstr "Integrität"
|
||||||
|
|
||||||
#: risks/models.py:54
|
#: risks/models.py:54 templates/risks/dashboard.html:84
|
||||||
msgid "Availability"
|
msgid "Availability"
|
||||||
msgstr "Verfügbarkeit"
|
msgstr "Verfügbarkeit"
|
||||||
|
|
||||||
|
@ -209,3 +191,35 @@ msgstr "Meldedatum"
|
||||||
#: risks/models.py:255
|
#: risks/models.py:255
|
||||||
msgid "Reported by"
|
msgid "Reported by"
|
||||||
msgstr "Gemeldet von"
|
msgstr "Gemeldet von"
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:9
|
||||||
|
msgid "Dashboard"
|
||||||
|
msgstr "Dashboard"
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:12
|
||||||
|
msgid "Overview of Risks, Controls and Incidents"
|
||||||
|
msgstr "Übersicht der Risiken, Maßnahmen und Vorfälle"
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:25
|
||||||
|
msgid "Total Risks"
|
||||||
|
msgstr "Restrisiken"
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:33
|
||||||
|
msgid "Residual Risks Needing Review"
|
||||||
|
msgstr "Restrisiken ohne Verifizierung"
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:41
|
||||||
|
msgid "Unread Notifications"
|
||||||
|
msgstr "Ungelesene Nachrichten"
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:48
|
||||||
|
msgid "Controls by Status"
|
||||||
|
msgstr "Maßnahmen nach Status"
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:58
|
||||||
|
msgid "Incidents by Status"
|
||||||
|
msgstr "Vorfälle nach Status"
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:68
|
||||||
|
msgid "Risks by CIA"
|
||||||
|
msgstr "CIA Risiken"
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-09-09 14:20+0200\n"
|
"POT-Creation-Date: 2025-09-09 21:18+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -46,7 +46,7 @@ msgstr ""
|
||||||
msgid "Risk"
|
msgid "Risk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: risks/models.py:36
|
#: risks/models.py:36 templates/risks/dashboard.html:85
|
||||||
msgid "Risks"
|
msgid "Risks"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -86,15 +86,15 @@ msgstr ""
|
||||||
msgid "Critical (> 100,000 € – existential threat)"
|
msgid "Critical (> 100,000 € – existential threat)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: risks/models.py:52
|
#: risks/models.py:52 templates/risks/dashboard.html:74
|
||||||
msgid "Confidentiality"
|
msgid "Confidentiality"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: risks/models.py:53
|
#: risks/models.py:53 templates/risks/dashboard.html:79
|
||||||
msgid "Integrity"
|
msgid "Integrity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: risks/models.py:54
|
#: risks/models.py:54 templates/risks/dashboard.html:84
|
||||||
msgid "Availability"
|
msgid "Availability"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -197,3 +197,35 @@ msgstr ""
|
||||||
#: risks/models.py:255
|
#: risks/models.py:255
|
||||||
msgid "Reported by"
|
msgid "Reported by"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:9
|
||||||
|
msgid "Dashboard"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:12
|
||||||
|
msgid "Overview of Risks, Controls and Incidents"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:25
|
||||||
|
msgid "Total Risks"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:33
|
||||||
|
msgid "Residual Risks Needing Review"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:41
|
||||||
|
msgid "Unread Notifications"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:48
|
||||||
|
msgid "Controls by Status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:58
|
||||||
|
msgid "Incidents by Status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/risks/dashboard.html:68
|
||||||
|
msgid "Risks by CIA"
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -2,10 +2,12 @@ from django.contrib.admin.models import LogEntry
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.db.models import Count, Q
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
from .models import Risk, Control, ResidualRisk, AuditLog, Incident
|
from collections import Counter
|
||||||
|
from .models import Risk, Control, ResidualRisk, AuditLog, Incident, Notification
|
||||||
from .serializers import ControlSerializer, RiskSerializer, ResidualRiskSerializer, UserSerializer, AuditSerializer, IncidentSerializer
|
from .serializers import ControlSerializer, RiskSerializer, ResidualRiskSerializer, UserSerializer, AuditSerializer, IncidentSerializer
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
@ -93,13 +95,9 @@ class IncidentViewSet(viewsets.ModelViewSet):
|
||||||
instance._changed_by = self.request.user
|
instance._changed_by = self.request.user
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Web
|
# Web => Risks, Controls, Incidents
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
@login_required
|
|
||||||
def dashboard(request):
|
|
||||||
return render(request, "risks/dashboard.html")
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def stats(request):
|
def stats(request):
|
||||||
return render(request, "risks/statistics.html")
|
return render(request, "risks/statistics.html")
|
||||||
|
@ -220,3 +218,49 @@ def show_incident(request, id):
|
||||||
).order_by("-action_time")
|
).order_by("-action_time")
|
||||||
|
|
||||||
return render(request, "risks/item_incident.html", {"incident": incident, "logs": logs})
|
return render(request, "risks/item_incident.html", {"incident": incident, "logs": logs})
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Web => Dashboard
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def dashboard(request):
|
||||||
|
# Risikoübersicht
|
||||||
|
risks_total = Risk.objects.count()
|
||||||
|
risks_by_level = Risk.objects.values('level').annotate(count=Count('id'))
|
||||||
|
|
||||||
|
# CIA-Zähler für MultiSelectField
|
||||||
|
risks_cia = Risk.objects.values_list('cia', flat=True)
|
||||||
|
cia_counter = Counter()
|
||||||
|
for cia_list in risks_cia:
|
||||||
|
if isinstance(cia_list, list): # MultiSelectField gibt Liste zurück
|
||||||
|
for c in cia_list:
|
||||||
|
cia_counter[c] += 1
|
||||||
|
elif cia_list: # Falls irgendwie noch ein String drin ist
|
||||||
|
cia_counter[cia_list] += 1
|
||||||
|
|
||||||
|
# Residualrisiken
|
||||||
|
residual_review_required = ResidualRisk.objects.filter(review_required=True).count()
|
||||||
|
|
||||||
|
# Kontrollen
|
||||||
|
controls_by_status = Control.objects.values('status').annotate(count=Count('id'))
|
||||||
|
|
||||||
|
# Incidents
|
||||||
|
incidents_status = Incident.objects.values('status').annotate(count=Count('id'))
|
||||||
|
|
||||||
|
# Benachrichtigungen
|
||||||
|
notifications_unread = Notification.objects.filter(user=request.user, read=False).count()
|
||||||
|
|
||||||
|
print(type(cia_counter), cia_counter)
|
||||||
|
|
||||||
|
# Context für Template
|
||||||
|
context = {
|
||||||
|
'risks_total': risks_total,
|
||||||
|
'risks_by_level': risks_by_level,
|
||||||
|
'risks_by_cia': dict(cia_counter), # <-- hier Counter in dict umwandeln
|
||||||
|
'residual_review_required': residual_review_required,
|
||||||
|
'controls_by_status': controls_by_status,
|
||||||
|
'incidents_status': incidents_status,
|
||||||
|
'notifications_unread': notifications_unread,
|
||||||
|
}
|
||||||
|
return render(request, 'risks/dashboard.html', context)
|
||||||
|
|
|
@ -141,4 +141,23 @@
|
||||||
|
|
||||||
.content li+li {
|
.content li+li {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* DARK MODE */
|
||||||
|
|
||||||
|
/* static/css/custom.css */
|
||||||
|
body.dark-mode {
|
||||||
|
background-color: #121212;
|
||||||
|
color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark-mode .box {
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optional: Buttons, Links etc. anpassen */
|
||||||
|
body.dark-mode a {
|
||||||
|
color: #bb86fc;
|
||||||
}
|
}
|
|
@ -27,17 +27,10 @@
|
||||||
|
|
||||||
<div id="mainNavbar" class="navbar-menu">
|
<div id="mainNavbar" class="navbar-menu">
|
||||||
<div class="navbar-start">
|
<div class="navbar-start">
|
||||||
<div class="navbar-item has-dropdown is-hoverable">
|
|
||||||
<a class="navbar-link">Allgemein</a>
|
|
||||||
<div class="navbar-dropdown">
|
|
||||||
<a class="navbar-item" href="/risks/index">Dashboard</a>
|
|
||||||
<a class="navbar-item" href="/risks/stats">Statistiken</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="navbar-item has-dropdown is-hoverable">
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
<a class="navbar-link">Risikomanagement</a>
|
<a class="navbar-link">Risikomanagement</a>
|
||||||
<div class="navbar-dropdown">
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="/risks/index">Dashboard</a>
|
||||||
<a class="navbar-item" href="/risks/list_risks">Risikoanalyse</a>
|
<a class="navbar-item" href="/risks/list_risks">Risikoanalyse</a>
|
||||||
<a class="navbar-item" href="/risks/list_controls">Maßnahmen</a>
|
<a class="navbar-item" href="/risks/list_controls">Maßnahmen</a>
|
||||||
<a class="navbar-item" href="/risks/list_incidents">Vorfälle</a>
|
<a class="navbar-item" href="/risks/list_incidents">Vorfälle</a>
|
||||||
|
@ -77,6 +70,11 @@
|
||||||
<hr class="navbar-divider">
|
<hr class="navbar-divider">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Dark Mode Toggle -->
|
||||||
|
<button id="dark-mode-toggle" class="button is-small is-light">
|
||||||
|
🌙 Dark Mode
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- Logout als POST über Hidden-Form -->
|
<!-- Logout als POST über Hidden-Form -->
|
||||||
<a class="navbar-item" href="#"
|
<a class="navbar-item" href="#"
|
||||||
onclick="document.getElementById('logout-form').submit(); return false;">
|
onclick="document.getElementById('logout-form').submit(); return false;">
|
||||||
|
@ -123,5 +121,38 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const toggleButton = document.getElementById('dark-mode-toggle');
|
||||||
|
|
||||||
|
// Dark Mode aus localStorage laden
|
||||||
|
if (localStorage.getItem('darkMode') === 'enabled') {
|
||||||
|
document.body.classList.add('dark-mode');
|
||||||
|
toggleButton.textContent = '☀️ Light Mode';
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleButton.addEventListener('click', () => {
|
||||||
|
document.body.classList.toggle('dark-mode');
|
||||||
|
if (document.body.classList.contains('dark-mode')) {
|
||||||
|
localStorage.setItem('darkMode', 'enabled');
|
||||||
|
toggleButton.textContent = '☀️ Light Mode';
|
||||||
|
} else {
|
||||||
|
localStorage.setItem('darkMode', 'disabled');
|
||||||
|
toggleButton.textContent = '🌙 Dark Mode';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Burger Menu für mobile
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const burger = document.querySelector('.navbar-burger');
|
||||||
|
const menu = document.getElementById(burger.dataset.target);
|
||||||
|
|
||||||
|
burger.addEventListener('click', () => {
|
||||||
|
burger.classList.toggle('is-active');
|
||||||
|
menu.classList.toggle('is-active');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,4 +1,95 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
Dashboard
|
<!-- Hero Section -->
|
||||||
{% endblock %}
|
<section class="hero is-small is-bold">
|
||||||
|
<div class="hero-body">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="title is-1 has-text-black">
|
||||||
|
{% trans "Dashboard" %}
|
||||||
|
</h1>
|
||||||
|
<h2 class="subtitle is-4 has-text-black">
|
||||||
|
{% trans "Overview of Risks, Controls and Incidents" %}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="columns is-multiline">
|
||||||
|
<!-- Gesamt-Risiken -->
|
||||||
|
<div class="column is-one-quarter">
|
||||||
|
<div class="box has-background-control-mid has-text-centered">
|
||||||
|
<h2 class="title is-4">{{ risks_total }}</h2>
|
||||||
|
<p>{% trans "Total Risks" %}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Residual-Risiken -->
|
||||||
|
<div class="column is-one-quarter">
|
||||||
|
<div class="box has-text-centered {% if residual_review_required > 0 %}has-background-control-high{% else %}has-background-control-low{% endif %}">
|
||||||
|
<h2 class="title is-4">{{ residual_review_required }}</h2>
|
||||||
|
<p>{% trans "Residual Risks Needing Review" %}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Ungelesene Notifications -->
|
||||||
|
<div class="column is-one-quarter">
|
||||||
|
<div class="box has-background-control-mid has-text-centered">
|
||||||
|
<h2 class="title is-4">{{ notifications_unread }}</h2>
|
||||||
|
<p>{% trans "Unread Notifications" %}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Controls by Status -->
|
||||||
|
<div class="box">
|
||||||
|
<h2 class="subtitle">{% trans "Controls by Status" %}</h2>
|
||||||
|
<ul>
|
||||||
|
{% for status in controls_by_status %}
|
||||||
|
<li>{{ status.status }}: {{ status.count }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Incidents by Status -->
|
||||||
|
<div class="box">
|
||||||
|
<h2 class="subtitle">{% trans "Incidents by Status" %}</h2>
|
||||||
|
<ul>
|
||||||
|
{% for incident in incidents_status %}
|
||||||
|
<li>{{ incident.status }}: {{ incident.count }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Risks by CIA -->
|
||||||
|
<div class="box">
|
||||||
|
<h2 class="subtitle">{% trans "Risks by CIA" %}</h2>
|
||||||
|
<div class="columns is-multiline">
|
||||||
|
{% for cia, count in risks_by_cia.items %}
|
||||||
|
<div class="column is-one-quarter">
|
||||||
|
{% if cia == '1' %}
|
||||||
|
<div class="box has-background-control-verylow has-text-centered">
|
||||||
|
<h3 class="title is-5">{% trans "Confidentiality" %}</h3>
|
||||||
|
<p>{{ count }} Risks</p>
|
||||||
|
</div>
|
||||||
|
{% elif cia == '2' %}
|
||||||
|
<div class="box has-background-control-mid has-text-centered">
|
||||||
|
<h3 class="title is-5">{% trans "Integrity" %}</h3>
|
||||||
|
<p>{{ count }} Risks</p>
|
||||||
|
</div>
|
||||||
|
{% elif cia == '3' %}
|
||||||
|
<div class="box has-background-control-high has-text-centered">
|
||||||
|
<h3 class="title is-5">{% trans "Availability" %}</h3>
|
||||||
|
<p>{{ count }} {% trans "Risks" %}</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
|
|
Loading…
Add table
Reference in a new issue