add navbar select, add continous scroll, paging persistance

This commit is contained in:
Christoph Kluge
2025-01-10 18:02:54 +01:00
parent 5ea11a5ad2
commit e55798944e
5 changed files with 150 additions and 64 deletions

View File

@@ -26,6 +26,7 @@
export let username;
export let authlevel;
export let clusters;
export let subClusters;
export let roles;
let isOpen = false;
@@ -138,11 +139,13 @@
{#if screenSize > 1500 || screenSize < 768}
<NavbarLinks
{clusters}
{subClusters}
links={views.filter((item) => item.requiredRole <= authlevel)}
/>
{:else if screenSize > 1300}
<NavbarLinks
{clusters}
{subClusters}
links={views.filter(
(item) => item.requiredRole <= authlevel && item.menu != "Info",
)}
@@ -156,6 +159,7 @@
<DropdownMenu class="dropdown-menu-lg-end">
<NavbarLinks
{clusters}
{subClusters}
direction="right"
links={views.filter(
(item) =>
@@ -168,6 +172,7 @@
{:else}
<NavbarLinks
{clusters}
{subClusters}
links={views.filter(
(item) => item.requiredRole <= authlevel && item.menu == "none",
)}
@@ -180,6 +185,7 @@
<DropdownMenu class="dropdown-menu-lg-end">
<NavbarLinks
{clusters}
{subClusters}
direction="right"
links={views.filter(
(item) => item.requiredRole <= authlevel && item.menu == 'Jobs',
@@ -196,6 +202,7 @@
<DropdownMenu class="dropdown-menu-lg-end">
<NavbarLinks
{clusters}
{subClusters}
direction="right"
links={views.filter(
(item) => item.requiredRole <= authlevel && item.menu == 'Groups',
@@ -212,6 +219,7 @@
<DropdownMenu class="dropdown-menu-lg-end">
<NavbarLinks
{clusters}
{subClusters}
direction="right"
links={views.filter(
(item) => item.requiredRole <= authlevel && item.menu == 'Info',

View File

@@ -3,6 +3,7 @@
Properties:
- `clusters [String]`: List of cluster names
- `subClusters map[String][]string`: Map of subclusters by cluster names
- `links [Object]`: Pre-filtered link objects based on user auth
- `direction String?`: The direcion of the drop-down menue [default: down]
-->
@@ -18,6 +19,7 @@
} from "@sveltestrap/sveltestrap";
export let clusters;
export let subClusters;
export let links;
export let direction = "down";
</script>
@@ -47,6 +49,13 @@
>
Node List
</DropdownItem>
{#each subClusters[cluster.name] as subCluster}
<DropdownItem class="py-1 px-2"
href={item.href + 'list/' + cluster.name + '/' + subCluster}
>
{subCluster} Node List
</DropdownItem>
{/each}
</DropdownMenu>
</Dropdown>
{/each}

View File

@@ -10,15 +10,16 @@
-->
<script>
import { queryStore, gql, getContextClient } from "@urql/svelte";
import { getContext } from "svelte";
import { queryStore, gql, getContextClient, mutationStore } from "@urql/svelte";
import { Row, Col, Card, Table, Spinner } from "@sveltestrap/sveltestrap";
import { init, stickyHeader } from "../generic/utils.js";
import { stickyHeader } from "../generic/utils.js";
import NodeListRow from "./nodelist/NodeListRow.svelte";
import Pagination from "../generic/joblist/Pagination.svelte";
export let cluster;
export let subCluster = "";
export const ccconfig = null;
export let ccconfig = null;
export let selectedMetrics = [];
export let selectedResolution = 0;
export let hostnameFilter = "";
@@ -26,8 +27,9 @@
export let from = null;
export let to = null;
// let usePaging = ccconfig.node_list_usePaging
let itemsPerPage = 10 // usePaging ? ccconfig.node_list_jobsPerPage : 10;
// Decouple from Job List Paging Params?
let usePaging = ccconfig.job_list_usePaging
let itemsPerPage = usePaging ? ccconfig.plot_list_jobsPerPage : 10;
let page = 1;
let paging = { itemsPerPage, page };
@@ -37,7 +39,8 @@
(x) => (headerPaddingTop = x),
);
const { query: initq } = init();
// const { query: initq } = init();
const initialized = getContext("initialized");
const client = getContextClient();
const nodeListQuery = gql`
query ($cluster: String!, $subCluster: String!, $nodeFilter: String!, $metrics: [String!], $scopes: [MetricScope!]!, $from: Time!, $to: Time!, $paging: PageRequest!, $selectedResolution: Int) {
@@ -88,6 +91,50 @@
}
`
const updateConfigurationMutation = ({ name, value }) => {
return mutationStore({
client: client,
query: gql`
mutation ($name: String!, $value: String!) {
updateConfiguration(name: $name, value: $value)
}
`,
variables: { name, value },
});
};
// Decouple from Job List Paging Params?
function updateConfiguration(value, page) {
updateConfigurationMutation({
name: "plot_list_jobsPerPage",
value: value,
}).subscribe((res) => {
if (res.fetching === false && !res.error) {
nodes = [] // Empty List
paging = { itemsPerPage: value, page: page }; // Trigger reload of nodeList
} else if (res.fetching === false && res.error) {
throw res.error;
}
});
}
if (!usePaging) {
window.addEventListener('scroll', () => {
let {
scrollTop,
scrollHeight,
clientHeight
} = document.documentElement;
// Add 100 px offset to trigger load earlier
if (scrollTop + clientHeight >= scrollHeight - 100 && $nodesQuery?.data != null && $nodesQuery.data?.nodeMetricsList.hasNextPage) {
let pendingPaging = { ...paging }
pendingPaging.page += 1
paging = pendingPaging
};
});
};
$: nodesQuery = queryStore({
client: client,
query: nodeListQuery,
@@ -105,76 +152,86 @@
requestPolicy: "network-only", // Resolution queries are cached, but how to access them? For now: reload on every change
});
$: matchedNodes = $nodesQuery.data?.nodeMetricsList.totalNodes || 0;
$: orderedData = $nodesQuery.data?.nodeMetricsList.items.sort((a, b) => a.host.localeCompare(b.host));
let nodes = [];
$: if ($initialized && $nodesQuery.data) {
if (usePaging) {
nodes = [...$nodesQuery.data.nodeMetricsList.items].sort((a, b) => a.host.localeCompare(b.host));
} else {
nodes = nodes.concat([...$nodesQuery.data.nodeMetricsList.items].sort((a, b) => a.host.localeCompare(b.host)))
}
}
$: matchedNodes = $nodesQuery.data?.nodeMetricsList.totalNodes || matchedNodes;
</script>
{#if $nodesQuery.error}
<Row>
<Col>
<Card body color="danger">{$nodesQuery.error.message}</Card>
</Col>
</Row>
{:else if $nodesQuery.fetching }
<Row>
<Col>
<Spinner />
</Col>
</Row>
{:else if $initq?.data && $nodesQuery?.data}
<Row>
<div class="col cc-table-wrapper">
<Table cellspacing="0px" cellpadding="0px">
<thead>
<tr>
<th
class="position-sticky top-0 text-capitalize"
scope="col"
style="padding-top: {headerPaddingTop}px;"
>
{cluster} Node Info
</th>
<Row>
<div class="col cc-table-wrapper">
<Table cellspacing="0px" cellpadding="0px">
<thead>
<tr>
<th
class="position-sticky top-0 text-capitalize"
scope="col"
style="padding-top: {headerPaddingTop}px;"
>
{cluster} Node Info
</th>
{#each selectedMetrics as metric (metric)}
<th
class="position-sticky top-0 text-center"
scope="col"
style="padding-top: {headerPaddingTop}px"
>
{metric} ({systemUnits[metric]})
</th>
{/each}
</tr>
</thead>
<tbody>
{#each orderedData as nodeData (nodeData.host)}
{#each selectedMetrics as metric (metric)}
<th
class="position-sticky top-0 text-center"
scope="col"
style="padding-top: {headerPaddingTop}px"
>
{metric} ({systemUnits[metric]})
</th>
{/each}
</tr>
</thead>
<tbody>
{#if $nodesQuery.error}
<Row>
<Col>
<Card body color="danger">{$nodesQuery.error.message}</Card>
</Col>
</Row>
{:else}
{#each nodes as nodeData (nodeData.host)}
<NodeListRow {nodeData} {cluster} {selectedMetrics}/>
{:else}
<tr>
<td>No nodes found </td>
<td colspan={selectedMetrics.length + 1}> No nodes found </td>
</tr>
{/each}
</tbody>
</Table>
</div>
</Row>
{/if}
{/if}
{#if $nodesQuery.fetching || !$nodesQuery.data}
<tr>
<td colspan={selectedMetrics.length + 1}>
<div style="text-align:center;">
<p><b>Loading nodes {nodes.length + 1} to {nodes.length + paging.itemsPerPage} {matchedNodes ? `of ${matchedNodes} total` : ``}</b></p>
<Spinner secondary />
</div>
</td>
</tr>
{/if}
</tbody>
</Table>
</div>
</Row>
{#if true} <!-- usePaging -->
{#if usePaging}
<Pagination
bind:page
{itemsPerPage}
itemText="Nodes"
totalItems={matchedNodes}
on:update-paging={({ detail }) => {
paging = { itemsPerPage: detail.itemsPerPage, page: detail.page }
// if (detail.itemsPerPage != itemsPerPage) {
// updateConfiguration(detail.itemsPerPage.toString(), detail.page);
// } else {
// // nodes = []
// paging = { itemsPerPage: detail.itemsPerPage, page: detail.page };
// }
if (detail.itemsPerPage != itemsPerPage) {
updateConfiguration(detail.itemsPerPage.toString(), detail.page);
} else {
nodes = []
paging = { itemsPerPage: detail.itemsPerPage, page: detail.page };
}
}}
/>
{/if}