refactor(keys): ConfirmDialog with type-to-confirm, EmptyState
- Replace window.confirm for key revoke with ConfirmDialog + confirmText prop requiring user to type the key label before confirmation - Replace terse "No API keys" text with EmptyState component (Key, Plus, Shield icons) - Add focus-visible ring on Revoke button Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a605ba44eb
commit
b118166693
1 changed files with 45 additions and 6 deletions
|
|
@ -9,8 +9,11 @@ import Button from '@/components/ui/Button.vue'
|
|||
import Dialog from '@/components/ui/Dialog.vue'
|
||||
import Input from '@/components/ui/Input.vue'
|
||||
import Spinner from '@/components/ui/Spinner.vue'
|
||||
import ConfirmDialog from '@/components/ui/ConfirmDialog.vue'
|
||||
import EmptyState from '@/components/ui/EmptyState.vue'
|
||||
import { toast } from 'vue-sonner'
|
||||
import { formatDate } from '@/lib/utils'
|
||||
import { Key, Plus, Shield } from 'lucide-vue-next'
|
||||
import type { ApiKey } from '@/types'
|
||||
|
||||
const keys = ref<ApiKey[]>([])
|
||||
|
|
@ -20,6 +23,10 @@ const newKeyLabel = ref('')
|
|||
const creating = ref(false)
|
||||
const generatedKey = ref<string | null>(null)
|
||||
|
||||
// Revoke confirm state
|
||||
const showRevokeConfirm = ref(false)
|
||||
const pendingRevokeKey = ref<ApiKey | null>(null)
|
||||
|
||||
onMounted(() => loadKeys())
|
||||
|
||||
async function loadKeys() {
|
||||
|
|
@ -48,8 +55,15 @@ async function handleCreate() {
|
|||
}
|
||||
}
|
||||
|
||||
async function handleRevoke(key: ApiKey) {
|
||||
if (!confirm(`Revoke key "${key.label}"? This cannot be undone.`)) return
|
||||
function requestRevoke(key: ApiKey) {
|
||||
pendingRevokeKey.value = key
|
||||
showRevokeConfirm.value = true
|
||||
}
|
||||
|
||||
async function executeRevoke() {
|
||||
if (!pendingRevokeKey.value) return
|
||||
const key = pendingRevokeKey.value
|
||||
pendingRevokeKey.value = null
|
||||
try {
|
||||
await adminApi.revokeKey(key.id)
|
||||
toast.success('Key revoked')
|
||||
|
|
@ -58,13 +72,18 @@ async function handleRevoke(key: ApiKey) {
|
|||
toast.error('Failed to revoke key')
|
||||
}
|
||||
}
|
||||
|
||||
function openCreateDialog() {
|
||||
showCreate.value = true
|
||||
generatedKey.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-6">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h2 class="text-lg font-semibold text-foreground">API Keys</h2>
|
||||
<Button size="sm" @click="showCreate = true; generatedKey = null">
|
||||
<Button size="sm" @click="openCreateDialog">
|
||||
<svg class="h-4 w-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
|
|
@ -77,8 +96,14 @@ async function handleRevoke(key: ApiKey) {
|
|||
<div v-if="loading" class="flex items-center justify-center h-20">
|
||||
<Spinner class="text-primary" />
|
||||
</div>
|
||||
<div v-else-if="keys.length === 0" class="text-center text-muted-foreground py-8 text-sm">
|
||||
No API keys
|
||||
<div v-else-if="keys.length === 0" class="p-6">
|
||||
<EmptyState
|
||||
title="No API keys yet"
|
||||
description="Create a key to ingest Claude Code sessions."
|
||||
:icons="[Key, Plus, Shield]"
|
||||
action-label="Generate key"
|
||||
@action="openCreateDialog"
|
||||
/>
|
||||
</div>
|
||||
<table v-else class="w-full">
|
||||
<thead>
|
||||
|
|
@ -103,7 +128,12 @@ async function handleRevoke(key: ApiKey) {
|
|||
{{ key.last_used ? formatDate(key.last_used) : 'Never' }}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<Button variant="ghost" size="sm" class="text-destructive" @click="handleRevoke(key)">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
class="text-destructive focus-visible:ring-destructive"
|
||||
@click="requestRevoke(key)"
|
||||
>
|
||||
Revoke
|
||||
</Button>
|
||||
</td>
|
||||
|
|
@ -137,5 +167,14 @@ async function handleRevoke(key: ApiKey) {
|
|||
</Button>
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
<!-- Revoke confirmation -->
|
||||
<ConfirmDialog
|
||||
v-model:open="showRevokeConfirm"
|
||||
title="Revoke API key"
|
||||
:description="`This will permanently revoke '${pendingRevokeKey?.label}'. Sessions using this key will stop ingesting.`"
|
||||
:confirm-text="pendingRevokeKey?.label"
|
||||
@confirm="executeRevoke"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue