adapt and improve svelte taglist component

This commit is contained in:
Christoph Kluge 2025-04-22 17:33:17 +02:00
parent 277f964b30
commit a3fb471546
3 changed files with 137 additions and 38 deletions

View File

@ -2,49 +2,142 @@
@component Tag List Svelte Component. Displays All Tags, Allows deletion.
Properties:
- `authlevel Int!`: Current Users Authority Level
- `tagmap Object!`: Map of Appwide Tags
- `username String!`: Users username.
- `isAdmin Bool!`: User has Admin Auth.
- `tagmap Object!`: Map of accessible, appwide tags. Prefiltered in backend.
-->
<script>
// import { Card, CardHeader, CardTitle } from "@sveltestrap/sveltestrap";
import {
gql,
getContextClient,
mutationStore,
} from "@urql/svelte";
import {
Badge,
InputGroup,
Icon,
Button,
Spinner,
} from "@sveltestrap/sveltestrap";
import {
init,
} from "./generic/utils.js";
// export let authlevel;
export let username;
export let isAdmin;
export let tagmap;
const {} = init();
const client = getContextClient();
let pendingChange = "none";
const removeTagMutation = ({ tagIds }) => {
return mutationStore({
client: client,
query: gql`
mutation ($job: ID!, $tagIds: [ID!]!) {
removeTag(tagIds: $tagIds) {
id
type
name
scope
}
}
`,
variables: { tagIds },
});
};
function removeTag(tag, tagType) {
pendingChange = tagType;
removeTagMutation({tagIds: [tag.id] }).subscribe(
(res) => {
if (res.fetching === false && !res.error) {
tagmap = res.data.removeTag;
pendingChange = "none";
} else if (res.fetching === false && res.error) {
throw res.error;
}
},
);
}
$: console.log(username, isAdmin)
$: console.log(pendingChange, tagmap)
</script>
<div class="container">
<div class="row justify-content-center">
<div class="col-10">
{#each Object.entries(tagmap) as [tagType, tagList]}
<div class="my-3 p-2 bg-secondary rounded text-white"> <!-- text-capitalize -->
Tag Type: <b>{tagType}</b>
<span style="float: right; padding-bottom: 0.4rem; padding-top: 0.4rem;" class="badge bg-light text-secondary">
{tagList.length} Tag{(tagList.length != 1)?'s':''}
</span>
</div>
{#each tagList as tag (tag.id)}
{#if tag.scope == "global"}
<a class="btn btn-outline-secondary" href="/monitoring/jobs/?tag={tag.id}" role="button">
{tag.name}
<span class="badge bg-primary mr-1">{tag.count} Job{(tag.count != 1)?'s':''}</span>
<span style="background-color:#c85fc8;" class="badge text-dark">Global</span>
</a>
{:else if tag.scope == "admin"}
<a class="btn btn-outline-secondary" href="/monitoring/jobs/?tag={tag.id}" role="button">
{tag.name}
<span class="badge bg-primary mr-1">{tag.count} Job{(tag.count != 1)?'s':''}</span>
<span style="background-color:#19e5e6;" class="badge text-dark">Admin</span>
</a>
{:else}
<a class="btn btn-outline-secondary" href="/monitoring/jobs/?tag={tag.id}" role="button">
{tag.name}
<span class="badge bg-primary mr-1">{tag.count} Job{(tag.count != 1)?'s':''}</span>
<span class="badge bg-warning text-dark">Private</span>
</a>
{/if}
{/each}
{/each}
<div class="row justify-content-center">
<div class="col-10">
{#each Object.entries(tagmap) as [tagType, tagList]}
<div class="my-3 p-2 bg-secondary rounded text-white"> <!-- text-capitalize -->
Tag Type: <b>{tagType}</b>
{#if pendingChange === tagType}
<Spinner size="sm" secondary />
{/if}
<span style="float: right; padding-bottom: 0.4rem; padding-top: 0.4rem;" class="badge bg-light text-secondary">
{tagList.length} Tag{(tagList.length != 1)?'s':''}
</span>
</div>
<div class="d-inline-flex flex-wrap">
{#each tagList as tag (tag.id)}
{#if tag.scope == "global"}
<InputGroup class="w-auto flex-nowrap" style="margin-right: 0.5rem; margin-bottom: 0.5rem;">
<Button outline color="secondary" href="/monitoring/jobs/?tag={tag.id}" target="_blank">
<Badge color="light" style="font-size:medium;" border>{tag.name}</Badge> :
<Badge color="primary" pill>{tag.count} Job{(tag.count != 1)?'s':''}</Badge>
<Badge style="background-color:#c85fc8 !important;" pill>Global</Badge>
</Button>
{#if isAdmin}
<Button
size="sm"
color="danger"
on:click={() => removeTag(tag, tagType)}
>
<Icon name="x" />
</Button>
{/if}
</InputGroup>
{:else if tag.scope == "admin"}
<InputGroup class="w-auto flex-nowrap" style="margin-right: 0.5rem; margin-bottom: 0.5rem;">
<Button outline color="secondary" href="/monitoring/jobs/?tag={tag.id}" target="_blank">
<Badge color="light" style="font-size:medium;" border>{tag.name}</Badge> :
<Badge color="primary" pill>{tag.count} Job{(tag.count != 1)?'s':''}</Badge>
<Badge style="background-color:#19e5e6 !important;" pill>Admin</Badge>
</Button>
{#if isAdmin}
<Button
size="sm"
color="danger"
on:click={() => removeTag(tag, tagType)}
>
<Icon name="x" />
</Button>
{/if}
</InputGroup>
{:else}
<InputGroup class="w-auto flex-nowrap" style="margin-right: 0.5rem; margin-bottom: 0.5rem;">
<Button outline color="secondary" href="/monitoring/jobs/?tag={tag.id}" target="_blank">
<Badge color="light" style="font-size:medium;" border>{tag.name}</Badge> :
<Badge color="primary" pill>{tag.count} Job{(tag.count != 1)?'s':''}</Badge>
<Badge color="warning" pill>Private</Badge>
</Button>
{#if tag.scope == username}
<Button
size="sm"
color="danger"
on:click={() => removeTag(tag, tagType)}
>
<Icon name="x" />
</Button>
{/if}
</InputGroup>
{/if}
{/each}
</div>
{/each}
</div>
</div>
</div>

View File

@ -4,9 +4,13 @@ import Tags from './Tags.root.svelte'
new Tags({
target: document.getElementById('svelte-app'),
props: {
// authlevel: authlevel,
username: username,
isAdmin: isAdmin,
tagmap: tagmap,
}
},
context: new Map([
['cc-config', clusterCockpitConfig]
])
})

View File

@ -6,8 +6,10 @@
{{end}}
{{define "javascript"}}
<script>
const authlevel = {{ .User.GetAuthLevel }};
const username = {{ .User.Username }};
const isAdmin = {{ .User.HasRole .Roles.admin }};
const tagmap = {{ .Infos.tagmap }};
const clusterCockpitConfig = {{ .Config }};
</script>
<script src='/build/taglist.js'></script>
{{end}}