Skip to content

Commit 28046dd

Browse files
committed
feat(ui): add style and template for curation dashboard
Signed-off-by: Keshav Priyadarshi <git@keshav.space>
1 parent 574b9c6 commit 28046dd

6 files changed

Lines changed: 778 additions & 1 deletion

File tree

vulnerabilities/templates/advisory_detail.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
{% load static %}
55
{% load show_cvss %}
66
{% load url_filters %}
7+
{% load utils %}
78

89
{% block title %}
910
VulnerableCode Advisory Details - {{ advisory.advisory_id }}
@@ -115,7 +116,7 @@
115116
</tr>
116117
<tr>
117118
<td class="two-col-left">Summary</td>
118-
<td class="two-col-right wrap-strings">{{ advisory.summary }}
119+
<td class="two-col-right wrap-strings">{{ advisory.summary|linebreaksbr }}
119120
</td>
120121
</tr>
121122
{% if severity_score_range %}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
{% extends "base.html" %}
2+
{% load static %}
3+
{% load utils %}
4+
5+
{% block extrahead %}
6+
<link rel="stylesheet" href="{% static 'css/package_curation.css' %}">
7+
{% endblock %}
8+
9+
{% block content %}
10+
<section class="section">
11+
<div class="container is-fluid">
12+
<div class="columns is-vcentered mb-5">
13+
<div class="column">
14+
<h1 class="title is-3">Curation Dashboard</h1>
15+
<h2 class="subtitle is-5 has-text-grey">{{ vulnerability_id }}</h2>
16+
</div>
17+
18+
<div class="column is-narrow">
19+
<div class="box p-4 has-background-white shadow-soft" style="min-width: 260px; border-radius: 5px;">
20+
<div class="is-flex is-justify-content-space-between is-align-items-center mb-2">
21+
<span class="is-size-7 has-text-grey-dark uppercase has-text-weight-bold mr-2 mt-1">
22+
Progress
23+
</span>
24+
<span class="tag is-info is-light has-text-weight-bold" id="progress-text">0 / 0</span>
25+
</div>
26+
<progress class="progress is-info is-small mb-0" id="main-progress" value="0" max="100"></progress>
27+
</div>
28+
</div>
29+
30+
</div>
31+
32+
<div class="columns">
33+
<div class="column is-3">
34+
<h3 class="title is-5">Advisory Summaries</h3>
35+
<div id="summaries-container" style="max-height: 800px; overflow-y: auto;">
36+
{% for avid, text in advisory_summaries.items %}
37+
<div class="notification is-info is-light p-3 mb-3">
38+
<p><strong>{{ avid }}</strong></p>
39+
<div class="summary-text">{{ text|normalize_links|urlize }}</div>
40+
</div>
41+
{% empty %}
42+
<div class="notification is-light p-3 mb-3 has-text-grey">
43+
No summaries available.
44+
</div>
45+
{% endfor %}
46+
</div>
47+
</div>
48+
49+
<div class="column is-9">
50+
<div class="box">
51+
<div class="mb-4">
52+
<h4 class="title is-4 mb-1" id="current-purl"></h4>
53+
<p class="help" id="conflict-reason"></p>
54+
</div>
55+
56+
<div class="table-container" style="max-height: 800px; overflow-y: auto; border: 1px solid #dbdbdb; border-radius: 6px;">
57+
<table class="table is-bordered is-narrow is-fullwidth is-hoverable">
58+
<thead style="position: sticky; top: 0; background: white; z-index: 10;">
59+
<tr id="table-header">
60+
61+
</tr>
62+
</thead>
63+
<tbody id="curation-body">
64+
65+
</tbody>
66+
</table>
67+
</div>
68+
69+
<div class="level mt-5">
70+
<div class="level-left">
71+
<button class="button is-light" id="prev-btn" onclick="app.navigate(-1)">
72+
<span class="icon"><i class="fa fa-chevron-left"></i></span>
73+
<span>Previous Item</span>
74+
</button>
75+
</div>
76+
<div class="level-right">
77+
<button class="button is-info" id="next-btn" onclick="app.navigate(1)">
78+
<span>Next Item</span>
79+
<span class="icon"><i class="fa fa-chevron-right"></i></span>
80+
</button>
81+
<button class="button is-info is-hidden" id="finish-btn" disabled>
82+
<span class="icon"><i class="fa fa-check"></i></span>
83+
<span>Validate</span>
84+
</button>
85+
</div>
86+
</div>
87+
</div>
88+
</div>
89+
</div>
90+
</div>
91+
</section>
92+
{% endblock %}
93+
94+
95+
{% block scripts %}
96+
<script>
97+
const baseAdvisoryUrl = "{% url 'advisory_details' 0 %}";
98+
const curationItems = {{ curation_items|safe }};
99+
</script>
100+
<script src="{% static 'js/package_curation.min.js' %}"></script>
101+
{% endblock %}

vulnerabilities/templatetags/utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#
99

1010

11+
import re
12+
1113
from aboutcode.pipeline import humanize_time
1214
from django import template
1315

