nexus/auth-frontend/src/lib/components/modals/IdentityDetailsModal.svelte
2026-01-26 11:58:04 -05:00

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>