put delete actions behind a kebab menu to prevent accidental deletion
Replace the always-visible delete buttons on the movie detail modal and TV series header with a ⋮ kebab menu. Selecting "Delete" from the menu shows an inline confirmation banner before any action is taken. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,14 +15,19 @@ interface Props {
|
|||||||
|
|
||||||
export default function MovieDetailModal({ movie, libraryId, onClose, onTagsChanged, onDeleted }: Props) {
|
export default function MovieDetailModal({ movie, libraryId, onClose, onTagsChanged, onDeleted }: Props) {
|
||||||
const overlayRef = useRef<HTMLDivElement>(null)
|
const overlayRef = useRef<HTMLDivElement>(null)
|
||||||
|
const menuRef = useRef<HTMLDivElement>(null)
|
||||||
const [playing, setPlaying] = useState(false)
|
const [playing, setPlaying] = useState(false)
|
||||||
|
const [menuOpen, setMenuOpen] = useState(false)
|
||||||
const [confirming, setConfirming] = useState(false)
|
const [confirming, setConfirming] = useState(false)
|
||||||
const [deleting, setDeleting] = useState(false)
|
const [deleting, setDeleting] = useState(false)
|
||||||
const cancelRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKey = (e: KeyboardEvent) => {
|
const handleKey = (e: KeyboardEvent) => {
|
||||||
if (e.key === 'Escape') onClose()
|
if (e.key === 'Escape') {
|
||||||
|
if (menuOpen) { setMenuOpen(false); return }
|
||||||
|
if (confirming) { setConfirming(false); return }
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
document.addEventListener('keydown', handleKey)
|
document.addEventListener('keydown', handleKey)
|
||||||
document.body.style.overflow = 'hidden'
|
document.body.style.overflow = 'hidden'
|
||||||
@@ -30,7 +35,19 @@ export default function MovieDetailModal({ movie, libraryId, onClose, onTagsChan
|
|||||||
document.removeEventListener('keydown', handleKey)
|
document.removeEventListener('keydown', handleKey)
|
||||||
document.body.style.overflow = ''
|
document.body.style.overflow = ''
|
||||||
}
|
}
|
||||||
}, [onClose])
|
}, [onClose, menuOpen, confirming])
|
||||||
|
|
||||||
|
// Close menu on outside click
|
||||||
|
useEffect(() => {
|
||||||
|
if (!menuOpen) return
|
||||||
|
const handler = (e: MouseEvent) => {
|
||||||
|
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
|
||||||
|
setMenuOpen(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('mousedown', handler)
|
||||||
|
return () => document.removeEventListener('mousedown', handler)
|
||||||
|
}, [menuOpen])
|
||||||
|
|
||||||
const handleOverlayClick = (e: React.MouseEvent) => {
|
const handleOverlayClick = (e: React.MouseEvent) => {
|
||||||
if (e.target === overlayRef.current) onClose()
|
if (e.target === overlayRef.current) onClose()
|
||||||
@@ -38,13 +55,7 @@ export default function MovieDetailModal({ movie, libraryId, onClose, onTagsChan
|
|||||||
|
|
||||||
const videoUrl = `/api/file?libraryId=${encodeURIComponent(libraryId)}&path=${encodeURIComponent(movie.videoPath)}`
|
const videoUrl = `/api/file?libraryId=${encodeURIComponent(libraryId)}&path=${encodeURIComponent(movie.videoPath)}`
|
||||||
|
|
||||||
const handleDeleteClick = () => {
|
const handleConfirmDelete = () => {
|
||||||
if (!confirming) {
|
|
||||||
setConfirming(true)
|
|
||||||
cancelRef.current = setTimeout(() => setConfirming(false), 4000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (cancelRef.current) clearTimeout(cancelRef.current)
|
|
||||||
setDeleting(true)
|
setDeleting(true)
|
||||||
fetch(`/api/movies?libraryId=${encodeURIComponent(libraryId)}&movieId=${encodeURIComponent(movie.id)}`, {
|
fetch(`/api/movies?libraryId=${encodeURIComponent(libraryId)}&movieId=${encodeURIComponent(movie.id)}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
@@ -53,11 +64,6 @@ export default function MovieDetailModal({ movie, libraryId, onClose, onTagsChan
|
|||||||
.catch(() => setDeleting(false))
|
.catch(() => setDeleting(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCancelDelete = () => {
|
|
||||||
if (cancelRef.current) clearTimeout(cancelRef.current)
|
|
||||||
setConfirming(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (playing) {
|
if (playing) {
|
||||||
return <VideoPlayerModal url={videoUrl} name={movie.title} onClose={() => setPlaying(false)} />
|
return <VideoPlayerModal url={videoUrl} name={movie.title} onClose={() => setPlaying(false)} />
|
||||||
}
|
}
|
||||||
@@ -103,8 +109,9 @@ export default function MovieDetailModal({ movie, libraryId, onClose, onTagsChan
|
|||||||
|
|
||||||
{/* Info */}
|
{/* Info */}
|
||||||
<div className="p-5">
|
<div className="p-5">
|
||||||
<div className="flex items-start justify-between gap-3 mb-1">
|
{/* Title row with kebab menu */}
|
||||||
<h2 className="text-lg font-semibold" style={{ color: 'var(--text-primary)' }}>
|
<div className="flex items-start gap-2 mb-1">
|
||||||
|
<h2 className="text-lg font-semibold flex-1 min-w-0" style={{ color: 'var(--text-primary)' }}>
|
||||||
{movie.title}
|
{movie.title}
|
||||||
</h2>
|
</h2>
|
||||||
{movie.year && (
|
{movie.year && (
|
||||||
@@ -112,6 +119,35 @@ export default function MovieDetailModal({ movie, libraryId, onClose, onTagsChan
|
|||||||
{movie.year}
|
{movie.year}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{/* Kebab menu */}
|
||||||
|
<div className="relative flex-shrink-0" ref={menuRef}>
|
||||||
|
<button
|
||||||
|
onClick={() => { setMenuOpen((o) => !o); setConfirming(false) }}
|
||||||
|
className="w-7 h-7 rounded-lg flex items-center justify-center transition-colors"
|
||||||
|
style={{ color: 'var(--text-secondary)', backgroundColor: 'transparent' }}
|
||||||
|
onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'var(--border)')}
|
||||||
|
onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'transparent')}
|
||||||
|
aria-label="More options"
|
||||||
|
>
|
||||||
|
⋮
|
||||||
|
</button>
|
||||||
|
{menuOpen && (
|
||||||
|
<div
|
||||||
|
className="absolute right-0 top-full mt-1 rounded-lg shadow-lg overflow-hidden z-20 min-w-max"
|
||||||
|
style={{ backgroundColor: 'var(--surface)', border: '1px solid var(--border)' }}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() => { setMenuOpen(false); setConfirming(true) }}
|
||||||
|
className="flex items-center gap-2 w-full px-4 py-2 text-sm text-left transition-colors"
|
||||||
|
style={{ color: '#fca5a5' }}
|
||||||
|
onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'var(--border)')}
|
||||||
|
onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'transparent')}
|
||||||
|
>
|
||||||
|
Delete movie
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Meta row */}
|
{/* Meta row */}
|
||||||
@@ -141,6 +177,37 @@ export default function MovieDetailModal({ movie, libraryId, onClose, onTagsChan
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Confirmation banner */}
|
||||||
|
{confirming && (
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-3 mb-4 px-3 py-2.5 rounded-lg text-sm"
|
||||||
|
style={{ backgroundColor: '#7f1d1d33', border: '1px solid #7f1d1d' }}
|
||||||
|
>
|
||||||
|
<p className="flex-1 text-xs" style={{ color: '#fca5a5' }}>
|
||||||
|
Permanently delete this movie and all its files?
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={() => setConfirming(false)}
|
||||||
|
className="text-xs px-2 py-1 rounded flex-shrink-0 transition-colors"
|
||||||
|
style={{ color: 'var(--text-secondary)', backgroundColor: 'var(--border)' }}
|
||||||
|
onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.color = 'var(--text-primary)')}
|
||||||
|
onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.color = 'var(--text-secondary)')}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleConfirmDelete}
|
||||||
|
disabled={deleting}
|
||||||
|
className="text-xs px-2 py-1 rounded flex-shrink-0 transition-colors disabled:opacity-50"
|
||||||
|
style={{ backgroundColor: '#7f1d1d', color: '#fca5a5' }}
|
||||||
|
onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = '#991b1b')}
|
||||||
|
onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = '#7f1d1d')}
|
||||||
|
>
|
||||||
|
{deleting ? 'Deleting…' : 'Yes, delete'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Play button */}
|
{/* Play button */}
|
||||||
<button
|
<button
|
||||||
onClick={() => setPlaying(true)}
|
onClick={() => setPlaying(true)}
|
||||||
@@ -160,47 +227,6 @@ export default function MovieDetailModal({ movie, libraryId, onClose, onTagsChan
|
|||||||
</p>
|
</p>
|
||||||
<TagSelector mediaKey={`${libraryId}:${movie.id}`} onTagsChanged={onTagsChanged} />
|
<TagSelector mediaKey={`${libraryId}:${movie.id}`} onTagsChanged={onTagsChanged} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Delete */}
|
|
||||||
<div className="mt-4 pt-4 flex items-center gap-2" style={{ borderTop: '1px solid var(--border)' }}>
|
|
||||||
{confirming && (
|
|
||||||
<p className="text-xs flex-1" style={{ color: 'var(--text-secondary)' }}>
|
|
||||||
This will permanently delete the folder and all its contents.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{confirming && (
|
|
||||||
<button
|
|
||||||
onClick={handleCancelDelete}
|
|
||||||
className="text-xs px-2.5 py-1.5 rounded-lg flex-shrink-0"
|
|
||||||
style={{ backgroundColor: 'var(--border)', color: 'var(--text-secondary)' }}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
onClick={handleDeleteClick}
|
|
||||||
disabled={deleting}
|
|
||||||
className="text-xs px-2.5 py-1.5 rounded-lg flex-shrink-0 transition-colors disabled:opacity-50 ml-auto"
|
|
||||||
style={{
|
|
||||||
backgroundColor: confirming ? '#7f1d1d' : 'var(--border)',
|
|
||||||
color: confirming ? '#fca5a5' : 'var(--text-secondary)',
|
|
||||||
}}
|
|
||||||
onMouseEnter={(e) => {
|
|
||||||
if (!confirming) {
|
|
||||||
;(e.currentTarget as HTMLElement).style.backgroundColor = '#7f1d1d'
|
|
||||||
;(e.currentTarget as HTMLElement).style.color = '#fca5a5'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onMouseLeave={(e) => {
|
|
||||||
if (!confirming) {
|
|
||||||
;(e.currentTarget as HTMLElement).style.backgroundColor = 'var(--border)'
|
|
||||||
;(e.currentTarget as HTMLElement).style.color = 'var(--text-secondary)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{deleting ? 'Deleting…' : confirming ? 'Confirm delete?' : 'Delete movie'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect, useState, useCallback } from 'react'
|
import { useEffect, useRef, useState, useCallback } from 'react'
|
||||||
import type { TvSeries, TvSeason, TvEpisode } from '@/types'
|
import type { TvSeries, TvSeason, TvEpisode } from '@/types'
|
||||||
import FilterPanel from '@/components/FilterPanel'
|
import FilterPanel from '@/components/FilterPanel'
|
||||||
import VideoPlayerModal from '@/components/mixed/VideoPlayerModal'
|
import VideoPlayerModal from '@/components/mixed/VideoPlayerModal'
|
||||||
@@ -26,8 +26,10 @@ export default function TvView({ libraryId }: Props) {
|
|||||||
const [selectedTagIds, setSelectedTagIds] = useState<Set<string>>(new Set())
|
const [selectedTagIds, setSelectedTagIds] = useState<Set<string>>(new Set())
|
||||||
const [assignments, setAssignments] = useState<Record<string, string[]>>({})
|
const [assignments, setAssignments] = useState<Record<string, string[]>>({})
|
||||||
const [filterRefreshKey, setFilterRefreshKey] = useState(0)
|
const [filterRefreshKey, setFilterRefreshKey] = useState(0)
|
||||||
|
const [menuOpen, setMenuOpen] = useState(false)
|
||||||
const [confirming, setConfirming] = useState(false)
|
const [confirming, setConfirming] = useState(false)
|
||||||
const [deleting, setDeleting] = useState(false)
|
const [deleting, setDeleting] = useState(false)
|
||||||
|
const menuRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
const toggleTag = (tagId: string) =>
|
const toggleTag = (tagId: string) =>
|
||||||
setSelectedTagIds((prev) => {
|
setSelectedTagIds((prev) => {
|
||||||
@@ -80,10 +82,23 @@ export default function TvView({ libraryId }: Props) {
|
|||||||
.catch(() => { setError('Failed to load episodes'); setLoading(false) })
|
.catch(() => { setError('Failed to load episodes'); setLoading(false) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close menu on outside click
|
||||||
|
useEffect(() => {
|
||||||
|
if (!menuOpen) return
|
||||||
|
const handler = (e: MouseEvent) => {
|
||||||
|
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
|
||||||
|
setMenuOpen(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('mousedown', handler)
|
||||||
|
return () => document.removeEventListener('mousedown', handler)
|
||||||
|
}, [menuOpen])
|
||||||
|
|
||||||
const goToSeries = () => {
|
const goToSeries = () => {
|
||||||
setView('series')
|
setView('series')
|
||||||
setSelectedSeries(null)
|
setSelectedSeries(null)
|
||||||
setSelectedSeason(null)
|
setSelectedSeason(null)
|
||||||
|
setMenuOpen(false)
|
||||||
setConfirming(false)
|
setConfirming(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,11 +110,6 @@ export default function TvView({ libraryId }: Props) {
|
|||||||
|
|
||||||
const handleDeleteSeries = () => {
|
const handleDeleteSeries = () => {
|
||||||
if (!selectedSeries) return
|
if (!selectedSeries) return
|
||||||
if (!confirming) {
|
|
||||||
setConfirming(true)
|
|
||||||
setTimeout(() => setConfirming(false), 4000)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
setDeleting(true)
|
setDeleting(true)
|
||||||
fetch(
|
fetch(
|
||||||
`/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(selectedSeries.id)}`,
|
`/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(selectedSeries.id)}`,
|
||||||
@@ -235,13 +245,45 @@ export default function TvView({ libraryId }: Props) {
|
|||||||
{view === 'seasons' && selectedSeries && (
|
{view === 'seasons' && selectedSeries && (
|
||||||
<div>
|
<div>
|
||||||
{/* Series info header */}
|
{/* Series info header */}
|
||||||
<div className="flex items-start gap-4 mb-6 p-4 rounded-xl" style={{ backgroundColor: 'var(--surface)', border: '1px solid var(--border)' }}>
|
<div className="mb-6 p-4 rounded-xl" style={{ backgroundColor: 'var(--surface)', border: '1px solid var(--border)' }}>
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
{selectedSeries.posterUrl && (
|
{selectedSeries.posterUrl && (
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
<img src={selectedSeries.posterUrl} alt={selectedSeries.title} className="w-16 rounded-lg object-cover flex-shrink-0" style={{ aspectRatio: '2/3' }} />
|
<img src={selectedSeries.posterUrl} alt={selectedSeries.title} className="w-16 rounded-lg object-cover flex-shrink-0" style={{ aspectRatio: '2/3' }} />
|
||||||
)}
|
)}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<h2 className="text-lg font-semibold" style={{ color: 'var(--text-primary)' }}>{selectedSeries.title}</h2>
|
<div className="flex items-start gap-2">
|
||||||
|
<h2 className="text-lg font-semibold flex-1 min-w-0" style={{ color: 'var(--text-primary)' }}>{selectedSeries.title}</h2>
|
||||||
|
{/* Kebab menu */}
|
||||||
|
<div className="relative flex-shrink-0" ref={menuRef}>
|
||||||
|
<button
|
||||||
|
onClick={() => { setMenuOpen((o) => !o); setConfirming(false) }}
|
||||||
|
className="w-7 h-7 rounded-lg flex items-center justify-center transition-colors"
|
||||||
|
style={{ color: 'var(--text-secondary)', backgroundColor: 'transparent' }}
|
||||||
|
onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'var(--border)')}
|
||||||
|
onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'transparent')}
|
||||||
|
aria-label="More options"
|
||||||
|
>
|
||||||
|
⋮
|
||||||
|
</button>
|
||||||
|
{menuOpen && (
|
||||||
|
<div
|
||||||
|
className="absolute right-0 top-full mt-1 rounded-lg shadow-lg overflow-hidden z-20 min-w-max"
|
||||||
|
style={{ backgroundColor: 'var(--surface)', border: '1px solid var(--border)' }}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() => { setMenuOpen(false); setConfirming(true) }}
|
||||||
|
className="flex items-center gap-2 w-full px-4 py-2 text-sm text-left transition-colors"
|
||||||
|
style={{ color: '#fca5a5' }}
|
||||||
|
onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'var(--border)')}
|
||||||
|
onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = 'transparent')}
|
||||||
|
>
|
||||||
|
Delete series
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{(selectedSeries.year || selectedSeries.genres.length > 0) && (
|
{(selectedSeries.year || selectedSeries.genres.length > 0) && (
|
||||||
<div className="flex flex-wrap items-center gap-2 mt-1">
|
<div className="flex flex-wrap items-center gap-2 mt-1">
|
||||||
{selectedSeries.year && <span className="text-xs" style={{ color: 'var(--text-secondary)' }}>{selectedSeries.year}</span>}
|
{selectedSeries.year && <span className="text-xs" style={{ color: 'var(--text-secondary)' }}>{selectedSeries.year}</span>}
|
||||||
@@ -254,42 +296,37 @@ export default function TvView({ libraryId }: Props) {
|
|||||||
<p className="text-sm mt-2 line-clamp-3" style={{ color: 'var(--text-secondary)' }}>{selectedSeries.plot}</p>
|
<p className="text-sm mt-2 line-clamp-3" style={{ color: 'var(--text-secondary)' }}>{selectedSeries.plot}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* Delete series button */}
|
</div>
|
||||||
<div className="flex items-center gap-2 flex-shrink-0">
|
{/* Confirmation banner */}
|
||||||
{confirming && (
|
{confirming && (
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-3 mt-3 px-3 py-2.5 rounded-lg"
|
||||||
|
style={{ backgroundColor: '#7f1d1d33', border: '1px solid #7f1d1d' }}
|
||||||
|
>
|
||||||
|
<p className="flex-1 text-xs" style={{ color: '#fca5a5' }}>
|
||||||
|
Permanently delete this series and all its files?
|
||||||
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => setConfirming(false)}
|
onClick={() => setConfirming(false)}
|
||||||
className="text-xs px-2.5 py-1.5 rounded-lg"
|
className="text-xs px-2 py-1 rounded flex-shrink-0 transition-colors"
|
||||||
style={{ backgroundColor: 'var(--border)', color: 'var(--text-secondary)' }}
|
style={{ color: 'var(--text-secondary)', backgroundColor: 'var(--border)' }}
|
||||||
|
onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.color = 'var(--text-primary)')}
|
||||||
|
onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.color = 'var(--text-secondary)')}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
)}
|
|
||||||
<button
|
<button
|
||||||
onClick={handleDeleteSeries}
|
onClick={handleDeleteSeries}
|
||||||
disabled={deleting}
|
disabled={deleting}
|
||||||
className="text-xs px-2.5 py-1.5 rounded-lg transition-colors disabled:opacity-50"
|
className="text-xs px-2 py-1 rounded flex-shrink-0 transition-colors disabled:opacity-50"
|
||||||
style={{
|
style={{ backgroundColor: '#7f1d1d', color: '#fca5a5' }}
|
||||||
backgroundColor: confirming ? '#7f1d1d' : 'var(--border)',
|
onMouseEnter={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = '#991b1b')}
|
||||||
color: confirming ? '#fca5a5' : 'var(--text-secondary)',
|
onMouseLeave={(e) => ((e.currentTarget as HTMLElement).style.backgroundColor = '#7f1d1d')}
|
||||||
}}
|
|
||||||
onMouseEnter={(e) => {
|
|
||||||
if (!confirming) {
|
|
||||||
;(e.currentTarget as HTMLElement).style.backgroundColor = '#7f1d1d'
|
|
||||||
;(e.currentTarget as HTMLElement).style.color = '#fca5a5'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onMouseLeave={(e) => {
|
|
||||||
if (!confirming) {
|
|
||||||
;(e.currentTarget as HTMLElement).style.backgroundColor = 'var(--border)'
|
|
||||||
;(e.currentTarget as HTMLElement).style.color = 'var(--text-secondary)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
title={confirming ? 'Click again to permanently delete this series and all its files' : 'Delete this series'}
|
|
||||||
>
|
>
|
||||||
{deleting ? 'Deleting…' : confirming ? 'Confirm delete?' : 'Delete series'}
|
{deleting ? 'Deleting…' : 'Yes, delete'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user