Migrate user list and analysis view

This commit is contained in:
Christoph Kluge
2025-06-02 13:51:15 +02:00
parent 0b529a5c3c
commit 703556d893
4 changed files with 330 additions and 262 deletions

View File

@@ -31,10 +31,8 @@
} from "./generic/utils.js";
import Filters from "./generic/Filters.svelte";
const {} = init();
export let type;
export let filterPresets;
/* Svelte 5 Props */
let { type, filterPresets } = $props();
// By default, look at the jobs of the last 30 days:
if (filterPresets?.startTime == null) {
@@ -51,35 +49,38 @@
"Invalid list type provided!",
);
let filterComponent; // see why here: https://stackoverflow.com/questions/58287729/how-can-i-export-a-function-from-a-svelte-component-that-changes-a-value-in-the
let jobFilters = [];
let nameFilter = "";
let sorting = { field: "totalJobs", direction: "down" };
/* Const Init */
const {} = init();
const client = getContextClient();
$: stats = queryStore({
client: client,
query: gql`
query($jobFilters: [JobFilter!]!) {
rows: jobsStatistics(filter: $jobFilters, groupBy: ${type}) {
id
name
totalJobs
totalWalltime
totalCoreHours
totalAccHours
}
}`,
variables: { jobFilters },
});
function changeSorting(event, field) {
let target = event.target;
while (target.tagName != "BUTTON") target = target.parentElement;
/* State Init*/
let filterComponent = $state(); // see why here: https://stackoverflow.com/questions/58287729/how-can-i-export-a-function-from-a-svelte-component-that-changes-a-value-in-the
let jobFilters = $state([]);
let nameFilter = $state("");
let sorting = $state({ field: "totalJobs", direction: "down" });
let direction = target.children[0].className.includes("up") ? "down" : "up";
target.children[0].className = `bi-sort-numeric-${direction}`;
sorting = { field, direction };
/* Derived Vars */
let stats = $derived(
queryStore({
client: client,
query: gql`
query($jobFilters: [JobFilter!]!) {
rows: jobsStatistics(filter: $jobFilters, groupBy: ${type}) {
id
name
totalJobs
totalWalltime
totalCoreHours
totalAccHours
}
}`,
variables: { jobFilters },
})
);
/* Functions */
function changeSorting(field) {
sorting = { field, direction: sorting?.direction == "down" ? "up" : "down" };
}
function sort(stats, sorting, nameFilter) {
@@ -87,10 +88,10 @@
? (a, b) => b.id.localeCompare(a.id)
: (a, b) => a.id.localeCompare(b.id)
// "-50": Forces empty strings to the end of the list
// Force empty or undefined strings to the end of the list
const nameCmp = sorting.direction == "up"
? (a, b) => (a.name == '') ? -50 : b.name.localeCompare(a.name)
: (a, b) => (b.name == '') ? -50 : a.name.localeCompare(b.name)
? (a, b) => !a?.name ? 1 : (!b?.name ? -1 : (b.name.localeCompare(a.name)))
: (a, b) => !a?.name ? 1 : (!b?.name ? -1 : (a.name.localeCompare(b.name)))
const intCmp = sorting.direction == "up"
? (a, b) => a[sorting.field] - b[sorting.field]
@@ -105,6 +106,7 @@
}
}
/* On Mount */
onMount(() => filterComponent.updateFilters());
</script>
@@ -129,7 +131,7 @@
{filterPresets}
startTimeQuickSelect={true}
menuText="Only {type.toLowerCase()}s with jobs that match the filters will show up"
on:update-filters={({ detail }) => {
applyFilters={(detail) => {
jobFilters = detail.filters;
}}
/>
@@ -147,9 +149,14 @@
<Button
color={sorting.field == "id" ? "primary" : "light"}
size="sm"
on:click={(e) => changeSorting(e, "id")}
onclick={() => changeSorting("id")}
>
<Icon name="sort-numeric-down" />
{#if sorting?.field == "id"}
<!-- Note on Icon-Name: Arrow-indicator always down, only alpha-indicator switches -->
<Icon name={`sort-alpha-${sorting?.direction == 'down' ? 'down' : 'down-alt'}`} />
{:else}
<Icon name="three-dots-vertical" />
{/if}
</Button>
</th>
{#if type == "USER"}
@@ -158,9 +165,13 @@
<Button
color={sorting.field == "name" ? "primary" : "light"}
size="sm"
on:click={(e) => changeSorting(e, "name")}
onclick={() => changeSorting("name")}
>
<Icon name="sort-numeric-down" />
{#if sorting?.field == "name"}
<Icon name={`sort-alpha-${sorting?.direction == 'down' ? 'down' : 'down-alt'}`} />
{:else}
<Icon name="three-dots-vertical" />
{/if}
</Button>
</th>
{/if}
@@ -169,9 +180,14 @@
<Button
color={sorting.field == "totalJobs" ? "primary" : "light"}
size="sm"
on:click={(e) => changeSorting(e, "totalJobs")}
onclick={() => changeSorting("totalJobs")}
>
<Icon name="sort-numeric-down" />
{#if sorting?.field == "totalJobs"}
<!-- Note on Icon-Name: Arrow-indicator always down, only numeric-indicator switches -->
<Icon name={`sort-numeric-${sorting?.direction == 'down' ? 'down-alt' : 'down'}`} />
{:else}
<Icon name="three-dots-vertical" />
{/if}
</Button>
</th>
<th scope="col">
@@ -179,9 +195,13 @@
<Button
color={sorting.field == "totalWalltime" ? "primary" : "light"}
size="sm"
on:click={(e) => changeSorting(e, "totalWalltime")}
onclick={() => changeSorting("totalWalltime")}
>
<Icon name="sort-numeric-down" />
{#if sorting?.field == "totalWalltime"}
<Icon name={`sort-numeric-${sorting?.direction == 'down' ? 'down-alt' : 'down'}`} />
{:else}
<Icon name="three-dots-vertical" />
{/if}
</Button>
</th>
<th scope="col">
@@ -189,9 +209,13 @@
<Button
color={sorting.field == "totalCoreHours" ? "primary" : "light"}
size="sm"
on:click={(e) => changeSorting(e, "totalCoreHours")}
onclick={() => changeSorting("totalCoreHours")}
>
<Icon name="sort-numeric-down" />
{#if sorting?.field == "totalCoreHours"}
<Icon name={`sort-numeric-${sorting?.direction == 'down' ? 'down-alt' : 'down'}`} />
{:else}
<Icon name="three-dots-vertical" />
{/if}
</Button>
</th>
<th scope="col">
@@ -199,9 +223,13 @@
<Button
color={sorting.field == "totalAccHours" ? "primary" : "light"}
size="sm"
on:click={(e) => changeSorting(e, "totalAccHours")}
onclick={() => changeSorting("totalAccHours")}
>
<Icon name="sort-numeric-down" />
{#if sorting?.field == "totalAccHours"}
<Icon name={`sort-numeric-${sorting?.direction == 'down' ? 'down-alt' : 'down'}`} />
{:else}
<Icon name="three-dots-vertical" />
{/if}
</Button>
</th>
</tr>