add delete
This commit is contained in:
parent
19661710c1
commit
6fecf02a46
@ -198,6 +198,11 @@ export default function StudioPage() {
|
|||||||
router.push(`/studio/${id}`)
|
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') => {
|
const handleUpdatePermissions = async (promptId: string, newPermissions: 'private' | 'public') => {
|
||||||
if (!user) return
|
if (!user) return
|
||||||
|
|
||||||
@ -753,6 +758,7 @@ export default function StudioPage() {
|
|||||||
isOpen={isEditModalOpen}
|
isOpen={isEditModalOpen}
|
||||||
onClose={handleEditModalClose}
|
onClose={handleEditModalClose}
|
||||||
onSave={handleEditModalSave}
|
onSave={handleEditModalSave}
|
||||||
|
onDelete={handleDeletePrompt}
|
||||||
userId={user?.id || ''}
|
userId={user?.id || ''}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -764,6 +770,8 @@ export default function StudioPage() {
|
|||||||
onClose={handleDetailModalClose}
|
onClose={handleDetailModalClose}
|
||||||
onEdit={handleEditPrompt}
|
onEdit={handleEditPrompt}
|
||||||
onDebug={handleDebugPrompt}
|
onDebug={handleDebugPrompt}
|
||||||
|
onDelete={handleDeletePrompt}
|
||||||
|
userId={user?.id}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Bulk Add Modal */}
|
{/* Bulk Add Modal */}
|
||||||
|
@ -7,11 +7,13 @@ import { Input } from '@/components/ui/input'
|
|||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { Textarea } from '@/components/ui/textarea'
|
import { Textarea } from '@/components/ui/textarea'
|
||||||
import { LoadingSpinner } from '@/components/ui/loading-spinner'
|
import { LoadingSpinner } from '@/components/ui/loading-spinner'
|
||||||
|
import { DeleteConfirmationDialog } from '@/components/ui/delete-confirmation-dialog'
|
||||||
import {
|
import {
|
||||||
X,
|
X,
|
||||||
Save,
|
Save,
|
||||||
Tag,
|
Tag,
|
||||||
Plus
|
Plus,
|
||||||
|
Trash2
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
|
|
||||||
interface Prompt {
|
interface Prompt {
|
||||||
@ -29,6 +31,7 @@ interface EditPromptModalProps {
|
|||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onSave: (updatedPrompt: Prompt) => void
|
onSave: (updatedPrompt: Prompt) => void
|
||||||
|
onDelete?: (id: string) => void
|
||||||
userId: string
|
userId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +40,7 @@ export function EditPromptModal({
|
|||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
onSave,
|
onSave,
|
||||||
|
onDelete,
|
||||||
userId
|
userId
|
||||||
}: EditPromptModalProps) {
|
}: EditPromptModalProps) {
|
||||||
const t = useTranslations('studio')
|
const t = useTranslations('studio')
|
||||||
@ -48,6 +52,8 @@ export function EditPromptModal({
|
|||||||
const [newTag, setNewTag] = useState('')
|
const [newTag, setNewTag] = useState('')
|
||||||
const [isSaving, setIsSaving] = useState(false)
|
const [isSaving, setIsSaving] = useState(false)
|
||||||
const [availableTags, setAvailableTags] = useState<string[]>([])
|
const [availableTags, setAvailableTags] = useState<string[]>([])
|
||||||
|
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
|
||||||
|
const [isDeleting, setIsDeleting] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
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) => {
|
const handleKeyPress = (e: React.KeyboardEvent) => {
|
||||||
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
|
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
|
||||||
handleSave()
|
handleSave()
|
||||||
@ -258,28 +287,54 @@ export function EditPromptModal({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="flex items-center justify-end space-x-2 p-6 border-t border-border">
|
<div className="flex items-center justify-between p-6 border-t border-border">
|
||||||
<Button
|
{/* Delete button on the left */}
|
||||||
variant="outline"
|
{onDelete && (
|
||||||
onClick={onClose}
|
<Button
|
||||||
disabled={isSaving}
|
variant="outline"
|
||||||
>
|
onClick={() => setIsDeleteDialogOpen(true)}
|
||||||
{tCommon('cancel')}
|
disabled={isSaving || isDeleting}
|
||||||
</Button>
|
className="flex items-center space-x-2 text-destructive hover:text-destructive hover:bg-destructive/10"
|
||||||
<Button
|
>
|
||||||
onClick={handleSave}
|
<Trash2 className="h-4 w-4" />
|
||||||
disabled={!name.trim() || isSaving}
|
<span>{tCommon('delete')}</span>
|
||||||
className="flex items-center space-x-2"
|
</Button>
|
||||||
>
|
)}
|
||||||
{isSaving ? (
|
|
||||||
<LoadingSpinner size="sm" />
|
{/* Save and Cancel buttons on the right */}
|
||||||
) : (
|
<div className="flex items-center space-x-2 ml-auto">
|
||||||
<Save className="h-4 w-4" />
|
<Button
|
||||||
)}
|
variant="outline"
|
||||||
<span>{tCommon('save')}</span>
|
onClick={onClose}
|
||||||
</Button>
|
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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Delete Confirmation Dialog */}
|
||||||
|
<DeleteConfirmationDialog
|
||||||
|
isOpen={isDeleteDialogOpen}
|
||||||
|
onClose={() => setIsDeleteDialogOpen(false)}
|
||||||
|
onConfirm={handleDelete}
|
||||||
|
title={t('deletePrompt')}
|
||||||
|
itemName={prompt.name}
|
||||||
|
isDeleting={isDeleting}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
|
import { useState } from 'react'
|
||||||
import { useTranslations } from 'next-intl'
|
import { useTranslations } from 'next-intl'
|
||||||
import { Button } from '@/components/ui/button'
|
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 {
|
interface Prompt {
|
||||||
id: string
|
id: string
|
||||||
@ -23,6 +25,8 @@ interface PromptDetailModalProps {
|
|||||||
onClose: () => void
|
onClose: () => void
|
||||||
onEdit: (id: string) => void
|
onEdit: (id: string) => void
|
||||||
onDebug: (id: string) => void
|
onDebug: (id: string) => void
|
||||||
|
onDelete?: (id: string) => void
|
||||||
|
userId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PromptDetailModal({
|
export function PromptDetailModal({
|
||||||
@ -30,11 +34,16 @@ export function PromptDetailModal({
|
|||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
onEdit,
|
onEdit,
|
||||||
onDebug
|
onDebug,
|
||||||
|
onDelete,
|
||||||
|
userId
|
||||||
}: PromptDetailModalProps) {
|
}: PromptDetailModalProps) {
|
||||||
const t = useTranslations('studio')
|
const t = useTranslations('studio')
|
||||||
const tCommon = useTranslations('common')
|
const tCommon = useTranslations('common')
|
||||||
|
|
||||||
|
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
|
||||||
|
const [isDeleting, setIsDeleting] = useState(false)
|
||||||
|
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string) => {
|
||||||
return new Intl.DateTimeFormat('default', {
|
return new Intl.DateTimeFormat('default', {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
@ -45,6 +54,29 @@ export function PromptDetailModal({
|
|||||||
}).format(new Date(dateString))
|
}).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
|
if (!isOpen || !prompt) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -147,36 +179,61 @@ export function PromptDetailModal({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer Actions */}
|
{/* Footer Actions */}
|
||||||
<div className="flex items-center justify-end space-x-3 p-6 border-t border-border">
|
<div className="flex items-center justify-between p-6 border-t border-border">
|
||||||
<Button
|
{/* Delete button on the left */}
|
||||||
variant="outline"
|
{onDelete && userId && (
|
||||||
onClick={onClose}
|
<Button
|
||||||
>
|
variant="outline"
|
||||||
{tCommon('close')}
|
onClick={() => setIsDeleteDialogOpen(true)}
|
||||||
</Button>
|
className="flex items-center space-x-2 text-destructive hover:text-destructive hover:bg-destructive/10"
|
||||||
<Button
|
>
|
||||||
variant="outline"
|
<Trash2 className="h-4 w-4" />
|
||||||
onClick={() => {
|
<span>{tCommon('delete')}</span>
|
||||||
onEdit(prompt.id)
|
</Button>
|
||||||
onClose()
|
)}
|
||||||
}}
|
|
||||||
className="flex items-center space-x-2"
|
{/* Other actions on the right */}
|
||||||
>
|
<div className="flex items-center space-x-3 ml-auto">
|
||||||
<Edit className="h-4 w-4" />
|
<Button
|
||||||
<span>{tCommon('edit')}</span>
|
variant="outline"
|
||||||
</Button>
|
onClick={onClose}
|
||||||
<Button
|
>
|
||||||
onClick={() => {
|
{tCommon('close')}
|
||||||
onDebug(prompt.id)
|
</Button>
|
||||||
onClose()
|
<Button
|
||||||
}}
|
variant="outline"
|
||||||
className="flex items-center space-x-2"
|
onClick={() => {
|
||||||
>
|
onEdit(prompt.id)
|
||||||
<Play className="h-4 w-4" />
|
onClose()
|
||||||
<span>{t('debugPrompt')}</span>
|
}}
|
||||||
</Button>
|
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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Delete Confirmation Dialog */}
|
||||||
|
<DeleteConfirmationDialog
|
||||||
|
isOpen={isDeleteDialogOpen}
|
||||||
|
onClose={() => setIsDeleteDialogOpen(false)}
|
||||||
|
onConfirm={handleDelete}
|
||||||
|
title={t('deletePrompt')}
|
||||||
|
itemName={prompt?.name}
|
||||||
|
isDeleting={isDeleting}
|
||||||
|
/>
|
||||||
</div>
|
</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