mirror of
https://github.com/seriousm4x/UpSnap.git
synced 2026-07-23 22:13:53 -04:00
add settings page
This commit is contained in:
@@ -17,9 +17,7 @@
|
||||
.collection('ports')
|
||||
.delete(port.id)
|
||||
.then(() => {
|
||||
deviceClone.ports = deviceClone.ports.filter(
|
||||
(id: number) => id !== deviceClone.ports[index]
|
||||
);
|
||||
deviceClone.ports = deviceClone.ports.filter((id) => id !== deviceClone.ports[index]);
|
||||
})
|
||||
.catch((err) => {
|
||||
clearTimeout(portErrTimeout);
|
||||
@@ -68,7 +66,7 @@
|
||||
</div>
|
||||
<div class="card-actions justify-end">
|
||||
<button
|
||||
class="btn btn-xs btn-error"
|
||||
class="btn btn-xs btn-outline btn-error"
|
||||
on:click={() => deletePort(deviceClone.expand.ports[index])}
|
||||
type="button"><Fa icon={faTrash} />Delete</button
|
||||
>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { pocketbase, backendUrl } from '$lib/stores/pocketbase';
|
||||
import { settingsPub } from '$lib/stores/settings';
|
||||
import Fa from 'svelte-fa';
|
||||
@@ -38,17 +39,25 @@
|
||||
tabindex="-1"
|
||||
class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-300 rounded-box w-52"
|
||||
>
|
||||
<li><a href="/"><Fa icon={faHome} />Home</a></li>
|
||||
<li><a href="/" class:active={$page.url.pathname === '/'}><Fa icon={faHome} />Home</a></li>
|
||||
{#if $pocketbase.authStore.model?.collectionName !== 'users'}
|
||||
<li><a href="/"><Fa icon={faKey} />Permissions</a></li>
|
||||
<li><a href="/"><Fa icon={faCog} />Settings</a></li>
|
||||
<li>
|
||||
<a href="/" class:active={$page.url.pathname.startsWith('/permissions')}
|
||||
><Fa icon={faKey} />Permissions</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/settings/" class:active={$page.url.pathname.startsWith('/settings')}
|
||||
><Fa icon={faCog} />Settings</a
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
</div>
|
||||
<a class="btn btn-ghost normal-case text-xl" href="/">
|
||||
<img
|
||||
src={$settingsPub?.collectionId && $settingsPub?.favicon
|
||||
? `/api/files/settings_public/${$settingsPub?.collectionId}/${$settingsPub?.favicon}`
|
||||
src={$settingsPub?.id && $settingsPub?.favicon
|
||||
? `${backendUrl}api/files/settings_public/${$settingsPub?.id}/${$settingsPub?.favicon}`
|
||||
: '/gopher.svg'}
|
||||
alt={$settingsPub?.website_title ? $settingsPub?.website_title : 'UpSnap'}
|
||||
width="45"
|
||||
@@ -58,10 +67,18 @@
|
||||
</div>
|
||||
<div class="navbar-center hidden md:flex">
|
||||
<ul class="menu menu-horizontal px-1">
|
||||
<li><a href="/"><Fa icon={faHome} />Home</a></li>
|
||||
<li><a href="/" class:active={$page.url.pathname === '/'}><Fa icon={faHome} />Home</a></li>
|
||||
{#if $pocketbase.authStore.model?.collectionName !== 'users'}
|
||||
<li><a href="/"><Fa icon={faKey} />Permissions</a></li>
|
||||
<li><a href="/"><Fa icon={faCog} />Settings</a></li>
|
||||
<li>
|
||||
<a href="/" class:active={$page.url.pathname.startsWith('/permissions')}
|
||||
><Fa icon={faKey} />Permissions</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/settings/" class:active={$page.url.pathname.startsWith('/settings')}
|
||||
><Fa icon={faCog} />Settings</a
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -55,10 +55,15 @@
|
||||
<svelte:head>
|
||||
<link
|
||||
rel="shortcut icon"
|
||||
href={$settingsPub?.collectionId && $settingsPub?.favicon
|
||||
? `${backendUrl}/api/files/settings_public/${$settingsPub?.collectionId}/${$settingsPub?.favicon}`
|
||||
href={$settingsPub?.id && $settingsPub?.favicon
|
||||
? `${backendUrl}api/files/settings_public/${$settingsPub?.id}/${$settingsPub?.favicon}`
|
||||
: '/gopher.svg'}
|
||||
/>
|
||||
{#if $settingsPub === undefined}
|
||||
<title>UpSnap</title>
|
||||
{:else}
|
||||
<title>{$settingsPub.website_title === '' ? 'UpSnap' : $settingsPub.website_title}</title>
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
{#if $pocketbase.authStore.isValid}
|
||||
|
||||
157
frontend/src/routes/settings/+page.svelte
Normal file
157
frontend/src/routes/settings/+page.svelte
Normal file
@@ -0,0 +1,157 @@
|
||||
<script lang="ts">
|
||||
import { settingsPub, settingsPriv } from '$lib/stores/settings';
|
||||
import { pocketbase, backendUrl } from '$lib/stores/pocketbase';
|
||||
import type { SettingsPublic, SettingsPrivate } from '$lib/types/settings';
|
||||
|
||||
let settingsPubClone: SettingsPublic | undefined;
|
||||
let settingsPrivClone: SettingsPrivate | undefined;
|
||||
let faviconPreview: HTMLImageElement;
|
||||
let faviconInputElement: HTMLInputElement;
|
||||
let saveBtn: HTMLButtonElement;
|
||||
let version = import.meta.env.UPSNAP_VERSION;
|
||||
|
||||
settingsPub.subscribe((settings) => {
|
||||
settingsPubClone = settings;
|
||||
});
|
||||
settingsPriv.subscribe((settings) => {
|
||||
settingsPrivClone = settings;
|
||||
});
|
||||
|
||||
if (!$settingsPriv && $pocketbase.authStore.isValid) {
|
||||
$pocketbase
|
||||
.collection('settings_private')
|
||||
.getList(1, 1)
|
||||
.then((res) => {
|
||||
settingsPriv.set(res.items[0] as SettingsPrivate);
|
||||
});
|
||||
}
|
||||
|
||||
function resetFavicon() {
|
||||
if (settingsPubClone === undefined) return;
|
||||
settingsPubClone.favicon = '';
|
||||
faviconInputElement.value = '';
|
||||
faviconPreview.src = '/gopher.svg';
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (
|
||||
settingsPubClone === undefined ||
|
||||
settingsPrivClone === undefined ||
|
||||
faviconInputElement === undefined ||
|
||||
saveBtn === undefined
|
||||
)
|
||||
return;
|
||||
|
||||
saveBtn.classList.remove('btn-primary');
|
||||
saveBtn.classList.add('btn-warning');
|
||||
|
||||
if (faviconInputElement.files !== null && faviconInputElement.files?.length > 0) {
|
||||
let form = new FormData();
|
||||
form.append('favicon', faviconInputElement.files[0]);
|
||||
await $pocketbase.collection('settings_public').update(settingsPubClone.id, form);
|
||||
}
|
||||
const res = await $pocketbase
|
||||
.collection('settings_public')
|
||||
.update(settingsPubClone.id, settingsPubClone);
|
||||
settingsPub.set(res as SettingsPublic);
|
||||
|
||||
saveBtn.classList.remove('btn-warning');
|
||||
saveBtn.classList.add('btn-primary');
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if settingsPubClone === undefined || settingsPrivClone === undefined}
|
||||
<div class="container mx-auto text-center"><span class="loading loading-ring loading-md" /></div>
|
||||
{:else}
|
||||
<h1 class="text-3xl font-bold mb-8">Settings</h1>
|
||||
<form on:submit|preventDefault={save}>
|
||||
<div class="card w-full bg-base-300 shadow-xl mt-6">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Ping interval</h2>
|
||||
<p class="mt-2">
|
||||
Sets the interval in which the devices are pinged. Leave blank to use default value of <span
|
||||
class="badge">@every 3s</span
|
||||
>.
|
||||
</p>
|
||||
<p class="mb-2">
|
||||
Learn more about the correct syntax for cron on
|
||||
<a target="_blank" class="link" href="https://en.wikipedia.org/wiki/Cron">Wikipedia</a>
|
||||
or refer to the
|
||||
<a target="_blank" class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3"
|
||||
>package documentation</a
|
||||
>.
|
||||
</p>
|
||||
<div class="form-control w-full">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="e.g. '@every 5s' or '@every 1m'"
|
||||
class="input w-full max-w-xs"
|
||||
bind:value={settingsPrivClone.interval}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card w-full bg-base-300 shadow-xl mt-6">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Website title</h2>
|
||||
<p class="my-2">Sets the title of the website and in the browser tab.</p>
|
||||
<div class="form-control w-full">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="e.g. 'UpSnap'"
|
||||
class="input w-full max-w-xs"
|
||||
bind:value={settingsPubClone.website_title}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card w-full bg-base-300 shadow-xl mt-6">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Icon</h2>
|
||||
<p class="my-2">
|
||||
Set a custom favicon. Supported file types are
|
||||
<span class="badge">.ico</span>, <span class="badge">.png</span>,
|
||||
<span class="badge">.svg</span>, <span class="badge">.gif</span> and
|
||||
<span class="badge">.jpg/.jpeg</span>.
|
||||
</p>
|
||||
<div>
|
||||
<img
|
||||
class="h-36"
|
||||
src={$settingsPub.favicon !== ''
|
||||
? `${backendUrl}api/files/settings_public/${$settingsPub.id}/${$settingsPub.favicon}`
|
||||
: '/gopher.svg'}
|
||||
alt="Favicon preview"
|
||||
bind:this={faviconPreview}
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control w-full max-w-md flex flex-row gap-4">
|
||||
<input
|
||||
type="file"
|
||||
class="file-input w-full max-w-xs"
|
||||
accept=".ico,.png,.svg,.gif,.jpg,.jpeg"
|
||||
bind:this={faviconInputElement}
|
||||
/>
|
||||
<button
|
||||
class="btn btn-outline btn-error"
|
||||
on:click={() => resetFavicon()}
|
||||
on:keydown={() => resetFavicon()}>Reset</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-actions justify-end mt-4">
|
||||
<button class="btn btn-primary" type="submit" bind:this={saveBtn}>Save</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="container mx-auto text-center">
|
||||
{#if version === undefined}
|
||||
UpSnap version: (untracked)
|
||||
{:else}
|
||||
UpSnap version: <a
|
||||
href="https://github.com/seriousm4x/UpSnap/releases/tag/{version}"
|
||||
class="text-reset">{version}</a
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
Reference in New Issue
Block a user