feat: use new svelte 5 syntax

This commit is contained in:
Maxi Quoß
2025-08-31 10:05:25 +02:00
parent 2d1c64c9b4
commit f6a208f4d6
14 changed files with 258 additions and 180 deletions

View File

@@ -19,11 +19,9 @@
import { scale } from 'svelte/transition';
import DeviceCardNic from './DeviceCardNic.svelte';
export let device: Device;
let { device = $bindable() }: { device: Device } = $props();
let modalReboot: HTMLDialogElement;
$: moreButtons = [
let moreButtons = $derived([
{
text: m.device_card_btn_more_sleep(),
icon: faBed,
@@ -48,18 +46,17 @@
onClick: () => goto(`/device/${device.id}`),
requires: $pocketbase.authStore.isSuperuser || $permission.update?.includes(device.id)
}
];
]);
// update device status change
let now = Date.now();
let now = $state(Date.now());
let interval: number;
$: {
$effect(() => {
clearInterval(interval);
interval = setInterval(() => {
// eslint-disable-next-line svelte/infinite-reactive-loop
now = Date.now();
}, 1000);
}
});
function sleep() {
fetch(`${backendUrl}api/upsnap/sleep/${device.id}`, {
@@ -146,7 +143,7 @@
{#each moreButtons as btn, i (i)}
{#if btn.requires}
<div class="tooltip" data-tip={btn.text}>
<button class="btn btn-sm btn-circle" on:click={btn.onClick}>
<button class="btn btn-sm btn-circle" onclick={btn.onClick}>
<Fa icon={btn.icon} />
</button>
</div>
@@ -167,7 +164,7 @@
<div class="modal-action">
<form method="dialog">
<button class="btn">{m.buttons_cancel()}</button>
<button class="btn btn-success" on:click={reboot}>{m.buttons_confirm()}</button>
<button class="btn btn-success" onclick={reboot}>{m.buttons_confirm()}</button>
</form>
</div>
</div>

View File

@@ -6,44 +6,46 @@
import Fa from 'svelte-fa';
import toast from 'svelte-french-toast';
export let device: Device;
let hoverText = '';
let disabled = false;
let { device = $bindable() }: { device: Device } = $props();
let hoverText = $state('');
let disabled = $state(false);
let timeout = 120;
let interval: number;
let modalWake: HTMLDialogElement;
let modalShutdown: HTMLDialogElement;
let seconds = $derived(timeout % 60);
let minutes = $derived(Math.floor(timeout / 60));
$: if (device.status === 'pending' && !interval) {
// eslint-disable-next-line svelte/infinite-reactive-loop
countdown(Date.parse(device.updated), 'wake');
}
$: minutes = Math.floor(timeout / 60);
$: seconds = timeout % 60;
$: if (device.status === 'pending' || device.status === '') {
disabled = true;
hoverText = m.device_card_nic_tooltip_pending();
} else if (device.status === 'online') {
if (device.shutdown_cmd === '') {
disabled = true;
hoverText = m.device_card_nic_tooltip_shutdown_no_cmd();
} else if (!$pocketbase.authStore.isSuperuser && !$permission.power?.includes(device.id)) {
disabled = true;
hoverText = m.device_card_nic_tooltip_shutdown_no_permission();
} else {
disabled = false;
hoverText = m.device_card_nic_tooltip_shutdown();
$effect(() => {
if (device.status === 'pending' && !interval) {
countdown(Date.parse(device.updated), 'wake');
}
} else if (device.status === 'offline') {
if (!$pocketbase.authStore.isSuperuser && !$permission.power?.includes(device.id)) {
});
$effect(() => {
if (device.status === 'pending' || device.status === '') {
disabled = true;
hoverText = m.device_card_nic_tooltip_power_no_permission();
} else {
disabled = false;
hoverText = m.device_card_nic_tooltip_power();
hoverText = m.device_card_nic_tooltip_pending();
} else if (device.status === 'online') {
if (device.shutdown_cmd === '') {
disabled = true;
hoverText = m.device_card_nic_tooltip_shutdown_no_cmd();
} else if (!$pocketbase.authStore.isSuperuser && !$permission.power?.includes(device.id)) {
disabled = true;
hoverText = m.device_card_nic_tooltip_shutdown_no_permission();
} else {
disabled = false;
hoverText = m.device_card_nic_tooltip_shutdown();
}
} else if (device.status === 'offline') {
if (!$pocketbase.authStore.isSuperuser && !$permission.power?.includes(device.id)) {
disabled = true;
hoverText = m.device_card_nic_tooltip_power_no_permission();
} else {
disabled = false;
hoverText = m.device_card_nic_tooltip_power();
}
}
}
});
function wake() {
countdown(Date.now(), 'wake');
@@ -114,7 +116,7 @@
if (timeout <= 0 || device.status !== 'pending') {
clearInterval(interval);
// eslint-disable-next-line svelte/infinite-reactive-loop
interval = 0;
}
}, 1000);
@@ -148,8 +150,8 @@
<div
class={`tooltip ${disabled ? 'cursor-not-allowed' : 'hover:bg-base-300 cursor-pointer'} bg-base-100 rounded-box flex items-start gap-4 p-2`}
data-tip={hoverText}
on:click={disabled ? null : handleClick}
on:keydown={disabled ? null : handleClick}
onclick={disabled ? null : handleClick}
onkeydown={disabled ? null : handleClick}
role="none"
>
{#if device.status === 'offline'}
@@ -176,7 +178,9 @@
<div>{device.mac}</div>
<div class="flex flex-wrap gap-x-4">
{#if device?.expand?.ports}
{#each device?.expand?.ports.sort((a, b) => a.number - b.number) as port (port.id)}
{#each $state
.snapshot(device)
.expand?.ports.sort((a, b) => a.number - b.number) as port (port.id)}
<span class="flex items-center gap-1 break-all">
{#if port.status}
<div class="inline-grid *:[grid-area:1/1]">
@@ -194,7 +198,7 @@
href={port.link}
target="_blank"
class="underline"
on:click={(e) => e.stopPropagation()}>{port.name} ({port.number})</a
onclick={(e) => e.stopPropagation()}>{port.name} ({port.number})</a
>
{:else}
{port.name} ({port.number})
@@ -215,7 +219,7 @@
<div class="modal-action">
<form method="dialog">
<button class="btn">{m.buttons_cancel()}</button>
<button class="btn btn-success" on:click={wake}>{m.buttons_confirm()}</button>
<button class="btn btn-success" onclick={wake}>{m.buttons_confirm()}</button>
</form>
</div>
</div>
@@ -230,7 +234,7 @@
<div class="modal-action">
<form method="dialog">
<button class="btn">{m.buttons_cancel()}</button>
<button class="btn btn-success" on:click={shutdown}>{m.buttons_confirm()}</button>
<button class="btn btn-success" onclick={shutdown}>{m.buttons_confirm()}</button>
</form>
</div>
</div>

View File

@@ -194,7 +194,13 @@
}
</script>
<form on:submit|preventDefault={save}>
<form
onsubmit={(e) => {
e.preventDefault();
save();
}}
class="flex w-full flex-col"
>
<div class="card bg-base-200 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{m.device_general()}</h2>
@@ -275,7 +281,7 @@
</div>
<button
class="btn max-w-xs {device.expand.ports.length > 0 ? 'mt-4' : ''}"
on:click={() => createEmptyPort()}
onclick={() => createEmptyPort()}
type="button">{m.device_ports_add_new()}</button
>
</div>
@@ -666,12 +672,12 @@ second (059, optional)
<button
class="btn btn-error join-item"
type="button"
on:click={() => deleteGroup(group)}><Fa icon={faX} /></button
onclick={() => deleteGroup(group)}><Fa icon={faX} /></button
>
</div>
<div
class="btn join-item bg-base-100 hover:bg-base-200"
on:click={() => toggleGroup(group.id)}
onclick={() => toggleGroup(group.id)}
role="none"
>
<input
@@ -689,7 +695,7 @@ second (059, optional)
class="input join-item"
placeholder={m.device_groups_placeholder()}
type="text"
on:keydown={(e) => {
onkeydown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
addGroup();
@@ -697,7 +703,7 @@ second (059, optional)
}}
bind:value={newGroup}
/>
<button class="btn btn-neutral join-item" type="button" on:click={() => addGroup()}
<button class="btn btn-neutral join-item" type="button" onclick={() => addGroup()}
>{m.buttons_add()}</button
>
</div>
@@ -705,7 +711,7 @@ second (059, optional)
</div>
<div class="card-actions mt-6 justify-end gap-4">
{#if $pocketbase.authStore.isSuperuser || $permission.delete?.includes(device.id)}
<button class="btn btn-error" type="button" on:click={() => deleteModal.showModal()}
<button class="btn btn-error" type="button" onclick={() => deleteModal.showModal()}
><Fa icon={faTrash} />{m.buttons_delete()}</button
>
{/if}
@@ -719,7 +725,7 @@ second (059, optional)
<div class="modal-action">
<form method="dialog">
<button class="btn">{m.buttons_cancel()}</button>
<button class="btn btn-error" on:click={() => deleteDevice()}>{m.buttons_delete()}</button>
<button class="btn btn-error" onclick={() => deleteDevice()}>{m.buttons_delete()}</button>
</form>
</div>
</div>

View File

@@ -70,7 +70,7 @@
</fieldset>
<button
class="btn btn-error btn-xs btn-soft ms-auto"
on:click={() => deletePort(device.expand.ports[index])}
onclick={() => deletePort(device.expand.ports[index])}
type="button"><Fa icon={faTrash} />{m.buttons_delete()}</button
>
</fieldset>

View File

@@ -145,8 +145,8 @@
<button
class="gap-3 px-2"
data-set-theme={theme}
on:click={() => (activeTheme = theme)}
on:keydown={() => (activeTheme = theme)}
onclick={() => (activeTheme = theme)}
onkeydown={() => (activeTheme = theme)}
>
<div
data-theme={theme}
@@ -217,8 +217,8 @@
<button
class="gap-3 px-2"
data-set-theme={theme}
on:click={() => (activeTheme = theme)}
on:keydown={() => (activeTheme = theme)}
onclick={() => (activeTheme = theme)}
onkeydown={() => (activeTheme = theme)}
>
<div
data-theme={theme}
@@ -266,7 +266,7 @@
<a href="/account"><Fa icon={faUserGear} />{m.navbar_edit_account()}</a>
</li>
<li>
<div on:click={async () => logout()} on:keydown={async () => logout()} role="none">
<div onclick={async () => logout()} onkeydown={async () => logout()} role="none">
<Fa icon={faDoorOpen} />{m.navbar_logout()}
</div>
</li>

View File

@@ -125,7 +125,12 @@
{m.device_network_scan_desc()}
</p>
<div class="flex flex-row flex-wrap items-end gap-4">
<form on:submit|preventDefault={saveSettings}>
<form
onsubmit={(e) => {
e.preventDefault();
saveSettings();
}}
>
<fieldset class="fieldset p-0">
<label class="floating-label mt-2">
<span>{m.device_network_scan_ip_range()}</span>
@@ -160,7 +165,7 @@
{m.device_network_scan_running()}
</button>
{:else}
<button class="btn btn-success" on:click={() => scan()}>
<button class="btn btn-success" onclick={() => scan()}>
<Fa icon={faMagnifyingGlass} />
{m.device_network_scan()}
</button>
@@ -197,7 +202,7 @@
<div class="ms-auto">
<button
class="btn btn-success btn-sm"
on:click={(e) => {
onclick={(e) => {
addSingle(device);
e.currentTarget.disabled = true;
}}><Fa icon={faPlus} />{m.buttons_add()}</button
@@ -240,7 +245,7 @@
{#if addAllCheckbox}
<button
class="btn btn-success"
on:click={() => addAll()}
onclick={() => addAll()}
disabled={scanResponse.devices.length === 0}
>
<Fa icon={faPlus} />
@@ -249,7 +254,7 @@
{:else}
<button
class="btn btn-success"
on:click={() => addAll()}
onclick={() => addAll()}
disabled={scanResponse.devices.filter((dev) => dev.name !== 'Unknown').length === 0}
>
<Fa icon={faPlus} />
@@ -263,7 +268,7 @@
<div class="max-w-fit">
<button
class="btn btn-success"
on:click={() => addAll()}
onclick={() => addAll()}
disabled={scanResponse.devices.length === 0}
>
<Fa icon={faPlus} />

View File

@@ -17,13 +17,17 @@
import Fa from 'svelte-fa';
import toast from 'svelte-french-toast';
let loading = true;
let devices: Device[] = [];
let orderBy: 'name' | 'ip' = 'name';
let orderExpanded = false;
let orderByGroups = true;
let searchQuery = '';
let searchInput: HTMLInputElement;
let loading = $state(true);
let devices: Device[] = $state([]);
let orderBy: 'name' | 'ip' = $state('name');
let orderExpanded = $state(false);
let orderByGroups = $state(true);
let searchQuery = $state('');
let searchInput: HTMLInputElement | undefined = $state();
let filteredDevices: Device[] = $derived([]);
let devicesWithoutGroups: Device[] = $derived([]);
let devicesWithGroup: Record<string, Device[]> = $derived({});
const isMac = /Mac|iPhone|iPad/.test(navigator.userAgent);
const gridClass = 'grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-4';
@@ -35,32 +39,41 @@
orderByGroups = localStorage.getItem('orderByGroups') !== 'false';
}
$: if (browser) {
localStorage.setItem('orderBy', orderBy);
}
$: if (browser) {
localStorage.setItem('orderByGroups', String(orderByGroups));
}
$: filteredDevices = devices.filter(
(dev) =>
dev.ip.includes(searchQuery.toLowerCase()) ||
dev.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
dev.mac.toLowerCase().includes(searchQuery.toLowerCase()) ||
dev.description.toLowerCase().includes(searchQuery.toLowerCase())
);
$: devicesWithoutGroups = filteredDevices.filter((dev) => dev.groups.length === 0);
$: devicesWithGroup = filteredDevices.reduce(
(groups, dev) => {
dev.expand?.groups?.forEach((group: Group) => {
groups[group.id] = [...(groups[group.id] || []), dev];
});
return groups;
},
{} as Record<string, Device[]>
);
$effect(() => {
if (browser) {
localStorage.setItem('orderBy', orderBy);
}
});
$effect(() => {
if (browser) {
localStorage.setItem('orderByGroups', String(orderByGroups));
}
});
$effect(() => {
filteredDevices = devices.filter(
(dev) =>
dev.ip.includes(searchQuery.toLowerCase()) ||
dev.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
dev.mac.toLowerCase().includes(searchQuery.toLowerCase()) ||
dev.description.toLowerCase().includes(searchQuery.toLowerCase())
);
});
$effect(() => {
devicesWithoutGroups = filteredDevices
.filter((dev) => dev.groups.length === 0)
.sort((a, b) => a[orderBy].localeCompare(b[orderBy], $localeStore, { numeric: true }));
});
$effect(() => {
devicesWithGroup = filteredDevices.reduce(
(groups, dev) => {
dev.expand?.groups?.forEach((group: Group) => {
groups[group.id] = [...(groups[group.id] || []), dev];
});
return groups;
},
{} as Record<string, Device[]>
);
});
function getAllDevices() {
$pocketbase
@@ -74,7 +87,7 @@
function handleShortcut(event: KeyboardEvent) {
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
event.preventDefault();
searchInput.focus();
searchInput?.focus();
}
}
@@ -98,7 +111,7 @@
});
</script>
<svelte:document on:keydown={handleShortcut} />
<svelte:document onkeydown={handleShortcut} />
{#if loading}
<PageLoading />
@@ -127,7 +140,7 @@
</label>
<div class="join ms-auto">
{#if orderExpanded}
<button class="btn join-item !outline-0" on:click={() => (orderByGroups = !orderByGroups)}>
<button class="btn join-item !outline-0" onclick={() => (orderByGroups = !orderByGroups)}>
{m.home_order_groups()}
<input type="checkbox" class="checkbox" checked={orderByGroups} />
</button>
@@ -151,7 +164,7 @@
<button
class="btn join-item tooltip {orderExpanded ? '' : 'rounded-field'}"
data-tip={m.home_order_tooltip()}
on:click={() => (orderExpanded = !orderExpanded)}
onclick={() => (orderExpanded = !orderExpanded)}
>
<Fa icon={orderExpanded ? faChevronCircleRight : faChevronCircleLeft} size="lg" />
</button>
@@ -162,7 +175,7 @@
<div class="space-y-6">
{#if devicesWithoutGroups.length > 0}
<div class={gridClass}>
{#each structuredClone(devicesWithoutGroups).sort( (a, b) => a[orderBy].localeCompare( b[orderBy], $localeStore, { numeric: true } ) ) as device (device.id)}
{#each devicesWithoutGroups as device (device.id)}
<DeviceCard {device} />
{/each}
</div>
@@ -172,7 +185,7 @@
<h1 class="mb-3 text-2xl font-bold">
{groupDevices[0].expand.groups.find((grp) => grp.id === group)?.name ||
'Unknown group name'}
<button class="btn btn-sm btn-success btn-soft" on:click={() => wakeGroup(group)}
<button class="btn btn-sm btn-success btn-soft" onclick={() => wakeGroup(group)}
><Fa icon={faPowerOff} /> {m.home_wake_group()}</button
>
</h1>
@@ -186,7 +199,9 @@
</div>
{:else}
<div class={gridClass}>
{#each structuredClone(filteredDevices).sort( (a, b) => a[orderBy].localeCompare( b[orderBy], $localeStore, { numeric: true } ) ) as device (device.id)}
{#each $state
.snapshot(filteredDevices)
.sort( (a, b) => a[orderBy].localeCompare( b[orderBy], $localeStore, { numeric: true } ) ) as device (device.id)}
<DeviceCard {device} />
{/each}
</div>

View File

@@ -10,9 +10,8 @@
import Fa from 'svelte-fa';
import toast from 'svelte-french-toast';
let newAvatar: number;
let selectedLang: Locale;
let newAvatar: number | undefined = $state();
let selectedLang: Locale = $state('en-US' as Locale);
// locales
const languageEmojis = {
@@ -36,21 +35,27 @@
};
// password change
let newPassword = {
let newPassword = $state({
old: '',
password: '',
confirm: ''
};
$: adminBody = JSON.stringify({
password: newPassword.password,
passwordConfirm: newPassword.confirm
});
$: userBody = JSON.stringify({
oldPassword: newPassword.old,
password: newPassword.password,
passwordConfirm: newPassword.confirm
});
let adminBody: string;
let userBody: string;
$effect(() => {
adminBody = JSON.stringify({
password: newPassword.password,
passwordConfirm: newPassword.confirm
});
});
$effect(() => {
userBody = JSON.stringify({
oldPassword: newPassword.old,
password: newPassword.password,
passwordConfirm: newPassword.confirm
});
});
onMount(() => {
selectedLang = getLocale();
});
@@ -157,7 +162,12 @@
</h3>
</div>
</div>
<form on:submit|preventDefault={saveUser}>
<form
onsubmit={(e) => {
e.preventDefault();
saveUser();
}}
>
<h2 class="card-title mt-4 mb-2">{m.account_avatar_title()}</h2>
<div class="flex flex-row flex-wrap gap-4">
{#each [...Array(10).keys()] as i (i)}
@@ -176,7 +186,7 @@
class:ring-offset-2={newAvatar !== undefined
? i === newAvatar
: i === $pocketbase.authStore.record?.avatar}
on:click={() => (newAvatar = i)}
onclick={() => (newAvatar = i)}
role="none"
>
{#if $pocketbase.authStore.record?.id}
@@ -207,7 +217,12 @@
<div class="card-body">
<h2 class="card-title">{m.account_change_password_title()}</h2>
<p>{m.account_change_password_body()}</p>
<form on:submit|preventDefault={changePassword}>
<form
onsubmit={(e) => {
e.preventDefault();
changePassword();
}}
>
<div class="w-full max-w-xs">
{#if !$pocketbase.authStore.isSuperuser}
<fieldset class="fieldset">

View File

@@ -8,27 +8,31 @@
import type { Device, Group, Port } from '$lib/types/device';
import toast from 'svelte-french-toast';
$: if (Object.hasOwn($permission, 'update')) {
if (!$pocketbase.authStore.isSuperuser && !$permission.update.includes(page.params.id)) {
toast(m.toasts_no_permission({ url: page.url.pathname }), {
icon: '⛔'
});
goto('/');
$effect(() => {
if (Object.hasOwn($permission, 'update')) {
const id = page.params.id;
if (!$pocketbase.authStore.isSuperuser && !$permission.update.includes(id || '')) {
toast(m.toasts_no_permission({ url: page.url.pathname }), {
icon: '⛔'
});
goto('/');
}
}
}
});
async function getDevice(): Promise<Device> {
const resp = await $pocketbase
.collection('devices')
.getOne(page.params.id, { expand: 'ports,groups' });
const id = page.params.id;
if (!id) throw new Error('No device ID provided');
const resp = await $pocketbase.collection('devices').getOne(id, { expand: 'ports,groups' });
let device = resp as Device;
if (!device.expand)
if (!device.expand) {
device.expand = {} as {
ports: Port[];
groups: Group[];
};
}
if (!device.expand.ports) {
device.expand.ports = [] as Port[];

View File

@@ -43,15 +43,18 @@
sol_password: '',
sol_port: 0
} as Device;
let activeTab = $state('manual');
$: if (Object.hasOwn($permission, 'create')) {
if (!$pocketbase.authStore.isSuperuser && !$permission.create) {
toast(m.toasts_no_permission({ url: $page.url.pathname }), {
icon: '⛔'
});
goto('/');
$effect(() => {
if (Object.hasOwn($permission, 'create')) {
if (!$pocketbase.authStore.isSuperuser && !$permission.create) {
toast(m.toasts_no_permission({ url: $page.url.pathname }), {
icon: '⛔'
});
goto('/');
}
}
}
});
let tabs = [
{
@@ -67,7 +70,6 @@
show: $pocketbase.authStore.isSuperuser
}
];
let activeTab = 'manual';
</script>
<h1 class="mb-8 text-3xl font-bold">{m.device_page_title()}</h1>
@@ -76,7 +78,7 @@
{#each tabs as tab (tab)}
{#if tab.show}
<li>
<button class:menu-active={activeTab === tab.name} on:click={() => (activeTab = tab.name)}
<button class:menu-active={activeTab === tab.name} onclick={() => (activeTab = tab.name)}
>{tab.ll_name} <Fa icon={tab.icon} class="ms-2" /></button
>
</li>

View File

@@ -73,7 +73,13 @@
</figure>
<h2 class="card-title">{m.login_welcome()}</h2>
</div>
<form class="w-full" on:submit|preventDefault={tryAdminThenUser}>
<form
class="w-full"
onsubmit={(e) => {
e.preventDefault();
tryAdminThenUser();
}}
>
<label class="label" for="email">
<span>{m.login_email_label()}</span>
</label>
@@ -85,8 +91,8 @@
<div
class="absolute top-1/2 right-4 -translate-y-1/2 cursor-pointer"
role="none"
on:click={() => toggleVisibility(inputPassword)}
on:keydown={() => toggleVisibility(inputPassword)}
onclick={() => toggleVisibility(inputPassword)}
onkeydown={() => toggleVisibility(inputPassword)}
>
<Fa icon={faEye} />
</div>
@@ -118,7 +124,7 @@
{#if authMethods.oauth2.enabled && authMethods.oauth2.providers.length > 0}
{#each authMethods.oauth2.providers as provider (provider)}
<li>
<button type="button" on:click={() => loginWithProvider(provider)}
<button type="button" onclick={() => loginWithProvider(provider)}
>{provider.displayName}</button
>
</li>

View File

@@ -107,7 +107,13 @@
<PageLoading />
{:else}
<h1 class="mb-8 text-3xl font-bold">{m.settings_page_title()}</h1>
<form on:submit|preventDefault={save}>
<form
onsubmit={(e) => {
e.preventDefault();
save();
}}
class="flex w-full flex-col"
>
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{m.settings_ping_interval_title()}</h2>
@@ -201,8 +207,8 @@ second (059, optional)
/>
<button
class="btn btn-error"
on:click={() => resetFavicon()}
on:keydown={() => resetFavicon()}>{m.buttons_reset()}</button
onclick={() => resetFavicon()}
onkeydown={() => resetFavicon()}>{m.buttons_reset()}</button
>
</div>
</div>

View File

@@ -12,24 +12,26 @@
import Fa from 'svelte-fa';
import toast from 'svelte-french-toast';
let getUsersPromise = getUsers();
let getPermissionsPromise = getPermissions();
let getDevicesPromise = getDevices();
let deleteModal = [] as HTMLDialogElement[];
let getUsersPromise = $state(getUsers());
let getPermissionsPromise = $state(getPermissions());
let getDevicesPromise = $state(getDevices());
let deleteModal = $state([] as HTMLDialogElement[]);
let users = [] as User[];
let permissions = [] as Permission[];
let devices = [] as Device[];
let newUser = {
let users = $state([] as User[]);
let permissions = $state([] as Permission[]);
let devices = $state([] as Device[]);
let newUser = $state({
username: '',
password: '',
passwordConfirm: ''
};
});
$: users.map((user) => {
if (!permissions.find((perm) => perm.user === user.id)) {
permissions = [...permissions, { user: user.id } as Permission];
}
$effect(() => {
users.map((user) => {
if (!permissions.find((perm) => perm.user === user.id)) {
permissions = [...permissions, { user: user.id } as Permission];
}
});
});
onMount(() => {
@@ -183,7 +185,12 @@
{:then}
<h1 class="mb-8 text-3xl font-bold">{m.users_page_title()}</h1>
{#each users as user, index (user.id)}
<form on:submit|preventDefault={() => save(user)}>
<form
onsubmit={(e) => {
e.preventDefault();
save(user);
}}
>
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body gap-4">
<h2 class="card-title">
@@ -255,7 +262,7 @@
<button
class="btn btn-sm md:col-start-2"
type="button"
on:click={() => {
onclick={() => {
toggleAllPermissions(user, 'read');
}}
><span class="hidden md:block">{m.users_toggle()}</span><Fa
@@ -266,7 +273,7 @@
<button
class=" btn btn-sm"
type="button"
on:click={() => {
onclick={() => {
toggleAllPermissions(user, 'update');
}}
><span class="hidden md:block">{m.users_toggle()}</span><Fa
@@ -277,7 +284,7 @@
<button
class=" btn btn-sm"
type="button"
on:click={() => {
onclick={() => {
toggleAllPermissions(user, 'delete');
}}
><span class="hidden md:block">{m.users_toggle()}</span><Fa
@@ -288,7 +295,7 @@
<button
class=" btn btn-sm"
type="button"
on:click={() => {
onclick={() => {
toggleAllPermissions(user, 'power');
}}
><span class="hidden md:block">{m.users_toggle()}</span><Fa
@@ -305,7 +312,7 @@
<button
class="btn btn-error join-item"
type="button"
on:click={() => deleteModal[index].showModal()}
onclick={() => deleteModal[index].showModal()}
><Fa icon={faTrash} />{m.buttons_delete()}</button
>
<button class="btn btn-success join-item" type="submit"
@@ -322,7 +329,7 @@
<div class="modal-action">
<form method="dialog">
<button class="btn">{m.buttons_cancel()}</button>
<button class="btn btn-error" on:click={() => deleteUser(user)}
<button class="btn btn-error" onclick={() => deleteUser(user)}
>{m.buttons_delete()}</button
>
</form>
@@ -333,7 +340,12 @@
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{m.users_create_new_user()}</h2>
<form on:submit|preventDefault={createUser}>
<form
onsubmit={(e) => {
e.preventDefault();
createUser();
}}
>
<div class="flex flex-row flex-wrap gap-4">
<div class="w-full max-w-xs">
<label class="label" for="username">

View File

@@ -76,7 +76,7 @@
<h2 class="card-title">{m.welcome_not_expected_title()}</h2>
<p>{m.welcome_not_expected_desc()}</p>
<div class="card-actions justify-end">
<button class="btn btn-success" on:click={() => goto('/')}
<button class="btn btn-success" onclick={() => goto('/')}
>{m.welcome_not_expected_back()}</button
>
</div>
@@ -87,7 +87,7 @@
<h2 class="card-title">{m.welcome_step1_page_title()}</h2>
<p>{m.welcome_step1_setup_desc()}</p>
<div class="card-actions justify-end">
<button class="btn btn-success" on:click={() => (stepsCompleted = 1)}
<button class="btn btn-success" onclick={() => (stepsCompleted = 1)}
>{m.welcome_step1_setup_btn_next()} <Fa icon={faArrowRight} /></button
>
</div>
@@ -98,7 +98,13 @@
<figure class="w-16"><img src="/gopher.svg" alt="Gopher" /></figure>
<h2 class="card-title">{m.welcome_step2_page_title()}</h2>
</div>
<form class="w-full" on:submit|preventDefault={register}>
<form
class="w-full"
onsubmit={(e) => {
e.preventDefault();
register();
}}
>
<label class="label" for="email">
<span>{m.welcome_step2_label_email()}</span>
</label>
@@ -111,8 +117,8 @@
<div
class="absolute top-1/2 right-4 -translate-y-1/2 cursor-pointer"
role="none"
on:click={() => toggleVisibility(inputPassword)}
on:keydown={() => toggleVisibility(inputPassword)}
onclick={() => toggleVisibility(inputPassword)}
onkeydown={() => toggleVisibility(inputPassword)}
>
<Fa icon={faEye} />
</div>
@@ -134,8 +140,8 @@
<div
class="absolute top-1/2 right-4 -translate-y-1/2 cursor-pointer"
role="none"
on:click={() => toggleVisibility(inputConfirm)}
on:keydown={() => toggleVisibility(inputConfirm)}
onclick={() => toggleVisibility(inputConfirm)}
onkeydown={() => toggleVisibility(inputConfirm)}
>
<Fa icon={faEye} />
</div>
@@ -163,7 +169,7 @@
<h2 class="card-title">{m.welcome_step3_page_title()}</h2>
<p>{m.welcome_step3_page_desc()}</p>
<div class="card-actions justify-end">
<button class="btn btn-success" on:click={() => goto('/')}
<button class="btn btn-success" onclick={() => goto('/')}
>{m.welcome_step3_btn_done()}</button
>
</div>