mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-07-01 11:13:50 +02:00
Migrate config, migrate analysis plotselection
This commit is contained in:
parent
d6d92071bf
commit
6a6dca3fce
@ -14,15 +14,18 @@
|
|||||||
import SupportSettings from "./config/SupportSettings.svelte";
|
import SupportSettings from "./config/SupportSettings.svelte";
|
||||||
import AdminSettings from "./config/AdminSettings.svelte";
|
import AdminSettings from "./config/AdminSettings.svelte";
|
||||||
|
|
||||||
export let isAdmin;
|
/* Svelte 5 Props */
|
||||||
export let isSupport;
|
let {
|
||||||
export let isApi;
|
isAdmin,
|
||||||
export let username;
|
isSupport,
|
||||||
export let ncontent;
|
isApi,
|
||||||
|
username,
|
||||||
|
ncontent,
|
||||||
|
} = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if isAdmin}
|
{#if isAdmin}
|
||||||
<Card style="margin-bottom: 1.5em;">
|
<Card style="margin-bottom: 1.5rem;">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle class="mb-1">Admin Options</CardTitle>
|
<CardTitle class="mb-1">Admin Options</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
@ -31,7 +34,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if isSupport || isAdmin}
|
{#if isSupport || isAdmin}
|
||||||
<Card style="margin-bottom: 1.5em;">
|
<Card style="margin-bottom: 1.5rem;">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle class="mb-1">Support Options</CardTitle>
|
<CardTitle class="mb-1">Support Options</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
@ -21,10 +21,14 @@
|
|||||||
} from "@sveltestrap/sveltestrap";
|
} from "@sveltestrap/sveltestrap";
|
||||||
import { gql, getContextClient, mutationStore } from "@urql/svelte";
|
import { gql, getContextClient, mutationStore } from "@urql/svelte";
|
||||||
|
|
||||||
export let availableMetrics;
|
/* Svelte 5 Props */
|
||||||
export let metricsInHistograms;
|
let {
|
||||||
export let metricsInScatterplots;
|
availableMetrics,
|
||||||
|
metricsInHistograms = $bindable(),
|
||||||
|
metricsInScatterplots = $bindable(),
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
/* Const Init */
|
||||||
const client = getContextClient();
|
const client = getContextClient();
|
||||||
const updateConfigurationMutation = ({ name, value }) => {
|
const updateConfigurationMutation = ({ name, value }) => {
|
||||||
return mutationStore({
|
return mutationStore({
|
||||||
@ -38,11 +42,13 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let isHistogramConfigOpen = false,
|
/* State Init */
|
||||||
isScatterPlotConfigOpen = false;
|
let isHistogramConfigOpen = $state(false);
|
||||||
let selectedMetric1 = null,
|
let isScatterPlotConfigOpen = $state(false);
|
||||||
selectedMetric2 = null;
|
let selectedMetric1 = $state(null);
|
||||||
|
let selectedMetric2 = $state(null);
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
function updateConfiguration(data) {
|
function updateConfiguration(data) {
|
||||||
updateConfigurationMutation({
|
updateConfigurationMutation({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
@ -55,12 +61,12 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Button outline on:click={() => (isHistogramConfigOpen = true)}>
|
<Button outline onclick={() => (isHistogramConfigOpen = true)}>
|
||||||
<Icon name="" />
|
<Icon name="" />
|
||||||
Select Plots for Histograms
|
Select Plots for Histograms
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button outline on:click={() => (isScatterPlotConfigOpen = true)}>
|
<Button outline onclick={() => (isScatterPlotConfigOpen = true)}>
|
||||||
<Icon name="" />
|
<Icon name="" />
|
||||||
Select Plots in Scatter Plots
|
Select Plots in Scatter Plots
|
||||||
</Button>
|
</Button>
|
||||||
@ -78,7 +84,7 @@
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
bind:group={metricsInHistograms}
|
bind:group={metricsInHistograms}
|
||||||
value={metric}
|
value={metric}
|
||||||
on:change={() =>
|
onchange={() =>
|
||||||
updateConfiguration({
|
updateConfiguration({
|
||||||
name: "analysis_view_histogramMetrics",
|
name: "analysis_view_histogramMetrics",
|
||||||
value: metricsInHistograms,
|
value: metricsInHistograms,
|
||||||
@ -91,7 +97,7 @@
|
|||||||
</ListGroup>
|
</ListGroup>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="primary" on:click={() => (isHistogramConfigOpen = false)}>
|
<Button color="primary" onclick={() => (isHistogramConfigOpen = false)}>
|
||||||
Close
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
@ -112,7 +118,7 @@
|
|||||||
style="float: right;"
|
style="float: right;"
|
||||||
outline
|
outline
|
||||||
color="danger"
|
color="danger"
|
||||||
on:click={() => {
|
onclick={() => {
|
||||||
metricsInScatterplots = metricsInScatterplots.filter(
|
metricsInScatterplots = metricsInScatterplots.filter(
|
||||||
(p) => pair != p,
|
(p) => pair != p,
|
||||||
);
|
);
|
||||||
@ -146,7 +152,7 @@
|
|||||||
<Button
|
<Button
|
||||||
outline
|
outline
|
||||||
disabled={selectedMetric1 == null || selectedMetric2 == null}
|
disabled={selectedMetric1 == null || selectedMetric2 == null}
|
||||||
on:click={() => {
|
onclick={() => {
|
||||||
metricsInScatterplots = [
|
metricsInScatterplots = [
|
||||||
...metricsInScatterplots,
|
...metricsInScatterplots,
|
||||||
[selectedMetric1, selectedMetric2],
|
[selectedMetric1, selectedMetric2],
|
||||||
@ -164,7 +170,7 @@
|
|||||||
</InputGroup>
|
</InputGroup>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="primary" on:click={() => (isScatterPlotConfigOpen = false)}>
|
<Button color="primary" onclick={() => (isScatterPlotConfigOpen = false)}>
|
||||||
Close
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
@ -12,13 +12,17 @@
|
|||||||
import Options from "./admin/Options.svelte";
|
import Options from "./admin/Options.svelte";
|
||||||
import NoticeEdit from "./admin/NoticeEdit.svelte";
|
import NoticeEdit from "./admin/NoticeEdit.svelte";
|
||||||
|
|
||||||
export let ncontent;
|
/* Svelte 5 Props */
|
||||||
|
let { ncontent } = $props();
|
||||||
let users = [];
|
|
||||||
let roles = [];
|
|
||||||
|
|
||||||
|
/* Const Init*/
|
||||||
const ccconfig = getContext("cc-config");
|
const ccconfig = getContext("cc-config");
|
||||||
|
|
||||||
|
/* State Init */
|
||||||
|
let users = $state([]);
|
||||||
|
let roles = $state([]);
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
function getUserList() {
|
function getUserList() {
|
||||||
fetch("/config/users/?via-ldap=false¬-just-user=true")
|
fetch("/config/users/?via-ldap=false¬-just-user=true")
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
@ -40,21 +44,22 @@
|
|||||||
getValidRoles();
|
getValidRoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* on Mount */
|
||||||
onMount(() => initAdmin());
|
onMount(() => initAdmin());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Row cols={2} class="p-2 g-2">
|
<Row cols={2} class="p-2 g-2">
|
||||||
<Col class="mb-1">
|
<Col class="mb-1">
|
||||||
<AddUser {roles} on:reload={getUserList} />
|
<AddUser {roles} reloadUser={() => getUserList()} />
|
||||||
</Col>
|
</Col>
|
||||||
<Col class="mb-1">
|
<Col class="mb-1">
|
||||||
<ShowUsers on:reload={getUserList} bind:users />
|
<ShowUsers reloadUser={() => getUserList()} bind:users />
|
||||||
</Col>
|
</Col>
|
||||||
<Col>
|
<Col>
|
||||||
<EditRole {roles} on:reload={getUserList} />
|
<EditRole {roles} reloadUser={() => getUserList()} />
|
||||||
</Col>
|
</Col>
|
||||||
<Col>
|
<Col>
|
||||||
<EditProject on:reload={getUserList} />
|
<EditProject reloadUser={() => getUserList()} />
|
||||||
</Col>
|
</Col>
|
||||||
<Options config={ccconfig}/>
|
<Options config={ccconfig}/>
|
||||||
<NoticeEdit {ncontent}/>
|
<NoticeEdit {ncontent}/>
|
||||||
|
@ -12,17 +12,26 @@
|
|||||||
import PlotRenderOptions from "./user/PlotRenderOptions.svelte";
|
import PlotRenderOptions from "./user/PlotRenderOptions.svelte";
|
||||||
import PlotColorScheme from "./user/PlotColorScheme.svelte";
|
import PlotColorScheme from "./user/PlotColorScheme.svelte";
|
||||||
|
|
||||||
export let username
|
/* Svelte 5 Props */
|
||||||
export let isApi
|
let {
|
||||||
|
username,
|
||||||
|
isApi
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
/* Const Init */
|
||||||
const ccconfig = getContext("cc-config");
|
const ccconfig = getContext("cc-config");
|
||||||
let message = { msg: "", target: "", color: "#d63384" };
|
|
||||||
let displayMessage = false;
|
|
||||||
let cbmode = ccconfig?.plot_general_colorblindMode || false;
|
|
||||||
|
|
||||||
async function handleSettingSubmit(event) {
|
/* State Init */
|
||||||
const selector = event.detail.selector
|
let message = $state({ msg: "", target: "", color: "#d63384" });
|
||||||
const target = event.detail.target
|
let displayMessage = $state(false);
|
||||||
|
let cbmode = $state(ccconfig?.plot_general_colorblindMode || false);
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
|
async function handleSettingSubmit(event, setting) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const selector = setting.selector
|
||||||
|
const target = setting.target
|
||||||
let form = document.querySelector(selector);
|
let form = document.querySelector(selector);
|
||||||
let formData = new FormData(form);
|
let formData = new FormData(form);
|
||||||
try {
|
try {
|
||||||
@ -53,6 +62,6 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<UserOptions config={ccconfig} {username} {isApi} bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/>
|
<UserOptions config={ccconfig} {username} {isApi} bind:message bind:displayMessage updateSetting={(e, newSetting) => handleSettingSubmit(e, newSetting)}/>
|
||||||
<PlotRenderOptions config={ccconfig} bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/>
|
<PlotRenderOptions config={ccconfig} bind:message bind:displayMessage updateSetting={(e, newSetting) => handleSettingSubmit(e, newSetting)}/>
|
||||||
<PlotColorScheme config={ccconfig} bind:cbmode bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/>
|
<PlotColorScheme config={ccconfig} bind:cbmode bind:message bind:displayMessage updateSetting={(e, newSetting) => handleSettingSubmit(e, newSetting)}/>
|
||||||
|
@ -10,17 +10,19 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Button, Card, CardTitle } from "@sveltestrap/sveltestrap";
|
import { Button, Card, CardTitle } from "@sveltestrap/sveltestrap";
|
||||||
import { createEventDispatcher } from "svelte";
|
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
/* Svelte 5 Props */
|
||||||
|
let { roles, reloadUser } = $props();
|
||||||
|
|
||||||
let message = { msg: "", color: "#d63384" };
|
/* State Init */
|
||||||
let displayMessage = false;
|
let message = $state({ msg: "", color: "#d63384" });
|
||||||
|
let displayMessage = $state(false);
|
||||||
|
|
||||||
export let roles;
|
/* Functions */
|
||||||
|
async function handleUserSubmit(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
async function handleUserSubmit() {
|
|
||||||
let form = document.querySelector("#create-user-form");
|
let form = document.querySelector("#create-user-form");
|
||||||
let formData = new FormData(form);
|
let formData = new FormData(form);
|
||||||
|
|
||||||
@ -29,7 +31,7 @@
|
|||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
let text = await res.text();
|
let text = await res.text();
|
||||||
popMessage(text, "#048109");
|
popMessage(text, "#048109");
|
||||||
reloadUserList();
|
reloadUser();
|
||||||
form.reset();
|
form.reset();
|
||||||
} else {
|
} else {
|
||||||
let text = await res.text();
|
let text = await res.text();
|
||||||
@ -47,10 +49,6 @@
|
|||||||
displayMessage = false;
|
displayMessage = false;
|
||||||
}, 3500);
|
}, 3500);
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadUserList() {
|
|
||||||
dispatch("reload");
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
@ -60,7 +58,7 @@
|
|||||||
action="/config/users/"
|
action="/config/users/"
|
||||||
class="card-body"
|
class="card-body"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
on:submit|preventDefault={handleUserSubmit}
|
onsubmit={(e) => handleUserSubmit(e)}
|
||||||
>
|
>
|
||||||
<CardTitle class="mb-3">Create User</CardTitle>
|
<CardTitle class="mb-3">Create User</CardTitle>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
|
@ -7,15 +7,19 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Card, CardTitle, CardBody } from "@sveltestrap/sveltestrap";
|
import { Card, CardTitle, CardBody } from "@sveltestrap/sveltestrap";
|
||||||
import { createEventDispatcher } from "svelte";
|
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
/* Svelte 5 Props */
|
||||||
|
let { reloadUser } = $props();
|
||||||
|
|
||||||
let message = { msg: "", color: "#d63384" };
|
/* State Init */
|
||||||
let displayMessage = false;
|
let message = $state({ msg: "", color: "#d63384" });
|
||||||
|
let displayMessage = $state(false);
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
|
async function handleAddProject(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
async function handleAddProject() {
|
|
||||||
const username = document.querySelector("#project-username").value;
|
const username = document.querySelector("#project-username").value;
|
||||||
const project = document.querySelector("#project-id").value;
|
const project = document.querySelector("#project-id").value;
|
||||||
|
|
||||||
@ -36,7 +40,7 @@
|
|||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
let text = await res.text();
|
let text = await res.text();
|
||||||
popMessage(text, "#048109");
|
popMessage(text, "#048109");
|
||||||
reloadUserList();
|
reloadUser();
|
||||||
} else {
|
} else {
|
||||||
let text = await res.text();
|
let text = await res.text();
|
||||||
throw new Error("Response Code " + res.status + "-> " + text);
|
throw new Error("Response Code " + res.status + "-> " + text);
|
||||||
@ -46,7 +50,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleRemoveProject() {
|
async function handleRemoveProject(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
const username = document.querySelector("#project-username").value;
|
const username = document.querySelector("#project-username").value;
|
||||||
const project = document.querySelector("#project-id").value;
|
const project = document.querySelector("#project-id").value;
|
||||||
|
|
||||||
@ -67,7 +73,7 @@
|
|||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
let text = await res.text();
|
let text = await res.text();
|
||||||
popMessage(text, "#048109");
|
popMessage(text, "#048109");
|
||||||
reloadUserList();
|
reloadUser();
|
||||||
} else {
|
} else {
|
||||||
let text = await res.text();
|
let text = await res.text();
|
||||||
throw new Error("Response Code " + res.status + "-> " + text);
|
throw new Error("Response Code " + res.status + "-> " + text);
|
||||||
@ -84,10 +90,6 @@
|
|||||||
displayMessage = false;
|
displayMessage = false;
|
||||||
}, 3500);
|
}, 3500);
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadUserList() {
|
|
||||||
dispatch("reload");
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
@ -108,19 +110,17 @@
|
|||||||
placeholder="project-id"
|
placeholder="project-id"
|
||||||
id="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
|
<button
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
type="button"
|
type="button"
|
||||||
id="add-project-button"
|
id="add-project-button"
|
||||||
on:click|preventDefault={() => handleAddProject()}>Add</button
|
onclick={(e) => handleAddProject(e)}>Add</button
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="btn btn-danger"
|
class="btn btn-danger"
|
||||||
type="button"
|
type="button"
|
||||||
id="remove-project-button"
|
id="remove-project-button"
|
||||||
on:click|preventDefault={() => handleRemoveProject()}>Remove</button
|
onclick={(e) => handleRemoveProject(e)}>Remove</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
|
@ -10,17 +10,19 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Card, CardTitle, CardBody } from "@sveltestrap/sveltestrap";
|
import { Card, CardTitle, CardBody } from "@sveltestrap/sveltestrap";
|
||||||
import { createEventDispatcher } from "svelte";
|
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
/* SVelte 5 Props */
|
||||||
|
let {roles, reloadUser } = $props();
|
||||||
|
|
||||||
let message = { msg: "", color: "#d63384" };
|
/* State Init */
|
||||||
let displayMessage = false;
|
let message = $state({ msg: "", color: "#d63384" });
|
||||||
|
let displayMessage = $state(false);
|
||||||
|
|
||||||
export let roles;
|
/* Functions */
|
||||||
|
async function handleAddRole(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
async function handleAddRole() {
|
|
||||||
const username = document.querySelector("#role-username").value;
|
const username = document.querySelector("#role-username").value;
|
||||||
const role = document.querySelector("#role-select").value;
|
const role = document.querySelector("#role-select").value;
|
||||||
|
|
||||||
@ -41,7 +43,7 @@
|
|||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
let text = await res.text();
|
let text = await res.text();
|
||||||
popMessage(text, "#048109");
|
popMessage(text, "#048109");
|
||||||
reloadUserList();
|
reloadUser();
|
||||||
} else {
|
} else {
|
||||||
let text = await res.text();
|
let text = await res.text();
|
||||||
throw new Error("Response Code " + res.status + "-> " + text);
|
throw new Error("Response Code " + res.status + "-> " + text);
|
||||||
@ -51,7 +53,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleRemoveRole() {
|
async function handleRemoveRole(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
const username = document.querySelector("#role-username").value;
|
const username = document.querySelector("#role-username").value;
|
||||||
const role = document.querySelector("#role-select").value;
|
const role = document.querySelector("#role-select").value;
|
||||||
|
|
||||||
@ -72,7 +76,7 @@
|
|||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
let text = await res.text();
|
let text = await res.text();
|
||||||
popMessage(text, "#048109");
|
popMessage(text, "#048109");
|
||||||
reloadUserList();
|
reloadUser();
|
||||||
} else {
|
} else {
|
||||||
let text = await res.text();
|
let text = await res.text();
|
||||||
throw new Error("Response Code " + res.status + "-> " + text);
|
throw new Error("Response Code " + res.status + "-> " + text);
|
||||||
@ -89,10 +93,6 @@
|
|||||||
displayMessage = false;
|
displayMessage = false;
|
||||||
}, 3500);
|
}, 3500);
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadUserList() {
|
|
||||||
dispatch("reload");
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
@ -113,19 +113,17 @@
|
|||||||
>
|
>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</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
|
<button
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
type="button"
|
type="button"
|
||||||
id="add-role-button"
|
id="add-role-button"
|
||||||
on:click|preventDefault={() => handleAddRole()}>Add</button
|
onclick={(e) => handleAddRole(e)}>Add</button
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="btn btn-danger"
|
class="btn btn-danger"
|
||||||
type="button"
|
type="button"
|
||||||
id="remove-role-button"
|
id="remove-role-button"
|
||||||
on:click|preventDefault={() =>handleRemoveRole()}>Remove</button
|
onclick={(e) =>handleRemoveRole(e)}>Remove</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
|
@ -6,14 +6,18 @@
|
|||||||
import { Col, Card, CardTitle, CardBody } from "@sveltestrap/sveltestrap";
|
import { Col, Card, CardTitle, CardBody } from "@sveltestrap/sveltestrap";
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
|
|
||||||
export let ncontent;
|
/* Svelte 5 Props */
|
||||||
|
let { ncontent } = $props();
|
||||||
|
|
||||||
let message = { msg: "", color: "#d63384" };
|
/* State Init */
|
||||||
let displayMessage = false;
|
let message = $state({ msg: "", color: "#d63384" });
|
||||||
|
let displayMessage = $state(false);
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
|
async function handleEditNotice(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
async function handleEditNotice() {
|
|
||||||
const content = document.querySelector("#notice-content").value;
|
const content = document.querySelector("#notice-content").value;
|
||||||
|
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append("new-content", content);
|
formData.append("new-content", content);
|
||||||
|
|
||||||
@ -56,14 +60,11 @@
|
|||||||
value={ncontent}
|
value={ncontent}
|
||||||
id="notice-content"
|
id="notice-content"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 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
|
<button
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
type="button"
|
type="button"
|
||||||
id="edit-notice-button"
|
id="edit-notice-button"
|
||||||
on:click|preventDefault={() => handleEditNotice()}>Edit Notice</button
|
onclick={(e) => handleEditNotice(e)}>Edit Notice</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
|
@ -6,10 +6,13 @@
|
|||||||
import { getContext, onMount } from "svelte";
|
import { getContext, onMount } from "svelte";
|
||||||
import { Col, Card, CardBody, CardTitle } from "@sveltestrap/sveltestrap";
|
import { Col, Card, CardBody, CardTitle } from "@sveltestrap/sveltestrap";
|
||||||
|
|
||||||
let scrambled;
|
/*Const Init */
|
||||||
|
|
||||||
const resampleConfig = getContext("resampling");
|
const resampleConfig = getContext("resampling");
|
||||||
|
|
||||||
|
/* State Init */
|
||||||
|
let scrambled = $state(false);
|
||||||
|
|
||||||
|
/* on Mount */
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
scrambled = window.localStorage.getItem("cc-scramble-names") != null;
|
scrambled = window.localStorage.getItem("cc-scramble-names") != null;
|
||||||
});
|
});
|
||||||
@ -33,7 +36,7 @@
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="scramble-names-checkbox"
|
id="scramble-names-checkbox"
|
||||||
style="margin-right: 1em;"
|
style="margin-right: 1em;"
|
||||||
on:click={() => handleScramble()}
|
onclick={() => handleScramble()}
|
||||||
bind:checked={scrambled}
|
bind:checked={scrambled}
|
||||||
/>
|
/>
|
||||||
Active?
|
Active?
|
||||||
|
@ -16,23 +16,19 @@
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
CardBody,
|
CardBody,
|
||||||
} from "@sveltestrap/sveltestrap";
|
} from "@sveltestrap/sveltestrap";
|
||||||
import { createEventDispatcher } from "svelte";
|
|
||||||
import ShowUsersRow from "./ShowUsersRow.svelte";
|
import ShowUsersRow from "./ShowUsersRow.svelte";
|
||||||
|
|
||||||
export let users = [];
|
/*Svelte 5 Props */
|
||||||
|
let { users = $bindable([]), reloadUser } = $props();
|
||||||
const dispatch = createEventDispatcher();
|
|
||||||
function reloadUserList() {
|
|
||||||
dispatch("reload");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
function deleteUser(username) {
|
function deleteUser(username) {
|
||||||
if (confirm("Are you sure?")) {
|
if (confirm("Are you sure?")) {
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append("username", username);
|
formData.append("username", username);
|
||||||
fetch("/config/users/", { method: "DELETE", body: formData }).then((res) => {
|
fetch("/config/users/", { method: "DELETE", body: formData }).then((res) => {
|
||||||
if (res.status == 200) {
|
if (res.status == 200) {
|
||||||
reloadUserList();
|
reloadUser();
|
||||||
} else {
|
} else {
|
||||||
confirm(res.statusText);
|
confirm(res.statusText);
|
||||||
}
|
}
|
||||||
@ -40,7 +36,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: userList = users;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card class="h-100">
|
<Card class="h-100">
|
||||||
@ -53,7 +48,7 @@
|
|||||||
<Button
|
<Button
|
||||||
color="secondary"
|
color="secondary"
|
||||||
size="sm"
|
size="sm"
|
||||||
on:click={() => reloadUserList()}
|
onclick={() => reloadUser()}
|
||||||
style="float: right;">Reload</Button
|
style="float: right;">Reload</Button
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
@ -71,13 +66,13 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="users-list">
|
<tbody id="users-list">
|
||||||
{#each userList as user}
|
{#each users as user}
|
||||||
<tr id="user-{user.username}">
|
<tr id="user-{user.username}">
|
||||||
<ShowUsersRow {user} />
|
<ShowUsersRow {user} />
|
||||||
<td
|
<td
|
||||||
><button
|
><button
|
||||||
class="btn btn-danger del-user"
|
class="btn btn-danger del-user"
|
||||||
on:click={() => deleteUser(user.username)}>Delete</button
|
onclick={() => deleteUser(user.username)}>Delete</button
|
||||||
></td
|
></td
|
||||||
>
|
>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -10,9 +10,13 @@
|
|||||||
import { Button } from "@sveltestrap/sveltestrap";
|
import { Button } from "@sveltestrap/sveltestrap";
|
||||||
import { fetchJwt } from "../../generic/utils.js"
|
import { fetchJwt } from "../../generic/utils.js"
|
||||||
|
|
||||||
export let user;
|
/* Svelte 5 Props */
|
||||||
|
let { user } = $props();
|
||||||
|
|
||||||
let jwt = "";
|
/* State Init */
|
||||||
|
let jwt = $state("");
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
function getUserJwt(username) {
|
function getUserJwt(username) {
|
||||||
const p = fetchJwt(username);
|
const p = fetchJwt(username);
|
||||||
p.then((content) => {
|
p.then((content) => {
|
||||||
@ -30,7 +34,7 @@
|
|||||||
<td><code>{user?.roles ? user.roles.join(", ") : "No Roles"}</code></td>
|
<td><code>{user?.roles ? user.roles.join(", ") : "No Roles"}</code></td>
|
||||||
<td>
|
<td>
|
||||||
{#if !jwt}
|
{#if !jwt}
|
||||||
<Button color="success" on:click={() => getUserJwt(user.username)}
|
<Button color="success" onclick={() => getUserJwt(user.username)}
|
||||||
>Gen. JWT</Button
|
>Gen. JWT</Button
|
||||||
>
|
>
|
||||||
{:else}
|
{:else}
|
||||||
|
@ -6,12 +6,17 @@
|
|||||||
import { Row, Col, Card, CardTitle, CardBody, Button} from "@sveltestrap/sveltestrap";
|
import { Row, Col, Card, CardTitle, CardBody, Button} from "@sveltestrap/sveltestrap";
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
|
|
||||||
export let config;
|
/* Svelte 5 Props */
|
||||||
|
let { config } = $props();
|
||||||
|
|
||||||
let message;
|
/* State Init */
|
||||||
let displayMessage;
|
let message = $state("");
|
||||||
|
let displayMessage = $state(false);
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
|
async function handleSettingSubmit(event, selector, target) {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
async function handleSettingSubmit(selector, target) {
|
|
||||||
let form = document.querySelector(selector);
|
let form = document.querySelector(selector);
|
||||||
let formData = new FormData(form);
|
let formData = new FormData(form);
|
||||||
try {
|
try {
|
||||||
@ -58,7 +63,7 @@
|
|||||||
id="node-paging-form"
|
id="node-paging-form"
|
||||||
method="post"
|
method="post"
|
||||||
action="/frontend/configuration/"
|
action="/frontend/configuration/"
|
||||||
on:submit|preventDefault={() => handleSettingSubmit("#node-paging-form", "npag")}>
|
onsubmit={(e) => handleSettingSubmit(e, "#node-paging-form", "npag")}>
|
||||||
<input type="hidden" name="key" value="node_list_usePaging" />
|
<input type="hidden" name="key" value="node_list_usePaging" />
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div>
|
<div>
|
||||||
|
@ -19,21 +19,20 @@
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@sveltestrap/sveltestrap";
|
} from "@sveltestrap/sveltestrap";
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
import { createEventDispatcher } from 'svelte';
|
|
||||||
|
|
||||||
export let config;
|
/* Svelte 5 Props */
|
||||||
export let message;
|
let {
|
||||||
export let displayMessage;
|
config,
|
||||||
export let cbmode = false;
|
message = $bindable(),
|
||||||
|
displayMessage = $bindable(),
|
||||||
|
cbmode = $bindable(false),
|
||||||
|
updateSetting
|
||||||
|
} = $props();
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
/* State Init */
|
||||||
function updateSetting(selector, target) {
|
let activeRow = $state(JSON.stringify(config?.plot_general_colorscheme));
|
||||||
dispatch('update-config', {
|
|
||||||
selector: selector,
|
|
||||||
target: target
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Const Init */
|
||||||
const colorschemes = {
|
const colorschemes = {
|
||||||
Default: [
|
Default: [
|
||||||
"#00bfff",
|
"#00bfff",
|
||||||
@ -321,7 +320,6 @@
|
|||||||
"rgb(189,189,189)",
|
"rgb(189,189,189)",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Row cols={1} class="p-2 g-2">
|
<Row cols={1} class="p-2 g-2">
|
||||||
@ -350,37 +348,34 @@
|
|||||||
<input type="hidden" name="key" value="plot_general_colorscheme" />
|
<input type="hidden" name="key" value="plot_general_colorscheme" />
|
||||||
<Table hover>
|
<Table hover>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each Object.entries(cbmode ? cvdschemes : colorschemes) as [name, rgbrow]}
|
{#key activeRow}
|
||||||
<tr>
|
{#each Object.entries(cbmode ? cvdschemes : colorschemes) as [name, rgbrow]}
|
||||||
<th scope="col">{name}</th>
|
<tr>
|
||||||
<td>
|
<th scope="col">{name}</th>
|
||||||
{#if rgbrow.join(",") == config.plot_general_colorscheme}
|
<td>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name="value"
|
name="value"
|
||||||
value={JSON.stringify(rgbrow)}
|
value={JSON.stringify(rgbrow)}
|
||||||
checked
|
checked={activeRow == JSON.stringify(rgbrow)}
|
||||||
on:click={() =>
|
onclick={(e) => {
|
||||||
updateSetting("#colorscheme-form", "cs")}
|
activeRow = JSON.stringify(rgbrow)
|
||||||
|
updateSetting(e, {
|
||||||
|
selector: "#colorscheme-form",
|
||||||
|
target: "cs",
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{:else}
|
</td>
|
||||||
<input
|
<td>
|
||||||
type="radio"
|
{#each rgbrow as rgb}
|
||||||
name="value"
|
<span class="color-dot" style="background-color: {rgb};"
|
||||||
value={JSON.stringify(rgbrow)}
|
></span>
|
||||||
on:click={() =>
|
{/each}
|
||||||
updateSetting("#colorscheme-form", "cs")}
|
</td>
|
||||||
/>
|
</tr>
|
||||||
{/if}
|
{/each}
|
||||||
</td>
|
{/key}
|
||||||
<td>
|
|
||||||
{#each rgbrow as rgb}
|
|
||||||
<span class="color-dot" style="background-color: {rgb};"
|
|
||||||
></span>
|
|
||||||
{/each}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</form>
|
</form>
|
||||||
|
@ -19,33 +19,29 @@
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@sveltestrap/sveltestrap";
|
} from "@sveltestrap/sveltestrap";
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
import { createEventDispatcher } from 'svelte';
|
|
||||||
|
|
||||||
export let config;
|
/* Svelte 5 Props */
|
||||||
export let message;
|
let {
|
||||||
export let displayMessage;
|
config,
|
||||||
|
message = $bindable(),
|
||||||
const dispatch = createEventDispatcher();
|
displayMessage = $bindable(),
|
||||||
function updateSetting(selector, target) {
|
updateSetting
|
||||||
dispatch('update-config', {
|
} = $props();
|
||||||
selector: selector,
|
|
||||||
target: target
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Row cols={3} class="p-2 g-2">
|
<Row cols={3} class="p-2 g-2">
|
||||||
<!-- LINE WIDTH -->
|
<!-- LINE WIDTH -->
|
||||||
<Col
|
<Col
|
||||||
><Card class="h-100">
|
><Card class="h-100">
|
||||||
<!-- Important: Function with arguments needs to be event-triggered like on:submit={() => functionName('Some','Args')} OR no arguments and like this: on:submit={functionName} -->
|
|
||||||
<form
|
<form
|
||||||
id="line-width-form"
|
id="line-width-form"
|
||||||
method="post"
|
method="post"
|
||||||
action="/frontend/configuration/"
|
action="/frontend/configuration/"
|
||||||
class="card-body"
|
class="card-body"
|
||||||
on:submit|preventDefault={() =>
|
onsubmit={(e) => updateSetting(e, {
|
||||||
updateSetting("#line-width-form", "lw")}
|
selector: "#line-width-form",
|
||||||
|
target: "lw",
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->
|
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->
|
||||||
<CardTitle
|
<CardTitle
|
||||||
@ -90,8 +86,10 @@
|
|||||||
method="post"
|
method="post"
|
||||||
action="/frontend/configuration/"
|
action="/frontend/configuration/"
|
||||||
class="card-body"
|
class="card-body"
|
||||||
on:submit|preventDefault={() =>
|
onsubmit={(e) => updateSetting(e, {
|
||||||
updateSetting("#plots-per-row-form", "ppr")}
|
selector: "#plots-per-row-form",
|
||||||
|
target: "ppr",
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->
|
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->
|
||||||
<CardTitle
|
<CardTitle
|
||||||
@ -136,8 +134,10 @@
|
|||||||
method="post"
|
method="post"
|
||||||
action="/frontend/configuration/"
|
action="/frontend/configuration/"
|
||||||
class="card-body"
|
class="card-body"
|
||||||
on:submit|preventDefault={() =>
|
onsubmit={(e) => updateSetting(e, {
|
||||||
updateSetting("#backgrounds-form", "bg")}
|
selector: "#backgrounds-form",
|
||||||
|
target: "bg",
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->
|
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->
|
||||||
<CardTitle
|
<CardTitle
|
||||||
@ -180,8 +180,10 @@
|
|||||||
method="post"
|
method="post"
|
||||||
action="/frontend/configuration/"
|
action="/frontend/configuration/"
|
||||||
class="card-body"
|
class="card-body"
|
||||||
on:submit|preventDefault={() =>
|
onsubmit={(e) => updateSetting(e, {
|
||||||
updateSetting("#colorblindmode-form", "cbm")}
|
selector: "#colorblindmode-form",
|
||||||
|
target: "cbm",
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->
|
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->
|
||||||
<CardTitle
|
<CardTitle
|
||||||
|
@ -22,16 +22,23 @@
|
|||||||
CardBody
|
CardBody
|
||||||
} from "@sveltestrap/sveltestrap";
|
} from "@sveltestrap/sveltestrap";
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
import { createEventDispatcher } from 'svelte';
|
|
||||||
import { fetchJwt } from "../../generic/utils.js";
|
import { fetchJwt } from "../../generic/utils.js";
|
||||||
|
|
||||||
export let config;
|
/* Svelte 5 Props */
|
||||||
export let message;
|
let {
|
||||||
export let displayMessage;
|
config,
|
||||||
export let username;
|
message = $bindable(),
|
||||||
export let isApi;
|
displayMessage = $bindable(),
|
||||||
|
username,
|
||||||
|
isApi,
|
||||||
|
updateSetting
|
||||||
|
} = $props();
|
||||||
|
|
||||||
let jwt = "";
|
/* State Init */
|
||||||
|
let jwt = $state("");
|
||||||
|
let displayCheck = $state(false);
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
function getUserJwt(username) {
|
function getUserJwt(username) {
|
||||||
if (username) {
|
if (username) {
|
||||||
const p = fetchJwt(username);
|
const p = fetchJwt(username);
|
||||||
@ -43,7 +50,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let displayCheck = false;
|
|
||||||
function clipJwt() {
|
function clipJwt() {
|
||||||
displayCheck = true;
|
displayCheck = true;
|
||||||
// Navigator clipboard api needs a secure context (https)
|
// Navigator clipboard api needs a secure context (https)
|
||||||
@ -71,14 +77,6 @@
|
|||||||
displayCheck = false;
|
displayCheck = false;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
|
||||||
function updateSetting(selector, target) {
|
|
||||||
dispatch('update-config', {
|
|
||||||
selector: selector,
|
|
||||||
target: target
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Row cols={isApi ? 3 : 1} class="p-2 g-2">
|
<Row cols={isApi ? 3 : 1} class="p-2 g-2">
|
||||||
@ -90,8 +88,10 @@
|
|||||||
method="post"
|
method="post"
|
||||||
action="/frontend/configuration/"
|
action="/frontend/configuration/"
|
||||||
class="card-body"
|
class="card-body"
|
||||||
on:submit|preventDefault={() =>
|
onsubmit={(e) => updateSetting(e, {
|
||||||
updateSetting("#paging-form", "pag")}
|
selector: "#paging-form",
|
||||||
|
target: "pag",
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->
|
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->
|
||||||
<CardTitle
|
<CardTitle
|
||||||
@ -137,7 +137,7 @@
|
|||||||
<CardBody>
|
<CardBody>
|
||||||
<CardTitle>Generate JWT</CardTitle>
|
<CardTitle>Generate JWT</CardTitle>
|
||||||
{#if jwt}
|
{#if jwt}
|
||||||
<Button color="secondary" on:click={() => clipJwt()}>
|
<Button color="secondary" onclick={() => clipJwt()}>
|
||||||
Copy JWT to Clipboard
|
Copy JWT to Clipboard
|
||||||
</Button>
|
</Button>
|
||||||
<p class="mt-2">
|
<p class="mt-2">
|
||||||
@ -149,7 +149,7 @@
|
|||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<Button color="success" on:click={() => getUserJwt(username)}>
|
<Button color="success" onclick={() => getUserJwt(username)}>
|
||||||
Generate JWT for '{username}'
|
Generate JWT for '{username}'
|
||||||
</Button>
|
</Button>
|
||||||
<p class="mt-2">
|
<p class="mt-2">
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
* Requirements
|
* Requirements
|
||||||
* - Parent Components must be already Migrated
|
* - Parent Components must be already Migrated
|
||||||
* - TODO: Job.root.svelte, Node.root.svelte
|
* - TODO: Job.root.svelte, Node.root.svelte
|
||||||
|
* - DONE: Analysis, Status, User
|
||||||
*
|
*
|
||||||
* How-To
|
* How-To
|
||||||
* - Define "Plot-Slotcode" as SV5 Snippet with argument "item" in parent (!)
|
* - Define "Plot-Slotcode" as SV5 Snippet with argument "item" in parent (!)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user