Rework Frontend UI
This commit is contained in:
parent
9d02badf14
commit
324347e849
7 changed files with 594 additions and 548 deletions
|
@ -10,6 +10,17 @@ _LIKELIHOOD_LABELS = dict(Risk.LIKELIHOOD_CHOICES)
|
||||||
_IMPACT_LABELS = dict(Risk.IMPACT_CHOICES)
|
_IMPACT_LABELS = dict(Risk.IMPACT_CHOICES)
|
||||||
LEVEL_ID_MAP = {"Low": 1, "Medium": 2, "High": 3, "Critical": 4}
|
LEVEL_ID_MAP = {"Low": 1, "Medium": 2, "High": 3, "Critical": 4}
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def sort_url(request, field, current_sort, current_dir):
|
||||||
|
query = request.GET.copy()
|
||||||
|
# Richtung bestimmen
|
||||||
|
if current_sort == field and current_dir == "asc":
|
||||||
|
query["dir"] = "desc"
|
||||||
|
else:
|
||||||
|
query["dir"] = "asc"
|
||||||
|
query["sort"] = field
|
||||||
|
return f"?{query.urlencode()}"
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def dict_get(d, key):
|
def dict_get(d, key):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -116,12 +116,9 @@ class IncidentViewSet(viewsets.ModelViewSet):
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def list_risks(request):
|
def list_risks(request):
|
||||||
"""
|
|
||||||
View for listing all Risks
|
|
||||||
"""
|
|
||||||
qs = Risk.objects.all().select_related("owner", "residual_risk")
|
qs = Risk.objects.all().select_related("owner", "residual_risk")
|
||||||
|
|
||||||
# GET-Parameter lesen
|
# Filter
|
||||||
risk_id = request.GET.get("risk")
|
risk_id = request.GET.get("risk")
|
||||||
control_id = request.GET.get("control")
|
control_id = request.GET.get("control")
|
||||||
owner_id = request.GET.get("owner")
|
owner_id = request.GET.get("owner")
|
||||||
|
@ -142,24 +139,29 @@ def list_risks(request):
|
||||||
if process:
|
if process:
|
||||||
qs = qs.filter(process=process)
|
qs = qs.filter(process=process)
|
||||||
|
|
||||||
risks = qs.order_by("title").distinct()
|
sort = request.GET.get("sort") or "title"
|
||||||
|
direction = request.GET.get("dir") or "asc"
|
||||||
|
if direction == "desc":
|
||||||
|
qs = qs.order_by(f"-{sort}")
|
||||||
|
else:
|
||||||
|
qs = qs.order_by(sort)
|
||||||
|
|
||||||
controls = Control.objects.all().order_by("title")
|
risks = qs.distinct()
|
||||||
owners = User.objects.filter(owned_risks__isnull=False).distinct().order_by("username")
|
|
||||||
categories = (Risk.objects
|
risk_choices = Risk.objects.all().order_by("title")
|
||||||
.exclude(category__isnull=True)
|
control_choices = Control.objects.all().order_by("title")
|
||||||
|
owner_choices = User.objects.filter(owned_risks__isnull=False).distinct().order_by("username")
|
||||||
|
category_choices = (Risk.objects.exclude(category__isnull=True)
|
||||||
.exclude(category__exact="")
|
.exclude(category__exact="")
|
||||||
.values_list("category", flat=True)
|
.values_list("category", flat=True)
|
||||||
.distinct()
|
.distinct()
|
||||||
.order_by("category"))
|
.order_by("category"))
|
||||||
assets = (Risk.objects
|
asset_choices = (Risk.objects.exclude(asset__isnull=True)
|
||||||
.exclude(asset__isnull=True)
|
|
||||||
.exclude(asset__exact="")
|
.exclude(asset__exact="")
|
||||||
.values_list("asset", flat=True)
|
.values_list("asset", flat=True)
|
||||||
.distinct()
|
.distinct()
|
||||||
.order_by("asset"))
|
.order_by("asset"))
|
||||||
processes = (Risk.objects
|
process_choices = (Risk.objects.exclude(process__isnull=True)
|
||||||
.exclude(process__isnull=True)
|
|
||||||
.exclude(process__exact="")
|
.exclude(process__exact="")
|
||||||
.values_list("process", flat=True)
|
.values_list("process", flat=True)
|
||||||
.distinct()
|
.distinct()
|
||||||
|
@ -167,11 +169,14 @@ def list_risks(request):
|
||||||
|
|
||||||
return render(request, "risks/list_risks.html", {
|
return render(request, "risks/list_risks.html", {
|
||||||
"risks": risks,
|
"risks": risks,
|
||||||
"controls": controls,
|
"risk_choices": risk_choices,
|
||||||
"owners": owners,
|
"control_choices": control_choices,
|
||||||
"categories": categories,
|
"owner_choices": owner_choices,
|
||||||
"assets": assets,
|
"category_choices": category_choices,
|
||||||
"processes": processes,
|
"asset_choices": asset_choices,
|
||||||
|
"process_choices": process_choices,
|
||||||
|
"current_sort": sort,
|
||||||
|
"current_dir": direction,
|
||||||
})
|
})
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
|
|
@ -1,176 +1,197 @@
|
||||||
/* Base palette */
|
/* =========================
|
||||||
:root{
|
Base Palette
|
||||||
|
========================= */
|
||||||
|
:root {
|
||||||
--c-verylow:#22c55e; --c-verylow-100:#dcfce7; --c-verylow-300:#86efac; --c-verylow-inv:#000;
|
--c-verylow:#22c55e; --c-verylow-100:#dcfce7; --c-verylow-300:#86efac; --c-verylow-inv:#000;
|
||||||
--c-low:#84cc16; --c-low-100:#ecfccb; --c-low-300:#bef264; --c-low-inv:#111;
|
--c-low:#84cc16; --c-low-100:#ecfccb; --c-low-300:#bef264; --c-low-inv:#111;
|
||||||
--c-mid:#eab308; --c-mid-100:#fef9c3; --c-mid-300:#fde047; --c-mid-inv:#111;
|
--c-mid:#eab308; --c-mid-100:#fef9c3; --c-mid-300:#fde047; --c-mid-inv:#111;
|
||||||
--c-high:#f97316; --c-high-100:#ffedd5; --c-high-300:#fbbf24; --c-high-inv:#111;
|
--c-high:#f97316; --c-high-100:#ffedd5; --c-high-300:#fbbf24; --c-high-inv:#111;
|
||||||
--c-veryhigh:#dc2626; --c-veryhigh-100:#fee2e2;--c-veryhigh-300:#fca5a5; --c-veryhigh-inv:#000;
|
--c-veryhigh:#dc2626; --c-veryhigh-100:#fee2e2;--c-veryhigh-300:#fca5a5; --c-veryhigh-inv:#000;
|
||||||
|
--prosoft-normal:#6f3165;
|
||||||
|
--prosoft-inv:#fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helpers (wie Bulma) */
|
/* =========================
|
||||||
.has-text-control-verylow{color:var(--c-verylow)!important}
|
Helpers (similar to Bulma)
|
||||||
.has-text-control-low{color:var(--c-low)!important}
|
========================= */
|
||||||
.has-text-control-mid{color:var(--c-mid)!important}
|
.has-text-prosoft { color: var(--prosoft-inv) !important; }
|
||||||
.has-text-control-high{color:var(--c-high)!important}
|
.has-background-prosoft { background: var(--prosoft-normal) !important; color: var(--prosoft-inv) !important; }
|
||||||
.has-text-control-veryhigh{color:var(--c-veryhigh)!important}
|
|
||||||
|
|
||||||
.has-background-control-verylow{background:var(--c-verylow)!important;color:var(--c-verylow-inv)!important}
|
.has-background-prosoft a,
|
||||||
.has-background-control-low{background:var(--c-low)!important;color:var(--c-low-inv)!important}
|
.has-background-prosoft abbr { color: var(--prosoft-inv); }
|
||||||
.has-background-control-mid{background:var(--c-mid)!important;color:var(--c-mid-inv)!important}
|
|
||||||
.has-background-control-high{background:var(--c-high)!important;color:var(--c-high-inv)!important}
|
|
||||||
.has-background-control-veryhigh{background:var(--c-veryhigh)!important;color:var(--c-veryhigh-inv)!important}
|
|
||||||
|
|
||||||
/* Buttons */
|
abbr { text-decoration: none; }
|
||||||
.button.is-control-verylow{background:var(--c-verylow);border-color:transparent;color:var(--c-verylow-inv)}
|
|
||||||
.button.is-control-low{background:var(--c-low);border-color:transparent;color:var(--c-low-inv)}
|
|
||||||
.button.is-control-mid{background:var(--c-mid);border-color:transparent;color:var(--c-mid-inv)}
|
|
||||||
.button.is-control-high{background:var(--c-high);border-color:transparent;color:var(--c-high-inv)}
|
|
||||||
.button.is-control-veryhigh{background:var(--c-veryhigh);border-color:transparent;color:var(--c-veryhigh-inv)}
|
|
||||||
.button.is-control-verylow:hover{filter:brightness(.92)}
|
|
||||||
.button.is-control-low:hover{filter:brightness(.92)}
|
|
||||||
.button.is-control-mid:hover{filter:brightness(.92)}
|
|
||||||
.button.is-control-high:hover{filter:brightness(.92)}
|
|
||||||
.button.is-control-veryhigh:hover{filter:brightness(.92)}
|
|
||||||
.button.is-control-verylow.is-light{background:var(--c-verylow-100);color:var(--c-verylow)}
|
|
||||||
.button.is-control-low.is-light{background:var(--c-low-100);color:var(--c-low)}
|
|
||||||
.button.is-control-mid.is-light{background:var(--c-mid-100);color:var(--c-mid)}
|
|
||||||
.button.is-control-high.is-light{background:var(--c-high-100);color:var(--c-high)}
|
|
||||||
.button.is-control-veryhigh.is-light{background:var(--c-veryhigh-100);color:var(--c-veryhigh)}
|
|
||||||
|
|
||||||
/* Tags */
|
/* =========================
|
||||||
.tag.is-control-verylow{background:var(--c-verylow);color:var(--c-verylow-inv)}
|
Colorized Text / Background
|
||||||
.tag.is-control-low{background:var(--c-low);color:var(--c-low-inv)}
|
========================= */
|
||||||
.tag.is-control-mid{background:var(--c-mid);color:var(--c-mid-inv)}
|
.has-text-control-verylow { color: var(--c-verylow) !important; }
|
||||||
.tag.is-control-high{background:var(--c-high);color:var(--c-high-inv)}
|
.has-text-control-low { color: var(--c-low) !important; }
|
||||||
.tag.is-control-veryhigh{background:var(--c-veryhigh);color:var(--c-veryhigh-inv)}
|
.has-text-control-mid { color: var(--c-mid) !important; }
|
||||||
.tag.is-control-verylow.is-light{background:var(--c-verylow-100);color:var(--c-verylow)}
|
.has-text-control-high { color: var(--c-high) !important; }
|
||||||
.tag.is-control-low.is-light{background:var(--c-low-100);color:var(--c-low)}
|
.has-text-control-veryhigh{ color: var(--c-veryhigh) !important; }
|
||||||
.tag.is-control-mid.is-light{background:var(--c-mid-100);color:var(--c-mid)}
|
|
||||||
.tag.is-control-high.is-light{background:var(--c-high-100);color:var(--c-high)}
|
|
||||||
.tag.is-control-veryhigh.is-light{background:var(--c-veryhigh-100);color:var(--c-veryhigh)}
|
|
||||||
|
|
||||||
/* Notifications */
|
.has-background-control-verylow { background: var(--c-verylow) !important; color: var(--c-verylow-inv) !important; }
|
||||||
.notification.is-control-verylow{background:var(--c-verylow-100);border-left:4px solid var(--c-verylow);color:#111}
|
.has-background-control-low { background: var(--c-low) !important; color: var(--c-low-inv) !important; }
|
||||||
.notification.is-control-low{background:var(--c-low-100);border-left:4px solid var(--c-low);color:#111}
|
.has-background-control-mid { background: var(--c-mid) !important; color: var(--c-mid-inv) !important; }
|
||||||
.notification.is-control-mid{background:var(--c-mid-100);border-left:4px solid var(--c-mid);color:#111}
|
.has-background-control-high { background: var(--c-high) !important; color: var(--c-high-inv) !important; }
|
||||||
.notification.is-control-high{background:var(--c-high-100);border-left:4px solid var(--c-high);color:#111}
|
.has-background-control-veryhigh{ background: var(--c-veryhigh) !important; color: var(--c-veryhigh-inv) !important; }
|
||||||
.notification.is-control-veryhigh{background:var(--c-veryhigh-100);border-left:4px solid var(--c-veryhigh);color:#111}
|
|
||||||
|
|
||||||
/* Messages */
|
/* =========================
|
||||||
.message.is-control-verylow .message-header{background:var(--c-verylow);color:var(--c-verylow-inv)}
|
Buttons
|
||||||
.message.is-control-low .message-header{background:var(--c-low);color:var(--c-low-inv)}
|
========================= */
|
||||||
.message.is-control-mid .message-header{background:var(--c-mid);color:var(--c-mid-inv)}
|
.button.is-prosoft { background: var(--prosoft-normal) !important; color: var(--prosoft-inv) !important; }
|
||||||
.message.is-control-high .message-header{background:var(--c-high);color:var(--c-high-inv)}
|
|
||||||
.message.is-control-veryhigh .message-header{background:var(--c-veryhigh);color:var(--c-veryhigh-inv)}
|
|
||||||
.message.is-control-verylow .message-body{border-color:var(--c-verylow-300)}
|
|
||||||
.message.is-control-low .message-body{border-color:var(--c-low-300)}
|
|
||||||
.message.is-control-mid .message-body{border-color:var(--c-mid-300)}
|
|
||||||
.message.is-control-high .message-body{border-color:var(--c-high-300)}
|
|
||||||
.message.is-control-veryhigh .message-body{border-color:var(--c-veryhigh-300)}
|
|
||||||
|
|
||||||
/* Progress (optional) */
|
.button.is-control-verylow,
|
||||||
.progress.is-control-verylow::-webkit-progress-value{background:var(--c-verylow)}
|
.button.is-control-low,
|
||||||
.progress.is-control-low::-webkit-progress-value{background:var(--c-low)}
|
.button.is-control-mid,
|
||||||
.progress.is-control-mid::-webkit-progress-value{background:var(--c-mid)}
|
.button.is-control-high,
|
||||||
.progress.is-control-high::-webkit-progress-value{background:var(--c-high)}
|
.button.is-control-veryhigh {
|
||||||
.progress.is-control-veryhigh::-webkit-progress-value{background:var(--c-veryhigh)}
|
border-color: transparent;
|
||||||
.progress.is-control-verylow::-moz-progress-bar{background:var(--c-verylow)}
|
}
|
||||||
.progress.is-control-low::-moz-progress-bar{background:var(--c-low)}
|
.button.is-control-verylow { background: var(--c-verylow); color: var(--c-verylow-inv); }
|
||||||
.progress.is-control-mid::-moz-progress-bar{background:var(--c-mid)}
|
.button.is-control-low { background: var(--c-low); color: var(--c-low-inv); }
|
||||||
.progress.is-control-high::-moz-progress-bar{background:var(--c-high)}
|
.button.is-control-mid { background: var(--c-mid); color: var(--c-mid-inv); }
|
||||||
.progress.is-control-veryhigh::-moz-progress-bar{background:var(--c-veryhigh)}
|
.button.is-control-high { background: var(--c-high); color: var(--c-high-inv); }
|
||||||
|
.button.is-control-veryhigh{ background: var(--c-veryhigh);color: var(--c-veryhigh-inv); }
|
||||||
|
|
||||||
abbr {
|
.button.is-control-verylow:hover,
|
||||||
text-decoration: none;
|
.button.is-control-low:hover,
|
||||||
|
.button.is-control-mid:hover,
|
||||||
|
.button.is-control-high:hover,
|
||||||
|
.button.is-control-veryhigh:hover {
|
||||||
|
filter: brightness(.92);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Topbar-Farbe erzwingen (Bulma überschreibt sonst mit weiß) */
|
.button.is-control-verylow.is-light { background: var(--c-verylow-100); color: var(--c-verylow); }
|
||||||
|
.button.is-control-low.is-light { background: var(--c-low-100); color: var(--c-low); }
|
||||||
|
.button.is-control-mid.is-light { background: var(--c-mid-100); color: var(--c-mid); }
|
||||||
|
.button.is-control-high.is-light { background: var(--c-high-100); color: var(--c-high); }
|
||||||
|
.button.is-control-veryhigh.is-light{ background: var(--c-veryhigh-100);color: var(--c-veryhigh); }
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
Tags
|
||||||
|
========================= */
|
||||||
|
.tag.is-prosoft { background: var(--prosoft-normal) !important; color: var(--prosoft-inv) !important; }
|
||||||
|
|
||||||
|
.tag.is-control-verylow { background: var(--c-verylow); color: var(--c-verylow-inv); }
|
||||||
|
.tag.is-control-low { background: var(--c-low); color: var(--c-low-inv); }
|
||||||
|
.tag.is-control-mid { background: var(--c-mid); color: var(--c-mid-inv); }
|
||||||
|
.tag.is-control-high { background: var(--c-high); color: var(--c-high-inv); }
|
||||||
|
.tag.is-control-veryhigh{ background: var(--c-veryhigh);color: var(--c-veryhigh-inv); }
|
||||||
|
|
||||||
|
.tag.is-control-verylow.is-light { background: var(--c-verylow-100); color: var(--c-verylow); }
|
||||||
|
.tag.is-control-low.is-light { background: var(--c-low-100); color: var(--c-low); }
|
||||||
|
.tag.is-control-mid.is-light { background: var(--c-mid-100); color: var(--c-mid); }
|
||||||
|
.tag.is-control-high.is-light { background: var(--c-high-100); color: var(--c-high); }
|
||||||
|
.tag.is-control-veryhigh.is-light{ background: var(--c-veryhigh-100);color: var(--c-veryhigh); }
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
Notifications
|
||||||
|
========================= */
|
||||||
|
.notification.is-control-verylow { background: var(--c-verylow-100); border-left: 4px solid var(--c-verylow); color:#111; }
|
||||||
|
.notification.is-control-low { background: var(--c-low-100); border-left: 4px solid var(--c-low); color:#111; }
|
||||||
|
.notification.is-control-mid { background: var(--c-mid-100); border-left: 4px solid var(--c-mid); color:#111; }
|
||||||
|
.notification.is-control-high { background: var(--c-high-100); border-left: 4px solid var(--c-high); color:#111; }
|
||||||
|
.notification.is-control-veryhigh{ background: var(--c-veryhigh-100);border-left: 4px solid var(--c-veryhigh);color:#111; }
|
||||||
|
.notification.is-prosoft { background: var(--prosoft-normal) !important; border-left: var(--prosoft-inv) !important; }
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
Messages
|
||||||
|
========================= */
|
||||||
|
.message.is-control-verylow .message-header { background: var(--c-verylow); color: var(--c-verylow-inv); }
|
||||||
|
.message.is-control-low .message-header { background: var(--c-low); color: var(--c-low-inv); }
|
||||||
|
.message.is-control-mid .message-header { background: var(--c-mid); color: var(--c-mid-inv); }
|
||||||
|
.message.is-control-high .message-header { background: var(--c-high); color: var(--c-high-inv); }
|
||||||
|
.message.is-control-veryhigh .message-header{ background: var(--c-veryhigh);color: var(--c-veryhigh-inv); }
|
||||||
|
|
||||||
|
.message.is-control-verylow .message-body { border-color: var(--c-verylow-300); }
|
||||||
|
.message.is-control-low .message-body { border-color: var(--c-low-300); }
|
||||||
|
.message.is-control-mid .message-body { border-color: var(--c-mid-300); }
|
||||||
|
.message.is-control-high .message-body { border-color: var(--c-high-300); }
|
||||||
|
.message.is-control-veryhigh .message-body{ border-color: var(--c-veryhigh-300); }
|
||||||
|
|
||||||
|
.message.is-prosoft { border-color: var(--prosoft-normal); }
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
Progress bars
|
||||||
|
========================= */
|
||||||
|
.progress.is-control-verylow::-webkit-progress-value { background: var(--c-verylow); }
|
||||||
|
.progress.is-control-low::-webkit-progress-value { background: var(--c-low); }
|
||||||
|
.progress.is-control-mid::-webkit-progress-value { background: var(--c-mid); }
|
||||||
|
.progress.is-control-high::-webkit-progress-value { background: var(--c-high); }
|
||||||
|
.progress.is-control-veryhigh::-webkit-progress-value{ background: var(--c-veryhigh); }
|
||||||
|
|
||||||
|
.progress.is-control-verylow::-moz-progress-bar { background: var(--c-verylow); }
|
||||||
|
.progress.is-control-low::-moz-progress-bar { background: var(--c-low); }
|
||||||
|
.progress.is-control-mid::-moz-progress-bar { background: var(--c-mid); }
|
||||||
|
.progress.is-control-high::-moz-progress-bar { background: var(--c-high); }
|
||||||
|
.progress.is-control-veryhigh::-moz-progress-bar{ background: var(--c-veryhigh); }
|
||||||
|
|
||||||
|
.progress.is-prosoft { background: var(--prosoft-normal) !important; }
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
Navbar / Topbar
|
||||||
|
========================= */
|
||||||
.navbar.topbar-nav {
|
.navbar.topbar-nav {
|
||||||
background-color: #d6801e !important; /* Orange wie im Screenshot */
|
background-color: #d6801e !important; /* Orange as in screenshot */
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Textfarben in der Topbar */
|
|
||||||
.navbar.topbar-nav .navbar-item,
|
.navbar.topbar-nav .navbar-item,
|
||||||
.navbar.topbar-nav .navbar-link {
|
.navbar.topbar-nav .navbar-link { color: #111; }
|
||||||
color: #111;
|
|
||||||
}
|
|
||||||
.navbar.topbar-nav .navbar-item:hover,
|
.navbar.topbar-nav .navbar-item:hover,
|
||||||
.navbar.topbar-nav .navbar-link:hover {
|
.navbar.topbar-nav .navbar-link:hover {
|
||||||
background-color: rgba(0,0,0,.04);
|
background-color: rgba(0,0,0,.04);
|
||||||
color: #111;
|
color: #111;
|
||||||
}
|
}
|
||||||
|
.navbar.topbar-nav .navbar-link::after { border-color: #6b2bbd; } /* purple arrow */
|
||||||
|
|
||||||
/* Dropdown-Pfeil in lila */
|
/* Logo block */
|
||||||
.navbar.topbar-nav .navbar-link::after {
|
.logo { background-color: var(--prosoft-normal) !important; color: #fff; }
|
||||||
border-color: #6b2bbd; /* lila */
|
.logo .logo-text { color: #fff; font-weight: 700; }
|
||||||
}
|
|
||||||
|
|
||||||
/* Lila Logo-Kachel links */
|
/* =========================
|
||||||
.logo {
|
Layout Elements
|
||||||
background: #5a2a82 !important;
|
========================= */
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
}
|
|
||||||
.logo .logo-text {
|
|
||||||
color: #fff;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Rechte Seite: Suche + Profil */
|
|
||||||
.actions { display: flex; align-items: center; gap: 10px; padding-right: 10px; }
|
.actions { display: flex; align-items: center; gap: 10px; padding-right: 10px; }
|
||||||
.search { border: 1px solid #c7c7c7; border-radius: 4px; padding: 4px 8px; font-size: 14px; }
|
.search { border: 1px solid #c7c7c7; border-radius: 4px; padding: 4px 8px; font-size: 14px; }
|
||||||
.profile { background: #3c7d74; color: #fff; width: 28px; height: 28px; border-radius: 9999px;
|
.profile { background: #3c7d74; color: #fff; width: 28px; height: 28px; border-radius: 9999px; display: flex; align-items: center; justify-content: center; font-weight: 700; }
|
||||||
display: flex; align-items: center; justify-content: center; font-weight: 700; }
|
|
||||||
|
|
||||||
/* Inhalt darunter */
|
|
||||||
.content { background: #fafafa; min-height: calc(100vh - 3.25rem); }
|
.content { background: #fafafa; min-height: calc(100vh - 3.25rem); }
|
||||||
.home-icon { font-size: 20px; display: inline-block; margin: 10px; }
|
.home-icon { font-size: 20px; display: inline-block; margin: 10px; }
|
||||||
|
|
||||||
/* Dropdown optisch näher am Screenshot */
|
|
||||||
.navbar-dropdown { border-top: none; box-shadow: 0 8px 16px rgba(0,0,0,.1); }
|
.navbar-dropdown { border-top: none; box-shadow: 0 8px 16px rgba(0,0,0,.1); }
|
||||||
|
|
||||||
/* Breadcrumbs */
|
/* =========================
|
||||||
.top-breadcrumb { padding: 10px 0;}
|
Breadcrumbs
|
||||||
|
========================= */
|
||||||
|
.top-breadcrumb { padding: 10px 0; }
|
||||||
|
.breadcrumb:not(:last-child) { margin-bottom: 0; border-bottom: 1px solid var(--prosoft-normal); }
|
||||||
|
.breadcrumb { background-color: #f0ebeb; }
|
||||||
|
.breadcrumb a { color: var(--prosoft-normal) !important; }
|
||||||
|
|
||||||
.breadcrumb {
|
/* =========================
|
||||||
margin-bottom: 20px;
|
Lists inside .content
|
||||||
background-color: #f0ebeb;
|
========================= */
|
||||||
}
|
.content li { margin-top: 5px !important; }
|
||||||
|
.content li+li { margin: 0 !important; }
|
||||||
|
|
||||||
.content li{
|
/* =========================
|
||||||
margin-top: 5px !important;
|
Dark Mode
|
||||||
}
|
========================= */
|
||||||
|
body.dark-mode { background-color: #121212; color: #f5f5f5; }
|
||||||
|
body.dark-mode .box { background-color: #1e1e1e; color: #f5f5f5; }
|
||||||
|
body.dark-mode a { color: #bb86fc; }
|
||||||
|
|
||||||
.content li+li {
|
/* =========================
|
||||||
margin: 0 !important;
|
Risk Chip (custom widget)
|
||||||
}
|
========================= */
|
||||||
|
.risk-chip {
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ticket-Button (ID links, Text rechts) */
|
|
||||||
.risk-chip{
|
|
||||||
--chip-w: 260px;
|
--chip-w: 260px;
|
||||||
--chip-id-w: 40px;
|
--chip-id-w: 40px;
|
||||||
width: var(--chip-w);
|
width: var(--chip-w);
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
flex-direction: column; /* stacked style */
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
@ -180,13 +201,7 @@ body.dark-mode a {
|
||||||
background: var(--chip-bg, #eee);
|
background: var(--chip-bg, #eee);
|
||||||
color: var(--chip-fg, #111);
|
color: var(--chip-fg, #111);
|
||||||
}
|
}
|
||||||
|
.risk-chip .chip-head {
|
||||||
.risk-chip{
|
|
||||||
display:inline-flex;
|
|
||||||
flex-direction:column; /* <— neu */
|
|
||||||
}
|
|
||||||
|
|
||||||
.risk-chip .chip-head{
|
|
||||||
padding:.35rem .6rem;
|
padding:.35rem .6rem;
|
||||||
font-size:.75rem;
|
font-size:.75rem;
|
||||||
font-weight:700;
|
font-weight:700;
|
||||||
|
@ -196,14 +211,10 @@ body.dark-mode a {
|
||||||
background:rgba(0,0,0,.10);
|
background:rgba(0,0,0,.10);
|
||||||
border-bottom:1px solid rgba(0,0,0,.12);
|
border-bottom:1px solid rgba(0,0,0,.12);
|
||||||
}
|
}
|
||||||
|
.risk-chip .chip-main { display:flex; align-items:stretch; }
|
||||||
|
|
||||||
.risk-chip .chip-main{
|
/* left ID column with texture */
|
||||||
display:flex;
|
.risk-chip .chip-id {
|
||||||
align-items:stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* linke ID-Spalte mit leichter Textur */
|
|
||||||
.risk-chip .chip-id{
|
|
||||||
flex: 0 0 var(--chip-id-w);
|
flex: 0 0 var(--chip-id-w);
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
|
@ -211,18 +222,18 @@ body.dark-mode a {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: rgba(0,0,0,.06);
|
background: rgba(0,0,0,.06);
|
||||||
}
|
}
|
||||||
.risk-chip .chip-id::after{
|
.risk-chip .chip-id::after {
|
||||||
content:"";
|
content:"";
|
||||||
position: absolute; inset:0;
|
position: absolute; inset:0;
|
||||||
background:
|
background:
|
||||||
linear-gradient( to right, rgba(255,255,255,.15), rgba(0,0,0,.08) 60% ),
|
linear-gradient(to right, rgba(255,255,255,.15), rgba(0,0,0,.08) 60%),
|
||||||
repeating-linear-gradient(135deg, rgba(255,255,255,.12) 0 6px, transparent 6px 12px);
|
repeating-linear-gradient(135deg, rgba(255,255,255,.12) 0 6px, transparent 6px 12px);
|
||||||
mix-blend-mode: soft-light;
|
mix-blend-mode: soft-light;
|
||||||
opacity:.6;
|
opacity:.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* rechte Text-Spalte */
|
/* right text column */
|
||||||
.risk-chip .chip-label{
|
.risk-chip .chip-label {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
padding: .5rem .75rem;
|
padding: .5rem .75rem;
|
||||||
line-height: 1.25;
|
line-height: 1.25;
|
||||||
|
@ -232,49 +243,61 @@ body.dark-mode a {
|
||||||
min-height: 2.25rem;
|
min-height: 2.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Farbzuteilung aus deinen Custom-Klassen */
|
/* Color assignments for chip */
|
||||||
.risk-chip.is-control-verylow { --chip-bg: var(--c-verylow); --chip-fg: var(--c-verylow-inv); }
|
.risk-chip.is-control-verylow { --chip-bg: var(--c-verylow); --chip-fg: var(--c-verylow-inv); }
|
||||||
.risk-chip.is-control-low { --chip-bg: var(--c-low); --chip-fg: var(--c-low-inv); }
|
.risk-chip.is-control-low { --chip-bg: var(--c-low); --chip-fg: var(--c-low-inv); }
|
||||||
.risk-chip.is-control-mid { --chip-bg: var(--c-mid); --chip-fg: var(--c-mid-inv); }
|
.risk-chip.is-control-mid { --chip-bg: var(--c-mid); --chip-fg: var(--c-mid-inv); }
|
||||||
.risk-chip.is-control-high { --chip-bg: var(--c-high); --chip-fg: var(--c-high-inv); }
|
.risk-chip.is-control-high { --chip-bg: var(--c-high); --chip-fg: var(--c-high-inv); }
|
||||||
.risk-chip.is-control-veryhigh{ --chip-bg: var(--c-veryhigh); --chip-fg: var(--c-veryhigh-inv); }
|
.risk-chip.is-control-veryhigh{ --chip-bg: var(--c-veryhigh); --chip-fg: var(--c-veryhigh-inv); }
|
||||||
|
|
||||||
/* Responsiv: auf schmalen Screens volle Breite */
|
/* Responsive */
|
||||||
@media (max-width: 480px){
|
@media (max-width: 480px) { .risk-chip{ width: 100%; } }
|
||||||
.risk-chip{ width: 100%; }
|
@media (max-width: 1215px){ .risk-chip{ --chip-w: 100%; width: var(--chip-w); } }
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1215px) {
|
|
||||||
.risk-chip { --chip-w: 100%; width: var(--chip-w); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Container für Avatar + Badge */
|
|
||||||
.avatar-wrap {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
Avatar with badge
|
||||||
|
========================= */
|
||||||
|
.avatar-wrap { position: relative; display: inline-block; }
|
||||||
.avatar-wrap .badge {
|
.avatar-wrap .badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -0.35rem;
|
top: -0.35rem; right: -0.35rem;
|
||||||
right: -0.35rem;
|
min-width: 1.25rem; height: 1.25rem;
|
||||||
min-width: 1.25rem;
|
|
||||||
height: 1.25rem;
|
|
||||||
padding: 0 .25rem;
|
padding: 0 .25rem;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem; line-height: 1.25rem;
|
||||||
line-height: 1.25rem;
|
display: flex; align-items: center; justify-content: center;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-shadow: 0 0 0 2px #fff;
|
box-shadow: 0 0 0 2px #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-wrap .tag.is-medium + .badge {
|
.avatar-wrap .tag.is-medium + .badge {
|
||||||
min-width: 1.15rem;
|
min-width: 1.15rem; height: 1.15rem;
|
||||||
height: 1.15rem;
|
font-size: 0.70rem; line-height: 1.15rem;
|
||||||
font-size: 0.70rem;
|
}
|
||||||
line-height: 1.15rem;
|
/* Dark navbar badge shadow */
|
||||||
|
.navbar.is-dark .avatar-wrap .badge { box-shadow: 0 0 0 2px hsl(229, 53%, 18%); }
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
Bulma Design Changes
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
/* ERP-style horizontal tabs */
|
||||||
|
.erp-tabs {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 2px solid var(--prosoft-normal);
|
||||||
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark-Mode/
|
.erp-tabs a {
|
||||||
.navbar.is-dark .avatar-wrap .badge { box-shadow: 0 0 0 2px hsl(229, 53%, 18%); }
|
padding: 0.5rem 1rem;
|
||||||
|
color: #111;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
border-bottom: 3px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erp-tabs a.is-active {
|
||||||
|
color: var(--prosoft-normal);
|
||||||
|
border-color: var(--prosoft-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.erp-tabs a:hover {
|
||||||
|
background: rgba(0,0,0,.03);
|
||||||
|
}
|
||||||
|
|
|
@ -122,7 +122,7 @@
|
||||||
<li>
|
<li>
|
||||||
<a href="/risks/index">
|
<a href="/risks/index">
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<i class="fas fa-home" aria-hidden="true" style="color: #6b2bbd"></i>
|
<i class="fas fa-home" aria-hidden="true" style="color: #6f3165"></i>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,95 +1,129 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load i18n risk_extras %}
|
{% load i18n risk_extras %}
|
||||||
|
{% block crumbs %}
|
||||||
|
<li><a href="{% url 'risks:index' %}">{% trans "Dashboard" %}</a></li>
|
||||||
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<!-- Hero Section -->
|
<!-- Hero Section -->
|
||||||
<section class="hero is-small is-bold">
|
<section class="hero is-small has-background-prosoft">
|
||||||
<div class="hero-body">
|
<div class="hero-body">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="title is-1 has-text-black">
|
<h2 class="subtitle is-5 has-text-white">
|
||||||
{% trans "Dashboard" %}
|
|
||||||
</h1>
|
|
||||||
<h2 class="subtitle is-4 has-text-black">
|
|
||||||
{% trans "Overview of Risks, Controls and Incidents" %}
|
{% trans "Overview of Risks, Controls and Incidents" %}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section><!-- Hero Section End -->
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<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>
|
|
||||||
|
|
||||||
<!-- Restrisiken -->
|
<!-- KPI Cards -->
|
||||||
|
<div class="columns is-multiline">
|
||||||
|
|
||||||
|
<!-- Total Risks -->
|
||||||
|
<div class="column is-one-quarter">
|
||||||
|
<div class="box has-text-centered">
|
||||||
|
<p class="heading">{% trans "Total Risks" %}</p>
|
||||||
|
<p class="title is-4">{{ risks_total }}</p>
|
||||||
|
</div>
|
||||||
|
</div><!-- Total Risks End -->
|
||||||
|
|
||||||
|
<!-- Residual Risks -->
|
||||||
<div class="column is-one-quarter">
|
<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 %}">
|
<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 class="heading">{% trans "Residual Risks Needing Review" %}</p>
|
||||||
<p>{% trans "Residual Risks Needing Review" %}</p>
|
<p class="title is-4">{{ residual_review_required }}</p>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div><!-- Residual Risks End -->
|
||||||
|
|
||||||
<!-- Ungelesene Notifications -->
|
<!-- Unread Notifications -->
|
||||||
<div class="column is-one-quarter">
|
<div class="column is-one-quarter">
|
||||||
<div class="box has-background-control-mid has-text-centered">
|
<div class="box has-text-centered">
|
||||||
<h2 class="title is-4">{{ notifications_unread }}</h2>
|
<p class="heading">{% trans "Unread Notifications" %}</p>
|
||||||
<p>{% trans "Unread Notifications" %}</p>
|
<p class="title is-4">{{ notifications_unread }}</p>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div><!-- Unread Notifications End -->
|
||||||
|
|
||||||
<!-- Controls by Status -->
|
</div><!-- KPI Cards End -->
|
||||||
<div class="box">
|
|
||||||
<h2 class="subtitle">{% trans "Controls by Status" %}</h2>
|
|
||||||
<ul>
|
|
||||||
{% for row in controls_by_status %}
|
|
||||||
<li>{{ row.status|control_status_label }}: {{ row.count }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Incidents by Status -->
|
|
||||||
<div class="box">
|
|
||||||
<h2 class="subtitle">{% trans "Incidents by Status" %}</h2>
|
|
||||||
<ul>
|
|
||||||
{% for row in incidents_status %}
|
|
||||||
<li>{{ row.status|incident_status_label }}: {{ row.count }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Risks by CIA -->
|
<!-- Risks by CIA -->
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h2 class="subtitle">{% trans "Risks by CIA" %}</h2>
|
<h2 class="title is-5">{% trans "Risks by CIA" %}</h2>
|
||||||
<div class="columns is-multiline">
|
<div class="columns is-multiline">
|
||||||
{% for cia, count in risks_by_cia.items %}
|
<div class="column">
|
||||||
<div class="column is-one-quarter">
|
<div class="notification is-control-verylow has-text-centered">
|
||||||
{% if cia == '1' %}
|
<strong>{% trans "Confidentiality" %}</strong><br>
|
||||||
<div class="box has-background-control-verylow has-text-centered">
|
{{ risks_by_cia.1|default:0 }}
|
||||||
<h3 class="title is-5">{% trans "Confidentiality" %}</h3>
|
|
||||||
<p>{{ count }} {% trans "Risks" %}</p>
|
|
||||||
</div>
|
</div>
|
||||||
{% elif cia == '2' %}
|
|
||||||
<div class="box has-background-control-mid has-text-centered">
|
|
||||||
<h3 class="title is-5">{% trans "Integrity" %}</h3>
|
|
||||||
<p>{{ count }} {% trans "Risks" %}</p>
|
|
||||||
</div>
|
</div>
|
||||||
{% elif cia == '3' %}
|
<div class="column">
|
||||||
<div class="box has-background-control-high has-text-centered">
|
<div class="notification is-control-mid has-text-centered">
|
||||||
<h3 class="title is-5">{% trans "Availability" %}</h3>
|
<strong>{% trans "Integrity" %}</strong><br>
|
||||||
<p>{{ count }} {% trans "Risks" %}</p>
|
{{ risks_by_cia.2|default:0 }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<div class="notification is-control-high has-text-centered">
|
||||||
|
<strong>{% trans "Availability" %}</strong><br>
|
||||||
|
{{ risks_by_cia.3|default:0 }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- Risks by CIA End -->
|
||||||
|
|
||||||
|
<!-- Controls by Status -->
|
||||||
|
<div class="box">
|
||||||
|
<h2 class="title is-5">{% trans "Controls by Status" %}</h2>
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table is-fullwidth is-narrow is-hoverable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Status" %}</th>
|
||||||
|
<th>{% trans "Count" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for row in controls_by_status %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.status|control_status_label }}</td>
|
||||||
|
<td>{{ row.count }}</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr><td colspan="2" class="has-text-grey has-text-centered">{% trans "No data" %}</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div><!-- Controls by Status End -->
|
||||||
|
|
||||||
|
<!-- Incidents by Status -->
|
||||||
|
<div class="box">
|
||||||
|
<h2 class="title is-5">{% trans "Incidents by Status" %}</h2>
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table is-fullwidth is-narrow is-hoverable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Status" %}</th>
|
||||||
|
<th>{% trans "Count" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for row in incidents_status %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.status|incident_status_label }}</td>
|
||||||
|
<td>{{ row.count }}</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr><td colspan="2" class="has-text-grey has-text-centered">{% trans "No data" %}</td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div><!-- Incidents by Status End -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section><!-- Dashboard Content End -->
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -4,206 +4,156 @@
|
||||||
<li><a href="{% url 'risks:list_risks' %}">{% trans "Risk analysis" %}</a></li>
|
<li><a href="{% url 'risks:list_risks' %}">{% trans "Risk analysis" %}</a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="section">
|
|
||||||
<div class="box">
|
|
||||||
<h2 class="title is-5">{% trans "Filter" %}</h2>
|
|
||||||
|
|
||||||
<!-- Filter -->
|
<!-- Filter Section -->
|
||||||
|
<section class="section has-background-light py-2">
|
||||||
<form method="get" class="mb-4">
|
<form method="get" class="mb-4">
|
||||||
<div class="columns is-multiline">
|
<div class="columns is-multiline is-vcentered">
|
||||||
|
|
||||||
|
<!-- Filter: Risk -->
|
||||||
<div class="column is-2">
|
<div class="column is-2">
|
||||||
|
<label class="label is-small">{% trans "Risks" %}</label>
|
||||||
<div class="select is-small is-fullwidth">
|
<div class="select is-small is-fullwidth">
|
||||||
<select name="risk" onchange="this.form.submit()">
|
<select name="risk" onchange="this.form.submit()">
|
||||||
<option value="">{% trans "Risk" %}</option>
|
<option value="">{% trans "All" %}</option>
|
||||||
{% for r in risks %}
|
{% for r in risk_choices %}
|
||||||
<option value="{{ r.id }}" {% if request.GET.risk == r.id|stringformat:"s" %}selected{% endif %}>
|
<option value="{{ r.id }}" {% if request.GET.risk == r.id|stringformat:"s" %}selected{% endif %}>
|
||||||
{{ r.title }}
|
{{ r.title }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div><!-- Filter: Risk End -->
|
||||||
|
|
||||||
<div class="column is-2">
|
|
||||||
<div class="select is-small is-fullwidth">
|
|
||||||
<select name="control" onchange="this.form.submit()">
|
|
||||||
<option value="">{% trans "Controls" %}</option>
|
|
||||||
{% for c in controls %}
|
|
||||||
<option value="{{ c.id }}" {% if request.GET.control == c.id|stringformat:"s" %}selected{% endif %}>
|
|
||||||
{{ c.title }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="column is-2">
|
|
||||||
<div class="select is-small is-fullwidth">
|
|
||||||
<select name="category" onchange="this.form.submit()">
|
|
||||||
<option value="">{% trans "Category" %}</option>
|
|
||||||
{% for cat in categories %}
|
|
||||||
<option value="{{ cat }}" {% if request.GET.category == cat|stringformat:"s" %}selected{% endif %}>
|
|
||||||
{{ cat }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<!-- Filter: Asset -->
|
||||||
<div class="column is-2">
|
<div class="column is-2">
|
||||||
|
<label class="label is-small">{% trans "Assets" %}</label>
|
||||||
<div class="select is-small is-fullwidth">
|
<div class="select is-small is-fullwidth">
|
||||||
<select name="asset" onchange="this.form.submit()">
|
<select name="asset" onchange="this.form.submit()">
|
||||||
<option value="">{% trans "Asset" %}</option>
|
<option value="">{% trans "All" %}</option>
|
||||||
{% for a in assets %}
|
{% for a in asset_choices %}
|
||||||
<option value="{{ a }}" {% if request.GET.asset == a|stringformat:"s" %}selected{% endif %}>
|
<option value="{{ a }}" {% if request.GET.asset == a|stringformat:"s" %}selected{% endif %}>
|
||||||
{{ a }}
|
{{ a }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div><!-- Filter: Asset End -->
|
||||||
|
|
||||||
|
<!-- Filter: Category -->
|
||||||
<div class="column is-2">
|
<div class="column is-2">
|
||||||
|
<label class="label is-small">{% trans "Categories" %}</label>
|
||||||
|
<div class="select is-small is-fullwidth">
|
||||||
|
<select name="category" onchange="this.form.submit()">
|
||||||
|
<option value="">{% trans "All" %}</option>
|
||||||
|
{% for cat in category_choices %}
|
||||||
|
<option value="{{ cat }}" {% if request.GET.category == cat|stringformat:"s" %}selected{% endif %}>
|
||||||
|
{{ cat }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div><!-- Filter: Category End -->
|
||||||
|
|
||||||
|
<!-- Filter: Process -->
|
||||||
|
<div class="column is-2">
|
||||||
|
<label class="label is-small">{% trans "Processes" %}</label>
|
||||||
<div class="select is-small is-fullwidth">
|
<div class="select is-small is-fullwidth">
|
||||||
<select name="process" onchange="this.form.submit()">
|
<select name="process" onchange="this.form.submit()">
|
||||||
<option value="">{% trans "Process" %}</option>
|
<option value="">{% trans "All" %}</option>
|
||||||
{% for p in processes %}
|
{% for p in process_choices %}
|
||||||
<option value="{{ p }}" {% if request.GET.process == p|stringformat:"s" %}selected{% endif %}>
|
<option value="{{ p }}" {% if request.GET.process == p|stringformat:"s" %}selected{% endif %}>
|
||||||
{{ p }}
|
{{ p }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div><!-- Filter: Process End -->
|
||||||
|
|
||||||
|
<!-- Filter: Owner -->
|
||||||
<div class="column is-2">
|
<div class="column is-2">
|
||||||
|
<label class="label is-small">{% trans "Owners" %}</label>
|
||||||
<div class="select is-small is-fullwidth">
|
<div class="select is-small is-fullwidth">
|
||||||
<select name="owner" onchange="this.form.submit()">
|
<select name="owner" onchange="this.form.submit()">
|
||||||
<option value="">{% trans "Owner" %}</option>
|
<option value="">{% trans "All" %}</option>
|
||||||
{% for u in owners %}
|
{% for u in owner_choices %}
|
||||||
<option value="{{ u.id }}" {% if request.GET.owner == u.id|stringformat:"s" %}selected{% endif %}>
|
<option value="{{ u.id }}" {% if request.GET.owner == u.id|stringformat:"s" %}selected{% endif %}>
|
||||||
{{ u.get_full_name|default:u.username }}
|
{{ u.get_full_name|default:u.username }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div><!-- Filter: Owner End -->
|
||||||
|
|
||||||
</div>
|
<!-- Filter: Reset -->
|
||||||
</form> <!-- Filter Ende -->
|
<div class="column is-2">
|
||||||
|
<label class="label is-small"> </label>
|
||||||
|
<div class="control">
|
||||||
<h2 class="title is-5">{% trans "Risks" %}</h2>
|
<a href="{% url 'risks:list_risks' %}" class="button is-small is-light is-fullwidth">
|
||||||
<!-- Risiken -->
|
<span class="icon"><i class="fas fa-undo"></i></span>
|
||||||
<div class="table-container">
|
<span>{% trans "Reset filters" %}</span>
|
||||||
<table class="table is-bordered is-striped is-hoverable is-fullwidth">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{% if request.user.is_staff %}
|
|
||||||
<th rowspan="2" class="has-text-centered">
|
|
||||||
<a class="icon has-text-success" href="{% url 'admin:risks_risk_add' %}" title="Risiko hinzufügen">
|
|
||||||
<i class="fas fa-add"></i>
|
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
|
</div><!-- Filter: Reset End -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section><!-- Filter Section End -->
|
||||||
|
|
||||||
|
<!-- Risk Table -->
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
|
||||||
|
<thead>
|
||||||
|
<tr class="has-background-prosoft">
|
||||||
|
<th class="has-text-centered">
|
||||||
|
<a href="{% sort_url request 'id' current_sort current_dir %}">{% trans "No." %}</a>
|
||||||
</th>
|
</th>
|
||||||
{% endif %}
|
<th class="has-text-centered"><abbr title="{% trans 'Confidentiality' %}">C</abbr></th>
|
||||||
<th rowspan="2" class="has-text-centered">{% trans "Risk" %}</th>
|
<th class="has-text-centered"><abbr title="{% trans 'Integrity' %}">I</abbr></th>
|
||||||
<th rowspan="2" class="has-text-centered">{% trans "Asset / Process" %}</th>
|
<th class="has-text-centered"><abbr title="{% trans 'Availability' %}">A</abbr></th>
|
||||||
<th rowspan="2" class="has-text-centered">{% trans "Category" %}</th>
|
<th class="has-text-centered">
|
||||||
<th rowspan="2" class="has-text-centered">{% trans "Risk Owner" %}</th>
|
<a href="{% sort_url request 'title' current_sort current_dir %}">{% trans "Risk" %}</a>
|
||||||
<th colspan="4" class="has-text-centered has-background-light">{% trans "Gross Risk" %}</th>
|
</th>
|
||||||
<th colspan="4" class="has-text-centered has-background-info-light">{% trans "Net Risk" %}</th>
|
<th class="has-text-centered">
|
||||||
</tr>
|
<a href="{% sort_url request 'asset' current_sort current_dir %}">{% trans "Asset" %}</a>
|
||||||
<tr>
|
</th>
|
||||||
<th class="has-text-centered has-background-light">{% trans "Likelihood" %}</th>
|
<th class="has-text-centered">
|
||||||
<th class="has-text-centered has-background-light">{% trans "Impact" %}</th>
|
<a href="{% sort_url request 'category' current_sort current_dir %}">{% trans "Category" %}</a>
|
||||||
<th class="has-text-centered has-background-light">{% trans "Score" %}</th>
|
</th>
|
||||||
<th class="has-text-centered has-background-light">{% trans "Level" %}</th>
|
<th class="has-text-centered">
|
||||||
<th class="has-text-centered has-background-info-light">{% trans "Likelihood" %}</th>
|
<a href="{% sort_url request 'process' current_sort current_dir %}">{% trans "Process" %}</a>
|
||||||
<th class="has-text-centered has-background-info-light">{% trans "Impact" %}</th>
|
</th>
|
||||||
<th class="has-text-centered has-background-info-light">{% trans "Score" %}</th>
|
<th class="has-text-centered">
|
||||||
<th class="has-text-centered has-background-info-light">{% trans "Level" %}</th>
|
<a href="{% sort_url request 'status' current_sort current_dir %}">{% trans "Status" %}</a>
|
||||||
|
</th>
|
||||||
|
<th class="has-text-centered has-text-prosoft">{% trans "Created" %}</th>
|
||||||
|
<th class="has-text-centered has-text-prosoft">{% trans "by" %}</th>
|
||||||
|
<th class="has-text-centered has-text-prosoft">{% trans "Changed" %}</th>
|
||||||
|
<th class="has-text-centered has-text-prosoft">{% trans "by" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for r in risks %}
|
{% for r in risks %}
|
||||||
<tr>
|
<tr onclick="window.location.href='{% url 'risks:show_risk' r.id %}'" style="cursor:pointer;">
|
||||||
{% if request.user.is_staff %}
|
<td>{{ r.id }}</td>
|
||||||
<td class="has-text-centered">
|
<td class="has-text-centered"><input type="checkbox" disabled {% if "1" in r.cia %}checked{% endif %}></td>
|
||||||
<a class="icon has-text-warning" href="{% url 'admin:risks_risk_change' r.id %}" title="Risiko bearbeiten">
|
<td class="has-text-centered"><input type="checkbox" disabled {% if "2" in r.cia %}checked{% endif %}></td>
|
||||||
<i class="fas fa-edit"></i>
|
<td class="has-text-centered"><input type="checkbox" disabled {% if "3" in r.cia %}checked{% endif %}></td>
|
||||||
</a>
|
<td>{{ r.title }}</td>
|
||||||
</td>
|
<td>{{ r.asset }}</td>
|
||||||
{% endif %}
|
<td>{{ r.category }}</td>
|
||||||
<td onclick="window.location.href='{% url 'risks:show_risk' r.id %}'" style="cursor:pointer;">{{ r.title }}</td>
|
<td>{{ r.process }}</td>
|
||||||
<td onclick="window.location.href='{% url 'risks:show_risk' r.id %}'" style="cursor:pointer;">
|
<td>{{ r.get_status_display }}</td>
|
||||||
{{ r.asset }}
|
<td>{{ r.created_at|date:"d.m.Y H:i" }}</td>
|
||||||
{% if r.process %}
|
<td>{{ r.owner|user_display }}</td>
|
||||||
<br><small>{{ r.process }}</small>
|
<td>{{ r.updated_at|date:"d.m.Y H:i" }}</td>
|
||||||
{% endif %}
|
<td>{{ r.owner|user_display }}</td>
|
||||||
</td>
|
|
||||||
<td onclick="window.location.href='{% url 'risks:show_risk' r.id %}'" style="cursor:pointer;">{{ r.category }}</td>
|
|
||||||
<td onclick="window.location.href='{% url 'risks:show_risk' r.id %}'" style="cursor:pointer;">
|
|
||||||
{% if r.owner %}
|
|
||||||
{{ r.owner|user_display }}
|
|
||||||
{% else %}
|
|
||||||
–
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<!-- Brutto Risiko -->
|
|
||||||
<td onclick="window.location.href='{% url 'risks:show_risk' r.id %}'" class="has-text-centered {{ r.likelihood|likelihood_class|to_bg }}" style="cursor:pointer;">
|
|
||||||
<abbr title="{{ r.likelihood|likelihood_id_label }}">{{ r.likelihood }}</abbr>
|
|
||||||
</td>
|
|
||||||
<td onclick="window.location.href='{% url 'risks:show_risk' r.id %}'" class="has-text-centered {{ r.impact|impact_class|to_bg }}" style="cursor:pointer;">
|
|
||||||
<abbr title="{{ r.impact|impact_id_label }}">{{ r.impact }}</abbr>
|
|
||||||
</td>
|
|
||||||
<td onclick="window.location.href='{% url 'risks:show_risk' r.id %}'" class="has-text-centered {{ r.score|score_class|to_bg }}" style="cursor:pointer;">
|
|
||||||
{{ r.score }} / 20
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td onclick="window.location.href='{% url 'risks:show_risk' r.id %}'" class="has-text-centered {{ r.level|level_class|to_bg }}" style="cursor:pointer;">
|
|
||||||
{{ r.level }}
|
|
||||||
</td>
|
|
||||||
<!-- Netto Risiko -->
|
|
||||||
{% if r.residual_risk %}
|
|
||||||
<td onclick="window.location.href='{% url 'risks:show_risk' r.id %}'"
|
|
||||||
class="has-text-centered {{ r.residual_risk.likelihood|likelihood_class|to_bg }}"
|
|
||||||
style="cursor:pointer;">
|
|
||||||
<abbr title="{{ r.residual_risk.likelihood|likelihood_id_label }}">
|
|
||||||
{{ r.residual_risk.likelihood }}
|
|
||||||
</abbr>
|
|
||||||
</td>
|
|
||||||
<td onclick="window.location.href='{% url 'risks:show_risk' r.id %}'"
|
|
||||||
class="has-text-centered {{ r.residual_risk.impact|impact_class|to_bg }}"
|
|
||||||
style="cursor:pointer;">
|
|
||||||
<abbr title="{{ r.residual_risk.impact|impact_id_label }}">
|
|
||||||
{{ r.residual_risk.impact }}
|
|
||||||
</abbr>
|
|
||||||
</td>
|
|
||||||
<td onclick="window.location.href='{% url 'risks:show_risk' r.id %}'"
|
|
||||||
class="has-text-centered {{ r.residual_risk.score|score_class|to_bg }}"
|
|
||||||
style="cursor:pointer;">
|
|
||||||
{{ r.residual_risk.score }} / 20
|
|
||||||
</td>
|
|
||||||
<td onclick="window.location.href='{% url 'risks:show_risk' r.id %}'"
|
|
||||||
class="has-text-centered {{ r.residual_risk.level|level_class|to_bg }}"
|
|
||||||
style="cursor:pointer;">
|
|
||||||
{{ r.residual_risk.level }}
|
|
||||||
</td>
|
|
||||||
{% else %}
|
|
||||||
<td colspan="4" class="has-text-centered has-text-grey">
|
|
||||||
{% trans "No residual risk defined" %}
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<tr>
|
<tr><td colspan="13" class="has-text-grey has-text-centered">{% trans "No data" %}</td></tr>
|
||||||
<td colspan="8" class="has-text-centered has-text-grey">{% trans "No risks present" %}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div> <!-- Ende Risiken -->
|
</div><!-- Risk Table End -->
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -4,39 +4,43 @@
|
||||||
<li><a href="{% url 'risks:risk_matrix' %}">{% trans "Risk Matrix" %}</a></li>
|
<li><a href="{% url 'risks:risk_matrix' %}">{% trans "Risk Matrix" %}</a></li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<div class="erp-tabs">
|
||||||
|
<a class="is-active" data-tab="matrix">{% trans "Risk Matrix" %}</a>
|
||||||
|
<a data-tab="details">{% trans "Detail View" %}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
|
|
||||||
<!-- Tabs -->
|
<!-- Main Container -->
|
||||||
<div class="tabs is-boxed" role="tablist">
|
|
||||||
<ul>
|
|
||||||
<li class="is-active" data-tab="riskmatrix" role="tab" aria-selected="true"><a>{% trans "Risk Matrix" %}</a></li>
|
|
||||||
<li data-tab="details" role="tab" aria-selected="false"><a>{% trans "Detail View" %}</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h2 class="title is-4">{% trans "Risk Matrix" %}</h2>
|
<!-- Panel: Matrix View -->
|
||||||
|
<div class="tab-panel" data-tab="matrix">
|
||||||
{# Panel: Brutto (Score-Matrix) #}
|
<table class="table is-bordered is-fullwidth has-text-centered risk-matrix-table">
|
||||||
<div class="tab-panel" data-tab="riskmatrix">
|
|
||||||
<table class="table is-bordered has-text-centered risk-matrix-table">
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="has-text-left">{% trans "Impact" %} / {% trans "Likelihood" %}</th>
|
<th class="has-text-left">{% trans "Impact" %} / {% trans "Likelihood" %}</th>
|
||||||
{% for l_val, l_label in likelihoods %}
|
{% for l_val, l_label in likelihoods %}
|
||||||
<th class="py-6 {{ l_val|likelihood_class|to_bg }}">{{ l_label }}</th>
|
<th class="{{ l_val|likelihood_class|to_bg }}">{{ l_label }}</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for i_val, i_label in impacts reversed %}
|
{% for i_val, i_label in impacts reversed %}
|
||||||
<tr>
|
<tr>
|
||||||
<th class="py-6 has-text-left {{ i_val|impact_class|to_bg }}">{{ i_label }}</th>
|
<th class="has-text-left {{ i_val|impact_class|to_bg }}">{{ i_label }}</th>
|
||||||
{% for l_val, l_label in likelihoods %}
|
{% for l_val, l_label in likelihoods %}
|
||||||
{% with s=i_val|mul:l_val %}
|
{% with s=i_val|mul:l_val %}
|
||||||
<td class="risk-matrix-cell {{ s|score_bg_class }}">
|
<td class="risk-matrix-cell {{ s|score_bg_class }}">
|
||||||
<div class="is-flex is-justify-content-center is-align-items-center py-6">
|
<div class="is-flex is-justify-content-center is-align-items-center">
|
||||||
<span class="tag is-light is-rounded">{% trans "Score" %} {{ s }}</span>
|
{% if s <= 4 %}
|
||||||
|
<span class="tag is-control-verylow is-light">Low ({{ s }})</span>
|
||||||
|
{% elif s <= 8 %}
|
||||||
|
<span class="tag is-control-low is-light">Medium ({{ s }})</span>
|
||||||
|
{% elif s <= 12 %}
|
||||||
|
<span class="tag is-control-mid is-light">High ({{ s }})</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="tag is-control-veryhigh is-light">Critical ({{ s }})</span>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
@ -45,10 +49,12 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div><!-- Panel: Matrix View End -->
|
||||||
|
|
||||||
{# Panel: Details (Listen je Zelle, mit Brutto/Netto-Umschalter) #}
|
<!-- Panel: Details View -->
|
||||||
<div class="tab-panel is-hidden" data-tab="details">
|
<div class="tab-panel is-hidden" data-tab="details">
|
||||||
|
|
||||||
|
<!-- Mode Toggle (Gross / Net) -->
|
||||||
<div class="level mb-3">
|
<div class="level mb-3">
|
||||||
<div class="level-left">
|
<div class="level-left">
|
||||||
<div class="level-item"><strong>{% trans "Show" %}:</strong></div>
|
<div class="level-item"><strong>{% trans "Show" %}:</strong></div>
|
||||||
|
@ -63,32 +69,32 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div><!-- Mode Toggle End -->
|
||||||
|
|
||||||
{# Brutto-Listen #}
|
<!-- Gross Risk List -->
|
||||||
<div class="details-table" data-mode="gross">
|
<div class="details-table" data-mode="gross">
|
||||||
<table class="table is-bordered has-text-centered risk-matrix-table">
|
<table class="table is-bordered is-fullwidth has-text-centered risk-matrix-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="has-text-left">{% trans "Impact" %} / {% trans "Likelihood" %}</th>
|
<th class="has-text-left">{% trans "Impact" %} / {% trans "Likelihood" %}</th>
|
||||||
{% for l_val, l_label in likelihoods %}
|
{% for l_val, l_label in likelihoods %}
|
||||||
<th class="py-6 {{ l_val|likelihood_class|to_bg }}">{{ l_label }}</th>
|
<th class="{{ l_val|likelihood_class|to_bg }}">{{ l_label }}</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for i_val, i_label in impacts reversed %}
|
{% for i_val, i_label in impacts reversed %}
|
||||||
<tr>
|
<tr>
|
||||||
<th class="py-6 has-text-left {{ i_val|impact_class|to_bg }}">{{ i_label }}</th>
|
<th class="has-text-left {{ i_val|impact_class|to_bg }}">{{ i_label }}</th>
|
||||||
{% for l_val, l_label in likelihoods %}
|
{% for l_val, l_label in likelihoods %}
|
||||||
{% with row=gross_matrix|dict_get:i_val %}
|
{% with row=gross_matrix|dict_get:i_val %}
|
||||||
{% with cell=row|dict_get:l_val %}
|
{% with cell=row|dict_get:l_val %}
|
||||||
{% with s=i_val|mul:l_val %}
|
{% with s=i_val|mul:l_val %}
|
||||||
<td class="risk-matrix-cell {{ s|score_bg_class }}">
|
<td class="risk-matrix-cell {{ s|score_bg_class }}">
|
||||||
{% if cell and cell|length %}
|
{% if cell %}
|
||||||
<ul class="risk-cell-list">
|
<ul class="risk-cell-list">
|
||||||
{% for risk in cell %}
|
{% for risk in cell %}
|
||||||
<li style="list-style: none;">
|
<li style="list-style:none;">
|
||||||
<a href="{% url 'risks:show_risk' risk.id %}" class="tag">{{ risk.title }}</a>
|
<a href="{% url 'risks:show_risk' risk.id %}" class="tag">{{ risk.title }}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -105,32 +111,32 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div><!-- Gross Risk List End -->
|
||||||
|
|
||||||
{# Netto-Listen #}
|
<!-- Net Risk List -->
|
||||||
<div class="details-table is-hidden" data-mode="net">
|
<div class="details-table is-hidden" data-mode="net">
|
||||||
<table class="table is-bordered has-text-centered risk-matrix-table">
|
<table class="table is-bordered is-fullwidth has-text-centered risk-matrix-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="has-text-left">{% trans "Impact" %} / {% trans "Likelihood" %}</th>
|
<th class="has-text-left">{% trans "Impact" %} / {% trans "Likelihood" %}</th>
|
||||||
{% for l_val, l_label in likelihoods %}
|
{% for l_val, l_label in likelihoods %}
|
||||||
<th class="py-6 {{ l_val|likelihood_class|to_bg }}">{{ l_label }}</th>
|
<th class="{{ l_val|likelihood_class|to_bg }}">{{ l_label }}</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for i_val, i_label in impacts reversed %}
|
{% for i_val, i_label in impacts reversed %}
|
||||||
<tr>
|
<tr>
|
||||||
<th class="py-6 has-text-left {{ i_val|impact_class|to_bg }}">{{ i_label }}</th>
|
<th class="has-text-left {{ i_val|impact_class|to_bg }}">{{ i_label }}</th>
|
||||||
{% for l_val, l_label in likelihoods %}
|
{% for l_val, l_label in likelihoods %}
|
||||||
{% with row=net_matrix|dict_get:i_val %}
|
{% with row=net_matrix|dict_get:i_val %}
|
||||||
{% with cell=row|dict_get:l_val %}
|
{% with cell=row|dict_get:l_val %}
|
||||||
{% with s=i_val|mul:l_val %}
|
{% with s=i_val|mul:l_val %}
|
||||||
<td class="risk-matrix-cell {{ s|score_bg_class }}">
|
<td class="risk-matrix-cell {{ s|score_bg_class }}">
|
||||||
{% if cell and cell|length %}
|
{% if cell %}
|
||||||
<ul class="risk-cell-list">
|
<ul class="risk-cell-list">
|
||||||
{% for risk in cell %}
|
{% for risk in cell %}
|
||||||
<li style="list-style: none;">
|
<li style="list-style:none;">
|
||||||
<a href="{% url 'risks:show_risk' risk.id %}" class="tag">{{ risk.title }}</a>
|
<a href="{% url 'risks:show_risk' risk.id %}" class="tag">{{ risk.title }}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -147,39 +153,56 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div><!-- Net Risk List End -->
|
||||||
</div>
|
|
||||||
|
</div><!-- Panel: Details View End -->
|
||||||
|
|
||||||
|
</div><!-- Main Container End -->
|
||||||
|
</section><!-- Section End -->
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.risk-matrix-table th, .risk-matrix-table td { padding: .5rem; }
|
|
||||||
.risk-matrix-cell { min-height: 120px; vertical-align: middle; }
|
|
||||||
.tab-panel.is-hidden { display: none; }
|
|
||||||
.risk-cell-list { text-align: left; margin: 0; padding-left: 1rem; }
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
// Tabs
|
// Handle ERP-style tabs
|
||||||
const tabs = document.querySelectorAll('.tabs li[data-tab]');
|
const tabs = document.querySelectorAll('.erp-tabs a[data-tab]');
|
||||||
const panels = document.querySelectorAll('.tab-panel');
|
const panels = document.querySelectorAll('.tab-panel');
|
||||||
tabs.forEach(t => t.addEventListener('click', () => {
|
|
||||||
tabs.forEach(x => { x.classList.remove('is-active'); x.setAttribute('aria-selected','false'); });
|
|
||||||
t.classList.add('is-active'); t.setAttribute('aria-selected','true');
|
|
||||||
const target = t.getAttribute('data-tab');
|
|
||||||
panels.forEach(p => p.classList.toggle('is-hidden', p.getAttribute('data-tab') !== target));
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Umschalter im „Details“-Tab
|
tabs.forEach(tab => {
|
||||||
|
tab.addEventListener('click', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// Deactivate all
|
||||||
|
tabs.forEach(x => x.classList.remove('is-active'));
|
||||||
|
panels.forEach(p => p.classList.add('is-hidden'));
|
||||||
|
|
||||||
|
// Activate clicked
|
||||||
|
tab.classList.add('is-active');
|
||||||
|
const target = tab.getAttribute('data-tab');
|
||||||
|
const activePanel = document.querySelector(`.tab-panel[data-tab="${target}"]`);
|
||||||
|
if (activePanel) activePanel.classList.remove('is-hidden');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle Gross/Net toggle buttons in "Details" tab
|
||||||
const toggles = document.querySelectorAll('.details-toggle');
|
const toggles = document.querySelectorAll('.details-toggle');
|
||||||
const tables = document.querySelectorAll('.details-table');
|
const tables = document.querySelectorAll('.details-table');
|
||||||
toggles.forEach(btn => btn.addEventListener('click', () => {
|
|
||||||
const mode = btn.getAttribute('data-mode'); // 'gross' | 'net'
|
toggles.forEach(btn => {
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
const mode = btn.getAttribute('data-mode'); // "gross" or "net"
|
||||||
|
|
||||||
|
// Toggle active state on buttons
|
||||||
toggles.forEach(b => b.classList.toggle('is-active', b === btn));
|
toggles.forEach(b => b.classList.toggle('is-active', b === btn));
|
||||||
tables.forEach(t => t.classList.toggle('is-hidden', t.getAttribute('data-mode') !== mode));
|
|
||||||
}));
|
// Show the right table
|
||||||
|
tables.forEach(t => {
|
||||||
|
t.classList.toggle('is-hidden', t.getAttribute('data-mode') !== mode);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Loading…
Add table
Reference in a new issue