feat: daisyUI 5, tailwind 4, design adjustments

This commit is contained in:
Maxi Quoß
2025-01-29 22:31:16 +01:00
parent 9107e4f252
commit b22ca4525e
25 changed files with 2883 additions and 2029 deletions

View File

@@ -19,25 +19,23 @@
"@eslint/js": "^9.19.0",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.16.1",
"@sveltejs/vite-plugin-svelte": "^3.1.2",
"autoprefixer": "^10.4.20",
"daisyui": "^4.12.23",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"daisyui": "5.0.0-beta.2",
"eslint": "^9.19.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-svelte": "^2.46.1",
"globals": "^15.14.0",
"husky": "^9.1.7",
"postcss": "^8.5.1",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"svelte": "^4.2.19",
"svelte": "^5.19.5",
"svelte-check": "^4.1.4",
"tailwindcss": "^3.4.17",
"tailwindcss": "^4.0.1",
"tslib": "^2.8.1",
"typescript": "^5.7.3",
"typescript-eslint": "^8.22.0",
"vite": "^5.4.14"
"vite": "^6.0.11"
},
"type": "module",
"lint-staged": {
@@ -46,10 +44,12 @@
},
"dependencies": {
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@tailwindcss/postcss": "^4.0.1",
"cron-parser": "^4.9.0",
"date-fns": "^4.1.0",
"npm-run-all": "^4.1.5",
"pocketbase": "^0.25.1",
"postcss": "^8.5.1",
"svelte-fa": "^4.0.3",
"svelte-french-toast": "^1.2.0",
"theme-change": "^2.5.0",

3939
frontend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +0,0 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};

View File

@@ -0,0 +1,5 @@
export default {
plugins: {
'@tailwindcss/postcss': {}
}
};

View File

@@ -1,3 +1,5 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import 'tailwindcss';
@plugin "daisyui" {
themes: all;
}

View File

