Skip to content

Commit

Permalink
feat: edit and save tag/device config with merged values
Browse files Browse the repository at this point in the history
  • Loading branch information
MSchmoecker committed Jan 24, 2024
1 parent 874dba7 commit d380494
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 52 deletions.
6 changes: 3 additions & 3 deletions controller/app/models/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ def __init__(self, **data):
# first set name then call super
if "name" not in data:
data["name"] = self.__class__.__name__.lower()
if "type" not in data:
data["type"] = f"{self.__class__.__module__}.{self.__class__.__name__}"
super().__init__(**data)

@classmethod
Expand All @@ -35,9 +37,7 @@ def from_dict(cls, d):

@model_serializer(mode="wrap")
def ser_model(self, nxt: SerializerFunctionWrapHandler):
d = nxt(self)
d["type"] = f"{self.__class__.__module__}.{self.__class__.__name__}"
return d
return nxt(self)

def write_nix(
self,
Expand Down
19 changes: 7 additions & 12 deletions controller/app/models/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
loader=PackageLoader("app", "models"),
)

ALL_MODULES = [
models.Module,
models.Minio,
models.Thymis,
ALL_MODULES: List[models.Module] = [
models.Module(),
models.Minio(),
models.Thymis(),
]

HOST_PRIORITY = 100
Expand Down Expand Up @@ -49,7 +49,7 @@ def write_nix(self, path: os.PathLike):
# write its modules
for module_settings in device.modules:
# module holds settings right now.
module = next(m for m in self.modules if m.type == module_settings.type)
module = next(m for m in self.available_modules() if m.type == module_settings.type)
module.write_nix(device_path, module_settings, HOST_PRIORITY)
# for each tag create its own folder
for tag in self.tags:
Expand All @@ -58,16 +58,11 @@ def write_nix(self, path: os.PathLike):
# write its modules
for module_settings in tag.modules:
# module holds settings right now.
module = next(m for m in self.modules if m.type == module_settings.type)
module = next(m for m in self.available_modules() if m.type == module_settings.type)
module.write_nix(tag_path, module_settings, tag.priority)

def available_modules(self):
# return all modules that are not already included in the state
out = []
for module in ALL_MODULES:
if module not in [type(m) for m in self.modules]:
out.append(module())
return out
return ALL_MODULES

@classmethod
def load_from_dict(cls, d):
Expand Down
9 changes: 7 additions & 2 deletions frontend/src/lib/config/ConfigBool.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
<script lang="ts">
import { SlideToggle } from '@skeletonlabs/skeleton';
export let name: string;
export let value: boolean = false;
export let value: boolean | undefined = false;
export let onChange: (value: boolean) => void = () => {};
let change = (e: Event) => {
onChange((e.target as HTMLInputElement).checked);
};
</script>

<SlideToggle size="sm" {name} bind:checked={value} />
<SlideToggle size="sm" {name} checked={value} on:change={change} />
11 changes: 10 additions & 1 deletion frontend/src/lib/config/ConfigString.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
<script lang="ts">
export let placeholder: string | null;
export let value: unknown;
export let onChange: (value: string) => void = () => {};
let change = (e: Event) => {
onChange((e.target as HTMLInputElement).value);
};
$: console.log(value);
</script>

<input
class="w-full rounded-md bg-transparent border border-blue-gray-200 border-t-transparent outline outline-1 outline-secondary-400/70 p-1"
type="text"
{placeholder}
bind:value
value={value || ''}
on:change={change}
/>
4 changes: 2 additions & 2 deletions frontend/src/lib/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ export type Setting = SettingTypes & {
// type: string;
};

export type Module = { name: string } & Record<string, Setting>;
export type Module = { type: string, name: string } & Record<string, Setting>;

export type SettingValue = { value: SettingTypes }
export type ModuleSettings = { type: string, settings: { [key: string]: SettingValue } };
export type Tag = { name: string, priority: number, modules: ModuleSettings[] };
export type Tag = { name: string, priority: number, modules: (ModuleSettings & { priority: number })[] };
export type Device = { hostname: string, displayName: string, modules: ModuleSettings[], tags: string[] };

export type State = {
Expand Down
144 changes: 112 additions & 32 deletions frontend/src/routes/config/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import ConfigString from '$lib/config/ConfigString.svelte';
import type { PageData } from './$types';
import { queryParam } from 'sveltekit-search-params';
import { saveState } from '$lib/state';
import { saveState, type Module } from '$lib/state';
import { page } from '$app/stores';
import { get } from 'svelte/store';
const selected = queryParam<number>('selected', {
decode: (value) => (value ? parseInt(value, 10) : 0),
Expand All @@ -13,43 +15,113 @@
export let data: PageData;
$: state = data.state;
$: console.log(selected);
let tagParam = $page.url.searchParams.get('tag');
let deviceParam = $page.url.searchParams.get('device');
$: tag = state.tags.find((t) => t.name === tagParam);
$: device = state.devices.find((d) => d.hostname === deviceParam);
$: getModuleSettings = () => {
if (tag) {
return tag.modules;
}
if (device) {
return [
...device.modules,
...device.tags.flatMap((t) => state.tags.find((tag) => tag.name === t)?.modules ?? [])
];
}
};
$: getModules = () => {
let settings = getModuleSettings();
return data.availableModules.filter((m) => settings?.find((s) => s.type === m.type));
};
$: addModule = (module: Module) => {
if (tag && !tag.modules.find((m) => m.type === module.type)) {
tag.modules.push({ type: module.type, priority: 5, settings: {} });
}
if (device && !device.modules.find((m) => m.type === module.type)) {
device.modules.push({ type: module.type, settings: {} });
}
};
$: removeModule = (module: Module) => {
if (tag) {
tag.modules = tag.modules.filter((m) => m.type !== module.type);
}
if (device) {
device.modules = device.modules.filter((m) => m.type !== module.type);
}
};
$: getSetting = (module: Module, settingKey: string) => {
let settings = getModuleSettings();
return settings?.find(
(s) => s.type === module.type && Object.keys(s.settings).includes(settingKey)
)?.settings[settingKey].value as any;
};
$: setSetting = (module: Module, settingKey: string, value: any) => {
addModule(module);
if (tag) {
let tagModule = tag.modules.find((m) => m.type === module.type);
if (tagModule) {
tagModule.settings[settingKey] = { ...tagModule.settings[settingKey], value: value };
}
}
if (device) {
let deviceModule = device.modules.find((m) => m.type === module.type);
if (deviceModule) {
deviceModule.settings[settingKey] = { ...deviceModule.settings[settingKey], value: value };
}
}
};
$: modules = getModules();
</script>

<div class="grid grid-flow-row grid-cols-5 gap-12">
<div>
<div>
{#if tag}
Tag: {tag.name}
{:else if device}
Device: {device.hostname}
{/if}
</div>
<div class="mt-8">
Available Modules
<ul class="list">
{#each data.availableModules as module}
<li>
<span>(icon)</span>
<span class="flex-auto">{module.name}</span>
<button
class="btn"
on:click={() => {
state.modules = [...state.modules, module];
}}
>
add
</button>
</li>
{:else}
<li>no modules available</li>
<ListBox>
{#each data.availableModules.filter((m) => !modules.find((m2) => m2.type === m.type)) as module, i}
<ListBoxItem group={''} value={i} name={module.name} hover={''} active={''}>
<div class="flex place-content-between">
<div>{module.name}</div>
<button class="btn" on:click={() => addModule(module)}> add </button>
</div>
</ListBoxItem>
{/each}
</ul>
</ListBox>
</div>
<div>
<div class="mt-4">
Installed Modules
<ListBox>
{#each state.modules as module, i}
{#each modules as module, i}
<ListBoxItem bind:group={$selected} value={i} name={module.name}>
<div class="flex place-content-between">
<div>{module.name}</div>
<button
class="btn"
on:click={() => {
state.modules = state.modules.filter((_, index) => index !== i);
removeModule(module);
$selected = null;
}}
>
delete
Expand All @@ -58,30 +130,38 @@
</ListBoxItem>
{/each}
</ListBox>
</div>
<div class="mt-6">
<button type="button" class="btn variant-filled mt-8" on:click={() => saveState(state)}>
save
</button>
</div>
</div>
<div class="col-span-4 grid grid-cols-4 gap-8 gap-x-10">
{#if $selected != null && $selected < state.modules.length}
{#each Object.keys(state.modules[$selected]) as settingKey}
{#if $selected != null && $selected < modules.length}
{#each Object.keys(modules[$selected]) as settingKey}
{#if settingKey !== 'name' && settingKey !== 'type'}
<div class="col-span-1">{state.modules[$selected][settingKey].name}</div>
<div class="col-span-1">{modules[$selected][settingKey].name}</div>
<div class="col-span-1">
{#if state.modules[$selected][settingKey].type == 'bool'}
{#if modules[$selected][settingKey].type == 'bool'}
<ConfigBool
bind:value={state.modules[$selected][settingKey].value}
name={state.modules[$selected][settingKey].name}
value={getSetting(modules[$selected], settingKey)}
name={modules[$selected][settingKey].name}
onChange={(value) => {
if ($selected != null) setSetting(modules[$selected], settingKey, value);
}}
/>
{:else if state.modules[$selected][settingKey].type == 'string'}
{:else if modules[$selected][settingKey].type == 'string'}
<ConfigString
bind:value={state.modules[$selected][settingKey].value}
placeholder={state.modules[$selected][settingKey].default}
value={getSetting(modules[$selected], settingKey)}
placeholder={modules[$selected][settingKey].default}
onChange={(value) => {
if ($selected != null) setSetting(modules[$selected], settingKey, value);
}}
/>
{/if}
</div>
<div class="col-span-2">{state.modules[$selected][settingKey].description}</div>
<div class="col-span-2">{modules[$selected][settingKey].description}</div>
{/if}
{/each}
{/if}
Expand Down

0 comments on commit d380494

Please sign in to comment.