From 8afa7363d01712be0e2a56c1b4b1a9e50bfad75e Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 9 Sep 2025 21:34:03 +0200 Subject: [PATCH] feat: Implement dashboard with risk overview, controls, and incidents; add dark mode support --- locale/de/LC_MESSAGES/django.mo | Bin 1968 -> 3555 bytes locale/de/LC_MESSAGES/django.po | 60 ++++++++++++-------- locale/en/LC_MESSAGES/django.po | 42 ++++++++++++-- risks/views.py | 56 +++++++++++++++++-- static/css/design.css | 19 +++++++ templates/base.html | 47 +++++++++++++--- templates/risks/dashboard.html | 95 +++++++++++++++++++++++++++++++- 7 files changed, 275 insertions(+), 44 deletions(-) diff --git a/locale/de/LC_MESSAGES/django.mo b/locale/de/LC_MESSAGES/django.mo index 0584151e16258e0a5e42c405d6b8f04fca053d71..e9b04536e03a0195dac69ebfbd3451bf94f273ca 100644 GIT binary patch literal 3555 zcmZvdON<;x8GtJgLbBk1O%ekkqyoY0l5Kl-okh#~#oF1}8+*Oh?mD@FYG!I?itVW$ zRoCo#EQBJ2lv6G|4wg_76iX2gA-Gry$|YHa146(7aX=`C2si*zgpeXQfcXCIp2r$m z)BRO-*ZBLm+n;ReeiqmUieBizXk7R{u;ao zz6n1Fe_g%*1H6m*pQ`VFgCAo4E_^?{3#B4|FT4*PhKJxRoPuZIG58b|{nz0E_zn00 z_|5A33)TEr6@L#!@1LQ_y#qf2|5eQoV3cNl7*4}Q_(^CWKlLn+8TcF&JAYd7RVaF2 zhqC`KpxE;}C~^5Cd^zOn48J?+2mm{~!;xQqz#7 z>NpfX&OlP8z63uEEfo13D0Y4g%KGQwL3jh6fj@*2*S}WZ{|+S{|ArE``v{`QJql%g z4NBg6@HD&$NmpUpEPR+-t_AKR#i;bF8Wy!6WAWkB)l0EobUw;0XIC!qS>hx66%#0RCbrADkr>D&HT^kmIiqqtSfc0~(~RF!%_Vn|ua9v{ zJPvb9zCO+^@x$$+Zb5NggrAiM*9^DBRO0stx8y@DH@nW4PnXAFqRW`(xs7VM-*hp} z+M_|PmUoOxP2Hs~4%9glZ95AL(k!=4buR1nQX7krZ@H%R(V22(=3~fGRus%f1HEDM zUgmRGPwn_+eV{j^iG9`u%O*{2B10El!=!rpw4STgW^1*Ye*5b`(r-WeEp4x1d%2yC z9g&$)Pnf(@&rE1CidF~P%YxYUgv}el^$I^PxOPWRFNiMG%oT%?(r8ULN$rkJwd?jw z1D&g0UkO<|*gThUZ#-@k?((u6$meRL$KI@Z0)`s{b;)hpbWqry`G(a!8}z`MptGR6 zS+MF-wyUS-D#P~Mm1Yu!p-Hk(LQtr|*f7v@%ohrExmxd8kJIaX*l~7OXDz+q^6h+9 z4|Ac7Z|ae?>Pl+7NZ@Bf+}yBVu?>maMioH(VVt{WA1g%H{+UQje9ZM_Yn#ru^@bG# zR4IX&UNhdbZI`qaFA)^O^!l!BUycdgxz%O0xw(#ott@oQ^4t_ZEr~0tKr(k*Jp|jP zj3=VBMIl?YmBq^DTwV2)NmF0WqH9rBQVzWAHHYQ)v<-Z~5(WcZq~oM65;7_f4i4$c z$Xy{mv0WjPj&t1Yr_pgXbaY*tkFtHN%X#RegL02_n8`+?A99@%eTv!m$o-^sWmg3B)kzjv_l%W-VFE+pd|M?YHw}D_hc2SJo5BhX^KeJeKr)ZGIuC zeSx2zJHGVz!eh1hT5al*$z!q=jL%aOv#_KuBIWf3JD^}sZp-A0_L`A%f3%TxPb1*l zebSn2ae9{Yc}vdx=n^~9AxuS?UYO;=r% zricwyQ#|^An&NfGVyn;EX2?1?I@uZ5Y}&Mxe7~#Cit)j^y2Ro_CMp+1UC|f!>0R#U zl-f4ytk$-SFVJ!3in z%dGQ7jHJ~u?sC}ZzWOP?>a0mI?%ird8^}nIW;0D$6}A^Xj81HtZ5v`S>A{S;%qb79 zUH*}jswwfZiHd%5yAn{}^86?6eYY;>tiy=#ABbL+O#Bem_!=xzRGJC$+fy}iIuHOLELUcA;0^-HPb~e rMMLkcsz!RRDb8KFr=O?-w8yA(n_?|V7O|@4C=5=iZxw}CmcRNh#LuY- delta 1042 zcmXxjO-PhM9LMp$=K8v=wPt2oZf*)5TDsUxaA{3EgplSbsHL5ahxV<m6u zLW|iXR$3`Y(+A8Fn8y^(`t}lb(!PRS=rD*8QkFl5b8g}3&GJnkdm?rLi;oc0c*792+{aK<}{8b5`KcL5du5~f(+u2JE!I)!!^ zp-y}sHQ|x(U-SK&sExix1>VLyxAxJ$Z)F#H--(LThq|Fb-=9U@WKR98Z&OsX&@5_a z3#g40H2`m+0^Y-u_z*SzmG6Ixy7~{Oz@NO|P&e@#6=xSU@2_tsc)6+py%chQLIX!o z0VhxkpF>T|BXjK{9>PWBF-M_=t9Te!P$z$aI>>X>{0-!y>^ParvZMO_m2{0dnZASl zq(DjoO|0KLf@xnDV`Obc3lV>BBigi*4x=08li5orC^O_NSs#1C&Np{P-9}aTbjo|$ zt5Xk>kCFAq(CrM7mHOiKwx`+96(d*56eBB8$%L*^59zRiTfQC!jVP$s;xCC@Dsnf% zAi87o4Oerec&oh-r#r?c78d3+bG33ktQIxY7K5nb>~f>DA9cxvK{;@xcT1hWT6$)} z<;YbkQKndp*SiPf-R`sfbG2sdosGz5!kzc!a?M50lGE|kWG;T0Opg-NRV;rqx?QXU P%Yh3u-I{^#s%ifL0SkF0 diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index cbe4abe..ac4709e 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: wira-risk-management\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" "Last-Translator: Kevin Heyer \n" "Language-Team: German\n" @@ -40,73 +40,55 @@ msgstr "Risikomanagement" msgid "Risk" msgstr "Risiko" -#: risks/models.py:36 +#: risks/models.py:36 templates/risks/dashboard.html:85 msgid "Risks" msgstr "Risiken" #: 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" msgstr "Sehr niedrig – tritt seltener als einmal in fünf Jahren auf" #: risks/models.py:40 -#, fuzzy -#| msgid "Low once every 15 years" msgid "Low – once every 1–5 years" msgstr "Niedrig – einmal in 1–5 Jahren" #: risks/models.py:41 -#, fuzzy -#| msgid "Likely once per year or more" msgid "Likely – once per year or more" msgstr "Wahrscheinlich – einmal pro Jahr oder öfter" #: risks/models.py:42 -#, fuzzy -#| msgid "Very likely multiple times per year/monthly" msgid "Very likely – multiple times per year/monthly" msgstr "Sehr wahrscheinlich – mehrmals pro Jahr/monatlich" #: risks/models.py:45 -#, fuzzy -#| msgid "Low (< 1,000 minor operational impact)" msgid "Very Low (< 1,000 € – minor operational impact)" msgstr "Sehr Gering (< 1.000 € – geringe betriebliche Auswirkungen)" #: risks/models.py:46 -#, fuzzy -#| msgid "Medium (1,0005,000 local impact)" msgid "Low (1,000–5,000 € – local impact)" msgstr "Gering (1.000–5.000 € – lokale Auswirkungen)" #: risks/models.py:47 -#, fuzzy -#| msgid "High (5,00015,000 team-level impact)" msgid "High (5,000–15,000 € – team-level impact)" msgstr "Hoch (5.000–15.000 € – Auswirkungen auf Teamebene)" #: risks/models.py:48 -#, fuzzy -#| msgid "Severe (50,000100,000 regional impact)" msgid "Severe (50,000–100,000 € – regional impact)" msgstr "Schwerwiegend (50.000–100.000 € – regionale Auswirkungen)" #: risks/models.py:49 -#, fuzzy -#| msgid "Critical (> 100,000 existential threat)" msgid "Critical (> 100,000 € – existential threat)" msgstr "Kritisch (> 100.000 € – existenzielle Bedrohung)" -#: risks/models.py:52 +#: risks/models.py:52 templates/risks/dashboard.html:74 msgid "Confidentiality" msgstr "Vertraulichkeit" -#: risks/models.py:53 +#: risks/models.py:53 templates/risks/dashboard.html:79 msgid "Integrity" msgstr "Integrität" -#: risks/models.py:54 +#: risks/models.py:54 templates/risks/dashboard.html:84 msgid "Availability" msgstr "Verfügbarkeit" @@ -209,3 +191,35 @@ msgstr "Meldedatum" #: risks/models.py:255 msgid "Reported by" 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" diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 84faae8..048571b 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\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" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -46,7 +46,7 @@ msgstr "" msgid "Risk" msgstr "" -#: risks/models.py:36 +#: risks/models.py:36 templates/risks/dashboard.html:85 msgid "Risks" msgstr "" @@ -86,15 +86,15 @@ msgstr "" msgid "Critical (> 100,000 € – existential threat)" msgstr "" -#: risks/models.py:52 +#: risks/models.py:52 templates/risks/dashboard.html:74 msgid "Confidentiality" msgstr "" -#: risks/models.py:53 +#: risks/models.py:53 templates/risks/dashboard.html:79 msgid "Integrity" msgstr "" -#: risks/models.py:54 +#: risks/models.py:54 templates/risks/dashboard.html:84 msgid "Availability" msgstr "" @@ -197,3 +197,35 @@ msgstr "" #: risks/models.py:255 msgid "Reported by" 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 "" diff --git a/risks/views.py b/risks/views.py index fab38da..1a994e7 100644 --- a/risks/views.py +++ b/risks/views.py @@ -2,10 +2,12 @@ from django.contrib.admin.models import LogEntry from django.contrib.auth import get_user_model from django.contrib.auth.decorators import login_required from django.contrib.contenttypes.models import ContentType +from django.db.models import Count, Q from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated 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 User = get_user_model() @@ -93,13 +95,9 @@ class IncidentViewSet(viewsets.ModelViewSet): instance._changed_by = self.request.user # --------------------------------------------------------------------------- -# Web +# Web => Risks, Controls, Incidents # --------------------------------------------------------------------------- -@login_required -def dashboard(request): - return render(request, "risks/dashboard.html") - @login_required def stats(request): return render(request, "risks/statistics.html") @@ -220,3 +218,49 @@ def show_incident(request, id): ).order_by("-action_time") 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) diff --git a/static/css/design.css b/static/css/design.css index dcbc5d7..b29d3a4 100644 --- a/static/css/design.css +++ b/static/css/design.css @@ -141,4 +141,23 @@ .content li+li { 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; } \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index e9fea9d..7be4056 100644 --- a/templates/base.html +++ b/templates/base.html @@ -27,17 +27,10 @@