add delete

This commit is contained in:
songtianlun 2025-08-03 13:25:06 +08:00
parent 19661710c1
commit 6fecf02a46
4 changed files with 277 additions and 51 deletions

View File

@ -198,6 +198,11 @@ export default function StudioPage() {
router.push(`/studio/${id}`)
}
const handleDeletePrompt = (id: string) => {
// Remove the prompt from the local state
setPrompts(prev => prev.filter(p => p.id !== id))
}
const handleUpdatePermissions = async (promptId: string, newPermissions: 'private' | 'public') => {
if (!user) return
@ -753,6 +758,7 @@ export default function StudioPage() {
isOpen={isEditModalOpen}
onClose={handleEditModalClose}
onSave={handleEditModalSave}
onDelete={handleDeletePrompt}
userId={user?.id || ''}
/>
)}
@ -764,6 +770,8 @@ export default function StudioPage() {
onClose={handleDetailModalClose}
onEdit={handleEditPrompt}
onDebug={handleDebugPrompt}
onDelete={handleDeletePrompt}
userId={user?.id}
/>
{/* Bulk Add Modal */}

View File

@ -7,11 +7,13 @@ import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import { LoadingSpinner } from '@/components/ui/loading-spinner'
import { DeleteConfirmationDialog } from '@/components/ui/delete-confirmation-dialog'
import {
X,
Save,
Tag,
Plus
Plus,
Trash2
} from 'lucide-react'
interface Prompt {
@ -29,6 +31,7 @@ interface EditPromptModalProps {
isOpen: boolean
onClose: () => void
onSave: (updatedPrompt: Prompt) => void
onDelete?: (id: string) => void
userId: string
}
@ -37,6 +40,7 @@ export function EditPromptModal({
isOpen,
onClose,
onSave,
onDelete,
userId
}: EditPromptModalProps) {
const t = useTranslations('studio')
@ -48,6 +52,8 @@ export function EditPromptModal({
const [newTag, setNewTag] = useState('')
const [isSaving, setIsSaving] = useState(false)
const [availableTags, setAvailableTags] = useState<string[]>([])
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
const [isDeleting, setIsDeleting] = useState(false)
useEffect(() => {
if (isOpen) {
@ -114,6 +120,29 @@ export function EditPromptModal({
}
}
const handleDelete = async () => {
if (!onDelete) return
setIsDeleting(true)
try {
const response = await fetch(`/api/prompts/${prompt.id}?userId=${userId}`, {
method: 'DELETE'
})
if (response.ok) {
onDelete(prompt.id)
setIsDeleteDialogOpen(false)
onClose()
} else {
console.error('Failed to delete prompt')
}
} catch (error) {
console.error('Error deleting prompt:', error)
} finally {
setIsDeleting(false)
}
}
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
handleSave()
@ -258,28 +287,54 @@ export function EditPromptModal({
</div>
{/* Footer */}
<div className="flex items-center justify-end space-x-2 p-6 border-t border-border">
<Button
variant="outline"
onClick={onClose}
disabled={isSaving}
>
{tCommon('cancel')}
</Button>
<Button
onClick={handleSave}
disabled={!name.trim() || isSaving}
className="flex items-center space-x-2"
>
{isSaving ? (
<LoadingSpinner size="sm" />
) : (
<Save className="h-4 w-4" />
)}
<span>{tCommon('save')}</span>
</Button>
<div className="flex items-center justify-between p-6 border-t border-border">
{/* Delete button on the left */}
{onDelete && (
<Button
variant="outline"
onClick={() => setIsDeleteDialogOpen(true)}
disabled={isSaving || isDeleting}
className="flex items-center space-x-2 text-destructive hover:text-destructive hover:bg-destructive/10"
>
<Trash2 className="h-4 w-4" />
<span>{tCommon('delete')}</span>
</Button>
)}
{/* Save and Cancel buttons on the right */}
<div className="flex items-center space-x-2 ml-auto">
<Button
variant="outline"
onClick={onClose}
disabled={isSaving || isDeleting}
>
{tCommon('cancel')}
</Button>
<Button
onClick={handleSave}
disabled={!name.trim() || isSaving || isDeleting}
className="flex items-center space-x-2"
>
{isSaving ? (
<LoadingSpinner size="sm" />
) : (
<Save className="h-4 w-4" />
)}
<span>{tCommon('save')}</span>
</Button>
</div>
</div>
</div>
{/* Delete Confirmation Dialog */}
<DeleteConfirmationDialog
isOpen={isDeleteDialogOpen}
onClose={() => setIsDeleteDialogOpen(false)}
onConfirm={handleDelete}
title={t('deletePrompt')}
itemName={prompt.name}
isDeleting={isDeleting}
/>
</div>
)
}

View File

@ -1,8 +1,10 @@
'use client'
import { useState } from 'react'
import { useTranslations } from 'next-intl'
import { Button } from '@/components/ui/button'
import { X, Edit, Play, Calendar, Tag } from 'lucide-react'
import { DeleteConfirmationDialog } from '@/components/ui/delete-confirmation-dialog'
import { X, Edit, Play, Calendar, Tag, Trash2 } from 'lucide-react'
interface Prompt {
id: string
@ -23,6 +25,8 @@ interface PromptDetailModalProps {
onClose: () => void
onEdit: (id: string) => void
onDebug: (id: string) => void
onDelete?: (id: string) => void
userId?: string
}
export function PromptDetailModal({
@ -30,11 +34,16 @@ export function PromptDetailModal({
isOpen,
onClose,
onEdit,
onDebug
onDebug,
onDelete,
userId
}: PromptDetailModalProps) {
const t = useTranslations('studio')
const tCommon = useTranslations('common')
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
const [isDeleting, setIsDeleting] = useState(false)
const formatDate = (dateString: string) => {
return new Intl.DateTimeFormat('default', {
year: 'numeric',
@ -45,6 +54,29 @@ export function PromptDetailModal({
}).format(new Date(dateString))
}
const handleDelete = async () => {
if (!prompt || !userId || !onDelete) return
setIsDeleting(true)
try {
const response = await fetch(`/api/prompts/${prompt.id}?userId=${userId}`, {
method: 'DELETE'
})
if (response.ok) {
onDelete(prompt.id)
setIsDeleteDialogOpen(false)
onClose()
} else {
console.error('Failed to delete prompt')
}
} catch (error) {
console.error('Error deleting prompt:', error)
} finally {
setIsDeleting(false)
}
}
if (!isOpen || !prompt) return null
return (
@ -147,36 +179,61 @@ export function PromptDetailModal({
</div>
{/* Footer Actions */}
<div className="flex items-center justify-end space-x-3 p-6 border-t border-border">
<Button
variant="outline"
onClick={onClose}
>
{tCommon('close')}
</Button>
<Button
variant="outline"
onClick={() => {
onEdit(prompt.id)
onClose()
}}
className="flex items-center space-x-2"
>
<Edit className="h-4 w-4" />
<span>{tCommon('edit')}</span>
</Button>
<Button
onClick={() => {
onDebug(prompt.id)
onClose()
}}
className="flex items-center space-x-2"
>
<Play className="h-4 w-4" />
<span>{t('debugPrompt')}</span>
</Button>
<div className="flex items-center justify-between p-6 border-t border-border">
{/* Delete button on the left */}
{onDelete && userId && (
<Button
variant="outline"
onClick={() => setIsDeleteDialogOpen(true)}
className="flex items-center space-x-2 text-destructive hover:text-destructive hover:bg-destructive/10"
>
<Trash2 className="h-4 w-4" />
<span>{tCommon('delete')}</span>
</Button>
)}
{/* Other actions on the right */}
<div className="flex items-center space-x-3 ml-auto">
<Button
variant="outline"
onClick={onClose}
>
{tCommon('close')}
</Button>
<Button
variant="outline"
onClick={() => {
onEdit(prompt.id)
onClose()
}}
className="flex items-center space-x-2"
>
<Edit className="h-4 w-4" />
<span>{tCommon('edit')}</span>
</Button>
<Button
onClick={() => {
onDebug(prompt.id)
onClose()
}}
className="flex items-center space-x-2"
>
<Play className="h-4 w-4" />
<span>{t('debugPrompt')}</span>
</Button>
</div>
</div>
</div>
{/* Delete Confirmation Dialog */}
<DeleteConfirmationDialog
isOpen={isDeleteDialogOpen}
onClose={() => setIsDeleteDialogOpen(false)}
onConfirm={handleDelete}
title={t('deletePrompt')}
itemName={prompt?.name}
isDeleting={isDeleting}
/>
</div>
)
}

View File

@ -0,0 +1,106 @@
'use client'
import { useTranslations } from 'next-intl'
import { Button } from '@/components/ui/button'
import { LoadingSpinner } from '@/components/ui/loading-spinner'
import { AlertTriangle, X } from 'lucide-react'
interface DeleteConfirmationDialogProps {
isOpen: boolean
onClose: () => void
onConfirm: () => void
title: string
description?: string
itemName?: string
isDeleting?: boolean
}
export function DeleteConfirmationDialog({
isOpen,
onClose,
onConfirm,
title,
description,
itemName,
isDeleting = false
}: DeleteConfirmationDialogProps) {
const t = useTranslations('studio')
const tCommon = useTranslations('common')
if (!isOpen) return null
return (
<div className="fixed inset-0 z-50 flex items-center justify-center">
{/* Backdrop */}
<div
className="absolute inset-0 bg-black/50"
onClick={onClose}
/>
{/* Dialog */}
<div className="relative bg-background border border-border rounded-lg shadow-lg max-w-md w-full mx-4">
{/* Header */}
<div className="flex items-center justify-between p-6 border-b border-border">
<div className="flex items-center space-x-3">
<div className="flex-shrink-0 w-10 h-10 bg-destructive/10 rounded-full flex items-center justify-center">
<AlertTriangle className="h-5 w-5 text-destructive" />
</div>
<h2 className="text-lg font-semibold text-foreground">
{title}
</h2>
</div>
<Button
variant="ghost"
size="sm"
onClick={onClose}
className="h-8 w-8 p-0"
disabled={isDeleting}
>
<X className="h-4 w-4" />
</Button>
</div>
{/* Content */}
<div className="p-6 space-y-4">
<div className="text-sm text-muted-foreground">
{description || t('confirmDelete')}
</div>
{itemName && (
<div className="bg-muted rounded-lg p-3">
<div className="text-sm font-medium text-foreground">
{itemName}
</div>
</div>
)}
<div className="text-sm text-destructive font-medium">
{t('deleteWarning')}
</div>
</div>
{/* Footer */}
<div className="flex items-center justify-end space-x-3 p-6 border-t border-border">
<Button
variant="outline"
onClick={onClose}
disabled={isDeleting}
>
{tCommon('cancel')}
</Button>
<Button
variant="destructive"
onClick={onConfirm}
disabled={isDeleting}
className="flex items-center space-x-2"
>
{isDeleting ? (
<LoadingSpinner size="sm" />
) : null}
<span>{tCommon('delete')}</span>
</Button>
</div>
</div>
</div>
)
}