From f6a208f4d62f9455f0a9d9f554fdb438f3ec6fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxi=20Quo=C3=9F?= Date: Sun, 31 Aug 2025 10:05:25 +0200 Subject: [PATCH] feat: use new svelte 5 syntax --- frontend/src/lib/components/DeviceCard.svelte | 19 ++-- .../src/lib/components/DeviceCardNic.svelte | 78 +++++++-------- frontend/src/lib/components/DeviceForm.svelte | 22 +++-- .../src/lib/components/DeviceFormPort.svelte | 2 +- frontend/src/lib/components/Navbar.svelte | 10 +- .../src/lib/components/NetworkScan.svelte | 17 ++-- frontend/src/routes/+page.svelte | 95 +++++++++++-------- frontend/src/routes/account/+page.svelte | 47 +++++---- frontend/src/routes/device/[id]/+page.svelte | 26 ++--- frontend/src/routes/device/new/+page.svelte | 20 ++-- frontend/src/routes/login/+page.svelte | 14 ++- frontend/src/routes/settings/+page.svelte | 12 ++- frontend/src/routes/users/+page.svelte | 54 +++++++---- frontend/src/routes/welcome/+page.svelte | 22 +++-- 14 files changed, 258 insertions(+), 180 deletions(-) diff --git a/frontend/src/lib/components/DeviceCard.svelte b/frontend/src/lib/components/DeviceCard.svelte index a4c4b534..cc9fd27a 100644 --- a/frontend/src/lib/components/DeviceCard.svelte +++ b/frontend/src/lib/components/DeviceCard.svelte @@ -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}
-
@@ -167,7 +164,7 @@ diff --git a/frontend/src/lib/components/DeviceCardNic.svelte b/frontend/src/lib/components/DeviceCardNic.svelte index 02a87907..d9240d25 100644 --- a/frontend/src/lib/components/DeviceCardNic.svelte +++ b/frontend/src/lib/components/DeviceCardNic.svelte @@ -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 @@
{#if device.status === 'offline'} @@ -176,7 +178,9 @@
{device.mac}
{#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)} {#if port.status}
@@ -194,7 +198,7 @@ href={port.link} target="_blank" class="underline" - on:click={(e) => e.stopPropagation()}>{port.name} ({port.number}) e.stopPropagation()}>{port.name} ({port.number}) {:else} {port.name} ({port.number}) @@ -215,7 +219,7 @@
@@ -230,7 +234,7 @@
diff --git a/frontend/src/lib/components/DeviceForm.svelte b/frontend/src/lib/components/DeviceForm.svelte index 0e9716ef..8cffe81a 100644 --- a/frontend/src/lib/components/DeviceForm.svelte +++ b/frontend/src/lib/components/DeviceForm.svelte @@ -194,7 +194,13 @@ } -
+ { + e.preventDefault(); + save(); + }} + class="flex w-full flex-col" +>

{m.device_general()}