@@ -52,3 +54,13 @@ def querystring(request, **kwargs):
5254
query[key] = value
5355

5456
return query.urlencode()
57+
58+
59+
@register.filter
60+
def normalize_links(value):
61+
"""Normalize Markdown URLs."""
62+
if not value:
63+
return ""
64+
65+
markdown_links = re.compile(r"\[([^\]]+)\]\((https?://[^\s)]+)\s*\)")
66+
return markdown_links.sub(r"\1 \2", value)
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
.state-affected {
2+
background-color: #f14668 !important;
3+
color: #fff !important;
4+
}
5+
6+
.state-fixed {
7+
background-color: #48c78e !important;
8+
color: #fff !important;
9+
}
10+
11+
.table {
12+
table-layout: fixed !important;
13+
width: 100% !important;
14+
}
15+
16+
.table th {
17+
border-width: 0 1px 1px 1px !important;
18+
border-color: #dbdbdb !important;
19+
background-color: #fff !important;
20+
}
21+
22+
#table-header th {
23+
height: 140px;
24+
vertical-align: top !important;
25+
padding: 0.75rem 0.5rem !important;
26+
}
27+
28+
#table-header th>div {
29+
display: inline-flex !important;
30+
flex-direction: column !important;
31+
justify-content: space-between !important;
32+
height: 100% !important;
33+
width: 100%;
34+
}
35+
36+
#table-header th:first-child {
37+
width: 100px !important;
38+
}
39+
40+
#table-header th:nth-child(2) {
41+
width: 100px !important;
42+
}
43+
44+
#table-header th:nth-child(n+3) {
45+
width: 100px !important;
46+
}
47+
48+
49+
.curation-cell,
50+
.advisory-cell,
51+
#curation-body td,
52+
.folded-row-marker,
53+
.range-data-cell,
54+
.help-instructions {
55+
font-size: 0.8rem !important;
56+
}
57+
58+
.curation-cell {
59+
cursor: pointer;
60+
text-align: center !important;
61+
font-weight: 700;
62+
user-select: none;
63+
transition: background-color 0.1s ease;
64+
border: 1px solid #dbdbdb !important;
65+
}
66+
67+
.curation-cell:hover {
68+
filter: brightness(0.95);
69+
box-shadow: inset 0 0 0 1px #3273dc;
70+
}
71+
72+
.advisory-cell,
73+
.advisory-cell * {
74+
cursor: not-allowed;
75+
}
76+
77+
.advisory-cell {
78+
text-align: center !important;
79+
vertical-align: middle !important;
80+
border: 1px solid #dbdbdb !important;
81+
}
82+
83+
.folded-row-marker,
84+
.range-row-marker {
85+
cursor: pointer;
86+
text-align: center;
87+
color: #4a4a4a;
88+
font-weight: 600;
89+
background-color: #f5f5f5;
90+
}
91+
92+
.folded-row-marker {
93+
height: 30px;
94+
}
95+
96+
.folded-row-marker:hover,
97+
.range-row-marker:hover {
98+
background-color: #ededed;
99+
color: #0a0a0a;
100+
}
101+
102+
.folded-row-marker .icon {
103+
transition: transform 0.2s ease;
104+
}
105+
106+
.folded-row-marker.is-expanded .icon {
107+
transform: rotate(180deg);
108+
}
109+
110+
.range-row-marker {
111+
font-size: 0.8rem;
112+
padding: 6px 0;
113+
border: 1px solid #dbdbdb !important;
114+
}
115+
116+
.range-data-cell {
117+
background-color: #fafafa;
118+
padding: 6px !important;
119+
vertical-align: top !important;
120+
border: 1px solid #dbdbdb !important;
121+
122+
white-space: normal !important;
123+
word-break: break-all !important;
124+
overflow-wrap: break-word !important;
125+
}
126+
127+
#summaries-container p strong {
128+
display: block;
129+
word-break: break-all;
130+
overflow-wrap: break-word;
131+
line-height: 1.3;
132+
margin-bottom: 4px;
133+
}
134+
135+
.summary-text {
136+
font-size: 0.85rem;
137+
line-height: 1.4;
138+
white-space: pre-wrap;
139+
word-break: break-word;
140+
}
141+
142+
div.summary-text a:link,
143+
div.summary-text a {
144+
color: #3273dc !important;
145+
}
146+
147+
div.summary-text a:visited {
148+
color: #8a4de8 !important;
149+
}
150+
151+
div.summary-text a:hover {
152+
color: #0a0a0a !important;
153+
}
154+
155+
.advisory-wrapper {
156+
width: 100%;
157+
word-break: break-all;
158+
white-space: normal;
159+
}
160+
161+
.advisory-link {
162+
display: inline-flex;
163+
align-items: center;
164+
gap: 4px;
165+
text-align: left;
166+
text-decoration: underline;
167+
white-space: normal;
168+
flex-wrap: wrap;
169+
}
170+
171+
.advisory-link .icon {
172+
font-size: 0.75rem;
173+
flex-shrink: 0;
174+
}

0 commit comments

Comments
 (0)