Add svelte-heros icon library
- Install svelte-heros for consistent Heroicons usage - Replace inline SVGs for password toggle with Eye/EyeOff icons - Fix USE_EMBEDDED mode to serve frontend from filesystem
This commit is contained in:
parent
8b2d2649ac
commit
46d9ff26dd
5 changed files with 49 additions and 45 deletions
|
|
@ -14,6 +14,7 @@
|
|||
"daisyui": "^5.5.19",
|
||||
"postcss": "^8.5.8",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-heros": "^8.0.1",
|
||||
"tailwindcss": "^4.2.2",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.0.0",
|
||||
|
|
@ -289,6 +290,8 @@
|
|||
|
||||
"svelte": ["svelte@5.55.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.4", "esm-env": "^1.2.1", "esrap": "^2.2.2", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-SThllKq6TRMBwPtat7ASnm/9CDXnIhBR0NPGw0ujn2DVYx9rVwsPZxDaDQcYGdUz/3BYVsCzdq7pZarRQoGvtw=="],
|
||||
|
||||
"svelte-heros": ["svelte-heros@8.0.1", "", { "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-67rDThxmdbjvJxKDqDUnkTsmyI5JtXcS0S0Ryg/EmTlHscFyJNgJyEARX5/xt3ONsZA0i6+dWnq+EX5jH9Wq1Q=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="],
|
||||
|
||||
"tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="],
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
"daisyui": "^5.5.19",
|
||||
"postcss": "^8.5.8",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-heros": "^8.0.1",
|
||||
"tailwindcss": "^4.2.2",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.0.0"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Eye, EyeOff } from 'svelte-heros';
|
||||
|
||||
interface User {
|
||||
id: number;
|
||||
|
|
@ -14,9 +15,8 @@
|
|||
let localPart = $state('');
|
||||
let newUser = $state({ password: '', quota: 0 });
|
||||
let createError = $state('');
|
||||
let createdPassword = $state('');
|
||||
let showCreatedPassword = $state(false);
|
||||
let domainName = $state('');
|
||||
let showPassword = $state(false);
|
||||
|
||||
async function loadUsers(name: string) {
|
||||
try {
|
||||
|
|
@ -37,10 +37,9 @@
|
|||
|
||||
function openModal() {
|
||||
createError = '';
|
||||
createdPassword = '';
|
||||
showCreatedPassword = false;
|
||||
localPart = '';
|
||||
newUser = { password: '', quota: 0 };
|
||||
showPassword = false;
|
||||
newUser = { password: generatePasswordStr(), quota: 0 };
|
||||
dialogEl?.showModal();
|
||||
}
|
||||
|
||||
|
|
@ -48,7 +47,7 @@
|
|||
dialogEl?.close();
|
||||
}
|
||||
|
||||
function generatePassword() {
|
||||
function generatePasswordStr(): string {
|
||||
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*';
|
||||
let password = '';
|
||||
const array = new Uint8Array(16);
|
||||
|
|
@ -56,7 +55,7 @@
|
|||
for (let i = 0; i < 16; i++) {
|
||||
password += chars[array[i] % chars.length];
|
||||
}
|
||||
newUser.password = password;
|
||||
return password;
|
||||
}
|
||||
|
||||
async function createUser() {
|
||||
|
|
@ -70,14 +69,13 @@
|
|||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({ email, password: newUser.password || undefined, quota: newUser.quota })
|
||||
body: JSON.stringify({ email, password: newUser.password, quota: newUser.quota })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
createdPassword = data.password || '';
|
||||
showCreatedPassword = true;
|
||||
await loadUsers(domainName);
|
||||
closeModal();
|
||||
} else {
|
||||
const data = await res.json();
|
||||
createError = data.error || 'Failed to create user';
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
@ -87,7 +85,7 @@
|
|||
}
|
||||
|
||||
async function copyPassword() {
|
||||
await navigator.clipboard.writeText(createdPassword);
|
||||
await navigator.clipboard.writeText(newUser.password);
|
||||
}
|
||||
|
||||
async function deleteUser(id: number) {
|
||||
|
|
@ -211,18 +209,26 @@
|
|||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="fieldset mt-4">
|
||||
<legend class="fieldset-legend">Password (leave empty to generate)</legend>
|
||||
<legend class="fieldset-legend">Password</legend>
|
||||
<div class="join w-full">
|
||||
<input
|
||||
type={showCreatedPassword ? 'text' : 'password'}
|
||||
class="input input-bordered join-item flex-1"
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
class="input input-bordered join-item flex-1 font-mono"
|
||||
bind:value={newUser.password}
|
||||
required
|
||||
/>
|
||||
<button type="button" class="btn join-item" onclick={() => showCreatedPassword = !showCreatedPassword}>
|
||||
{showCreatedPassword ? '🙈' : '👁️'}
|
||||
<button type="button" class="btn join-item" onclick={() => showPassword = !showPassword}>
|
||||
{#if showPassword}
|
||||
<EyeOff class="h-5 w-5" />
|
||||
{:else}
|
||||
<Eye class="h-5 w-5" />
|
||||
{/if}
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary join-item" onclick={generatePassword}>
|
||||
Generate
|
||||
<button type="button" class="btn join-item" onclick={() => navigator.clipboard.writeText(newUser.password)}>
|
||||
Copy
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary join-item" onclick={() => newUser.password = generatePasswordStr()}>
|
||||
Regenerate
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
@ -235,27 +241,14 @@
|
|||
min="0"
|
||||
/>
|
||||
</fieldset>
|
||||
{#if showCreatedPassword && createdPassword}
|
||||
<div class="alert alert-success mt-4">
|
||||
<div class="flex-1">
|
||||
<label class="text-sm font-medium">User created! Copy password:</label>
|
||||
<div class="flex items-center gap-2 mt-1">
|
||||
<code class="flex-1 bg-base-200 p-2 rounded text-sm break-all">{createdPassword}</code>
|
||||
<button type="button" class="btn btn-sm" onclick={copyPassword}>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if createError}
|
||||
<div class="alert alert-error mt-4">
|
||||
<span>{createError}</span>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="modal-action">
|
||||
<button type="button" class="btn" onclick={closeModal}>Close</button>
|
||||
<button type="submit" class="btn btn-primary" disabled={showCreatedPassword}>Create</button>
|
||||
<button type="button" class="btn" onclick={closeModal}>Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Create</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue