add delete
This commit is contained in:
parent
19661710c1
commit
6fecf02a46
@ -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 */}
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
106
src/components/ui/delete-confirmation-dialog.tsx
Normal file
106
src/components/ui/delete-confirmation-dialog.tsx
Normal 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>
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user