diff --git a/app/package-lock.json b/app/package-lock.json index 9a580648..9a5881d8 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -15,7 +15,7 @@ "devDependencies": { "@fontsource-variable/inter": "^5.2.8", "@internationalized/date": "^3.12.1", - "@lucide/svelte": "^1.17.0", + "@lucide/svelte": "^1.23.0", "@sveltejs/adapter-auto": "^7.0.1", "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.57.0", @@ -23,9 +23,11 @@ "@tailwindcss/vite": "^4.2.2", "bits-ui": "^2.18.0", "clsx": "^2.1.1", + "mode-watcher": "^1.1.0", "shadcn-svelte": "^1.2.7", "svelte": "^5.55.2", "svelte-check": "^4.4.6", + "svelte-sonner": "^1.1.1", "tailwind-merge": "^3.5.0", "tailwind-variants": "^3.2.2", "tailwindcss": "^4.2.2", @@ -682,9 +684,9 @@ } }, "node_modules/@lucide/svelte": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-1.17.0.tgz", - "integrity": "sha512-q06YCFBN5CO8cd1ADmLCxWRVMVb7xxvHzqC0lvNoxGa+FLW6Cd1Y1AOxgbQk4Iwe68vkAMCRveNHint4WoaVKg==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-1.23.0.tgz", + "integrity": "sha512-3LQbKXx9vId6Nx4E2Nu2qwgJfdmr5+CVeVJbxe5cy+HcnCRd9QVVtZXqvgBYAV1OJrPmQAf9/3gJWLCpASC/Ng==", "dev": true, "license": "ISC", "peerDependencies": { @@ -2129,6 +2131,73 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/mode-watcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mode-watcher/-/mode-watcher-1.1.0.tgz", + "integrity": "sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "runed": "^0.25.0", + "svelte-toolbelt": "^0.7.1" + }, + "peerDependencies": { + "svelte": "^5.27.0" + } + }, + "node_modules/mode-watcher/node_modules/runed": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.25.0.tgz", + "integrity": "sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "dependencies": { + "esm-env": "^1.0.0" + }, + "peerDependencies": { + "svelte": "^5.7.0" + } + }, + "node_modules/mode-watcher/node_modules/svelte-toolbelt": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.7.1.tgz", + "integrity": "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte" + ], + "dependencies": { + "clsx": "^2.1.1", + "runed": "^0.23.2", + "style-to-object": "^1.0.8" + }, + "engines": { + "node": ">=18", + "pnpm": ">=8.7.0" + }, + "peerDependencies": { + "svelte": "^5.0.0" + } + }, + "node_modules/mode-watcher/node_modules/svelte-toolbelt/node_modules/runed": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.23.4.tgz", + "integrity": "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "dependencies": { + "esm-env": "^1.0.0" + }, + "peerDependencies": { + "svelte": "^5.7.0" + } + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -2637,6 +2706,36 @@ "typescript": ">=5.0.0" } }, + "node_modules/svelte-sonner": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/svelte-sonner/-/svelte-sonner-1.1.1.tgz", + "integrity": "sha512-5cd3p7wa4cq0NsqslMwdlPb7x1JglEZ/GKrLePWNr5bCxR1nagAVrY01FRFrXfUGs41miLt3C327+8XJo5BzZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "runed": "^0.28.0" + }, + "peerDependencies": { + "svelte": "^5.0.0" + } + }, + "node_modules/svelte-sonner/node_modules/runed": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.28.0.tgz", + "integrity": "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "license": "MIT", + "dependencies": { + "esm-env": "^1.0.0" + }, + "peerDependencies": { + "svelte": "^5.7.0" + } + }, "node_modules/svelte-toolbelt": { "version": "0.10.6", "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.10.6.tgz", diff --git a/app/package.json b/app/package.json index 333e55a8..ea80f909 100644 --- a/app/package.json +++ b/app/package.json @@ -14,7 +14,7 @@ "devDependencies": { "@fontsource-variable/inter": "^5.2.8", "@internationalized/date": "^3.12.1", - "@lucide/svelte": "^1.17.0", + "@lucide/svelte": "^1.23.0", "@sveltejs/adapter-auto": "^7.0.1", "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.57.0", @@ -22,9 +22,11 @@ "@tailwindcss/vite": "^4.2.2", "bits-ui": "^2.18.0", "clsx": "^2.1.1", + "mode-watcher": "^1.1.0", "shadcn-svelte": "^1.2.7", "svelte": "^5.55.2", "svelte-check": "^4.4.6", + "svelte-sonner": "^1.1.1", "tailwind-merge": "^3.5.0", "tailwind-variants": "^3.2.2", "tailwindcss": "^4.2.2", diff --git a/app/src/lib/auth/right-panel.svelte b/app/src/lib/auth/right-panel.svelte index b1b6a029..b49014e9 100644 --- a/app/src/lib/auth/right-panel.svelte +++ b/app/src/lib/auth/right-panel.svelte @@ -7,12 +7,12 @@ class="relative w-full rounded-3xl overflow-hidden flex flex-col justify-between p-10" > diff --git a/app/src/lib/components/ui/sonner/index.ts b/app/src/lib/components/ui/sonner/index.ts new file mode 100644 index 00000000..1ad9f4a2 --- /dev/null +++ b/app/src/lib/components/ui/sonner/index.ts @@ -0,0 +1 @@ +export { default as Toaster } from "./sonner.svelte"; diff --git a/app/src/lib/components/ui/sonner/sonner.svelte b/app/src/lib/components/ui/sonner/sonner.svelte new file mode 100644 index 00000000..c263526e --- /dev/null +++ b/app/src/lib/components/ui/sonner/sonner.svelte @@ -0,0 +1,35 @@ + + + + {#snippet loadingIcon()} + + {/snippet} + {#snippet successIcon()} + + {/snippet} + {#snippet errorIcon()} + + {/snippet} + {#snippet infoIcon()} + + {/snippet} + {#snippet warningIcon()} + + {/snippet} + diff --git a/app/src/lib/components/ui/spinner/index.ts b/app/src/lib/components/ui/spinner/index.ts new file mode 100644 index 00000000..f8b1cedd --- /dev/null +++ b/app/src/lib/components/ui/spinner/index.ts @@ -0,0 +1 @@ +export { default as Spinner } from "./spinner.svelte"; diff --git a/app/src/lib/components/ui/spinner/spinner.svelte b/app/src/lib/components/ui/spinner/spinner.svelte new file mode 100644 index 00000000..b469ded5 --- /dev/null +++ b/app/src/lib/components/ui/spinner/spinner.svelte @@ -0,0 +1,18 @@ + + + diff --git a/app/src/routes/(app)/+page.svelte b/app/src/routes/(app)/+page.svelte index ae2319b4..497f86ca 100644 --- a/app/src/routes/(app)/+page.svelte +++ b/app/src/routes/(app)/+page.svelte @@ -1,11 +1,11 @@
- +
-

Dashboard

+

Dashboard

diff --git a/app/src/routes/(auth)/login/+page.svelte b/app/src/routes/(auth)/login/+page.svelte index 80b04130..0e769e58 100644 --- a/app/src/routes/(auth)/login/+page.svelte +++ b/app/src/routes/(auth)/login/+page.svelte @@ -2,35 +2,39 @@ import { Button } from "$lib/components/ui/button/index.js"; import { Input } from "$lib/components/ui/input/index.js"; import { Label } from "$lib/components/ui/label/index.js"; - import { Lock, User } from "@lucide/svelte"; + import { Lock, User, CircleCheck } from "@lucide/svelte"; import { goto } from "$app/navigation"; import { login, TwoFactorRequiredError } from "$lib/auth/api"; import { auth } from "$lib/auth/store.svelte"; + import Spinner from "$lib/components/ui/spinner/spinner.svelte"; + import { toast } from "svelte-sonner"; + + type Status = "idle" | "loading" | "success"; let username = $state(""); let password = $state(""); - let loading = $state(false); - let error = $state(null); + let status = $state("idle"); async function handleSubmit() { - error = null; - loading = true; + status = "loading"; try { const response = await login(username, password); auth.setSession(response); - if (response.force_password_change) { - goto("/pw_change"); - } else { - goto("/"); - } + status = "success"; + const target = response.force_password_change ? "/pw_change" : "/"; + setTimeout(() => goto(target), 600); } catch (err) { + status = "idle"; if (err instanceof TwoFactorRequiredError) { - goto(`/otp?username=${encodeURIComponent(err.username)}&password=${encodeURIComponent(err.password)}`); + goto( + `/otp?username=${encodeURIComponent(err.username)}&password=${encodeURIComponent(err.password)}`, + ); } else { - error = "Invalid credentials"; + toast.error("Invalid credentials", { + description: + "Check your username and password and try again", + }); } - } finally { - loading = false; } } @@ -42,11 +46,19 @@ />

Sign in

-
{ e.preventDefault(); handleSubmit(); }} class="flex flex-col"> + { + e.preventDefault(); + handleSubmit(); + }} + class="flex flex-col" +>
- +
- +
- {#if error} -
{error}
- {/if} - - diff --git a/app/src/routes/(auth)/otp/+page.svelte b/app/src/routes/(auth)/otp/+page.svelte index e63240a7..98fcaab1 100644 --- a/app/src/routes/(auth)/otp/+page.svelte +++ b/app/src/routes/(auth)/otp/+page.svelte @@ -1,36 +1,38 @@ @@ -72,11 +76,13 @@ {/if}
- {#if error} -
{error}
- {/if} - diff --git a/app/src/routes/+layout.svelte b/app/src/routes/+layout.svelte index 316fbb7e..e7ceddb3 100644 --- a/app/src/routes/+layout.svelte +++ b/app/src/routes/+layout.svelte @@ -1,17 +1,22 @@ - + + + + {@render children()} + diff --git a/app/static/gradients/Gradient-dark.svg b/app/static/gradients/Gradient-dark.svg deleted file mode 100644 index af49adeb..00000000 --- a/app/static/gradients/Gradient-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/static/gradients/Gradient-dark.webp b/app/static/gradients/Gradient-dark.webp new file mode 100644 index 00000000..124c7564 Binary files /dev/null and b/app/static/gradients/Gradient-dark.webp differ diff --git a/app/static/gradients/Gradient-light.svg b/app/static/gradients/Gradient-light.svg deleted file mode 100755 index 263eda97..00000000 --- a/app/static/gradients/Gradient-light.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/static/gradients/Gradient-light.webp b/app/static/gradients/Gradient-light.webp new file mode 100644 index 00000000..4067b7d3 Binary files /dev/null and b/app/static/gradients/Gradient-light.webp differ