diff --git a/client/src/components/common/ConfirmDialog.jsx b/client/src/components/common/ConfirmDialog.jsx
new file mode 100644
index 0000000..7adbbfc
--- /dev/null
+++ b/client/src/components/common/ConfirmDialog.jsx
@@ -0,0 +1,81 @@
+import React from 'react';
+import { AlertTriangle, X } from 'lucide-react';
+
+const ConfirmDialog = ({ isOpen, onClose, onConfirm, title, message, confirmText = 'Confirm', cancelText = 'Cancel', type = 'warning' }) => {
+ if (!isOpen) return null;
+
+ const typeStyles = {
+ warning: {
+ icon: AlertTriangle,
+ iconColor: 'text-yellow-600',
+ confirmButton: 'bg-yellow-600 hover:bg-yellow-700 text-white'
+ },
+ danger: {
+ icon: AlertTriangle,
+ iconColor: 'text-red-600',
+ confirmButton: 'bg-red-600 hover:bg-red-700 text-white'
+ }
+ };
+
+ const config = typeStyles[type] || typeStyles.warning;
+ const Icon = config.icon;
+
+ return (
+
+
+ {/* Background overlay */}
+
+
+ {/* Modal */}
+
+ {/* Close button */}
+
+
+ {/* Content */}
+
+
+
+
+
+
+ {title}
+
+
+ {message}
+
+
+ {/* Actions */}
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ConfirmDialog;
\ No newline at end of file
diff --git a/client/src/pages/Tasks.jsx b/client/src/pages/Tasks.jsx
index e0fe59b..d72d3dd 100644
--- a/client/src/pages/Tasks.jsx
+++ b/client/src/pages/Tasks.jsx
@@ -5,6 +5,8 @@ import { projectService } from '../services/projectService';
import Layout from '../components/layout/Layout';
import { LoadingSpinner, Button, Select, Input } from '../components/common';
import TaskModal from '../components/tasks/TaskModal';
+import ConfirmDialog from '../components/common/ConfirmDialog';
+import { useToast } from '../hooks/useToast';
import {
Plus,
Search,
@@ -33,7 +35,10 @@ const Tasks = () => {
const [openMenuId, setOpenMenuId] = useState(null);
const [showTaskModal, setShowTaskModal] = useState(false);
const [selectedTask, setSelectedTask] = useState(null);
+ const [showConfirmDialog, setShowConfirmDialog] = useState(false);
+ const [taskToDelete, setTaskToDelete] = useState(null);
const menuRef = useRef(null);
+ const { toast } = useToast();
// Set search query from URL parameter
useEffect(() => {
@@ -47,6 +52,8 @@ const Tasks = () => {
fetchData();
}, []);
+
+
useEffect(() => {
filterTasks();
}, [tasks, searchQuery, statusFilter, priorityFilter, projectFilter]);
@@ -79,7 +86,8 @@ const Tasks = () => {
setProjects(projectsResponse.data.projects || []);
setTasks(recentTasksResponse.data || []);
} catch (error) {
- console.error('Error fetching data:', error);
+ const errorMessage = error.response?.data?.message || 'Failed to load tasks';
+ toast.error(errorMessage);
} finally {
setLoading(false);
}
@@ -114,21 +122,34 @@ const Tasks = () => {
setFilteredTasks(filtered);
};
- const handleDeleteTask = async (taskId) => {
- if (!window.confirm('Are you sure you want to delete this task?')) {
- return;
- }
+ const handleDeleteTask = (taskId) => {
+ const task = tasks.find(t => t._id === taskId);
+ setTaskToDelete(task);
+ setShowConfirmDialog(true);
+ setOpenMenuId(null);
+ };
+
+ const confirmDeleteTask = async () => {
+ if (!taskToDelete) return;
try {
- await taskService.deleteTask(taskId);
- setTasks(tasks.filter(task => task._id !== taskId));
- setOpenMenuId(null);
+ await taskService.deleteTask(taskToDelete._id);
+ setTasks(tasks.filter(task => task._id !== taskToDelete._id));
+ toast.success(`"${taskToDelete.title}" deleted successfully`);
} catch (error) {
- console.error('Error deleting task:', error);
- alert('Failed to delete task. Please try again.');
+ const errorMessage = error.response?.data?.message || 'Failed to delete task';
+ toast.error(errorMessage);
+ } finally {
+ setShowConfirmDialog(false);
+ setTaskToDelete(null);
}
};
+ const cancelDeleteTask = () => {
+ setShowConfirmDialog(false);
+ setTaskToDelete(null);
+ };
+
const handleEditTask = (taskId) => {
const task = tasks.find(t => t._id === taskId);
setSelectedTask(task);
@@ -155,8 +176,7 @@ const Tasks = () => {
setShowTaskModal(false);
fetchData(); // Refresh to get updated task with populated fields
} catch (error) {
- console.error('Error saving task:', error);
- throw error;
+ throw error; // Let TaskModal handle the error display
}
};
@@ -457,52 +477,23 @@ const Tasks = () => {
-
+
+
-
- {openMenuId === task._id && (() => {
- const button = document.getElementById(`menu-button-${task._id}`);
- const rect = button?.getBoundingClientRect();
- return (
-
-
-
-
- );
- })()}
|
@@ -515,52 +506,32 @@ const Tasks = () => {
{filteredTasks.map((task) => (
-
+
{getStatusIcon(task.status)}
{task.title}
-
+
+
-
- {openMenuId === task._id && (
-
-
-
-
- )}
-
-
- {task.description && (
+
{task.description && (
{task.description}
@@ -633,6 +604,17 @@ const Tasks = () => {
projectId={projectFilter !== 'all' ? projectFilter : null}
onSave={handleSaveTask}
/>
+
+ {/* Delete Confirmation Dialog */}
+
);
};