Skip to content

Commit 1ffa2db

Browse files
committed
search attributes scanoverview
1 parent 5b88a25 commit 1ffa2db

File tree

5 files changed

+254
-167
lines changed

5 files changed

+254
-167
lines changed

src/panels/ScanOverviewPanel.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ export class ScanOverview {
105105
webview.postMessage({ type: "applySearchFlowName", value: data.value });
106106
break;
107107
}
108+
case "searchAttributes": {
109+
webview.postMessage({ type: "applySearchAttributes", value: data.value });
110+
break;
111+
}
108112
case "init-view": {
109113
if (scanResults) {
110114
webview.postMessage({

src/panels/ViolationOverviewPanel.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ export class ViolationOverview {
9898
ScanOverview.createOrShow(this._extensionUri, data.value);
9999
break;
100100
}
101+
case "searchFlowName": {
102+
webview.postMessage({ type: "applySearchFlowName", value: data.value });
103+
break;
104+
}
105+
case "searchAttributes": {
106+
webview.postMessage({ type: "applySearchAttributes", value: data.value });
107+
break;
108+
}
101109
case "init-view": {
102110
webview.postMessage({
103111
type: "init",

webviews/components/Navigation.svelte

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@
1818
1919
function download() { dispatch("download", currentPage); }
2020
21-
// Global search → only flow name
2221
function onSearch(e) {
2322
tsvscode.postMessage({ type: "searchFlowName", value: e.target.value });
2423
}
24+
function onSecondarySearch(e) {
25+
tsvscode.postMessage({ type: "searchAttributes", value: e.target.value });
26+
}
2527
</script>
2628

2729
<div class="nav-menu">
30+
<!-- Left Button -->
2831
{#if currentPage === "overview"}
2932
<div class="nav-button-left">
3033
<button on:click={viewAll}>All Results</button>
@@ -35,15 +38,25 @@
3538
</div>
3639
{/if}
3740

38-
<div class="center-container">
41+
<!-- Search Bar (in-line, smaller) -->
42+
<div class="search-container">
3943
<input
4044
type="search"
4145
placeholder="Search flow name..."
4246
on:input={onSearch}
4347
class="flow-search-input"
4448
/>
4549
</div>
50+
<div class="search-container">
51+
<input
52+
type="search"
53+
placeholder="Search attributes..."
54+
on:input={onSecondarySearch}
55+
class="flow-search-input"
56+
/>
57+
</div>
4658

59+
<!-- Download Button -->
4760
{#if showDownload}
4861
<div class="nav-button-right">
4962
<button on:click={download}>Download</button>
@@ -52,16 +65,68 @@
5265
</div>
5366

5467
<style>
55-
.nav-menu { display: flex; justify-content: space-between; align-items: center; color: white; }
56-
.center-container { display: flex; flex-direction: column; align-items: center; flex: 1; }
57-
.banner { width: 100%; height: 75px; text-align: center; }
58-
.banner img { width: 100%; height: auto; }
68+
.nav-menu {
69+
display: flex;
70+
justify-content: space-between;
71+
align-items: center;
72+
color: white;
73+
height: 50px;
74+
padding: 0 12px;
75+
gap: 12px;
76+
}
77+
78+
.search-container {
79+
flex: 1;
80+
display: flex;
81+
justify-content: center;
82+
}
83+
5984
.flow-search-input {
60-
margin-top: 6px; width: 100%; max-width: 260px; height: 34px;
61-
padding: 6px 12px; border: none; border-radius: 17px;
62-
background: rgba(255,255,255,0.95); font-size: 13px;
63-
box-shadow: 0 2px 4px rgba(0,0,0,0.2); outline: none;
85+
width: 100%;
86+
max-width: 240px;
87+
height: 28px; /* Smaller height */
88+
padding: 4px 10px;
89+
border: none;
90+
border-radius: 14px;
91+
background: rgba(255, 255, 255, 0.95);
92+
font-size: 13px;
93+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
94+
outline: none;
95+
transition: all 0.2s;
96+
}
97+
98+
.flow-search-input:focus {
99+
box-shadow: 0 0 0 2px #2765ae;
100+
background: white;
101+
}
102+
103+
.flow-search-input::placeholder {
104+
color: #999;
105+
font-style: italic;
106+
}
107+
108+
button {
109+
background: #2765ae;
110+
color: white;
111+
border: none;
112+
border-radius: 14px;
113+
width: 140px;
114+
height: 32px;
115+
cursor: pointer;
116+
font-size: 13px;
117+
font-weight: 500;
118+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
119+
transition: all 0.2s;
120+
}
121+
122+
button:hover {
123+
background: #1e5190;
124+
transform: translateY(-1px);
125+
}
126+
127+
.nav-button-left,
128+
.nav-button-right {
129+
display: flex;
130+
align-items: center;
64131
}
65-
.flow-search-input:focus { box-shadow: 0 0 0 2px #2765ae; }
66-
button { background: #2765ae; color: white; border: none; border-radius: 15px; width: 150px; cursor: pointer; }
67132
</style>
Lines changed: 94 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,107 @@
11
<script lang="ts">
2-
import { TabulatorFull as Tabulator } from "tabulator-tables";
3-
import { onMount } from "svelte";
2+
import { TabulatorFull as Tabulator } from "tabulator-tables";
3+
import { onMount } from "svelte";
44
5-
export let scanResults: any[] = [];
6-
let tableComponent: HTMLDivElement;
7-
let table: Tabulator;
8-
let printData: any[] = [];
5+
export let scanResults: any[] = [];
6+
let tableComponent: HTMLDivElement;
7+
let table: Tabulator;
8+
let printData: any[] = [];
99
10-
const detailButton = () => `<button style="background:#2765ae;border-radius:10px;">Details</button>`;
10+
const detailButton = () =>
11+
`<button style="background:#2765ae;border-radius:10px;">Details</button>`;
1112
12-
onMount(() => {
13-
printData = scanResults.map(r => {
14-
const obj = { ...r };
15-
delete obj.flow;
16-
delete obj.ruleResults;
17-
return obj;
18-
});
13+
onMount(() => {
14+
printData = scanResults.map((r) => {
15+
const obj = { ...r };
16+
delete obj.flow;
17+
delete obj.ruleResults;
18+
return obj;
19+
});
1920
20-
table = new Tabulator(tableComponent, {
21-
data: scanResults,
22-
reactiveData: true,
23-
layout: "fitColumns",
24-
columns: [
25-
{ title: "# Results", field: "resultCount", hozAlign: "center", bottomCalc: "count", width: 100 },
26-
{
27-
title: "Label", field: "label", minWidth: 150, formatter: "link",
28-
formatterParams: (cell: any) => ({ label: cell.getValue(), url: "javascript:void(0);" }),
29-
cellClick: (_e: any, cell: any) => {
30-
tsvscode.postMessage({ type: "goToFile", value: cell.getRow().getData().flow });
31-
},
32-
headerFilter: true,
33-
headerFilterPlaceholder: "",
34-
},
35-
{ title: "Flow Type", field: "type", minWidth: 120 },
36-
{
37-
title: "Details", formatter: detailButton, width: 100, hozAlign: "center", print: false,
38-
cellClick: (_e: any, cell: any) => {
39-
tsvscode.postMessage({ type: "goToDetails", value: cell.getRow().getData() });
40-
},
41-
},
42-
],
43-
});
21+
table = new Tabulator(tableComponent, {
22+
data: scanResults,
23+
reactiveData: true,
24+
layout: "fitColumns",
25+
columns: [
26+
{
27+
title: "# Results",
28+
field: "resultCount",
29+
hozAlign: "center",
30+
bottomCalc: "count",
31+
width: 100,
32+
},
33+
{
34+
title: "Label",
35+
field: "label",
36+
minWidth: 150,
37+
formatter: "link",
38+
formatterParams: (cell: any) => ({
39+
label: cell.getValue(),
40+
url: "javascript:void(0);",
41+
}),
42+
cellClick: (_e: any, cell: any) => {
43+
tsvscode.postMessage({
44+
type: "goToFile",
45+
value: cell.getRow().getData().flow,
46+
});
47+
},
48+
headerFilter: true,
49+
headerFilterPlaceholder: "",
50+
},
51+
{ title: "Flow Type", field: "type", minWidth: 120, headerFilter: true, headerFilterPlaceholder: "" },
52+
{
53+
title: "Details",
54+
formatter: detailButton,
55+
width: 100,
56+
hozAlign: "center",
57+
print: false,
58+
cellClick: (_e: any, cell: any) => {
59+
tsvscode.postMessage({
60+
type: "goToDetails",
61+
value: cell.getRow().getData(),
62+
});
63+
},
64+
},
65+
],
4466
});
67+
});
68+
69+
export function download() {
70+
tsvscode.postMessage({ type: "download", value: printData });
71+
}
4572
46-
export function download() {
47-
tsvscode.postMessage({ type: "download", value: printData });
73+
function onMessage(e: MessageEvent) {
74+
const msg = e.data;
75+
if (msg.type === "applySearchFlowName") {
76+
const term = (msg.value ?? "").trim();
77+
if (!term) {
78+
table?.clearHeaderFilter();
79+
return;
80+
}
81+
table?.setHeaderFilterValue("label", term);
4882
}
4983
50-
function onMessage(e: MessageEvent) {
51-
const msg = e.data;
52-
if (msg.type === "applySearchFlowName") {
53-
const term = (msg.value ?? "").trim();
54-
if (!term) {
55-
table?.clearHeaderFilter();
56-
return;
57-
}
58-
table?.setHeaderFilterValue("label", term);
59-
}
84+
if (msg.type === "applySearchAttributes") {
85+
const term = (msg.value ?? "").trim();
86+
87+
if (term) {
88+
table?.setHeaderFilterValue("resultCount", term);
89+
table?.setHeaderFilterValue("type", term);
90+
} else {
91+
table?.clearHeaderFilter("resultCount");
92+
table?.clearHeaderFilter("type");
6093
}
94+
return;
95+
}
96+
97+
// Add new OR filter with unique ID
98+
table?.addFilter([
99+
{ field: "resultCount", type: "like", value: term },
100+
{ field: "type", type: "like", value: term },
101+
], "or", "attributes-or-filter");
102+
}
103+
61104
</script>
62105

63106
<svelte:window on:message={onMessage} />
64-
<div bind:this={tableComponent} class="tabulator-table" />
107+
<div bind:this={tableComponent} class="tabulator-table" />

0 commit comments

Comments
 (0)