Skip to content

Commit 2af8198

Browse files
authored
add path breadcrumbs and copy-to-clipboard button in resource_table_view (#1851)
* add breadcrumbs and copy-to-clipboard button in resource_table_view Signed-off-by: Aayush Kumar <aayush214.kumar@gmail.com> * resolve code format errors Signed-off-by: Aayush Kumar <aayush214.kumar@gmail.com> * fix clipboard tooltip issue and remove spacing between paths Signed-off-by: Aayush Kumar <aayush214.kumar@gmail.com> * fix spacing Signed-off-by: Aayush Kumar <aayush214.kumar@gmail.com> --------- Signed-off-by: Aayush Kumar <aayush214.kumar@gmail.com>
1 parent 0067725 commit 2af8198

File tree

3 files changed

+118
-76
lines changed

3 files changed

+118
-76
lines changed
Lines changed: 96 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,104 @@
11
{% load humanize %}
2+
<div class="mb-4">
3+
<div class="has-text-weight-semibold is-flex is-align-items-center is-family-monospace">
4+
{% if path_segments %}
5+
<span id="resource-path" class="has-text-weight-medium">
6+
{% spaceless %}
7+
{% for subpath, segment in path_segments %}
8+
{% if not forloop.first %}<span class="mx-1">/</span>{% endif %}
9+
{% if not forloop.last %}
10+
<a href="#" class="expand-in-tree has-text-link" data-path="{{ subpath }}" hx-get="{% url 'codebase_resource_table' project.slug %}?path={{ subpath }}" hx-target="#right-pane">{{ segment }}</a>
11+
{% else %}
12+
<span>{{ segment }}</span>
13+
{% endif %}
14+
{% endfor %}
15+
{% endspaceless %}
16+
</span>
17+
<button class="copy-to-clipboard ml-2 is-size-6 is-shadowless is-white" aria-label="Copy path" data-copy="{{ path }}" data-copy-feedback="Path copied!">
18+
<i class="fa-regular fa-copy"></i>
19+
</button>
20+
{% endif %}
21+
</div>
22+
</div>
223

3-
{% if resources %}
4-
<table class="table is-bordered is-striped is-narrow is-fullwidth">
5-
<thead class="is-sticky">
6-
<tr>
7-
<th>Name</th>
8-
<th>Status</th>
9-
<th>Language</th>
10-
<th>License</th>
11-
<th>Alert</th>
12-
</tr>
13-
</thead>
14-
<tbody>
15-
{% for resource in resources %}
24+
<div class="table-scroll-area">
25+
{% if resources %}
26+
<table class="table is-bordered is-striped is-narrow is-fullwidth">
27+
<thead class="is-sticky">
1628
<tr>
17-
<td class="is-flex is-align-items-center break-all" style="min-width: 100px;">
18-
<span class="icon is-small mr-2">
29+
<th>Name</th>
30+
<th>Status</th>
31+
<th>Language</th>
32+
<th>License</th>
33+
<th>Alert</th>
34+
</tr>
35+
</thead>
36+
<tbody>
37+
{% for resource in resources %}
38+
<tr>
39+
<td class="is-flex is-align-items-center break-all" style="min-width: 100px;">
40+
<span class="icon is-small mr-2">
41+
{% if resource.is_dir %}
42+
<i class="fas fa-folder"></i>
43+
{% else %}
44+
<i class="far fa-file"></i>
45+
{% endif %}
46+
</span>
1947
{% if resource.is_dir %}
20-
<i class="fas fa-folder"></i>
48+
<a href="#" class="expand-in-tree" data-path="{{ resource.path }}" hx-get="{% url 'codebase_resource_table' project.slug %}?path={{ resource.path }}" hx-target="#right-pane">{{ resource.name }}</a>
2149
{% else %}
22-
<i class="far fa-file"></i>
50+
<a href="{% url 'resource_detail' project.slug resource.path %}">{{ resource.name }}</a>
2351
{% endif %}
24-
</span>
25-
{% if resource.is_dir %}
26-
<a href="#" class="expand-in-tree" data-path="{{ resource.path }}" hx-get="{% url 'codebase_resource_table' project.slug %}?path={{ resource.path }}" hx-target="#right-pane">{{ resource.name }}</a>
27-
{% else %}
28-
<a href="{% url 'resource_detail' project.slug resource.path %}">{{ resource.name }}</a>
29-
{% endif %}
30-
{% if resource.tag %}
31-
<span class="tag is-rounded ml-2">{{ resource.tag }}</span>
32-
{% endif %}
33-
</td>
34-
<td>
35-
{{ resource.status }}
36-
</td>
37-
<td class="break-all">
38-
{{ resource.programming_language }}
39-
</td>
40-
<td>
41-
{{ resource.detected_license_expression }}
42-
</td>
43-
<td>
44-
{{ resource.compliance_alert }}
45-
</td>
46-
</tr>
47-
{% endfor %}
48-
</tbody>
49-
</table>
52+
{% if resource.tag %}
53+
<span class="tag is-rounded ml-2">{{ resource.tag }}</span>
54+
{% endif %}
55+
</td>
56+
<td>
57+
{{ resource.status }}
58+
</td>
59+
<td class="break-all">
60+
{{ resource.programming_language }}
61+
</td>
62+
<td>
63+
{{ resource.detected_license_expression }}
64+
</td>
65+
<td>
66+
{{ resource.compliance_alert }}
67+
</td>
68+
</tr>
69+
{% endfor %}
70+
</tbody>
71+
</table>
5072

51-
{% if is_paginated %}
52-
<nav class="pagination is-centered mt-4" role="navigation">
53-
{% if page_obj.has_previous %}
54-
<a class="pagination-previous" hx-get="{% url 'codebase_resource_table' project.slug %}?path={{ path }}&page={{ page_obj.previous_page_number }}" hx-target="#right-pane">Previous</a>
55-
{% endif %}
56-
{% if page_obj.has_next %}
57-
<a class="pagination-next" hx-get="{% url 'codebase_resource_table' project.slug %}?path={{ path }}&page={{ page_obj.next_page_number }}" hx-target="#right-pane">Next page</a>
58-
{% endif %}
59-
<ul class="pagination-list">
60-
<li>
61-
<span class="pagination-ellipsis">
62-
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
63-
</span>
64-
</li>
65-
</ul>
66-
</nav>
67-
{% endif %}
68-
{% else %}
69-
<div class="has-text-centered p-6">
70-
<div class="icon is-large has-text-grey-light mb-3">
71-
<i class="fas fa-folder-open fa-3x"></i>
73+
{% if is_paginated %}
74+
<nav class="pagination is-centered mt-4" role="navigation">
75+
{% if page_obj.has_previous %}
76+
<a class="pagination-previous" hx-get="{% url 'codebase_resource_table' project.slug %}?path={{ path }}&page={{ page_obj.previous_page_number }}" hx-target="#right-pane">Previous</a>
77+
{% endif %}
78+
{% if page_obj.has_next %}
79+
<a class="pagination-next" hx-get="{% url 'codebase_resource_table' project.slug %}?path={{ path }}&page={{ page_obj.next_page_number }}" hx-target="#right-pane">Next page</a>
80+
{% endif %}
81+
<ul class="pagination-list">
82+
<li>
83+
<span class="pagination-ellipsis">
84+
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
85+
</span>
86+
</li>
87+
</ul>
88+
</nav>
89+
{% endif %}
90+
{% else %}
91+
<div class="has-text-centered p-6">
92+
<div class="icon is-large has-text-grey-light mb-3">
93+
<i class="fas fa-folder-open fa-3x"></i>
94+
</div>
95+
<p class="has-text-grey">
96+
{% if path %}
97+
No resources found in this directory.
98+
{% else %}
99+
Select a file or folder from the tree to view its contents.
100+
{% endif %}
101+
</p>
72102
</div>
73-
<p class="has-text-grey">
74-
{% if path %}
75-
No resources found in this directory.
76-
{% else %}
77-
Select a file or folder from the tree to view its contents.
78-
{% endif %}
79-
</p>
80-
</div>
81-
{% endif %}
103+
{% endif %}
104+
</div>

scanpipe/templates/scanpipe/resource_tree.html

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,16 @@
5454
}
5555
.right-pane {
5656
flex: 1;
57-
overflow-y: auto;
58-
overflow-x: hidden;
5957
min-width: 0;
6058
transition: opacity 0.2s ease;
59+
display: flex;
60+
flex-direction: column;
61+
height: 100%;
62+
}
63+
.right-pane .table-scroll-area {
64+
flex: 1;
65+
overflow-y: auto;
66+
overflow-x: hidden;
6167
}
6268
.right-pane.collapsed {
6369
opacity: 0;
@@ -105,6 +111,14 @@
105111

106112
{% block scripts %}
107113
<script>
114+
document.body.addEventListener('htmx:afterSwap', function(evt) {
115+
if (evt.target && evt.target.id === 'right-pane') {
116+
if (typeof enableCopyToClipboard === 'function') {
117+
enableCopyToClipboard('.copy-to-clipboard');
118+
}
119+
}
120+
});
121+
108122
// Tree functionality
109123
document.addEventListener("click", async function (e) {
110124
const chevron = e.target.closest("[data-chevron]");

scanpipe/views.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2837,5 +2837,10 @@ def get_queryset(self):
28372837

28382838
def get_context_data(self, **kwargs):
28392839
context = super().get_context_data(**kwargs)
2840-
context["path"] = self.request.GET.get("path", "")
2840+
path = self.request.GET.get("path", "")
2841+
context["path"] = path
2842+
segments = path.strip("/").split("/")
2843+
context["path_segments"] = [
2844+
("/".join(segments[: i + 1]), segment) for i, segment in enumerate(segments)
2845+
]
28412846
return context

0 commit comments

Comments
 (0)