Filter installed charts by namespace (#101)

* filter by namespace

* exists

* Some improvements, one thing resolved

* cleanup

* merge

* allow filtering by name

* filter by namespace

* changes

* change url parameter name

* keep filtered namespaces when refreshing and combine inpt and namespace filtering

* Refactoring

* Cleanup

* Forced NS handle

* remove else

Co-authored-by: Andrei Pohilko <andrei.pokhilko@gmail.com>
This commit is contained in:
ronahk
2022-11-23 13:38:09 +02:00
committed by GitHub
parent bedb356b02
commit 3abae8e49e
5 changed files with 157 additions and 54 deletions

View File

@@ -130,12 +130,10 @@
<ul class="list-unstyled" id="cluster">
</ul>
<h4 id="limitNamespace" class="display-none">Forced Namespace: <span></span></h4>
<!-- TODO
<h4 class="mt-4">Namespaces</h4>
<ul class="list-unstyled" id="namespaces">
<p id="limitNamespace" class="display-none ps-3"><span class="fw-bold"></span> (forced)</p>
<ul class="list-unstyled" id="namespace">
</ul>
-->
</form>
</div>
<!-- /FILTER BLOCK -->
@@ -145,9 +143,12 @@
<!-- INSTALLED LIST -->
<div class="col ms-2" id="installedList">
<div class="col rounded rounded-1 b-shadow header">
<div class="bg-white rounded-top m-0">
<div class="bg-white rounded-top m-0 spaced-out">
<h2 class="m-0 p-1"><img class="m-2 mx-3 me-2" src="static/helm-gray.svg" alt="Installed Charts">Installed
Charts (<span></span>)</h2>
<div class="form-outline w-25">
<input type="text" id="installedSearch" class="form-control form-control-sm" placeholder="Filter..." />
</div>
</div>
<div class="bg-secondary rounded-bottom m-0 row p-2">
<div class="col-4 hdr-name">Name</div>
@@ -163,6 +164,8 @@
<div class="bg-white rounded shadow p-3 display-none no-charts">Looks like you don't have any charts
installed. "Repository" section may be a good place to start.
</div>
<div class="bg-white rounded shadow p-3 display-none all-filtered">There are no releases matching your filter criteria. Reset your filters or install more charts.
</div>
</div>
<!-- /INSTALLED LIST -->
</div>

View File

@@ -6,19 +6,21 @@ function loadChartsList() {
$.getJSON("/api/helm/charts").fail(function (xhr) {
reportError("Failed to get list of charts", xhr)
}).done(function (data) {
chartsCards.empty()
chartsCards.empty().hide()
$("#installedList .header h2 span").text(data.length)
data.forEach(function (elm) {
let card = buildChartCard(elm);
chartsCards.append(card)
})
filterInstalledList(chartsCards.find(".row"))
chartsCards.show()
if (!data.length) {
$("#installedList .no-charts").show()
}
})
}
function buildChartCard(elm) {
const card = $(`<div class="row m-0 py-4 bg-white rounded-1 b-shadow border-4 border-start">
<div class="col-4 rel-name"><span class="link">release-name</span><div></div></div>
@@ -33,16 +35,16 @@ function buildChartCard(elm) {
$.getJSON("/api/helm/repo/search?name=" + chartName).fail(function (xhr) {
reportError("Failed to get repo name for charts", xhr)
}).done(function (data) {
if(data.length > 0) {
if (data.length > 0) {
$.getJSON("/api/helm/charts/show?name=" + data[0].name).fail(function (xhr) {
reportError("Failed to get list of charts", xhr)
}).done(function (data) {
if(data) {
var res = data[0]
if(res.icon) {
if (data) {
const res = data[0];
if (res.icon) {
card.find(".rel-name").attr("style", "background-image: url(" + res.icon + ")")
}
if(res.description) {
if (res.description) {
card.find(".rel-name div").text(res.description)
}
}
@@ -57,6 +59,10 @@ function buildChartCard(elm) {
card.find(".rel-chart span").text(elm.chart)
card.find(".rel-date span").text(getAge(elm))
card.data("namespace", elm.namespace)
card.data("name", elm.name)
card.data("chart", elm.chart)
statusStyle(elm.status, card, card.find(".rel-status span"))
card.find("a").attr("href", '#context=' + getHashParam('context') + '&namespace=' + elm.namespace + '&name=' + elm.name)
@@ -74,3 +80,6 @@ function buildChartCard(elm) {
return card;
}
$("#installedSearch").keyup(function () {
filterInstalledList($("#installedList .body .row"))
})

View File

@@ -131,7 +131,7 @@ function repoChartClicked() {
window.location.reload()
} else {
const contexts = $("body").data("contexts")
contextNamespace = contexts.filter(obj => {return obj.Name === getHashParam("context")})[0].Namespace
const contextNamespace = contexts.filter(obj => {return obj.Name === getHashParam("context")})[0].Namespace
popUpUpgrade(elm, contextNamespace)
}
}

View File

@@ -1,32 +1,15 @@
$(function () {
const clusterSelect = $("#cluster");
clusterSelect.change(function () {
window.location.href = "/#context=" + clusterSelect.find("input:radio:checked").val()
window.location.reload()
})
$.getJSON("/api/kube/contexts").fail(function (xhr) {
reportError("Failed to get list of clusters", xhr)
let limNS = null
$.getJSON("/status").fail(function (xhr) { // maybe /options call in the future
reportError("Failed to get tool version", xhr)
}).done(function (data) {
$("body").data("contexts", data)
const context = getHashParam("context")
data.sort((a, b) => (getCleanClusterName(a.Name) > getCleanClusterName(b.Name)) - (getCleanClusterName(a.Name) < getCleanClusterName(b.Name)))
fillClusterList(data, context);
initView(); // can only do it after loading cluster list
$.getJSON("/api/kube/namespaces").fail(function (xhr) {
reportError("Failed to get namespaces", xhr)
}).done(function (res) {
const ns = res.items.map(i => i.metadata.name)
$.each(ns, function (i, item) {
$("#upgradeModal #ns-datalist").append($("<option>", {
value: item,
text: item
}))
fillToolVersion(data)
limNS = data.LimitedToNamespace
if (limNS) {
$("#limitNamespace").show().find("span").text(limNS)
}
fillClusters(limNS)
})
})
})
$.getJSON("/api/scanners").fail(function (xhr) {
reportError("Failed to get list of scanners", xhr)
@@ -38,17 +21,50 @@ $(function () {
}
}
})
$.getJSON("/status").fail(function (xhr) {
reportError("Failed to get tool version", xhr)
}).done(function (data) {
fillToolVersion(data)
if (data.LimitedToNamespace) {
$("#limitNamespace").show().find("span").text(data.LimitedToNamespace)
}
})
})
function fillClusters(limNS) {
const clusterSelect = $("#cluster");
clusterSelect.change(function () {
window.location.href = "/#context=" + clusterSelect.find("input:radio:checked").val()
window.location.reload()
})
const namespaceSelect = $("#namespace");
namespaceSelect.change(function () {
let filteredNamespaces = []
namespaceSelect.find("input:checkbox:checked").each(function () {
filteredNamespaces.push($(this).val());
})
setFilteredNamespaces(filteredNamespaces)
filterInstalledList($("#installedList .body .row"))
})
$.getJSON("/api/kube/contexts").fail(function (xhr) {
reportError("Failed to get list of clusters", xhr)
}).done(function (data) {
$("body").data("contexts", data)
const context = getHashParam("context")
data.sort((a, b) => (getCleanClusterName(a.Name) > getCleanClusterName(b.Name)) - (getCleanClusterName(a.Name) < getCleanClusterName(b.Name)))
fillClusterList(data, context);
$.getJSON("/api/kube/namespaces").fail(function (xhr) {
reportError("Failed to get namespaces", xhr)
}).done(function (res) {
const ns = res.items.map(i => i.metadata.name)
$.each(ns, function (i, item) {
$("#upgradeModal #ns-datalist").append($("<option>", {
value: item,
text: item
}))
})
if (!limNS) {
fillNamespaceList(res.items)
}
initView(); // can only do it after loading cluster and namespace lists
})
})
}
function initView() {
$(".section").hide()
@@ -77,8 +93,10 @@ $("#topNav ul a").click(function () {
$("#topNav ul a").removeClass("active")
const ctx = getHashParam("context")
const filteredNamespace = getHashParam("filteredNamespace")
setHashParam(null, null)
setHashParam("context", ctx)
setHashParam("filteredNamespace", filteredNamespace)
if (self.hasClass("section-repo")) {
setHashParam("section", "repository")
@@ -171,10 +189,9 @@ function fillClusterList(data, context) {
opt.attr('title', elm.Name)
opt.find("input").val(elm.Name).text(label)
opt.find("span").text(label)
if (elm.IsCurrent && !context) {
opt.find("input").prop("checked", true)
setCurrentContext(elm.Name)
} else if (context && elm.Name === context) {
const isCurrent = elm.IsCurrent && !context;
const isSelected = context && elm.Name === context
if (isCurrent || isSelected) {
opt.find("input").prop("checked", true)
setCurrentContext(elm.Name)
}
@@ -182,6 +199,34 @@ function fillClusterList(data, context) {
})
}
function fillNamespaceList(data) {
const curContextNamespaces = $("body").data("contexts").filter(obj => {
return obj.IsCurrent
})
console.log(curContextNamespaces)
if (!data || !data.length) {
$("#namespace").append("default")
return
}
Array.from(data).forEach(function (elm) {
const filteredNamespace = getHashParam("filteredNamespace")
let opt = $('<li><label><input type="checkbox" name="namespace" class="me-2"/><span></span></label></li>');
opt.attr('title', elm.metadata.name)
opt.find("input").val(elm.metadata.name).text(elm.metadata.name)
opt.find("span").text(elm.metadata.name)
if (filteredNamespace) {
if (filteredNamespace.split('+').includes(elm.metadata.name)) {
opt.find("input").prop("checked", true)
}
} else if (curContextNamespaces && curContextNamespaces[0].Namespace === elm.metadata.name) {
opt.find("input").prop("checked", true)
setFilteredNamespaces([elm.metadata.name])
}
$("#namespace").append(opt)
})
}
function setCurrentContext(ctx) {
setHashParam("context", ctx)
$.ajaxSetup({
@@ -259,3 +304,42 @@ $("#cacheClear").click(function () {
window.location.reload()
})
})
function showHideInstalledRelease(card, filteredNamespaces, filterStr) {
let releaseNamespace = card.data("namespace")
let releaseName = card.data("name")
let chartName = card.data("chart")
const shownByNS = !filteredNamespaces || filteredNamespaces.split('+').includes(releaseNamespace);
const shownByStr = releaseName.indexOf(filterStr) >= 0 || chartName.indexOf(filterStr) >= 0
if (shownByNS && shownByStr) {
card.show()
return true
} else {
card.hide()
return false
}
}
function filterInstalledList(list) {
const warnMsg = $("#installedList .all-filtered").hide();
let filterStr = $("#installedSearch").val().toLowerCase();
let filteredNamespaces = getHashParam("filteredNamespace")
let anyShown = false;
list.each(function (ix, card) {
anyShown |= showHideInstalledRelease($(card), filteredNamespaces, filterStr)
})
if (list.length && !anyShown) {
warnMsg.show()
}
}
function setFilteredNamespaces(filteredNamespaces) {
if (filteredNamespaces.length === 0 && getHashParam("filteredNamespace")) {
setHashParam("filteredNamespace")
} else if (filteredNamespaces.length !== 0) {
setHashParam("filteredNamespace", filteredNamespaces.join('+'))
}
}

View File

@@ -103,6 +103,13 @@ body > .container-fluid {
margin-bottom: 0.75rem !important;
}
#installedList .header .spaced-out {
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 0.5rem;
}
#installedList h2 {
font-family: Inter, serif;
font-weight: 700;