2023-01-27 18:36:58 +01:00
<!--
2024-07-25 17:10:00 +02:00
@component Main component for listing users or projects
Properties:
- `type String?`: The type of list ['USER' || 'PROJECT']
- `filterPresets Object?`: Optional predefined filter values [Default: {} ]
2022-06-22 11:20:57 +02:00
-->
2024-07-25 17:10:00 +02:00
2022-06-22 11:20:57 +02:00
< script >
2024-03-09 10:30:40 +01:00
import { onMount } from "svelte";
import {
Row,
Col,
Button,
Icon,
Table,
Card,
Spinner,
InputGroup,
Input,
} from "@sveltestrap/sveltestrap";
2024-07-26 12:34:18 +02:00
import {
queryStore,
gql,
getContextClient,
} from "@urql/svelte";
import {
init,
scramble,
scrambleNames,
} from "./generic/utils.js";
import Filters from "./generic/Filters.svelte";
2022-06-22 11:20:57 +02:00
2024-03-09 10:30:40 +01:00
const {} = init();
2022-06-22 11:20:57 +02:00
2024-03-09 10:30:40 +01:00
export let type;
export let filterPresets;
2022-06-22 11:20:57 +02:00
2024-03-09 10:30:40 +01:00
// By default, look at the jobs of the last 30 days:
if (filterPresets?.startTime == null) {
if (filterPresets == null) filterPresets = {} ;
2023-06-19 17:59:44 +02:00
2024-03-09 10:30:40 +01:00
filterPresets.startTime = {
2024-10-10 18:35:53 +02:00
range: "last30d",
2024-03-09 10:30:40 +01:00
text: "Last 30 Days",
};
}
2023-06-19 17:59:44 +02:00
2024-03-09 10:30:40 +01:00
console.assert(
type == "USER" || type == "PROJECT",
"Invalid list type provided!",
);
2022-06-22 11:20:57 +02:00
2024-03-09 10:30:40 +01:00
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" } ;
2023-06-06 17:03:08 +02:00
2024-03-09 10:30:40 +01:00
const client = getContextClient();
$: stats = queryStore({
client: client,
query: gql`
2023-06-06 17:03:08 +02:00
query($jobFilters: [JobFilter!]!) {
rows: jobsStatistics(filter: $jobFilters, groupBy: ${ type } ) {
2023-05-12 11:19:37 +02:00
id
name
totalJobs
totalWalltime
totalCoreHours
2023-06-09 12:28:24 +02:00
totalAccHours
2023-05-12 11:19:37 +02:00
}
}`,
2024-03-09 10:30:40 +01:00
variables: { jobFilters } ,
});
2022-06-22 11:20:57 +02:00
2024-03-09 10:30:40 +01:00
function changeSorting(event, field) {
let target = event.target;
while (target.tagName != "BUTTON") target = target.parentElement;
2022-06-22 11:20:57 +02:00
2024-03-09 10:30:40 +01:00
let direction = target.children[0].className.includes("up") ? "down" : "up";
target.children[0].className = `bi-sort-numeric-${ direction } `;
sorting = { field , direction } ;
}
2022-06-22 11:20:57 +02:00
2024-03-09 10:30:40 +01:00
function sort(stats, sorting, nameFilter) {
const cmp =
sorting.field == "id"
? sorting.direction == "up"
? (a, b) => a.id < b.id
: (a, b) => a.id > b.id
: sorting.direction == "up"
? (a, b) => a[sorting.field] - b[sorting.field]
: (a, b) => b[sorting.field] - a[sorting.field];
2023-01-27 18:36:58 +01:00
2024-03-09 10:30:40 +01:00
return stats.filter((u) => u.id.includes(nameFilter)).sort(cmp);
}
2022-06-22 11:20:57 +02:00
2024-07-25 17:10:00 +02:00
onMount(() => filterComponent.updateFilters());
2022-06-22 11:20:57 +02:00
< / script >
2024-10-07 17:36:40 +02:00
< Row cols = {{ xs : 1 , md : 2 }} >
< Col xs = "12" md = "5" lg = "4" xl = "3" class = "mb-2 mb-md-0" >
2024-03-09 10:30:40 +01:00
< InputGroup >
< Button disabled outline >
Search { type . toLowerCase ()} s
< / Button >
< Input
bind:value={ nameFilter }
placeholder="Filter by {{
USER: 'username',
PROJECT: 'project',
}[type]}"
/>
< / InputGroup >
< / Col >
2024-10-07 17:36:40 +02:00
< Col xs = "12" md = "7" lg = "8" xl = "9" >
2024-03-09 10:30:40 +01:00
< Filters
bind:this={ filterComponent }
{ filterPresets }
startTimeQuickSelect={ true }
menuText="Only { type . toLowerCase ()} s with jobs that match the filters will show up"
2024-07-25 17:10:00 +02:00
on:update-filters={({ detail }) => {
2024-03-09 10:30:40 +01:00
jobFilters = detail.filters;
}}
/>
< / Col >
2022-06-22 11:20:57 +02:00
< / Row >
< Table >
2024-03-09 10:30:40 +01:00
< thead >
< tr >
< th scope = "col" >
2024-10-07 17:36:40 +02:00
{ #if type === 'USER' }
Username
{ :else if type === 'PROJECT' }
Project Name
{ /if }
2024-03-09 10:30:40 +01:00
< Button
color={ sorting . field == "id" ? "primary" : "light" }
size="sm"
on:click={( e ) => changeSorting ( e , "id" )}
>
< Icon name = "sort-numeric-down" / >
< / Button >
< / th >
{ #if type == "USER" }
< th scope = "col" >
Name
< Button
color={ sorting . field == "name" ? "primary" : "light" }
size="sm"
on:click={( e ) => changeSorting ( e , "name" )}
>
< Icon name = "sort-numeric-down" / >
< / Button >
< / th >
{ /if }
< th scope = "col" >
Total Jobs
< Button
color={ sorting . field == "totalJobs" ? "primary" : "light" }
size="sm"
on:click={( e ) => changeSorting ( e , "totalJobs" )}
>
< Icon name = "sort-numeric-down" / >
< / Button >
< / th >
< th scope = "col" >
Total Walltime
< Button
color={ sorting . field == "totalWalltime" ? "primary" : "light" }
size="sm"
on:click={( e ) => changeSorting ( e , "totalWalltime" )}
>
< Icon name = "sort-numeric-down" / >
< / Button >
< / th >
< th scope = "col" >
Total Core Hours
< Button
color={ sorting . field == "totalCoreHours" ? "primary" : "light" }
size="sm"
on:click={( e ) => changeSorting ( e , "totalCoreHours" )}
>
< Icon name = "sort-numeric-down" / >
< / Button >
< / th >
< th scope = "col" >
Total Accelerator Hours
< Button
color={ sorting . field == "totalAccHours" ? "primary" : "light" }
size="sm"
on:click={( e ) => changeSorting ( e , "totalAccHours" )}
>
< Icon name = "sort-numeric-down" / >
< / Button >
< / th >
< / tr >
< / thead >
< tbody >
{ #if $stats . fetching }
< tr >
< td colspan = "4" style = "text-align: center;" > < Spinner secondary / > < / td >
< / tr >
{ :else if $stats . error }
< tr >
< td colspan = "4"
>< Card body color = "danger" class = "mb-3" > { $stats . error . message } < /Card
>< /td
>
< / tr >
{ :else if $stats . data }
{ #each sort ( $stats . data . rows , sorting , nameFilter ) as row ( row . id )}
2022-06-22 11:20:57 +02:00
< tr >
2024-03-09 10:30:40 +01:00
< td >
2023-05-03 16:41:17 +02:00
{ #if type == "USER" }
2024-03-09 10:30:40 +01:00
< a href = "/monitoring/user/ { row . id } "
>{ scrambleNames ? scramble ( row . id ) : row . id } < /a
>
{ :else if type == "PROJECT" }
< a href = "/monitoring/jobs/?project= { row . id } "
>{ scrambleNames ? scramble ( row . id ) : row . id } < /a
>
{ : else }
{ row . id }
2023-02-17 10:45:27 +01:00
{ /if }
2024-03-09 10:30:40 +01:00
< / td >
{ #if type == "USER" }
< td
>{ scrambleNames
? scramble(row?.name ? row.name : "-")
: row?.name
? row.name
: "-"}< /td
>
{ /if }
< td > { row . totalJobs } </ td >
< td > { row . totalWalltime } </ td >
< td > { row . totalCoreHours } </ td >
< td > { row . totalAccHours } </ td >
2022-06-22 11:20:57 +02:00
< / tr >
2024-03-09 10:30:40 +01:00
{ : else }
< tr >
< td colspan = "4" >< i > No { type . toLowerCase ()} s/jobs found</ i ></ td >
< / tr >
{ /each }
{ /if }
< / tbody >
2023-01-27 18:36:58 +01:00
< / Table >