diff --git a/messages/en.json b/messages/en.json index a5208ac..e7eded4 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1,19 +1,32 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", "_delete": "Delete", + "_save": "Save", "adminActions": "Actions", - "adminDescription": "All users here have equal privilege level. Use caution.", + "adminConfirmDelete": "I am ABSOLUTELY sure", + "adminDescription": "All users here have equal privilege level. Use caution", + "adminEmailBody": "Hello, please use this link to create a password for wireguard-web", + "adminEmailSubject": "WireGuard-Web Registration", + "adminNewUser": "New User", + "adminNewUserDescription": "Send the user this registration link, so they can create a password.", + "adminNotShownAgain": "For security, this will not be shown again.", "adminTitle": "User Management", "adminUserCreate": "Create User", - "adminUserCreated": "User {email} has been created.", + "adminUserCreated": "User {email} has been created", "adminUserDelete": "Delete User", "adminUserDeleteConfirm": "Are you sure you want to delete this user?", - "adminUserDeleted": "User {email} has been deleted.", + "adminUserDeleted": "User {email} has been deleted", + "adminUserDeleteDescription": "User {email} will be permanently deleted", + "adminUserDeleteWarning": "Are you ABSOLUTELY sure? This action cannot be undone.", "adminUserDisabled": "User is disabled", "adminUserEdit": "Edit User", "adminUserEnabled": "User is enabled", "adminUserToggle": "Enable / Disable User", + "adminUserUpdated": "User updated", "cancel": "Cancel", + "close": "Close", + "copiedToClipboard": "Copied to clipboard", + "copyLink": "Copy Link", "deploy": "Deploy", "email": "Email", "emailAddress": "Email Address", @@ -34,24 +47,25 @@ "home": "Home", "lastLogin": "Last Login", "lastName": "Last Name", + "loggingMultipleUsersCreated": "Multiple users created", + "loggingNoUserCreated": "No user created", + "loggingUnexpectedError": "An unexpected error occurred: {error}", "login": "Login", "loginDescription": "Login to your account", "logout": "Logout", - "loggingUnexpectedError": "An unexpected error occurred: {error}", - "loggingNoUserCreated": "No user created", - "loggingMultipleUsersCreated": "Multiple users created", "myAccount": "My Account", "name": "Name", "password": "Password", - "passwordChangeDescription": "Change your password here.", + "passwordChangeDescription": "Change your password here", "passwordConfirm": "Confirm Password", "passwordDescription": "Enter your password", "passwordNew": "New Password", - "passwordUpdated": "Your password has been updated.", + "passwordUpdated": "Your password has been updated", "profile": "Profile", - "profileDescription": "Edit your personal details here.", - "profileUpdated": "Your profile has been updated.", + "profileDescription": "Edit your personal details here", + "profileUpdated": "Your profile has been updated", "save": "Save", + "sendEmail": "Send Email", "settings": "Settings", "setupDescription": "Lets get you started...", "setupEmailDescription": "This will be your username", @@ -64,5 +78,6 @@ "status": "Status", "title": "WireGuard Web", "toggleTheme": "Toggle Theme", + "user": "User", "users": "Users" } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7ca0976..31030d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "@types/better-sqlite3": "^7.6.11", "@types/eslint": "^9.6.0", "autoprefixer": "^10.4.20", - "bits-ui": "^1.0.0-next.45", + "bits-ui": "^1.0.0-next.46", "clsx": "^2.1.1", "drizzle-kit": "^0.22.8", "eslint": "^9.7.0", @@ -3756,9 +3756,9 @@ } }, "node_modules/bits-ui": { - "version": "1.0.0-next.45", - "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-1.0.0-next.45.tgz", - "integrity": "sha512-kt7gYIirEo2Rg1hMudcGEzSHogQTA22d/j1x8v+wIshsIqqcCN6DXJZpTojSCQWxny8IEa9CRnLwAzY4B2qf1Q==", + "version": "1.0.0-next.46", + "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-1.0.0-next.46.tgz", + "integrity": "sha512-ATEyY4z3amOA5C3E7GsiV5H5kPpaNldrclDfDI0iUY6NfNJwNEv44crYlLNEyU8rBkSOGpQfonpwMp/mhtYoDQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 9784cef..f9ff7ec 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@types/better-sqlite3": "^7.6.11", "@types/eslint": "^9.6.0", "autoprefixer": "^10.4.20", - "bits-ui": "^1.0.0-next.45", + "bits-ui": "^1.0.0-next.46", "clsx": "^2.1.1", "drizzle-kit": "^0.22.8", "eslint": "^9.7.0", diff --git a/src/lib/components/ui/checkbox/checkbox.svelte b/src/lib/components/ui/checkbox/checkbox.svelte new file mode 100644 index 0000000..58f834b --- /dev/null +++ b/src/lib/components/ui/checkbox/checkbox.svelte @@ -0,0 +1,33 @@ + + + + {#snippet children({ checked })} + + {#if checked === "indeterminate"} + + {:else} + + {/if} + + {/snippet} + diff --git a/src/lib/components/ui/checkbox/index.ts b/src/lib/components/ui/checkbox/index.ts new file mode 100644 index 0000000..6d92d94 --- /dev/null +++ b/src/lib/components/ui/checkbox/index.ts @@ -0,0 +1,6 @@ +import Root from "./checkbox.svelte"; +export { + Root, + // + Root as Checkbox, +}; diff --git a/src/lib/components/ui/label/label.svelte b/src/lib/components/ui/label/label.svelte index 2a7d479..247d23c 100644 --- a/src/lib/components/ui/label/label.svelte +++ b/src/lib/components/ui/label/label.svelte @@ -2,20 +2,18 @@ import { Label as LabelPrimitive } from "bits-ui"; import { cn } from "$lib/utils.js"; - type $$Props = LabelPrimitive.Props; - type $$Events = LabelPrimitive.Events; - - let className: $$Props["class"] = undefined; - export { className as class }; + let { + ref = $bindable(null), + class: className, + ...restProps + }: LabelPrimitive.RootProps = $props(); - - + {...restProps} +/> diff --git a/src/routes/admin/+page.server.ts b/src/routes/admin/+page.server.ts index 23795a2..90d7678 100644 --- a/src/routes/admin/+page.server.ts +++ b/src/routes/admin/+page.server.ts @@ -7,11 +7,7 @@ import { v4 as uuid4 } from "uuid"; import { zod } from "sveltekit-superforms/adapters"; import * as m from '$lib/paraglide/messages'; import type { Actions, PageServerLoad } from "./$types"; - -type Message = { - text: string | undefined; - token: string | undefined; -} +import type { Message } from "./message"; export const load: PageServerLoad = async (event) => { // set up the form @@ -122,7 +118,12 @@ export const actions: Actions = { // return the created user and the registration token return message(form, { text: m.adminUserCreated({email: createdUser.email}), - token: regToken, + token: { + value: regToken, + email: createdUser.email, + firstname: createdUser.firstname, + lastname: createdUser.lastname, + } }); }, update: async (event) => { @@ -151,7 +152,7 @@ export const actions: Actions = { } // return the updated user return message(form, { - text: 'User updated', + text: m.adminUserUpdated(), token: undefined, }); } diff --git a/src/routes/admin/+page.svelte b/src/routes/admin/+page.svelte index d31c787..2f321c9 100644 --- a/src/routes/admin/+page.svelte +++ b/src/routes/admin/+page.svelte @@ -5,9 +5,10 @@ import type { PageData } from "./$types"; import type { User } from "$lib/server/db"; import { Button } from "$lib/components/ui/button"; + import { Checkbox } from "$lib/components/ui/checkbox/index.js"; import { CircleCheckBig, CircleX, UserPlus, Ellipsis, Pencil, Trash } from "lucide-svelte"; import { Input } from '$lib/components/ui/input'; - import { page } from "$app/stores"; + import { Label } from "$lib/components/ui/label/index.js"; import { toast } from "svelte-sonner"; import * as Card from "$lib/components/ui/card"; import * as Dialog from "$lib/components/ui/dialog"; @@ -16,6 +17,7 @@ import * as m from "$lib/paraglide/messages"; import * as Table from "$lib/components/ui/table"; import * as Tooltip from "$lib/components/ui/tooltip"; + import type { Token } from "./message"; let { data }: { data: PageData } = $props(); @@ -28,7 +30,21 @@ let dialogOpen = $state(false); let dialogAction = $state("create" as "create" | "update" | "delete" | "reg"); - let regToken = $state(undefined as string | undefined); + let disableRegCloseButton = $state(true); + let actuallyDelete = $state(false); + let disableDeleteButton = $derived(!actuallyDelete); + + let regToken = $state({ + value: "", + firstname: "", + lastname: "", + email: "", + } as Token | undefined); + + const generateRegistrationURL = (token: Token | undefined) => { + if (!token) return ""; + return `${window.location.origin}/register/${token.value}`; + } const openUserDelete = (user: User) => { dialogAction = "delete"; @@ -50,7 +66,7 @@ dialogOpen = true; } - const openRegDialog = (token: string) => { + const openRegDialog = (token: Token) => { dialogAction = "reg"; regToken = token; dialogOpen = true; @@ -101,7 +117,7 @@ if ($message.token) { closeDialog(); openRegDialog($message.token); - $message.token = undefined; + clearRegToken(); } } }); @@ -185,6 +201,12 @@ { + if (!open) { + clearFormData(); + clearRegToken(); + } + }} > - {#if dialogAction === "reg"} + {#if regToken !== undefined && dialogAction === "reg"} - New User Registration - - User {$formData.firstname} {$formData.lastname} created successfully. - Copy the token and send it to the user. + {m.adminNewUser()} - {regToken.firstname} {regToken.lastname} <{regToken.email}> + + {m.adminNotShownAgain()} + {m.adminNewUserDescription()} - { - closeDialog(); - clearRegToken(); - }}>Close + + + { + navigator.clipboard.writeText(generateRegistrationURL(regToken)); + toast.success(m.copiedToClipboard()); + disableRegCloseButton = false; + }}>{m.copyLink()} + {disableRegCloseButton = false;}} + > + {m.sendEmail()} + + + { + closeDialog(); + clearRegToken(); + }}>{m.close()} + {:else if dialogAction === "delete"} - - Delete User - - User {$formData.email} will be deleted. - Are you ABSOLUTELY sure? This action cannot be undone. + + {m.adminUserDelete()} + + {m.adminUserDeleteDescription({ email: $formData.email})} + {m.adminUserDeleteWarning()} - - Delete + + + + {m.adminConfirmDelete()} + + {m._delete()} {:else} - {dialogAction} User - Description + {dialogAction} {m.users()} @@ -254,7 +294,7 @@ - Save + {m._save()} {/if} diff --git a/src/routes/admin/message.ts b/src/routes/admin/message.ts new file mode 100644 index 0000000..46e3022 --- /dev/null +++ b/src/routes/admin/message.ts @@ -0,0 +1,11 @@ +export type Token = { + value: string, + email: string, + firstname: string, + lastname: string, +} + +export type Message = { + text: string | undefined, + token: Token | undefined, +} \ No newline at end of file
User {$formData.firstname} {$formData.lastname} created successfully.
Copy the token and send it to the user.
{m.adminNotShownAgain()}
{m.adminNewUserDescription()}
User {$formData.email} will be deleted.
Are you ABSOLUTELY sure? This action cannot be undone.
{m.adminUserDeleteDescription({ email: $formData.email})}
{m.adminUserDeleteWarning()}