@@ -275,7 +281,7 @@
@@ -666,12 +672,12 @@ second (0–59, optional) deleteGroup(group)}>
toggleGroup(group.id)} + onclick={() => toggleGroup(group.id)} role="none" > { + onkeydown={(e) => { if (e.key === 'Enter') { e.preventDefault(); addGroup(); @@ -697,7 +703,7 @@ second (0–59, optional) }} bind:value={newGroup} /> -
@@ -705,7 +711,7 @@ second (0–59, optional)
{#if $pocketbase.authStore.isSuperuser || $permission.delete?.includes(device.id)} - {/if} @@ -719,7 +725,7 @@ second (0–59, optional)
diff --git a/frontend/src/lib/components/DeviceFormPort.svelte b/frontend/src/lib/components/DeviceFormPort.svelte index 24cfc081..b2a389c5 100644 --- a/frontend/src/lib/components/DeviceFormPort.svelte +++ b/frontend/src/lib/components/DeviceFormPort.svelte @@ -70,7 +70,7 @@ diff --git a/frontend/src/lib/components/Navbar.svelte b/frontend/src/lib/components/Navbar.svelte index 526b3953..5ab45904 100644 --- a/frontend/src/lib/components/Navbar.svelte +++ b/frontend/src/lib/components/Navbar.svelte @@ -145,8 +145,8 @@ {:else} - @@ -197,7 +202,7 @@
addAll()} + onclick={() => addAll()} disabled={scanResponse.devices.length === 0} > @@ -249,7 +254,7 @@ {:else} @@ -151,7 +164,7 @@ @@ -162,7 +175,7 @@
{#if devicesWithoutGroups.length > 0}
- {#each structuredClone(devicesWithoutGroups).sort( (a, b) => a[orderBy].localeCompare( b[orderBy], $localeStore, { numeric: true } ) ) as device (device.id)} + {#each devicesWithoutGroups as device (device.id)} {/each}
@@ -172,7 +185,7 @@

{groupDevices[0].expand.groups.find((grp) => grp.id === group)?.name || 'Unknown group name'} -

@@ -186,7 +199,9 @@
{:else}
- {#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)} {/each}
diff --git a/frontend/src/routes/account/+page.svelte b/frontend/src/routes/account/+page.svelte index d296b6ed..013e27f1 100644 --- a/frontend/src/routes/account/+page.svelte +++ b/frontend/src/routes/account/+page.svelte @@ -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 @@
- + { + e.preventDefault(); + saveUser(); + }} + >

{m.account_avatar_title()}

{#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 @@

{m.account_change_password_title()}

{m.account_change_password_body()}

- + { + e.preventDefault(); + changePassword(); + }} + >
{#if !$pocketbase.authStore.isSuperuser}
diff --git a/frontend/src/routes/device/[id]/+page.svelte b/frontend/src/routes/device/[id]/+page.svelte index c9fdd588..8add870f 100644 --- a/frontend/src/routes/device/[id]/+page.svelte +++ b/frontend/src/routes/device/[id]/+page.svelte @@ -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 { - 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[]; diff --git a/frontend/src/routes/device/new/+page.svelte b/frontend/src/routes/device/new/+page.svelte index 672856c1..0329b13c 100644 --- a/frontend/src/routes/device/new/+page.svelte +++ b/frontend/src/routes/device/new/+page.svelte @@ -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';

{m.device_page_title()}

@@ -76,7 +78,7 @@ {#each tabs as tab (tab)} {#if tab.show}
  • -
  • diff --git a/frontend/src/routes/login/+page.svelte b/frontend/src/routes/login/+page.svelte index ec97b715..36757645 100644 --- a/frontend/src/routes/login/+page.svelte +++ b/frontend/src/routes/login/+page.svelte @@ -73,7 +73,13 @@

    {m.login_welcome()}

    - + { + e.preventDefault(); + tryAdminThenUser(); + }} + > @@ -85,8 +91,8 @@
    toggleVisibility(inputPassword)} - on:keydown={() => toggleVisibility(inputPassword)} + onclick={() => toggleVisibility(inputPassword)} + onkeydown={() => toggleVisibility(inputPassword)} >
    @@ -118,7 +124,7 @@ {#if authMethods.oauth2.enabled && authMethods.oauth2.providers.length > 0} {#each authMethods.oauth2.providers as provider (provider)}
  • -
  • diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index d157de39..d7691cec 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -107,7 +107,13 @@ {:else}

    {m.settings_page_title()}

    - + { + e.preventDefault(); + save(); + }} + class="flex w-full flex-col" + >

    {m.settings_ping_interval_title()}

    @@ -201,8 +207,8 @@ second (0–59, optional) /> resetFavicon()} + onkeydown={() => resetFavicon()}>{m.buttons_reset()}
    diff --git a/frontend/src/routes/users/+page.svelte b/frontend/src/routes/users/+page.svelte index bff7d4ef..4bf93a09 100644 --- a/frontend/src/routes/users/+page.svelte +++ b/frontend/src/routes/users/+page.svelte @@ -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}

    {m.users_page_title()}

    {#each users as user, index (user.id)} - save(user)}> + { + e.preventDefault(); + save(user); + }} + >

    @@ -255,7 +262,7 @@ - @@ -333,7 +340,12 @@

    {m.users_create_new_user()}

    -
    + { + e.preventDefault(); + createUser(); + }} + >
    - + { + e.preventDefault(); + register(); + }} + > @@ -111,8 +117,8 @@
    toggleVisibility(inputPassword)} - on:keydown={() => toggleVisibility(inputPassword)} + onclick={() => toggleVisibility(inputPassword)} + onkeydown={() => toggleVisibility(inputPassword)} >
    @@ -134,8 +140,8 @@
    toggleVisibility(inputConfirm)} - on:keydown={() => toggleVisibility(inputConfirm)} + onclick={() => toggleVisibility(inputConfirm)} + onkeydown={() => toggleVisibility(inputConfirm)} >
    @@ -163,7 +169,7 @@

    {m.welcome_step3_page_title()}

    {m.welcome_step3_page_desc()}

    -