Upgrade frontend dependencies

Change to most recent @sveltestrap/sveltestrap
Reformat with Svelte LSP
This commit is contained in:
2024-03-09 10:30:40 +01:00
parent 5a4671b7b1
commit 32a57661fd
50 changed files with 8712 additions and 6995 deletions

View File

@@ -1,103 +1,156 @@
<script>
import { Button, Card, CardTitle } from 'sveltestrap'
import { createEventDispatcher } from 'svelte'
import { fade } from 'svelte/transition'
import { Button, Card, CardTitle } from "@sveltestrap/sveltestrap";
import { createEventDispatcher } from "svelte";
import { fade } from "svelte/transition";
const dispatch = createEventDispatcher()
const dispatch = createEventDispatcher();
let message = {msg: '', color: '#d63384'}
let displayMessage = false
let message = { msg: "", color: "#d63384" };
let displayMessage = false;
export let roles = []
export let roles = [];
async function handleUserSubmit() {
let form = document.querySelector('#create-user-form')
let formData = new FormData(form)
async function handleUserSubmit() {
let form = document.querySelector("#create-user-form");
let formData = new FormData(form);
try {
const res = await fetch(form.action, { method: 'POST', body: formData });
if (res.ok) {
let text = await res.text()
popMessage(text, '#048109')
reloadUserList()
form.reset()
} else {
let text = await res.text()
// console.log(res.statusText)
throw new Error('Response Code ' + res.status + '-> ' + text)
}
} catch (err) {
popMessage(err, '#d63384')
}
try {
const res = await fetch(form.action, { method: "POST", body: formData });
if (res.ok) {
let text = await res.text();
popMessage(text, "#048109");
reloadUserList();
form.reset();
} else {
let text = await res.text();
// console.log(res.statusText)
throw new Error("Response Code " + res.status + "-> " + text);
}
} catch (err) {
popMessage(err, "#d63384");
}
}
function popMessage(response, rescolor) {
message = {msg: response, color: rescolor}
displayMessage = true
setTimeout(function() {
displayMessage = false
}, 3500)
}
function popMessage(response, rescolor) {
message = { msg: response, color: rescolor };
displayMessage = true;
setTimeout(function () {
displayMessage = false;
}, 3500);
}
function reloadUserList() {
dispatch('reload')
}
function reloadUserList() {
dispatch("reload");
}
</script>
<Card>
<form id="create-user-form" method="post" action="/api/users/" class="card-body" on:submit|preventDefault={handleUserSubmit}>
<CardTitle class="mb-3">Create User</CardTitle>
<div class="mb-3">
<label for="username" class="form-label">Username (ID)</label>
<input type="text" class="form-control" id="username" name="username" aria-describedby="usernameHelp"/>
<div id="usernameHelp" class="form-text">Must be unique.</div>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" aria-describedby="passwordHelp"/>
<div id="passwordHelp" class="form-text">Only API users are allowed to have a blank password. Users with a blank password can only authenticate via Tokens.</div>
</div>
<div class="mb-3">
<label for="name" class="form-label">Project</label>
<input type="text" class="form-control" id="project" name="project" aria-describedby="projectHelp"/>
<div id="projectHelp" class="form-text">Only Manager users can have a project. Allows to inspect jobs and users of given project.</div>
</div>
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" name="name" aria-describedby="nameHelp"/>
<div id="nameHelp" class="form-text">Optional, can be blank.</div>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email address</label>
<input type="email" class="form-control" id="email" name="email" aria-describedby="emailHelp"/>
<div id="emailHelp" class="form-text">Optional, can be blank.</div>
</div>
<form
id="create-user-form"
method="post"
action="/api/users/"
class="card-body"
on:submit|preventDefault={handleUserSubmit}
>
<CardTitle class="mb-3">Create User</CardTitle>
<div class="mb-3">
<label for="username" class="form-label">Username (ID)</label>
<input
type="text"
class="form-control"
id="username"
name="username"
aria-describedby="usernameHelp"
/>
<div id="usernameHelp" class="form-text">Must be unique.</div>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input
type="password"
class="form-control"
id="password"
name="password"
aria-describedby="passwordHelp"
/>
<div id="passwordHelp" class="form-text">
Only API users are allowed to have a blank password. Users with a blank
password can only authenticate via Tokens.
</div>
</div>
<div class="mb-3">
<label for="name" class="form-label">Project</label>
<input
type="text"
class="form-control"
id="project"
name="project"
aria-describedby="projectHelp"
/>
<div id="projectHelp" class="form-text">
Only Manager users can have a project. Allows to inspect jobs and users
of given project.
</div>
</div>
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input
type="text"
class="form-control"
id="name"
name="name"
aria-describedby="nameHelp"
/>
<div id="nameHelp" class="form-text">Optional, can be blank.</div>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email address</label>
<input
type="email"
class="form-control"
id="email"
name="email"
aria-describedby="emailHelp"
/>
<div id="emailHelp" class="form-text">Optional, can be blank.</div>
</div>
<div class="mb-3">
<p>Role:</p>
{#each roles as role, i}
{#if i == 0}
<div>
<input type="radio" id={role} name="role" value={role} checked/>
<label for={role}>{role.toUpperCase()} (Allowed to interact with REST API.)</label>
</div>
{:else if i == 1}
<div>
<input type="radio" id={role} name="role" value={role} checked/>
<label for={role}>{role.charAt(0).toUpperCase() + role.slice(1)} (Same as if created via LDAP sync.)</label>
</div>
{:else}
<div>
<input type="radio" id={role} name="role" value={role}/>
<label for={role}>{role.charAt(0).toUpperCase() + role.slice(1)}</label>
</div>
{/if}
{/each}
</div>
<p style="display: flex; align-items: center;">
<Button type="submit" color="primary">Submit</Button>
{#if displayMessage}<div style="margin-left: 1.5em;"><b><code style="color: {message.color};" out:fade>{message.msg}</code></b></div>{/if}
</p>
</form>
<div class="mb-3">
<p>Role:</p>
{#each roles as role, i}
{#if i == 0}
<div>
<input type="radio" id={role} name="role" value={role} checked />
<label for={role}
>{role.toUpperCase()} (Allowed to interact with REST API.)</label
>
</div>
{:else if i == 1}
<div>
<input type="radio" id={role} name="role" value={role} checked />
<label for={role}
>{role.charAt(0).toUpperCase() + role.slice(1)} (Same as if created
via LDAP sync.)</label
>
</div>
{:else}
<div>
<input type="radio" id={role} name="role" value={role} />
<label for={role}
>{role.charAt(0).toUpperCase() + role.slice(1)}</label
>
</div>
{/if}
{/each}
</div>
<p style="display: flex; align-items: center;">
<Button type="submit" color="primary">Submit</Button>
{#if displayMessage}<div style="margin-left: 1.5em;">
<b
><code style="color: {message.color};" out:fade>{message.msg}</code
></b
>
</div>{/if}
</p>
</form>
</Card>

View File

@@ -1,97 +1,129 @@
<script>
import { Card, CardTitle, CardBody } from 'sveltestrap'
import { createEventDispatcher } from 'svelte'
import { fade } from 'svelte/transition'
import { Card, CardTitle, CardBody } from "@sveltestrap/sveltestrap";
import { createEventDispatcher } from "svelte";
import { fade } from "svelte/transition";
const dispatch = createEventDispatcher()
const dispatch = createEventDispatcher();
let message = {msg: '', color: '#d63384'}
let displayMessage = false
let message = { msg: "", color: "#d63384" };
let displayMessage = false;
async function handleAddProject() {
const username = document.querySelector('#project-username').value
const project = document.querySelector('#project-id').value
async function handleAddProject() {
const username = document.querySelector("#project-username").value;
const project = document.querySelector("#project-id").value;
if (username == "" || project == "") {
alert('Please fill in a username and select a project.')
return
}
let formData = new FormData()
formData.append('username', username)
formData.append('add-project', project)
try {
const res = await fetch(`/api/user/${username}`, { method: 'POST', body: formData })
if (res.ok) {
let text = await res.text()
popMessage(text, '#048109')
reloadUserList()
} else {
let text = await res.text()
// console.log(res.statusText)
throw new Error('Response Code ' + res.status + '-> ' + text)
}
} catch (err) {
popMessage(err, '#d63384')
}
if (username == "" || project == "") {
alert("Please fill in a username and select a project.");
return;
}
async function handleRemoveProject() {
const username = document.querySelector('#project-username').value
const project = document.querySelector('#project-id').value
let formData = new FormData();
formData.append("username", username);
formData.append("add-project", project);
if (username == "" || project == "") {
alert('Please fill in a username and select a project.')
return
}
try {
const res = await fetch(`/api/user/${username}`, {
method: "POST",
body: formData,
});
if (res.ok) {
let text = await res.text();
popMessage(text, "#048109");
reloadUserList();
} else {
let text = await res.text();
// console.log(res.statusText)
throw new Error("Response Code " + res.status + "-> " + text);
}
} catch (err) {
popMessage(err, "#d63384");
}
}
let formData = new FormData()
formData.append('username', username)
formData.append('remove-project', project)
async function handleRemoveProject() {
const username = document.querySelector("#project-username").value;
const project = document.querySelector("#project-id").value;
try {
const res = await fetch(`/api/user/${username}`, { method: 'POST', body: formData })
if (res.ok) {
let text = await res.text()
popMessage(text, '#048109')
reloadUserList()
} else {
let text = await res.text()
// console.log(res.statusText)
throw new Error('Response Code ' + res.status + '-> ' + text)
}
} catch (err) {
popMessage(err, '#d63384')
}
if (username == "" || project == "") {
alert("Please fill in a username and select a project.");
return;
}
function popMessage(response, rescolor) {
message = {msg: response, color: rescolor}
displayMessage = true
setTimeout(function() {
displayMessage = false
}, 3500)
}
let formData = new FormData();
formData.append("username", username);
formData.append("remove-project", project);
function reloadUserList() {
dispatch('reload')
try {
const res = await fetch(`/api/user/${username}`, {
method: "POST",
body: formData,
});
if (res.ok) {
let text = await res.text();
popMessage(text, "#048109");
reloadUserList();
} else {
let text = await res.text();
// console.log(res.statusText)
throw new Error("Response Code " + res.status + "-> " + text);
}
} catch (err) {
popMessage(err, "#d63384");
}
}
function popMessage(response, rescolor) {
message = { msg: response, color: rescolor };
displayMessage = true;
setTimeout(function () {
displayMessage = false;
}, 3500);
}
function reloadUserList() {
dispatch("reload");
}
</script>
<Card>
<CardBody>
<CardTitle class="mb-3">Edit Project Managed By User (Manager Only)</CardTitle>
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="username" id="project-username"/>
<input type="text" class="form-control" placeholder="project-id" id="project-id"/>
<!-- PreventDefault on Sveltestrap-Button more complex to achieve than just use good ol' html button -->
<!-- see: https://stackoverflow.com/questions/69630422/svelte-how-to-use-event-modifiers-in-my-own-components -->
<button class="btn btn-primary" type="button" id="add-project-button" on:click|preventDefault={handleAddProject}>Add</button>
<button class="btn btn-danger" type="button" id="remove-project-button" on:click|preventDefault={handleRemoveProject}>Remove</button>
</div>
<p>
{#if displayMessage}<b><code style="color: {message.color};" out:fade>Update: {message.msg}</code></b>{/if}
</p>
</CardBody>
<CardBody>
<CardTitle class="mb-3"
>Edit Project Managed By User (Manager Only)</CardTitle
>
<div class="input-group mb-3">
<input
type="text"
class="form-control"
placeholder="username"
id="project-username"
/>
<input
type="text"
class="form-control"
placeholder="project-id"
id="project-id"
/>
<!-- PreventDefault on Sveltestrap-Button more complex to achieve than just use good ol' html button -->
<!-- see: https://stackoverflow.com/questions/69630422/svelte-how-to-use-event-modifiers-in-my-own-components -->
<button
class="btn btn-primary"
type="button"
id="add-project-button"
on:click|preventDefault={handleAddProject}>Add</button
>
<button
class="btn btn-danger"
type="button"
id="remove-project-button"
on:click|preventDefault={handleRemoveProject}>Remove</button
>
</div>
<p>
{#if displayMessage}<b
><code style="color: {message.color};" out:fade
>Update: {message.msg}</code
></b
>{/if}
</p>
</CardBody>
</Card>

View File

@@ -1,104 +1,131 @@
<script>
import { Card, CardTitle, CardBody } from 'sveltestrap'
import { createEventDispatcher } from 'svelte'
import { fade } from 'svelte/transition'
import { Card, CardTitle, CardBody } from "@sveltestrap/sveltestrap";
import { createEventDispatcher } from "svelte";
import { fade } from "svelte/transition";
const dispatch = createEventDispatcher()
const dispatch = createEventDispatcher();
let message = {msg: '', color: '#d63384'}
let displayMessage = false
let message = { msg: "", color: "#d63384" };
let displayMessage = false;
export let roles = []
export let roles = [];
async function handleAddRole() {
const username = document.querySelector('#role-username').value
const role = document.querySelector('#role-select').value
async function handleAddRole() {
const username = document.querySelector("#role-username").value;
const role = document.querySelector("#role-select").value;
if (username == "" || role == "") {
alert('Please fill in a username and select a role.')
return
}
let formData = new FormData()
formData.append('username', username)
formData.append('add-role', role)
try {
const res = await fetch(`/api/user/${username}`, { method: 'POST', body: formData })
if (res.ok) {
let text = await res.text()
popMessage(text, '#048109')
reloadUserList()
} else {
let text = await res.text()
// console.log(res.statusText)
throw new Error('Response Code ' + res.status + '-> ' + text)
}
} catch (err) {
popMessage(err, '#d63384')
}
if (username == "" || role == "") {
alert("Please fill in a username and select a role.");
return;
}
async function handleRemoveRole() {
const username = document.querySelector('#role-username').value
const role = document.querySelector('#role-select').value
let formData = new FormData();
formData.append("username", username);
formData.append("add-role", role);
if (username == "" || role == "") {
alert('Please fill in a username and select a role.')
return
}
try {
const res = await fetch(`/api/user/${username}`, {
method: "POST",
body: formData,
});
if (res.ok) {
let text = await res.text();
popMessage(text, "#048109");
reloadUserList();
} else {
let text = await res.text();
// console.log(res.statusText)
throw new Error("Response Code " + res.status + "-> " + text);
}
} catch (err) {
popMessage(err, "#d63384");
}
}
let formData = new FormData()
formData.append('username', username)
formData.append('remove-role', role)
async function handleRemoveRole() {
const username = document.querySelector("#role-username").value;
const role = document.querySelector("#role-select").value;
try {
const res = await fetch(`/api/user/${username}`, { method: 'POST', body: formData })
if (res.ok) {
let text = await res.text()
popMessage(text, '#048109')
reloadUserList()
} else {
let text = await res.text()
// console.log(res.statusText)
throw new Error('Response Code ' + res.status + '-> ' + text)
}
} catch (err) {
popMessage(err, '#d63384')
}
if (username == "" || role == "") {
alert("Please fill in a username and select a role.");
return;
}
function popMessage(response, rescolor) {
message = {msg: response, color: rescolor}
displayMessage = true
setTimeout(function() {
displayMessage = false
}, 3500)
}
let formData = new FormData();
formData.append("username", username);
formData.append("remove-role", role);
function reloadUserList() {
dispatch('reload')
try {
const res = await fetch(`/api/user/${username}`, {
method: "POST",
body: formData,
});
if (res.ok) {
let text = await res.text();
popMessage(text, "#048109");
reloadUserList();
} else {
let text = await res.text();
// console.log(res.statusText)
throw new Error("Response Code " + res.status + "-> " + text);
}
} catch (err) {
popMessage(err, "#d63384");
}
}
function popMessage(response, rescolor) {
message = { msg: response, color: rescolor };
displayMessage = true;
setTimeout(function () {
displayMessage = false;
}, 3500);
}
function reloadUserList() {
dispatch("reload");
}
</script>
<Card>
<CardBody>
<CardTitle class="mb-3">Edit User Roles</CardTitle>
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="username" id="role-username"/>
<select class="form-select" id="role-select">
<option selected value="">Role...</option>
{#each roles as role}
<option value={role}>{role.charAt(0).toUpperCase() + role.slice(1)}</option>
{/each}
</select>
<!-- PreventDefault on Sveltestrap-Button more complex to achieve than just use good ol' html button -->
<!-- see: https://stackoverflow.com/questions/69630422/svelte-how-to-use-event-modifiers-in-my-own-components -->
<button class="btn btn-primary" type="button" id="add-role-button" on:click|preventDefault={handleAddRole}>Add</button>
<button class="btn btn-danger" type="button" id="remove-role-button" on:click|preventDefault={handleRemoveRole}>Remove</button>
</div>
<p>
{#if displayMessage}<b><code style="color: {message.color};" out:fade>Update: {message.msg}</code></b>{/if}
</p>
</CardBody>
<CardBody>
<CardTitle class="mb-3">Edit User Roles</CardTitle>
<div class="input-group mb-3">
<input
type="text"
class="form-control"
placeholder="username"
id="role-username"
/>
<select class="form-select" id="role-select">
<option selected value="">Role...</option>
{#each roles as role}
<option value={role}
>{role.charAt(0).toUpperCase() + role.slice(1)}</option
>
{/each}
</select>
<!-- PreventDefault on Sveltestrap-Button more complex to achieve than just use good ol' html button -->
<!-- see: https://stackoverflow.com/questions/69630422/svelte-how-to-use-event-modifiers-in-my-own-components -->
<button
class="btn btn-primary"
type="button"
id="add-role-button"
on:click|preventDefault={handleAddRole}>Add</button
>
<button
class="btn btn-danger"
type="button"
id="remove-role-button"
on:click|preventDefault={handleRemoveRole}>Remove</button
>
</div>
<p>
{#if displayMessage}<b
><code style="color: {message.color};" out:fade
>Update: {message.msg}</code
></b
>{/if}
</p>
</CardBody>
</Card>

View File

@@ -1,29 +1,34 @@
<script>
import { onMount } from 'svelte'
import { Card, CardBody, CardTitle } from 'sveltestrap'
import { onMount } from "svelte";
import { Card, CardBody, CardTitle } from "@sveltestrap/sveltestrap";
let scrambled
let scrambled;
onMount(() => {
scrambled = window.localStorage.getItem("cc-scramble-names") != null
})
onMount(() => {
scrambled = window.localStorage.getItem("cc-scramble-names") != null;
});
function handleScramble() {
if (!scrambled) {
scrambled = true
window.localStorage.setItem("cc-scramble-names", "true")
} else {
scrambled = false
window.localStorage.removeItem("cc-scramble-names")
}
function handleScramble() {
if (!scrambled) {
scrambled = true;
window.localStorage.setItem("cc-scramble-names", "true");
} else {
scrambled = false;
window.localStorage.removeItem("cc-scramble-names");
}
}
</script>
<Card class="h-100">
<CardBody>
<CardTitle class="mb-3">Scramble Names / Presentation Mode</CardTitle>
<input type="checkbox" id="scramble-names-checkbox" style="margin-right: 1em;" on:click={handleScramble} bind:checked={scrambled}/>
Active?
</CardBody>
<CardBody>
<CardTitle class="mb-3">Scramble Names / Presentation Mode</CardTitle>
<input
type="checkbox"
id="scramble-names-checkbox"
style="margin-right: 1em;"
on:click={handleScramble}
bind:checked={scrambled}
/>
Active?
</CardBody>
</Card>

View File

@@ -1,68 +1,87 @@
<script>
import { Button, Table, Card, CardTitle, CardBody } from 'sveltestrap'
import { onMount, createEventDispatcher } from "svelte";
import ShowUsersRow from './ShowUsersRow.svelte'
import {
Button,
Table,
Card,
CardTitle,
CardBody,
} from "@sveltestrap/sveltestrap";
import { createEventDispatcher } from "svelte";
import ShowUsersRow from "./ShowUsersRow.svelte";
export let users = []
export let users = [];
const dispatch = createEventDispatcher();
function reloadUserList() {
dispatch('reload')
}
const dispatch = createEventDispatcher();
function reloadUserList() {
dispatch("reload");
}
function deleteUser(username) {
if (confirm('Are you sure?')) {
let formData = new FormData()
formData.append('username', username)
fetch('/api/users/', { method: 'DELETE', body: formData }).then(res => {
if (res.status == 200) {
reloadUserList()
} else {
confirm(res.statusText)
}
})
function deleteUser(username) {
if (confirm("Are you sure?")) {
let formData = new FormData();
formData.append("username", username);
fetch("/api/users/", { method: "DELETE", body: formData }).then((res) => {
if (res.status == 200) {
reloadUserList();
} else {
confirm(res.statusText);
}
});
}
}
$: userList = users
$: userList = users;
</script>
<Card class="h-100">
<CardBody>
<CardTitle class="mb-3">Special Users</CardTitle>
<p>
Not created by an LDAP sync and/or having a role other than <code>user</code>
<Button color="secondary" size="sm" on:click={reloadUserList} style="float: right;">Reload</Button>
</p>
<div style="width: 100%; max-height: 500px; overflow-y: scroll;">
<Table hover>
<thead>
<tr>
<th>Username</th>
<th>Name</th>
<th>Project(s)</th>
<th>Email</th>
<th>Roles</th>
<th>JWT</th>
<th>Delete</th>
</tr>
</thead>
<tbody id="users-list">
{#each userList as user}
<tr id="user-{user.username}">
<ShowUsersRow {user}/>
<td><button class="btn btn-danger del-user" on:click={deleteUser(user.username)}>Delete</button></td>
</tr>
{:else}
<tr>
<td colspan="4">
<div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div>
</td>
</tr>
{/each}
</tbody>
</Table>
</div>
</CardBody>
<CardBody>
<CardTitle class="mb-3">Special Users</CardTitle>
<p>
Not created by an LDAP sync and/or having a role other than <code
>user</code
>
<Button
color="secondary"
size="sm"
on:click={reloadUserList}
style="float: right;">Reload</Button
>
</p>
<div style="width: 100%; max-height: 500px; overflow-y: scroll;">
<Table hover>
<thead>
<tr>
<th>Username</th>
<th>Name</th>
<th>Project(s)</th>
<th>Email</th>
<th>Roles</th>
<th>JWT</th>
<th>Delete</th>
</tr>
</thead>
<tbody id="users-list">
{#each userList as user}
<tr id="user-{user.username}">
<ShowUsersRow {user} />
<td
><button
class="btn btn-danger del-user"
on:click={deleteUser(user.username)}>Delete</button
></td
>
</tr>
{:else}
<tr>
<td colspan="4">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</td>
</tr>
{/each}
</tbody>
</Table>
</div>
</CardBody>
</Card>

View File

@@ -1,28 +1,32 @@
<script>
import { Button } from 'sveltestrap'
import { Button } from "@sveltestrap/sveltestrap";
export let user
let jwt = ""
export let user;
let jwt = "";
function getUserJwt(username) {
fetch(`/api/jwt/?username=${username}`)
.then(res => res.text())
.then(text => {
jwt = text
navigator.clipboard.writeText(text).catch(reason => console.error(reason))
})
}
function getUserJwt(username) {
fetch(`/api/jwt/?username=${username}`)
.then((res) => res.text())
.then((text) => {
jwt = text;
navigator.clipboard
.writeText(text)
.catch((reason) => console.error(reason));
});
}
</script>
<td>{user.username}</td>
<td>{user.name}</td>
<td>{user.projects}</td>
<td>{user.email}</td>
<td><code>{user.roles.join(', ')}</code></td>
<td><code>{user.roles.join(", ")}</code></td>
<td>
{#if ! jwt}
<Button color="success" on:click={getUserJwt(user.username)}>Gen. JWT</Button>
{:else}
<textarea rows="3" cols="20">{jwt}</textarea>
{/if}
{#if !jwt}
<Button color="success" on:click={getUserJwt(user.username)}
>Gen. JWT</Button
>
{:else}
<textarea rows="3" cols="20">{jwt}</textarea>
{/if}
</td>