refactor: migrate shadcn-components to Svelte 5 and TW4 (#551)

Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
Kyle Mendell
2025-05-21 12:15:27 -05:00
committed by Elias Schneider
parent 05b443d984
commit 28c85990ba
197 changed files with 8142 additions and 7471 deletions

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import SignInWrapper from '$lib/components/login-wrapper.svelte';
import ScopeItem from '$lib/components/scope-item.svelte';
import { Button } from '$lib/components/ui/button';
import * as Card from '$lib/components/ui/card';
import { m } from '$lib/paraglide/messages';
@@ -8,25 +9,31 @@
import appConfigStore from '$lib/stores/application-configuration-store';
import userStore from '$lib/stores/user-store';
import { getWebauthnErrorMessage } from '$lib/utils/error-util';
import { LucideMail, LucideUser, LucideUsers } from '@lucide/svelte';
import { startAuthentication } from '@simplewebauthn/browser';
import { LucideMail, LucideUser, LucideUsers } from 'lucide-svelte';
import { onMount } from 'svelte';
import { slide } from 'svelte/transition';
import type { PageData } from './$types';
import ClientProviderImages from './components/client-provider-images.svelte';
import ScopeItem from '$lib/components/scope-item.svelte';
const webauthnService = new WebAuthnService();
const oidService = new OidcService();
let isLoading = false;
let success = false;
let errorMessage: string | null = null;
let authorizationRequired = false;
let authorizationConfirmed = false;
let {
scope,
nonce,
client,
authorizeState,
callbackURL,
codeChallenge,
codeChallengeMethod
}: PageData = $props();
export let data: PageData;
let { scope, nonce, client, state, callbackURL, codeChallenge, codeChallengeMethod } = data;
let isLoading = $state(false);
let success = $state(false);
let errorMessage: string | null = $state(null);
let authorizationRequired = $state(false);
let authorizationConfirmed = $state(false);
onMount(() => {
if ($userStore) {
@@ -40,7 +47,7 @@
// Get access token if not signed in
if (!$userStore?.id) {
const loginOptions = await webauthnService.getLoginOptions();
const authResponse = await startAuthentication({optionsJSON: loginOptions});
const authResponse = await startAuthentication({ optionsJSON: loginOptions });
const user = await webauthnService.finishLogin(authResponse);
userStore.setUser(user);
}
@@ -70,7 +77,7 @@
setTimeout(() => {
const redirectURL = new URL(callbackURL);
redirectURL.searchParams.append('code', code);
redirectURL.searchParams.append('state', state);
redirectURL.searchParams.append('state', authorizeState);
window.location.href = redirectURL.toString();
}, 1000);
@@ -84,10 +91,7 @@
{#if client == null}
<p>{m.client_not_found()}</p>
{:else}
<SignInWrapper
animate={!$appConfigStore.disableAnimations}
showAlternativeSignInMethodButton={$userStore == null}
>
<SignInWrapper showAlternativeSignInMethodButton={$userStore == null}>
<ClientProviderImages {client} {success} error={!!errorMessage} />
<h1 class="font-playfair mt-5 text-3xl font-bold sm:text-4xl">
{m.sign_in_to({ name: client.name })}
@@ -140,14 +144,18 @@
</Card.Root>
</div>
{/if}
<div class="flex w-full justify-stretch gap-2">
<Button onclick={() => history.back()} class="w-full" variant="secondary">{m.cancel()}</Button
>
{#if !errorMessage}
<Button class="w-full" {isLoading} on:click={authorize}>{m.sign_in()}</Button>
{:else}
<Button class="w-full" on:click={() => (errorMessage = null)}>{m.try_again()}</Button>
{/if}
<!-- Wrap the buttons in a container with the same width as in the login code page -->
<div class="w-full max-w-[450px]">
<div class="mt-8 flex justify-between gap-2">
<Button onclick={() => history.back()} class="flex-1" variant="secondary"
>{m.cancel()}</Button
>
{#if !errorMessage}
<Button class="flex-1" {isLoading} onclick={authorize}>{m.sign_in()}</Button>
{:else}
<Button class="flex-1" onclick={() => (errorMessage = null)}>{m.try_again()}</Button>
{/if}
</div>
</div>
</SignInWrapper>
{/if}

View File

@@ -10,7 +10,7 @@ export const load: PageLoad = async ({ url }) => {
return {
scope: url.searchParams.get('scope')!,
nonce: url.searchParams.get('nonce') || undefined,
state: url.searchParams.get('state')!,
authorizeState: url.searchParams.get('state')!,
callbackURL: url.searchParams.get('redirect_uri')!,
client,
codeChallenge: url.searchParams.get('code_challenge')!,

View File

@@ -35,7 +35,7 @@
? 'translate-x-[108px]'
: ''}"
>
<Logo class="h-10 w-10" />
<Logo class="size-10" />
</div>
<ConnectArrow
@@ -50,22 +50,22 @@
: ''} {animationDone ? (success ? 'bg-green-200' : 'bg-red-200') : 'bg-muted'}"
>
{#if animationDone && success}
<div class="flex h-10 w-10 items-center justify-center">
<CheckmarkAnimated class="h-7 w-7" />
<div class="flex size-10 items-center justify-center">
<CheckmarkAnimated class="size-7" />
</div>
{:else if animationDone && error}
<div class="flex h-10 w-10 items-center justify-center">
<CrossAnimated class="h-5 w-5" />
<div class="flex size-10 items-center justify-center">
<CrossAnimated class="size-5" />
</div>
{:else if client.hasLogo}
<img
class="h-10 w-10"
class="size-10"
src="/api/oidc/clients/{client.id}/logo"
draggable={false}
alt={m.client_logo()}
/>
{:else}
<div class="flex h-10 w-10 items-center justify-center text-3xl font-bold">
<div class="flex size-10 items-center justify-center text-3xl font-bold">
{client.name.charAt(0).toUpperCase()}
</div>
{/if}