mirror of
https://github.com/nikdoof/pocket-id.git
synced 2025-12-14 07:12:19 +00:00
feat: add warning if passkeys missing
This commit is contained in:
@@ -0,0 +1,13 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils/style.js";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn("text-sm [&_p]:leading-relaxed", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
21
frontend/src/lib/components/ui/alert/alert-title.svelte
Normal file
21
frontend/src/lib/components/ui/alert/alert-title.svelte
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import type { HeadingLevel } from "./index.js";
|
||||||
|
import { cn } from "$lib/utils/style.js";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLHeadingElement> & {
|
||||||
|
level?: HeadingLevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let level: $$Props["level"] = "h5";
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:element
|
||||||
|
this={level}
|
||||||
|
class={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</svelte:element>
|
||||||
17
frontend/src/lib/components/ui/alert/alert.svelte
Normal file
17
frontend/src/lib/components/ui/alert/alert.svelte
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { type Variant, alertVariants } from "./index.js";
|
||||||
|
import { cn } from "$lib/utils/style.js";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement> & {
|
||||||
|
variant?: Variant;
|
||||||
|
};
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let variant: $$Props["variant"] = "default";
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn(alertVariants({ variant }), className)} {...$$restProps} role="alert">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
35
frontend/src/lib/components/ui/alert/index.ts
Normal file
35
frontend/src/lib/components/ui/alert/index.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { type VariantProps, tv } from 'tailwind-variants';
|
||||||
|
|
||||||
|
import Description from './alert-description.svelte';
|
||||||
|
import Title from './alert-title.svelte';
|
||||||
|
import Root from './alert.svelte';
|
||||||
|
|
||||||
|
export const alertVariants = tv({
|
||||||
|
base: '[&>svg]:text-foreground relative w-full rounded-lg border p-4 [&:has(svg)]:pl-11 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4',
|
||||||
|
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: 'bg-background text-foreground',
|
||||||
|
destructive:
|
||||||
|
'border-destructive/50 text-destructive text-destructive dark:border-destructive [&>svg]:text-destructive',
|
||||||
|
warning:
|
||||||
|
'bg-amber-100 text-amber-900 dark:bg-amber-900 dark:text-amber-100 [&>svg]:text-amber-900 dark:[&>svg]:text-amber-100'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: 'default'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Variant = VariantProps<typeof alertVariants>['variant'];
|
||||||
|
export type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
||||||
|
|
||||||
|
export {
|
||||||
|
//
|
||||||
|
Root as Alert,
|
||||||
|
Description as AlertDescription,
|
||||||
|
Title as AlertTitle,
|
||||||
|
Description,
|
||||||
|
Root,
|
||||||
|
Title
|
||||||
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import * as Alert from '$lib/components/ui/alert';
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
import * as Card from '$lib/components/ui/card';
|
import * as Card from '$lib/components/ui/card';
|
||||||
import UserService from '$lib/services/user-service';
|
import UserService from '$lib/services/user-service';
|
||||||
@@ -8,6 +9,7 @@
|
|||||||
import type { UserCreate } from '$lib/types/user.type';
|
import type { UserCreate } from '$lib/types/user.type';
|
||||||
import { axiosErrorToast, getWebauthnErrorMessage } from '$lib/utils/error-util';
|
import { axiosErrorToast, getWebauthnErrorMessage } from '$lib/utils/error-util';
|
||||||
import { startRegistration } from '@simplewebauthn/browser';
|
import { startRegistration } from '@simplewebauthn/browser';
|
||||||
|
import { LucideAlertTriangle } from 'lucide-svelte';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import AccountForm from './account-form.svelte';
|
import AccountForm from './account-form.svelte';
|
||||||
import PasskeyList from './passkey-list.svelte';
|
import PasskeyList from './passkey-list.svelte';
|
||||||
@@ -52,6 +54,16 @@
|
|||||||
<title>Account Settings</title>
|
<title>Account Settings</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
|
{#if passkeys.length == 0}
|
||||||
|
<Alert.Root variant="warning">
|
||||||
|
<LucideAlertTriangle class="size-4" />
|
||||||
|
<Alert.Title>Passkey missing</Alert.Title>
|
||||||
|
<Alert.Description
|
||||||
|
>Please add a passkey to prevent losing access to your account.</Alert.Description
|
||||||
|
>
|
||||||
|
</Alert.Root>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if $appConfigStore.allowOwnAccountEdit}
|
{#if $appConfigStore.allowOwnAccountEdit}
|
||||||
<Card.Root>
|
<Card.Root>
|
||||||
<Card.Header>
|
<Card.Header>
|
||||||
@@ -77,7 +89,7 @@
|
|||||||
</Card.Header>
|
</Card.Header>
|
||||||
{#if passkeys.length != 0}
|
{#if passkeys.length != 0}
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<PasskeyList {passkeys} />
|
<PasskeyList bind:passkeys />
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
{/if}
|
{/if}
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
|
|||||||
@@ -9,15 +9,10 @@
|
|||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import RenamePasskeyModal from './rename-passkey-modal.svelte';
|
import RenamePasskeyModal from './rename-passkey-modal.svelte';
|
||||||
|
|
||||||
let { passkeys: initialsPasskeys }: { passkeys: Passkey[] } = $props();
|
let { passkeys = $bindable() }: { passkeys: Passkey[] } = $props();
|
||||||
let passkeys = $state<Passkey[]>(initialsPasskeys);
|
|
||||||
|
|
||||||
const webauthnService = new WebauthnService();
|
const webauthnService = new WebauthnService();
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
passkeys = initialsPasskeys;
|
|
||||||
});
|
|
||||||
|
|
||||||
let passkeyToRename: Passkey | null = $state(null);
|
let passkeyToRename: Passkey | null = $state(null);
|
||||||
|
|
||||||
async function deletePasskey(passkey: Passkey) {
|
async function deletePasskey(passkey: Passkey) {
|
||||||
|
|||||||
Reference in New Issue
Block a user