@@ -31,13 +31,9 @@
export let device: Device;
let modalReboot: HTMLDialogElement;
$: moreButtons = [
{
text: $LL.device.card_btn_more_edit(),
icon: faPen,
onClick: () => goto(`/device/${device.id}`),
requires: $pocketbase.authStore.isSuperuser || $permission.update?.includes(device.id)
},
{
text: $LL.device.card_btn_more_sleep(),
icon: faBed,
@@ -50,11 +46,17 @@
{
text: $LL.device.card_btn_more_reboot(),
icon: faRotateLeft,
onClick: () => reboot(),
onClick: () => askRebootConfirmation(),
requires:
($pocketbase.authStore.isSuperuser || $permission.power?.includes(device.id)) &&
device.status === 'online' &&
device.shutdown_cmd !== ''
},
{
text: $LL.device.card_btn_more_edit(),
icon: faPen,
onClick: () => goto(`/device/${device.id}`),
requires: $pocketbase.authStore.isSuperuser || $permission.update?.includes(device.id)
}
];
@@ -146,9 +148,17 @@
locale: dateFnsLocale
});
}
function askRebootConfirmation() {
if (device.shutdown_confirm) {
modalReboot.showModal();
} else {
reboot();
}
}
</script>
<div class="card rounded-3xl bg-base-300 shadow-md" transition:scale={{ delay: 0, duration: 200 }}>
<div class="card bg-base-200 shadow-sm" transition:scale={{ delay: 0, duration: 200 }}>
<div class="card-body p-6">
{#if device.link.toString() !== ''}
<a href={device.link.toString()} target="_blank">
@@ -160,9 +170,9 @@
{#if device.description}
<p>{device.description}</p>
{/if}
<ul class="menu rounded-box bg-base-200">
<div class="card rounded-box w-full">
<DeviceCardNic {device} />
</ul>
</div>
{#if device.wake_cron_enabled || device.shutdown_cron_enabled || device.password}
<div class="mt-1 flex flex-row flex-wrap gap-2">
{#if device.wake_cron_enabled}
@@ -203,27 +213,33 @@
{/if}
</span>
{#if moreButtons.filter((btn) => btn.requires).length > 0}
<div class="dropdown dropdown-end dropdown-top ms-auto bg-base-300">
<label tabindex="-1" class="btn btn-sm m-1" for="more-{device.id}"
>{$LL.device.card_btn_more()}</label
>
<ul
id="more-{device.id}"
tabindex="-1"
class="menu dropdown-content z-[1] w-fit rounded-box bg-base-100 p-2 shadow"
>
{#each moreButtons as btn}
{#if btn.requires}
<li>
<button on:click={btn.onClick}>
<Fa icon={btn.icon} />{btn.text}
</button>
</li>
{/if}
{/each}
</ul>
<div class="ms-auto flex flex-row flex-wrap gap-1">
{#each moreButtons as btn}
{#if btn.requires}
<div class="tooltip" data-tip={btn.text}>
<button class="btn btn-sm btn-circle" on:click={btn.onClick}>
<Fa icon={btn.icon} />
</button>
</div>
{/if}
{/each}
</div>
{/if}
</div>
</div>
</div>
<dialog class="modal" bind:this={modalReboot}>
<div class="modal-box">
<h3 class="text-lg font-bold">
{$LL.device.modal_confirm_shutdown_title({ device: device.name })}
</h3>
<p class="py-4">{$LL.device.modal_confirm_shutdown_desc({ device: device.name })}</p>
<div class="modal-action">
<form method="dialog" class="flex flex-row flex-wrap gap-2">
<button class="btn">{$LL.buttons.cancel()}</button>
<button class="btn btn-success" on:click={reboot}>{$LL.buttons.confirm()}</button>
</form>
</div>
</div>
</dialog>

View File

@@ -2,7 +2,7 @@
import LL from '$lib/i18n/i18n-svelte';
import { backendUrl, permission, pocketbase } from '$lib/stores/pocketbase';
import type { Device } from '$lib/types/device';
import { faCircle, faPowerOff } from '@fortawesome/free-solid-svg-icons';
import { faPowerOff } from '@fortawesome/free-solid-svg-icons';
import Fa from 'svelte-fa';
import toast from 'svelte-french-toast';
@@ -135,61 +135,66 @@
}
</script>
<li class="tooltip" class:disabled data-tip={hoverText}>
<div
class="flex items-start gap-4 p-2"
on:click={disabled ? null : handleClick}
on:keydown={disabled ? null : handleClick}
role="none"
>
{#if device.status === 'offline'}
<button class="btn btn-error flex-shrink"><Fa icon={faPowerOff} /></button>
{:else if device.status === 'online'}
<button
class="btn btn-success flex-shrink"
class:cursor-not-allowed={device.shutdown_cmd === ''}><Fa icon={faPowerOff} /></button
>
{:else if device.status === 'pending'}
<button class="btn btn-warning flex-shrink">
<span class="countdown font-mono">
<span style="--value:{minutes};" />:
<span style="--value:{seconds};" />
</span>
</button>
{:else}
<button class="btn btn-warning flex-shrink">
<span class="loading loading-ring loading-sm" />
</button>
{/if}
<div class="grow">
<div class="text-lg font-bold leading-4">{device.ip}</div>
<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}
<span class="flex items-center gap-1 break-all">
{#if port.status}
<Fa icon={faCircle} class="text-success" />
{:else}
<Fa icon={faCircle} class="text-error" />
{/if}
{#if port.link}
<a
href={port.link}
target="_blank"
class="underline"
on:click={(e) => e.stopPropagation()}>{port.name} ({port.number})</a
>
{:else}
{port.name} ({port.number})
{/if}
</span>
{/each}
{/if}
</div>
<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}
role="none"
>
{#if device.status === 'offline'}
<button class="btn btn-error btn-circle size-12"><Fa icon={faPowerOff} /></button>
{:else if device.status === 'online'}
<button
class="btn btn-success btn-circle size-12"
class:cursor-not-allowed={device.shutdown_cmd === ''}><Fa icon={faPowerOff} /></button
>
{:else if device.status === 'pending'}
<button class="btn btn-warning">
<span class="countdown font-mono">
<span style="--value:{minutes};"></span>:
<span style="--value:{seconds};"></span>
</span>
</button>
{:else}
<button class="btn btn-warning btn-circle size-12" aria-label="Unknown">
<span class="loading loading-ring loading-sm"></span>
</button>
{/if}
<div class="grow">
<div class="text-lg leading-4 font-bold">{device.ip}</div>
<div class="">{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}
<span class="flex items-center gap-1 break-all">
{#if port.status}
<div class="inline-grid *:[grid-area:1/1]">
<div class="status status-success h-3 w-3 animate-ping"></div>
<div class="status status-success h-3 w-3"></div>
</div>
{:else}
<div class="inline-grid *:[grid-area:1/1]">
<div class="status status-error h-3 w-3 animate-ping"></div>
<div class="status status-error h-3 w-3"></div>
</div>
{/if}
{#if port.link}
<a
href={port.link}
target="_blank"
class="underline"
on:click={(e) => e.stopPropagation()}>{port.name} ({port.number})</a
>
{:else}
{port.name} ({port.number})
{/if}
</span>
{/each}
{/if}
</div>
</div>
</li>
</div>
<dialog class="modal" bind:this={modalWake}>
<div class="modal-box">
@@ -199,7 +204,7 @@
<p class="py-4">{$LL.device.modal_confirm_wake_desc({ device: device.name })}</p>
<div class="modal-action">
<form method="dialog" class="flex flex-row flex-wrap gap-2">
<button class="btn btn-neutral">{$LL.buttons.cancel()}</button>
<button class="btn">{$LL.buttons.cancel()}</button>
<button class="btn btn-success" on:click={wake}>{$LL.buttons.confirm()}</button>
</form>
</div>
@@ -214,15 +219,9 @@
<p class="py-4">{$LL.device.modal_confirm_shutdown_desc({ device: device.name })}</p>
<div class="modal-action">
<form method="dialog" class="flex flex-row flex-wrap gap-2">
<button class="btn btn-neutral">{$LL.buttons.cancel()}</button>
<button class="btn">{$LL.buttons.cancel()}</button>
<button class="btn btn-success" on:click={shutdown}>{$LL.buttons.confirm()}</button>
</form>
</div>
</div>
</dialog>
<style>
:global(.menu li.disabled) {
color: inherit;
}
</style>

View File

@@ -178,11 +178,11 @@
</script>
<form on:submit|preventDefault={save}>
<div class="card w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.device.general()}</h2>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4">
<div class="form-control w-full max-w-xs">
<div class="w-full max-w-xs">
<label class="label" for="device-name">
<div class="label-text">
<span>{$LL.device.general_name()}</span>
@@ -198,7 +198,7 @@
required
/>
</div>
<div class="form-control w-full max-w-xs">
<div class="w-full max-w-xs">
<label class="label" for="device-ip">
<div class="label-text">
<span>{$LL.device.general_ip()}</span>
@@ -214,7 +214,7 @@
required
/>
</div>
<div class="form-control w-full max-w-xs">
<div class="w-full max-w-xs">
<label class="label" for="device-mac">
<div class="label-text">
<span>{$LL.device.general_mac()}</span>
@@ -230,7 +230,7 @@
required
/>
</div>
<div class="form-control w-full max-w-xs">
<div class="w-full max-w-xs">
<label class="label" for="device-netmask">
<div class="label-text">
<span>{$LL.device.general_netmask()}</span>
@@ -246,7 +246,7 @@
required
/>
</div>
<div class="form-control w-full max-w-xs">
<div class="w-full max-w-xs">
<label class="label" for="device-description">
<div class="label-text">
<span>{$LL.device.general_description()}</span>
@@ -261,15 +261,15 @@
/>
</div>
<span class="badge self-center text-error">* {$LL.device.general_required_field()}</span>
<span class="badge text-error self-center">* {$LL.device.general_required_field()}</span>
</div>
</div>
</div>
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.device.ports()}</h2>
<p class="my-2">{$LL.device.ports_desc()}</p>
<div class="form-control w-full">
<div class="w-full">
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<!-- eslint-disable-next-line @typescript-eslint/no-unused-vars -->
{#each device.expand.ports as _, index}
@@ -277,21 +277,21 @@
{/each}
</div>
<button
class="btn btn-primary mt-4 max-w-xs"
class="btn max-w-xs {device.expand.ports.length > 0 ? 'mt-4' : ''}"
on:click={() => createEmptyPort()}
type="button">{$LL.device.ports_add_new()}</button
>
</div>
</div>
</div>
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.device.link()}</h2>
<p class="my-2">
{$LL.device.link_desc()}
</p>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<div class="form-control w-full">
<div class="w-full max-w-xs">
<label class="label" for="device-link">
<div class="label-text">{$LL.device.link()}</div>
</label>
@@ -303,7 +303,7 @@
bind:value={device.link}
/>
</div>
<label class="form-control w-full max-w-xs">
<div class="w-full max-w-xs">
<label class="label" for="device-link-open">
<div class="label-text">{$LL.device.link_open()}</div>
</label>
@@ -316,18 +316,18 @@
<option value="same_tab">{$LL.device.link_open_same_tab()}</option>
<option value="new_tab">{$LL.device.link_open_new_tab()}</option>
</select>
</label>
</div>
</div>
</div>
</div>
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.device.ping()}</h2>
<p class="my-2">
<!-- eslint-disable svelte/no-at-html-tags -->
{@html $LL.device.ping_desc()}
</p>
<div class="form-control w-full">
<div class="w-full max-w-xs">
<label class="label cursor-pointer" for="ping-cmd">
<span class="label-text">{$LL.device.ping_cmd()}</span>
</label>
@@ -341,7 +341,7 @@
</div>
</div>
</div>
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.device.wake()}</h2>
<p class="my-2">
@@ -349,7 +349,7 @@
<!-- eslint-disable svelte/no-at-html-tags -->
{@html $LL.settings.ping_interval_desc2()}
</p>
<div class="form-control w-full">
<div class="w-full max-w-xs">
<label class="label cursor-pointer" for="wake-cmd">
<span class="label-text">{$LL.device.wake_cmd()}</span>
</label>
@@ -361,24 +361,33 @@
bind:value={device.wake_cmd}
/>
</div>
<div class="form-control flex flex-row flex-wrap gap-4">
<div class="flex flex-row items-center gap-2">
<div class="flex flex-col">
<label class="label cursor-pointer" for="wake-confirm">
<span class="label-text">{$LL.device.require_confirmation()}</span>
</label>
<input
id="wake-confirm"
type="checkbox"
class="toggle toggle-success"
bind:checked={device.wake_confirm}
/>
</div>
<div class="flex flex-row flex-wrap gap-4">
<div class="flex flex-col">
<label class="label cursor-pointer" for="wake-cron-enable">
<span class="label-text">{$LL.device.wake_cron_enable()}</span>
</label>
<input
id="wake-confirm"
id="wake-cron-enable"
type="checkbox"
class="toggle toggle-success"
bind:checked={device.wake_confirm}
bind:checked={device.wake_cron_enabled}
/>
<label class="label cursor-pointer" for="wake-confirm">
<span class="label-text">{$LL.device.require_confirmation()}</span>
</label>
</div>
</div>
<div class="form-control flex flex-row flex-wrap gap-4">
<div class="w-full max-w-xs">
<label class="label" for="wake-cron">
<span class="label-text"
>{$LL.device.wake_cron()}
>{$LL.device.shutdown_cron()}
{#if device.wake_cron_enabled}
<span class="text-error">*</span>
{/if}
@@ -394,22 +403,11 @@
required={device.wake_cron_enabled}
/>
</div>
<div class="flex flex-col">
<label class="label cursor-pointer" for="wake-cron-enable">
<span class="label-text">{$LL.device.wake_cron_enable()}</span>
</label>
<input
id="wake-cron-enable"
type="checkbox"
class="toggle toggle-success"
bind:checked={device.wake_cron_enabled}
/>
</div>
</div>
</div>
</div>
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">Sleep-On-LAN</h2>
<p class="mt-2">
@@ -423,78 +421,73 @@
<!-- eslint-disable svelte/no-at-html-tags -->
{@html $LL.device.sol_desc3()}
</p>
<div class="mt-4 flex flex-row flex-wrap items-end gap-4">
<div>
<div class="form-control flex flex-row flex-wrap gap-4">
<div class="flex flex-row items-center gap-2">
<input
id="sol-enable"
type="checkbox"
class="toggle toggle-success"
bind:checked={device.sol_enabled}
/>
<label class="label cursor-pointer" for="sol-enable">
<span class="label-text">{$LL.device.sol_enable()}</span>
</label>
</div>
<div class="flex flex-row flex-wrap gap-4">
<div class="flex flex-col">
<label class="label cursor-pointer" for="sol-enable">
<span class="label-text">{$LL.device.sol_enable()}</span>
</label>
<input
id="sol-enable"
type="checkbox"
class="toggle toggle-success"
bind:checked={device.sol_enabled}
/>
</div>
<div class="flex flex-col">
<label class="label" for="sol-port">
<span class="label-text"
>{$LL.device.sol_port()}
{#if device.sol_enabled}
<span class="text-error">*</span>
{/if}
</span>
</label>
<input
id="sol-port"
type="number"
min="1"
max="65535"
class="input"
bind:value={device.sol_port}
disabled={!device.sol_enabled}
required={device.sol_enabled}
/>
</div>
</div>
{#if device.sol_enabled}
<div class="flex flex-row flex-wrap gap-4">
<div class="flex flex-col">
<label class="label cursor-pointer" for="sol-auth">
<span class="label-text">{$LL.device.sol_authorization()}</span>
</label>
<input
id="sol-auth"
type="checkbox"
class="toggle toggle-success"
bind:checked={device.sol_auth}
/>
</div>
<div class="form-control flex flex-col">
<label class="label" for="sol-port">
<div class="flex flex-col">
<label class="label" for="sol-user">
<span class="label-text"
>{$LL.device.sol_port()}
{#if device.sol_enabled}
>{$LL.device.sol_user()}
{#if device.sol_auth}
<span class="text-error">*</span>
{/if}
</span>
</label>
<input
id="sol-port"
type="number"
min="1"
max="65535"
id="sol-user"
type="text"
placeholder={$LL.device.sol_user()}
class="input"
bind:value={device.sol_port}
disabled={!device.sol_enabled}
required={device.sol_enabled}
bind:value={device.sol_user}
disabled={!device.sol_auth}
required={device.sol_auth}
/>
</div>
</div>
{#if device.sol_enabled}
<div>
<div class="form-control flex flex-row flex-wrap gap-4">
<div class="flex flex-row items-center gap-2">
<input
id="sol-auth"
type="checkbox"
class="toggle toggle-success"
bind:checked={device.sol_auth}
/>
<label class="label cursor-pointer" for="sol-auth">
<span class="label-text">{$LL.device.sol_authorization()}</span>
</label>
</div>
</div>
<div class="form-control flex flex-col">
<label class="label" for="sol-user">
<span class="label-text"
>{$LL.device.sol_user()}
{#if device.sol_auth}
<span class="text-error">*</span>
{/if}
</span>
</label>
<input
id="sol-user"
type="text"
placeholder={$LL.device.sol_user()}
class="input"
bind:value={device.sol_user}
disabled={!device.sol_auth}
required={device.sol_auth}
/>
</div>
</div>
<div class="form-control flex flex-col">
<div class="flex flex-col">
<label class="label" for="sol-password">
<span class="label-text"
>{$LL.device.sol_password()}
@@ -513,12 +506,12 @@
required={device.sol_auth}
/>
</div>
{/if}
</div>
</div>
{/if}
</div>
</div>
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.device.shutdown()}</h2>
<p class="my-2">
@@ -526,19 +519,19 @@
{@html $LL.device.shutdown_desc()}
</p>
<p class="my-2 font-bold">{$LL.device.shutdown_examples()}</p>
<div class="mockup-code min-w-0 max-w-fit text-sm">
<div class="mockup-code max-w-fit min-w-0 text-sm">
<pre data-prefix="#"><code>{$LL.device.shutdown_examples_windows()}</code></pre>
<pre data-prefix="$" class="text-warning"><code
>net rpc shutdown -I 192.168.1.13 -U "user%password"</code
></pre>
</div>
<div class="mockup-code min-w-0 max-w-fit text-sm">
<div class="mockup-code max-w-fit min-w-0 text-sm">
<pre data-prefix="#"><code>{$LL.device.shutdown_examples_linux()}</code></pre>
<pre data-prefix="$" class="text-warning"><code
>sshpass -p password ssh -o "StrictHostKeyChecking=no" user@192.168.1.13 "sudo poweroff"</code
></pre>
</div>
<div class="form-control w-full">
<div class="w-full max-w-xs">
<label class="label cursor-pointer" for="shutdown-cmd">
<span class="label-text">{$LL.device.shutdown_cmd()}</span>
</label>
@@ -550,23 +543,34 @@
bind:value={device.shutdown_cmd}
/>
</div>
<div class="form-control flex flex-row flex-wrap gap-4">
<div class="flex flex-row items-center gap-2">
<div class="flex flex-row flex-wrap gap-4">
<div class="flex flex-col">
<label class="label cursor-pointer" for="shutdown-confirm">
<span class="label-text">{$LL.device.require_confirmation()}</span>
</label>
<input
id="shutdown-confirm"
type="checkbox"
class="toggle toggle-success"
bind:checked={device.shutdown_confirm}
/>
<label class="label cursor-pointer" for="shutdown-confirm">
<span class="label-text">{$LL.device.require_confirmation()}</span>
</label>
</div>
</div>
<p class="my-2">
{$LL.device.shutdown_cron_desc()}
</p>
<div class="form-control flex flex-row flex-wrap gap-4">
<div class="flex flex-row flex-wrap gap-4">
<div class="flex flex-col">
<label class="label cursor-pointer" for="shutdown-cron-enable">
<span class="label-text">{$LL.device.shutdown_cron_enable()}</span>
</label>
<input
id="shutdown-cron-enable"
type="checkbox"
class="toggle toggle-success"
bind:checked={device.shutdown_cron_enabled}
/>
</div>
<div class="w-full max-w-xs">
<label class="label" for="shutdown-cron">
<span class="label-text"
@@ -586,28 +590,17 @@
required={device.shutdown_cron_enabled}
/>
</div>
<div class="flex flex-col">
<label class="label cursor-pointer" for="shutdown-cron-enable">
<span class="label-text">{$LL.device.shutdown_cron_enable()}</span>
</label>
<input
id="shutdown-cron-enable"
type="checkbox"
class="toggle toggle-success"
bind:checked={device.shutdown_cron_enabled}
/>
</div>
</div>
</div>
</div>
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.device.password()}</h2>
<p class="my-2">
<!-- eslint-disable svelte/no-at-html-tags -->
{@html $LL.device.password_desc()}
</p>
<div class="form-control w-full">
<div class="w-full">
<input
type="password"
placeholder="123456"
@@ -618,7 +611,7 @@
</div>
</div>
</div>
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.device.groups()}</h2>
<p class="my-2">
@@ -641,7 +634,7 @@
>
<input
type="checkbox"
class="checkbox checked:checkbox-primary"
class="checkbox"
checked={device.groups.indexOf(group.id) !== -1}
/>
{group.name}
@@ -656,7 +649,7 @@
type="text"
bind:value={newGroup}
/>
<button class="btn btn-primary join-item" type="button" on:click={() => addGroup()}
<button class="btn btn-neutral join-item" type="button" on:click={() => addGroup()}
>{$LL.buttons.add()}</button
>
</div>
@@ -667,19 +660,17 @@
<button class="btn btn-error" type="button" on:click={() => deleteModal.showModal()}
><Fa icon={faTrash} />{$LL.buttons.delete()}</button
>
<dialog class="modal" bind:this={deleteModal}>
<form method="dialog" class="modal-box">
<h3 class="text-lg font-bold">{$LL.users.confirm_delete_title()}</h3>
<p class="py-4">{$LL.users.confirm_delete_desc({ username: device.name })}</p>
<div class="modal-action">
<button class="btn">{$LL.buttons.cancel()}</button>
<button class="btn btn-error" on:click={() => deleteDevice()}
>{$LL.buttons.delete()}</button
>
</div>
</form>
</dialog>
{/if}
<button class="btn btn-success" type="submit"><Fa icon={faSave} />{$LL.buttons.save()}</button>
</div>
</form>
<dialog class="modal" bind:this={deleteModal}>
<form method="dialog" class="modal-box">
<h3 class="text-lg font-bold">{$LL.users.confirm_delete_title()}</h3>
<p class="py-4">{$LL.users.confirm_delete_desc({ username: device.name })}</p>
<div class="modal-action">
<button class="btn">{$LL.buttons.cancel()}</button>
<button class="btn btn-error" on:click={() => deleteDevice()}>{$LL.buttons.delete()}</button>
</div>
</form>
</dialog>

View File

@@ -6,7 +6,6 @@
import type { RecordModel } from 'pocketbase';
import Fa from 'svelte-fa';
import toast from 'svelte-french-toast';
import { scale } from 'svelte/transition';
export let device: Device;
export let index: number;
@@ -28,57 +27,46 @@
</script>
{#if device.expand.ports[index]}
<div class="card bg-base-100 shadow-sm" transition:scale={{ delay: 0, duration: 200 }}>
<div class="card-body p-3">
<div class="flex flex-row gap-2">
<div>
<label class="label p-0" for="port-name-{index}">
<span class="label-text">{$LL.device.ports_name()}</span>
</label>
<input
id="port-name-{index}"
type="text"
placeholder="ssh"
class="input input-sm input-bordered w-full"
required
bind:value={device.expand.ports[index].name}
/>
</div>
<div>
<label class="label p-0" for="port-number-{index}">
<span class="label-text">{$LL.device.ports_number()}</span>
</label>
<input
id="port-number-{index}"
type="number"
placeholder="22"
min="1"
max="65535"
class="input input-sm input-bordered w-full"
required
bind:value={device.expand.ports[index].number}
/>
</div>
</div>
<fieldset class="fieldset bg-base-100 border-base-300 rounded-box w-xs border p-4">
<legend class="fieldset-legend"># {device.expand.ports.length}</legend>
<div class="flex flex-row gap-2">
<div>
<label class="label p-0" for="port-link-{index}">
<span class="label-text">{$LL.device.link()}</span>
</label>
<label class="fieldset-label" for="port-name-{index}">{$LL.device.ports_name()}</label>
<input
id="port-link-{index}"
type="url"
placeholder="https:// ..."
class="input input-sm input-bordered w-full"
bind:value={device.expand.ports[index].link}
id="port-name-{index}"
type="text"
class="input input-sm"
placeholder="ssh"
bind:value={device.expand.ports[index].name}
/>
</div>
<div class="card-actions justify-end">
<button
class="btn btn-outline btn-error btn-xs"
on:click={() => deletePort(device.expand.ports[index])}
type="button"><Fa icon={faTrash} />{$LL.buttons.delete()}</button
>
<div>
<label class="fieldset-label" for="port-number-{index}">{$LL.device.ports_number()}</label>
<input
type="number"
class="input input-sm"
min="1"
max="65535"
placeholder="22"
required
bind:value={device.expand.ports[index].number}
/>
</div>
</div>
</div>
<div>
<label class="fieldset-label" for="port-link-{index}">{$LL.device.link()}</label>
<input
id="port-link-{index}"
type="url"
class="input input-sm"
placeholder="https:// ..."
bind:value={device.expand.ports[index].link}
/>
</div>
<button
class="btn btn-error btn-xs btn-soft ms-auto"
on:click={() => deletePort(device.expand.ports[index])}
type="button"><Fa icon={faTrash} />{$LL.buttons.delete()}</button
>
</fieldset>
{/if}

View File

@@ -52,7 +52,9 @@
'dim',
'nord',
'sunset',
'colorful'
'caramellatte',
'abyss',
'silk'
];
let activeTheme: string | null = '';
$: avatar = $pocketbase.authStore.record?.avatar;
@@ -100,7 +102,7 @@
<ul
id="mobile-menu"
tabindex="-1"
class="menu dropdown-content z-[1] mt-3 w-max gap-1 rounded-box bg-base-300 p-2 shadow"
class="menu dropdown-content rounded-box bg-base-200 z-[1] mt-3 w-max gap-1 p-2 shadow-sm"
>
{#if $settingsPub?.website_title}
<div class="menu-title">
@@ -139,35 +141,24 @@
<ul>
<div class="h-fit max-h-72 overflow-scroll">
{#each availableThemes as theme}
<li>
<li class="w-full">
<button
class="overflow-hidden rounded-lg text-left outline-base-content"
class="gap-3 px-2"
data-set-theme={theme}
on:click={() => (activeTheme = theme)}
on:keydown={() => (activeTheme = theme)}
>
<div
data-theme={theme}
class="w-full cursor-pointer rounded bg-base-100 font-sans text-base-content"
class="bg-base-100 grid shrink-0 grid-cols-2 gap-0.5 rounded-md p-1 shadow-sm"
>
<div class="grid grid-cols-5 grid-rows-3">
<div
class="col-span-5 row-span-3 row-start-1 flex items-center gap-2 px-4 py-3"
>
<Fa
icon={faCheck}
class={activeTheme === theme ? 'visible' : 'invisible'}
/>
<div class="flex-grow text-sm">{theme}</div>
<div class="flex h-full flex-shrink-0 flex-wrap gap-1">
<div class="w-2 rounded bg-primary" />
<div class="w-2 rounded bg-secondary" />
<div class="w-2 rounded bg-accent" />
<div class="w-2 rounded bg-neutral" />
</div>
</div>
</div>
<div class="bg-base-content size-1 rounded-full"></div>
<div class="bg-primary size-1 rounded-full"></div>
<div class="bg-secondary size-1 rounded-full"></div>
<div class="bg-accent size-1 rounded-full"></div>
</div>
<div class="truncate">{theme}</div>
<Fa icon={faCheck} class={activeTheme === theme ? 'visible' : 'invisible'} />
</button>
</li>
{/each}
@@ -177,7 +168,7 @@
</li>
</ul>
</div>
<a class="btn btn-ghost px-2 text-xl normal-case" href="/">
<a class="btn btn-ghost h-full border-0 px-2 text-xl" href="/">
<img
src={$settingsPub?.id && $settingsPub?.favicon
? `${backendUrl}api/files/settings_public/${$settingsPub?.id}/${$settingsPub?.favicon}`
@@ -192,64 +183,61 @@
{#if $settingsPub?.website_title}
<span class="px-2">{$settingsPub?.website_title}</span>
{/if}
<ul class="menu menu-horizontal gap-1 px-1">
<li>
<a href="/" class="p-2" class:active={$page.url.pathname === '/'}
<ul class="menu menu-horizontal h-full gap-1 px-1">
<li class="h-full">
<a href="/" class="p-2" class:menu-active={$page.url.pathname === '/'}
><Fa icon={faHome} />{$LL.home.page_title()}</a
>
</li>
{#if $pocketbase.authStore.isSuperuser}
<li>
<a href="/users" class="p-2" class:active={$page.url.pathname.startsWith('/users')}
<li class="h-full">
<a href="/users" class="p-2" class:menu-active={$page.url.pathname.startsWith('/users')}
><Fa icon={faUsersGear} />{$LL.users.page_title()}</a
>
</li>
<li>
<a href="/settings/" class="p-2" class:active={$page.url.pathname.startsWith('/settings')}
<li class="h-full">
<a
href="/settings/"
class="p-2"
class:menu-active={$page.url.pathname.startsWith('/settings')}
><Fa icon={faCog} />{$LL.settings.page_title()}</a
>
</li>
{/if}
</ul>
<div class="dropdown dropdown-end">
<div tabindex="-1" class="btn btn-ghost normal-case">
<Fa icon={faSwatchbook} />
<span class="font-normal">{$LL.navbar.theme()}</span>
<Fa icon={faChevronDown} />
</div>
<div
class="dropdown-content z-[1] mt-3 h-fit max-h-96 w-56 overflow-y-auto rounded-box bg-base-200 text-base-content shadow"
>
<div class="grid grid-cols-1 gap-3 p-3" tabindex="-1">
{#each availableThemes as theme}
<button
class="overflow-hidden rounded-lg text-left outline-base-content"
data-set-theme={theme}
on:click={() => (activeTheme = theme)}
on:keydown={() => (activeTheme = theme)}
>
<div
data-theme={theme}
class="w-full cursor-pointer bg-base-100 font-sans text-base-content"
>
<div class="grid grid-cols-5 grid-rows-3">
<div class="col-span-5 row-span-3 row-start-1 flex items-center gap-2 px-4 py-3">
<Fa icon={faCheck} class={activeTheme === theme ? 'visible' : 'invisible'} />
<div class="flex-grow text-sm">{theme}</div>
<div class="flex h-full flex-shrink-0 flex-wrap gap-1">
<div class="w-2 rounded bg-primary" />
<div class="w-2 rounded bg-secondary" />
<div class="w-2 rounded bg-accent" />
<div class="w-2 rounded bg-neutral" />
</div>
<div class="dropdown dropdown-end">
<button class="btn btn-ghost hover:bg-base-content/10 h-full border-0 p-2">
<Fa icon={faSwatchbook} />
<span class="font-normal">{$LL.navbar.theme()}</span>
<Fa icon={faChevronDown} />
</button>
<div class="dropdown-content bg-base-200 rounded-box z-1 mt-3 w-52 shadow-sm">
<ul class="menu menu-horizontal h-fit max-h-96 overflow-y-auto">
{#each availableThemes as theme}
<li class="w-full">
<button
class="gap-3 px-2"
data-set-theme={theme}
on:click={() => (activeTheme = theme)}
on:keydown={() => (activeTheme = theme)}
>
<div
data-theme={theme}
class="bg-base-100 grid shrink-0 grid-cols-2 gap-0.5 rounded-md p-1 shadow-sm"
>
<div class="bg-base-content size-1 rounded-full"></div>
<div class="bg-primary size-1 rounded-full"></div>
<div class="bg-secondary size-1 rounded-full"></div>
<div class="bg-accent size-1 rounded-full"></div>
</div>
</div>
</div>
</button>
{/each}
<div class="truncate">{theme}</div>
<Fa icon={faCheck} class={activeTheme === theme ? 'visible' : 'invisible'} />
</button>
</li>
{/each}
</ul>
</div>
</div>
</div>
</ul>
</div>
<div class="ms-auto justify-end">
{#if $pocketbase.authStore?.record !== null}
@@ -267,7 +255,7 @@
</label>
<ul
tabindex="-1"
class="menu dropdown-content z-[1] mt-3 rounded-box bg-base-300 p-2 shadow"
class="menu dropdown-content rounded-box bg-base-200 z-[1] mt-3 w-52 p-2 shadow"
>
<li class="menu-title">
{$pocketbase.authStore.isSuperuser

View File

@@ -117,7 +117,7 @@
</script>
{#if $settingsPriv}
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.device.tabs[1]()}</h2>
<p class="my-2">
@@ -136,7 +136,7 @@
placeholder="192.168.1.0/24"
bind:value={scanRange}
/>
<button class="btn btn-primary join-item" type="submit">{$LL.buttons.save()}</button>
<button class="btn btn-neutral join-item" type="submit">{$LL.buttons.save()}</button>
</div>
</form>
<div>
@@ -153,7 +153,7 @@
</button>
{:else if scanRunning}
<button class="btn no-animation">
<span class="loading loading-spinner" />
<span class="loading loading-spinner"></span>
{$LL.device.network_scan_running()}
</button>
{:else}
@@ -167,7 +167,7 @@
</div>
{#if scanResponse.devices?.length > 0}
{#each scanResponse.devices.sort( (a, b) => a.ip.localeCompare( b.ip, undefined, { numeric: true } ) ) as device, index}
<div class="collapse collapse-arrow bg-base-200">
<div class="collapse-arrow bg-base-100 collapse">
<input type="radio" name="scanned-devices" checked={index === 0} />
<div class="collapse-title font-bold">
{device.name} <span class="badge">{device.ip}</span>
@@ -208,7 +208,9 @@
<div class="form-control max-w-fit">
<label class="label cursor-pointer">
<input type="checkbox" class="checkbox" bind:checked={replaceNetmaskCheckbox} />
<span class="label-text ms-2">{$LL.device.network_scan_replace_netmask()}</span>
<span class="label-text ms-2 text-wrap break-words"
>{$LL.device.network_scan_replace_netmask()}</span
>
</label>
</div>
{#if replaceNetmaskCheckbox}
@@ -229,7 +231,9 @@
<div class="form-control max-w-fit">
<label class="label cursor-pointer">
<input type="checkbox" class="checkbox" bind:checked={addAllCheckbox} />
<span class="label-text ms-2">{$LL.device.network_scan_include_unknown()}</span>
<span class="label-text ms-2 text-wrap break-words"
>{$LL.device.network_scan_include_unknown()}</span
>
</label>
{#if addAllCheckbox}
<button

View File

@@ -1,3 +1,3 @@
<div class="container mx-auto max-w-lg text-center">
<span class="loading loading-dots loading-lg" />
<span class="loading loading-dots loading-lg"></span>
</div>

View File

@@ -1,4 +1,5 @@
// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
import { initI18nSvelte } from 'typesafe-i18n/svelte';
import type { Formatters, Locales, TranslationFunctions, Translations } from './i18n-types';

View File

@@ -1,4 +1,5 @@
// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
import { initFormatters } from './formatters';
import type { Locales, Translations } from './i18n-types';

View File

@@ -1,4 +1,5 @@
// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
import { initFormatters } from './formatters';
import type { Locales, Translations } from './i18n-types';

View File

@@ -1,4 +1,5 @@
// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
import {
i18n as initI18n,

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { page } from '$app/state';
import Navbar from '$lib/components/Navbar.svelte';
import Transition from '$lib/components/Transition.svelte';
import LL, { setLocale } from '$lib/i18n/i18n-svelte';
@@ -19,6 +19,8 @@
duration: 5000
};
let authIsValid = false;
onMount(async () => {
// load locale
const detectedLocale = detectLocale(
@@ -31,6 +33,8 @@
setLocale(detectedLocale);
$pocketbase.authStore.onChange(() => {
authIsValid = $pocketbase.authStore.isValid;
// load user permissions
if ($pocketbase.authStore.record?.collectionName === 'users') {
$pocketbase
@@ -57,7 +61,7 @@
}
// redirect to welcome page if setup is not completed
if ($settingsPub.setup_completed === false && $page.url.pathname !== '/welcome') {
if ($settingsPub.setup_completed === false && page.url.pathname !== '/welcome') {
$pocketbase.authStore.clear();
goto('/welcome');
return;
@@ -98,13 +102,13 @@
{/if}
</svelte:head>
{#if $pocketbase.authStore.isValid}
{#if authIsValid}
<Navbar />
{/if}
<Toaster position="bottom-center" {toastOptions} />
<Transition url={$page.url}>
<Transition url={page.url}>
<div class="container mx-auto mb-4 p-2">
<slot />
</div>

View File

@@ -109,11 +109,7 @@
type="button"
on:click={() => (orderByGroups = !orderByGroups)}
>{$LL.home.order_groups()}
<input
type="checkbox"
class="checkbox checked:checkbox-primary"
checked={orderByGroups}
/>
<input type="checkbox" class="checkbox" checked={orderByGroups} />
</button>
</div>
</div>
@@ -134,8 +130,11 @@
value="ip"
/>
{/if}
<div class="tooltip" data-tip={$LL.home.order_tooltip()}>
<button class="btn join-item" on:click={() => (orderExpanded = !orderExpanded)}>
<div class="tooltip join-item" data-tip={$LL.home.order_tooltip()}>
<button
class="btn {orderExpanded ? '' : 'rounded-field'}"
on:click={() => (orderExpanded = !orderExpanded)}
>
{#if orderExpanded}
<Fa icon={faChevronCircleRight} size="lg" />
{:else}
@@ -175,18 +174,34 @@
</div>
{/if}
{:else}
<div class="container text-center">
<p>{$LL.home.no_devices()}</p>
{#if $pocketbase.authStore.isSuperuser || $permission.create}
<p>
<a href="/device/new" class="btn btn-ghost"
><Fa icon={faPlus} class="ms-2" />{$LL.home.add_first_device()}
</a>
</p>
{:else}
<p>
{$LL.home.grant_permissions()}
</p>
{/if}
<div class="flex justify-center">
<div role="alert" class="alert alert-horizontal w-fit">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 shrink-0 stroke-current"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
{#if $pocketbase.authStore.isSuperuser || $permission.create}
<div>
<h3 class="font-bold">{$LL.home.no_devices()}</h3>
<div class="text-xs">{$LL.home.add_first_device()}</div>
</div>
<a href="/device/new" class="btn btn-sm"
><Fa icon={faPlus} />{$LL.home.add_first_device()}</a
>
{:else}
<span>
Please ask the admin to grant you permissions to existing devices or to create new ones.
</span>
{/if}
</div>
</div>
{/if}

View File

@@ -139,7 +139,7 @@
</script>
<h1 class="mb-8 text-3xl font-bold">{$LL.account.page_title()}</h1>
<div class="card w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 w-full shadow-sm">
<div class="card-body">
<div class="flex flex-row items-center gap-4">
<div class="avatar">
@@ -166,16 +166,16 @@
</div>
</div>
<form on:submit|preventDefault={saveUser}>
<h2 class="card-title mb-2 mt-4">{$LL.account.avatar_title()}</h2>
<h2 class="card-title mt-4 mb-2">{$LL.account.avatar_title()}</h2>
<div class="flex flex-row flex-wrap gap-4">
{#each [...Array(10).keys()] as i}
<div class="avatar btn btn-circle btn-ghost">
<div class="avatar">
<div
class="w-11 rounded-full"
class:ring={newAvatar !== undefined
class="hover:ring-neutral w-11 rounded-full hover:ring-2 hover:ring-offset-2"
class:ring-2={newAvatar !== undefined
? i === newAvatar
: i === $pocketbase.authStore.record?.avatar}
class:ring-primary={newAvatar !== undefined
class:ring-success={newAvatar !== undefined
? i === newAvatar
: i === $pocketbase.authStore.record?.avatar}
class:ring-offset-base-100={newAvatar !== undefined
@@ -194,7 +194,7 @@
</div>
{/each}
</div>
<h2 class="card-title mb-2 mt-4">{$LL.account.language_title()}</h2>
<h2 class="card-title mt-4 mb-2">{$LL.account.language_title()}</h2>
<select class="select select-bordered w-full max-w-xs" bind:value={selectedLanguage}>
<option value="auto" selected={localStorageLang === null}
>🌐 {$LL.account.language_option_auto()}</option
@@ -214,7 +214,7 @@
</form>
</div>
</div>
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.account.change_password_title()}</h2>
<p>{$LL.account.change_password_body()}</p>

View File

@@ -45,7 +45,7 @@
<h1 class="mb-8 text-3xl font-bold">{$LL.device.page_title()}</h1>
<div class="mb-6 flex justify-center">
<ul class="menu menu-horizontal gap-1 rounded-box bg-base-300">
<ul class="menu menu-horizontal rounded-box bg-base-200">
{#each tabs as tab}
{#if tab.show}
<li>

View File

@@ -57,7 +57,7 @@
</script>
<div class="mt-10 flex items-center justify-center">
<div class="card w-full max-w-lg bg-base-300 shadow-xl">
<div class="card bg-base-200 w-full max-w-lg shadow-sm">
<div class="card-body">
{#if $settingsPub?.website_title}
<h1 class="text-center text-xl font-bold">{$settingsPub?.website_title}</h1>
@@ -89,7 +89,7 @@
</label>
<label class="relative block">
<div
class="absolute right-4 top-1/2 -translate-y-1/2 cursor-pointer"
class="absolute top-1/2 right-4 -translate-y-1/2 cursor-pointer"
role="none"
on:click={() => toggleVisibility(inputPassword)}
on:keydown={() => toggleVisibility(inputPassword)}
@@ -108,13 +108,11 @@
</label>
<div class="card-actions mt-4">
<div class="dropdown">
<label tabindex="-1" class="btn btn-neutral" for="other-providers"
>{$LL.login.btn_more()}</label
>
<label tabindex="-1" class="btn" for="other-providers">{$LL.login.btn_more()}</label>
<ul
id="other-providers"
tabindex="-1"
class="menu dropdown-content z-[1] w-56 rounded-box bg-base-200 p-2 shadow"
class="menu dropdown-content rounded-box bg-base-200 z-1 w-56 p-2 shadow-sm"
>
<li class="menu-title flex flex-row items-center gap-2">
{$LL.login.menu_title_auth_providers()}
@@ -135,7 +133,7 @@
{/await}
</ul>
</div>
<button class="btn btn-primary ms-auto" type="submit"
<button class="btn btn-success ms-auto" type="submit"
>{$LL.login.btn_login()} <Fa icon={faLockOpen} /></button
>
</div>

View File

@@ -92,7 +92,7 @@
{:else}
<h1 class="mb-8 text-3xl font-bold">{$LL.settings.page_title()}</h1>
<form on:submit|preventDefault={save}>
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.settings.ping_interval_title()}</h2>
<p class="mt-2">
@@ -120,17 +120,13 @@
</p>
<div class="form-control w-fit">
<label class="label cursor-pointer gap-2">
<input
type="checkbox"
class="checkbox-primary checkbox"
bind:checked={settingsPrivClone.lazy_ping}
/>
<input type="checkbox" class="checkbox" bind:checked={settingsPrivClone.lazy_ping} />
<span class="label-text">{$LL.settings.lazy_ping_enable()}</span>
</label>
</div>
</div>
</div>
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.settings.website_title_title()}</h2>
<p class="my-2">{$LL.settings.website_title_desc()}</p>
@@ -144,7 +140,7 @@
</div>
</div>
</div>
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.settings.icon_title()}</h2>
<p class="my-2">
@@ -171,7 +167,7 @@
bind:this={faviconInputElement}
/>
<button
class="btn btn-outline btn-error"
class="btn btn-error"
on:click={() => resetFavicon()}
on:keydown={() => resetFavicon()}>{$LL.buttons.reset()}</button
>

View File

@@ -184,7 +184,7 @@
<h1 class="mb-8 text-3xl font-bold">{$LL.users.page_title()}</h1>
{#each users as user, index}
<form on:submit|preventDefault={() => save(user)}>
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body gap-4">
<h2 class="card-title">
<label tabindex="-1" class="avatar" for="avatar{index}">
@@ -196,17 +196,13 @@
</h2>
{#each permissions.filter((perm) => perm.user === user.id) as permission}
<div class="form-control w-fit">
<label class="label cursor-pointer gap-2">
<input
type="checkbox"
bind:checked={permission.create}
class="checkbox checked:checkbox-primary"
/>
<label class="label cursor-pointer gap-2 text-wrap">
<input type="checkbox" bind:checked={permission.create} class="checkbox" />
{$LL.users.allow_create_devices({ username: user.username })}
</label>
</div>
{/each}
<div class="collapse collapse-arrow bg-base-200">
<div class="collapse-arrow bg-base-200 collapse">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">{$LL.users.device_permissions()}</div>
<div class="collapse-content">
@@ -222,7 +218,7 @@
<div class="font-bold">{$LL.users.delete()}</div>
<div class="font-bold">{$LL.users.power()}</div>
{#each devices as device}
<hr class="border-b-1 col-span-full w-full border-neutral opacity-30" />
<hr class="border-neutral col-span-full w-full border-b-1 opacity-30" />
{#each permissions.filter((perm) => perm.user === user.id) as permission}
<div
class="col-span-full flex flex-row flex-wrap gap-2 place-self-start break-all md:col-span-1"
@@ -232,32 +228,32 @@
</div>
<input
type="checkbox"
class="checkbox checked:checkbox-primary md:col-start-2"
class="checkbox md:col-start-2"
bind:group={permission.read}
value={device.id}
/>
<input
type="checkbox"
class="checkbox checked:checkbox-primary"
class="checkbox"
bind:group={permission.update}
value={device.id}
/>
<input
type="checkbox"
class="checkbox checked:checkbox-primary"
class="checkbox"
bind:group={permission.delete}
value={device.id}
/>
<input
type="checkbox"
class="checkbox checked:checkbox-primary"
class="checkbox"
bind:group={permission.power}
value={device.id}
/>
{/each}
{/each}
<button
class="btn btn-neutral btn-sm md:col-start-2"
class="btn btn-sm md:col-start-2"
type="button"
on:click={() => {
toggleAllPermissions(user, 'read');
@@ -268,7 +264,7 @@
/></button
>
<button
class=" btn btn-neutral btn-sm"
class=" btn btn-sm"
type="button"
on:click={() => {
toggleAllPermissions(user, 'update');
@@ -279,7 +275,7 @@
/></button
>
<button
class=" btn btn-neutral btn-sm"
class=" btn btn-sm"
type="button"
on:click={() => {
toggleAllPermissions(user, 'delete');
@@ -290,7 +286,7 @@
/></button
>
<button
class=" btn btn-neutral btn-sm"
class=" btn btn-sm"
type="button"
on:click={() => {
toggleAllPermissions(user, 'power');
@@ -332,7 +328,7 @@
</form>
</dialog>
{/each}
<div class="card mt-6 w-full bg-base-300 shadow-xl">
<div class="card bg-base-200 mt-6 w-full shadow-sm">
<div class="card-body">
<h2 class="card-title">{$LL.users.create_new_user()}</h2>
<form on:submit|preventDefault={createUser}>
@@ -390,7 +386,7 @@
/>
</div>
</div>
<span class="badge mt-4 self-center text-error">* {$LL.users.required_field()}</span>
<span class="badge text-error mt-4 self-center">* {$LL.users.required_field()}</span>
<div class="card-actions justify-end">
<button type="submit" class="btn btn-success mt-2"
><Fa icon={faPlus} />{$LL.buttons.add()}</button

View File

@@ -69,14 +69,14 @@
<div class="mt-10 flex items-center justify-center">
<div class="my-4 flex w-screen max-w-lg flex-col gap-16">
<div class="card bg-base-300 shadow-xl">
<div class="card bg-base-200 shadow-sm">
{#if $settingsPub?.setup_completed}
<figure class="mx-auto w-72 pt-6"><img src="/gopher.svg" alt="Gopher" /></figure>
<div class="card-body">
<h2 class="card-title">{$LL.welcome.not_expected_title()}</h2>
<p>{$LL.welcome.not_expected_desc()}</p>
<div class="card-actions justify-end">
<button class="btn btn-primary" on:click={() => goto('/')}
<button class="btn btn-success" on:click={() => goto('/')}
>{$LL.welcome.not_expected_back()}</button
>
</div>
@@ -87,7 +87,7 @@
<h2 class="card-title">{$LL.welcome.step1_page_title()}</h2>
<p>{$LL.welcome.step1_setup_desc()}</p>
<div class="card-actions justify-end">
<button class="btn btn-primary" on:click={() => (stepsCompleted = 1)}
<button class="btn btn-success" on:click={() => (stepsCompleted = 1)}
>{$LL.welcome.step1_setup_btn_next()} <Fa icon={faArrowRight} /></button
>
</div>
@@ -115,7 +115,7 @@
</label>
<label class="relative block">
<div
class="absolute right-4 top-1/2 -translate-y-1/2 cursor-pointer"
class="absolute top-1/2 right-4 -translate-y-1/2 cursor-pointer"
role="none"
on:click={() => toggleVisibility(inputPassword)}
on:keydown={() => toggleVisibility(inputPassword)}
@@ -138,7 +138,7 @@
</label>
<label class="relative block">
<div
class="absolute right-4 top-1/2 -translate-y-1/2 cursor-pointer"
class="absolute top-1/2 right-4 -translate-y-1/2 cursor-pointer"
role="none"
on:click={() => toggleVisibility(inputConfirm)}
on:keydown={() => toggleVisibility(inputConfirm)}
@@ -157,7 +157,7 @@
/>
</label>
<div class="card-actions mt-4 justify-end">
<button class="btn btn-primary" type="submit"
<button class="btn btn-success" type="submit"
>{$LL.welcome.step2_btn_create()} <Fa icon={faArrowRight} /></button
>
</div>
@@ -178,7 +178,7 @@
</div>
{#if !$settingsPub?.setup_completed}
<ul class="steps steps-horizontal">
<li class="step step-primary">{$LL.welcome.progress_step1()}</li>
<li class="step step-success">{$LL.welcome.progress_step1()}</li>
<li class="step" class:step-primary={stepsCompleted > 0}>{$LL.welcome.progress_step2()}</li>
<li class="step" class:step-primary={stepsCompleted > 1}>{$LL.welcome.progress_step3()}</li>
</ul>

View File

@@ -1,79 +0,0 @@
import themes from 'daisyui/src/theming/themes';
import daisyui from 'daisyui';
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {}
},
plugins: [daisyui],
daisyui: {
themes: [
{
light: {
...themes['light'],
primary: '#55BCD9',
secondary: '#D4A35C',
'primary-content': '#ffffff'
},
dark: {
...themes['dark'],
primary: '#55BCD9',
secondary: '#D4A35C',
'base-300': '#12161b',
'--rounded-btn': '1.9rem',
'--tab-border': '2px',
'--tab-radius': '.5rem'
},
cupcake: {
...themes['cupcake'],
primary: '#55BCD9',
secondary: '#D4A35C',
'primary-content': '#ffffff'
},
colorful: {
...themes['light'],
primary: '#00bdf8',
secondary: '#009aff',
accent: '#00b3a2',
neutral: '#161200',
'base-100': '#ffffff',
info: '#00c5dc',
success: '#00d892',
warning: '#f77800',
error: '#f36165'
}
},
'bumblebee',
'emerald',
'corporate',
'synthwave',
'retro',
'cyberpunk',
'valentine',
'halloween',
'garden',
'forest',
'aqua',
'lofi',
'pastel',
'fantasy',
'wireframe',
'black',
'luxury',
'dracula',
'cmyk',
'autumn',
'business',
'acid',
'lemonade',
'night',
'coffee',
'winter',
'dim',
'nord',
'sunset'
]
}
};