feat: Add delete collection modal
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
|
||||
50
frontend/src/components/DeleteCollectionModal.jsx
Normal file
50
frontend/src/components/DeleteCollectionModal.jsx
Normal 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;
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user