Skip to content

Commit 1dbbb7f

Browse files
Replace browser alerts with custom ConfirmationDialog component
Created reusable ConfirmationDialog component and updated API key creation dialog: - Added ConfirmationDialog with destructive styling and loading states - Updated API key creation dialog copy to mention dashboard access - Removed "I've saved my key" button, using X to close - Replaced simple copy button with EnhancedCopyButton for consistency - Replaced browser confirm() with custom dialog for API key revocation 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
1 parent dfde3ae commit 1dbbb7f

File tree

2 files changed

+111
-19
lines changed

2 files changed

+111
-19
lines changed

web/src/app/profile/components/api-keys-section.tsx

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ import {
1818
DialogContent,
1919
DialogHeader,
2020
DialogTitle,
21+
DialogDescription,
2122
DialogFooter,
2223
} from '@/components/ui/dialog'
2324
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
2425
import { useToast } from '@/components/ui/use-toast'
2526
import { Copy, Check, Plus } from 'lucide-react'
27+
import { EnhancedCopyButton } from '@/components/ui/enhanced-copy-button'
28+
import { ConfirmationDialog } from '@/components/ui/confirmation-dialog'
2629
import { ProfileSection } from './profile-section'
2730

2831
async function fetchTokens(): Promise<{
@@ -65,6 +68,8 @@ export function ApiKeysSection() {
6568
const [newTokenValue, setNewTokenValue] = useState('')
6669
const [showTokenValue, setShowTokenValue] = useState(false)
6770
const [copiedTokenId, setCopiedTokenId] = useState<string | null>(null)
71+
const [revokeDialogOpen, setRevokeDialogOpen] = useState(false)
72+
const [tokenToRevoke, setTokenToRevoke] = useState<string | null>(null)
6873

6974
const createTokenMutation = useMutation({
7075
mutationFn: async ({
@@ -137,9 +142,22 @@ export function ApiKeysSection() {
137142
setCreateTokenOpen(false)
138143
}
139144

140-
async function handleRevokeToken(tokenId: string) {
141-
if (!confirm('Revoke this API Key? This cannot be undone.')) return
142-
revokeTokenMutation.mutate(tokenId)
145+
function openRevokeDialog(tokenId: string) {
146+
setTokenToRevoke(tokenId)
147+
setRevokeDialogOpen(true)
148+
}
149+
150+
function handleConfirmRevoke() {
151+
if (tokenToRevoke) {
152+
revokeTokenMutation.mutate(tokenToRevoke)
153+
}
154+
setRevokeDialogOpen(false)
155+
setTokenToRevoke(null)
156+
}
157+
158+
function handleCloseRevokeDialog() {
159+
setRevokeDialogOpen(false)
160+
setTokenToRevoke(null)
143161
}
144162

145163
function copyToClipboard(text: string) {
@@ -243,7 +261,7 @@ export function ApiKeysSection() {
243261
<Button
244262
variant="destructive"
245263
size="sm"
246-
onClick={() => handleRevokeToken(token.id)}
264+
onClick={() => openRevokeDialog(token.id)}
247265
>
248266
Revoke
249267
</Button>
@@ -302,6 +320,10 @@ export function ApiKeysSection() {
302320
<DialogContent>
303321
<DialogHeader>
304322
<DialogTitle>API Key Created</DialogTitle>
323+
<DialogDescription>
324+
You can copy this key now or access it anytime from the API key
325+
dashboard.
326+
</DialogDescription>
305327
</DialogHeader>
306328
<div className="grid gap-4 py-4">
307329
<div className="grid gap-2">
@@ -312,26 +334,26 @@ export function ApiKeysSection() {
312334
readOnly
313335
className="font-mono text-sm"
314336
/>
315-
<Button
316-
variant="outline"
317-
onClick={() => copyToClipboard(newTokenValue)}
318-
>
319-
Copy
320-
</Button>
337+
<EnhancedCopyButton
338+
value={newTokenValue}
339+
className="p-2.5 rounded-md bg-muted/50 hover:bg-muted border border-border/50 hover:border-border transition-all duration-200 ease-in-out inline-flex items-center justify-center shadow-sm hover:shadow-md text-muted-foreground hover:text-foreground"
340+
/>
321341
</div>
322342
</div>
323-
<div className="text-sm text-muted-foreground">
324-
<strong>Important:</strong> This API key will only be shown once.
325-
Save it somewhere secure as you won't be able to see it again.
326-
</div>
327343
</div>
328-
<DialogFooter>
329-
<Button onClick={() => setShowTokenValue(false)}>
330-
I've saved my API key
331-
</Button>
332-
</DialogFooter>
333344
</DialogContent>
334345
</Dialog>
346+
347+
<ConfirmationDialog
348+
isOpen={revokeDialogOpen}
349+
onClose={handleCloseRevokeDialog}
350+
onConfirm={handleConfirmRevoke}
351+
title="Revoke API Key"
352+
description="Are you sure you want to revoke this API key? This action cannot be undone and the API key will be permanently deleted."
353+
confirmText="Revoke"
354+
isDestructive
355+
isConfirming={revokeTokenMutation.isPending}
356+
/>
335357
</ProfileSection>
336358
)
337359
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
'use client'
2+
3+
import { Button } from '@/components/ui/button'
4+
import {
5+
Dialog,
6+
DialogContent,
7+
DialogDescription,
8+
DialogFooter,
9+
DialogHeader,
10+
DialogTitle,
11+
} from '@/components/ui/dialog'
12+
13+
interface ConfirmationDialogProps {
14+
isOpen: boolean
15+
onClose: () => void
16+
onConfirm: () => void
17+
title: string
18+
description: string
19+
confirmText?: string
20+
isDestructive?: boolean
21+
isConfirming?: boolean
22+
}
23+
24+
export function ConfirmationDialog({
25+
isOpen,
26+
onClose,
27+
onConfirm,
28+
title,
29+
description,
30+
confirmText = 'Confirm',
31+
isDestructive = false,
32+
isConfirming = false,
33+
}: ConfirmationDialogProps) {
34+
const handleConfirm = () => {
35+
onConfirm()
36+
}
37+
38+
const handleOpenChange = (open: boolean) => {
39+
if (!open) {
40+
onClose()
41+
}
42+
}
43+
44+
return (
45+
<Dialog open={isOpen} onOpenChange={handleOpenChange}>
46+
<DialogContent>
47+
<DialogHeader>
48+
<DialogTitle>{title}</DialogTitle>
49+
<DialogDescription>{description}</DialogDescription>
50+
</DialogHeader>
51+
<DialogFooter>
52+
<Button
53+
variant="outline"
54+
onClick={onClose}
55+
disabled={isConfirming}
56+
>
57+
Cancel
58+
</Button>
59+
<Button
60+
onClick={handleConfirm}
61+
disabled={isConfirming}
62+
variant={isDestructive ? 'destructive' : 'default'}
63+
>
64+
{isConfirming ? 'Processing...' : confirmText}
65+
</Button>
66+
</DialogFooter>
67+
</DialogContent>
68+
</Dialog>
69+
)
70+
}

0 commit comments

Comments
 (0)