feat: Add delete collection modal

This commit is contained in:
Peifan Li
2025-03-12 21:59:25 -04:00
parent 33b3c00d1e
commit 8f6d9a6e9a
6 changed files with 167 additions and 11 deletions

View File

@@ -1413,3 +1413,57 @@ body {
.remove-from-collection:hover {
background-color: var(--primary-hover) !important;
}
/* Delete Collection Modal */
.modal-buttons {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 20px;
}
.modal-button {
padding: 10px 15px;
border-radius: var(--border-radius);
border: none;
cursor: pointer;
font-weight: 500;
transition: all 0.2s ease;
}
.modal-button.delete-collection-only {
background-color: #4a4a4a;
color: white;
}
.modal-button.delete-collection-only:hover {
background-color: #5a5a5a;
}
.modal-button.delete-all {
background-color: #d32f2f;
color: white;
}
.modal-button.delete-all:hover {
background-color: #f44336;
}
.modal-button.cancel {
background-color: transparent;
border: 1px solid var(--border-color);
color: var(--text-color);
}
.modal-button.cancel:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.modal-button.danger {
background-color: #d32f2f;
color: white;
}
.modal-button.danger:hover {
background-color: #f44336;
}

View File

@@ -319,10 +319,12 @@ function App() {
setVideos(prevVideos => prevVideos.filter(video => video.id !== id));
setLoading(false);
return { success: true };
} catch (error) {
console.error('Error deleting video:', error);
setError('Failed to delete video');
setLoading(false);
return { success: false, error: 'Failed to delete video' };
}
};
@@ -448,13 +450,31 @@ function App() {
};
// Delete a collection
const handleDeleteCollection = async (collectionId) => {
const handleDeleteCollection = async (collectionId, deleteVideos = false) => {
try {
// Confirm deletion
if (!window.confirm('Are you sure you want to delete this collection?')) {
// Get the collection to be deleted
const collectionToDelete = collections.find(c => c.id === collectionId);
if (!collectionToDelete) {
console.error('Collection not found');
return false;
}
// If deleteVideos is true, delete all videos in the collection
if (deleteVideos && collectionToDelete.videos.length > 0) {
// Delete each video in the collection
for (const videoId of collectionToDelete.videos) {
try {
await axios.delete(`${API_URL}/videos/${videoId}`);
// Update the videos state
setVideos(prevVideos => prevVideos.filter(video => video.id !== videoId));
} catch (videoError) {
console.error(`Error deleting video ${videoId}:`, videoError);
// Continue with other videos even if one fails
}
}
}
// Delete the collection
await axios.delete(`${API_URL}/collections/${collectionId}`);

View File

@@ -0,0 +1,50 @@
import React from 'react';
const DeleteCollectionModal = ({
isOpen,
onClose,
onDeleteCollectionOnly,
onDeleteCollectionAndVideos,
collectionName,
videoCount
}) => {
if (!isOpen) return null;
return (
<div className="modal-overlay">
<div className="modal-content">
<h2>Delete Collection</h2>
<p>
Are you sure you want to delete the collection "{collectionName}"?
</p>
<p>
This collection contains {videoCount} video{videoCount !== 1 ? 's' : ''}.
</p>
<div className="modal-buttons">
<button
className="modal-button delete-collection-only"
onClick={onDeleteCollectionOnly}
>
Delete Collection Only
</button>
{videoCount > 0 && (
<button
className="modal-button delete-all danger"
onClick={onDeleteCollectionAndVideos}
>
Delete Collection and All Videos
</button>
)}
<button
className="modal-button cancel"
onClick={onClose}
>
Cancel
</button>
</div>
</div>
</div>
);
};
export default DeleteCollectionModal;

View File

@@ -5,7 +5,6 @@ const BACKEND_URL = import.meta.env.VITE_BACKEND_URL;
const VideoCard = ({ video }) => {
const navigate = useNavigate();
// Format the date (assuming format YYYYMMDD from youtube-dl)
const formatDate = (dateString) => {
if (!dateString || dateString.length !== 8) {

View File

@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import DeleteCollectionModal from '../components/DeleteCollectionModal';
import VideoCard from '../components/VideoCard';
const CollectionPage = ({ collections, videos, onDeleteVideo, onDeleteCollection }) => {
@@ -8,6 +9,7 @@ const CollectionPage = ({ collections, videos, onDeleteVideo, onDeleteCollection
const [collection, setCollection] = useState(null);
const [collectionVideos, setCollectionVideos] = useState([]);
const [loading, setLoading] = useState(true);
const [showDeleteModal, setShowDeleteModal] = useState(false);
useEffect(() => {
if (collections && collections.length > 0) {
@@ -35,11 +37,28 @@ const CollectionPage = ({ collections, videos, onDeleteVideo, onDeleteCollection
navigate(-1);
};
const handleDelete = async () => {
if (await onDeleteCollection(id)) {
// If deletion was successful, navigate back to home
const handleShowDeleteModal = () => {
setShowDeleteModal(true);
};
const handleCloseDeleteModal = () => {
setShowDeleteModal(false);
};
const handleDeleteCollectionOnly = async () => {
const success = await onDeleteCollection(id, false);
if (success) {
navigate('/');
}
setShowDeleteModal(false);
};
const handleDeleteCollectionAndVideos = async () => {
const success = await onDeleteCollection(id, true);
if (success) {
navigate('/');
}
setShowDeleteModal(false);
};
if (loading) {
@@ -60,7 +79,7 @@ const CollectionPage = ({ collections, videos, onDeleteVideo, onDeleteCollection
<h2 className="collection-title">Collection: {collection.name}</h2>
<span className="video-count">{collectionVideos.length} video{collectionVideos.length !== 1 ? 's' : ''}</span>
</div>
<button className="delete-collection-button" onClick={handleDelete}>
<button className="delete-collection-button" onClick={handleShowDeleteModal}>
Delete Collection
</button>
</div>
@@ -81,6 +100,15 @@ const CollectionPage = ({ collections, videos, onDeleteVideo, onDeleteCollection
))}
</div>
)}
<DeleteCollectionModal
isOpen={showDeleteModal}
onClose={handleCloseDeleteModal}
onDeleteCollectionOnly={handleDeleteCollectionOnly}
onDeleteCollectionAndVideos={handleDeleteCollectionAndVideos}
collectionName={collection?.name || ''}
videoCount={collectionVideos.length}
/>
</div>
);
};

View File

@@ -103,10 +103,15 @@ const VideoPlayer = ({ videos, onDeleteVideo, collections, onAddToCollection, on
if (result.success) {
setIsDeleted(true);
// Navigate immediately to prevent further API calls
navigate('/', { replace: true });
// Navigate to the previous page if available, otherwise go to home
if (window.history.length > 1) {
navigate(-1); // Go back to the previous page
} else {
navigate('/', { replace: true });
}
} else {
setDeleteError(result.error);
setDeleteError(result.error || 'Failed to delete video');
setIsDeleting(false);
}
} catch (err) {