117 lines
3.5 KiB
Svelte
117 lines
3.5 KiB
Svelte
<script lang="ts">
|
|
import type { Identity } from '@ory/client';
|
|
import { Modal } from 'flowbite-svelte';
|
|
|
|
interface Props {
|
|
identity: Identity | null;
|
|
onClose: () => void;
|
|
onDeleteCredential: (identityId: string, type: string, identifier?: string) => Promise<void>;
|
|
}
|
|
|
|
let { identity, onClose, onDeleteCredential }: Props = $props();
|
|
|
|
let open = $derived(!!identity);
|
|
</script>
|
|
|
|
<Modal
|
|
bind:open
|
|
onclose={onClose}
|
|
size="xl"
|
|
autoclose={false}
|
|
dismissable={false}
|
|
outsideclose={false}
|
|
>
|
|
{#snippet header()}
|
|
<h3 class="text-lg leading-6 font-medium text-gray-900">Identity Details</h3>
|
|
{/snippet}
|
|
|
|
<div class="w-full">
|
|
<!-- Basic Info -->
|
|
<div class="mb-6 grid grid-cols-2 gap-4">
|
|
<div>
|
|
<span class="block text-sm font-medium text-gray-700">Email</span>
|
|
<p class="mt-1 text-sm text-gray-900">{identity?.traits?.email || 'N/A'}</p>
|
|
</div>
|
|
<div>
|
|
<span class="block text-sm font-medium text-gray-700">Name</span>
|
|
<p class="mt-1 text-sm text-gray-900">
|
|
{#if identity?.traits?.name}
|
|
{identity.traits.name.first || ''} {identity.traits.name.last || ''}
|
|
{:else}
|
|
N/A
|
|
{/if}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<span class="block text-sm font-medium text-gray-700">State</span>
|
|
<p class="mt-1 text-sm text-gray-900">{identity?.state || 'N/A'}</p>
|
|
</div>
|
|
<div>
|
|
<span class="block text-sm font-medium text-gray-700">Created</span>
|
|
<p class="mt-1 text-sm text-gray-900">
|
|
{identity?.created_at ? new Date(identity.created_at).toLocaleString() : 'N/A'}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Credentials Section -->
|
|
{#if identity?.credentials}
|
|
<div class="mb-6">
|
|
<h4 class="text-md mb-3 font-medium text-gray-900">Authentication Methods</h4>
|
|
<div class="space-y-3">
|
|
{#each Object.entries(identity.credentials) as [type, credential]}
|
|
<div class="rounded-lg border border-gray-200 p-3">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<span class="text-sm font-medium text-gray-900 capitalize">{type}</span>
|
|
{#if credential.identifiers && credential.identifiers.length > 0}
|
|
<div class="mt-1">
|
|
{#each credential.identifiers as identifier}
|
|
<span class="block text-xs text-gray-600">{identifier}</span>
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
{#if credential.created_at}
|
|
<p class="mt-1 text-xs text-gray-500">
|
|
Added {new Date(credential.created_at).toLocaleDateString()}
|
|
</p>
|
|
{/if}
|
|
</div>
|
|
{#if type !== 'password' && type !== 'code'}
|
|
<button
|
|
onclick={() =>
|
|
onDeleteCredential(identity.id, type, credential.identifiers?.[0])}
|
|
class="rounded border border-red-300 px-2 py-1 text-xs text-red-600 hover:bg-red-50 hover:text-red-900"
|
|
>
|
|
Remove
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
<!-- Raw JSON (collapsed by default) -->
|
|
<details class="mt-4">
|
|
<summary class="cursor-pointer text-sm font-medium text-gray-700 hover:text-gray-900">
|
|
View Raw JSON
|
|
</summary>
|
|
<div class="mt-2 rounded-lg bg-gray-50 p-4">
|
|
<pre class="overflow-x-auto text-xs text-gray-800">{JSON.stringify(identity, null, 2)}</pre>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
|
|
{#snippet footer()}
|
|
<button
|
|
type="button"
|
|
onclick={onClose}
|
|
class="rounded-md bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:outline-none"
|
|
>
|
|
Close
|
|
</button>
|
|
{/snippet}
|
|
</Modal>
|