Add functionality to delete tasks with a confirmation dialog

Implement task deletion with optimistic UI updates, confirmation dialogs using Shadcn UI's AlertDialog, and API integration for backend task removal. Includes i18n support for delete confirmation messages.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: ceced2fc-aa46-458d-ba87-ddd4b7bb1518
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/659922a9-0087-461c-90dd-6d9a58b81d4d/ceced2fc-aa46-458d-ba87-ddd4b7bb1518/nn5bWKE
This commit is contained in:
paul-nothaft
2025-10-24 06:12:45 +00:00
parent 8fe1c66d70
commit 814163b07f
5 changed files with 93 additions and 5 deletions
+75
View File
@@ -6,6 +6,16 @@ import { Toaster } from "@/components/ui/toaster";
import { TooltipProvider } from "@/components/ui/tooltip";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
// Components
import BottomNavigation from './components/BottomNavigation';
@@ -98,6 +108,7 @@ function App() {
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const [isTaskDetailsOpen, setIsTaskDetailsOpen] = useState(false);
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
const [deleteTaskId, setDeleteTaskId] = useState<string | null>(null);
// Fetch tasks and labels from server on mount
useEffect(() => {
@@ -229,6 +240,44 @@ function App() {
console.log('Task details saved:', updatedTask.title);
};
const handleTaskDelete = async (taskId: string) => {
setDeleteTaskId(taskId);
};
const confirmTaskDelete = async () => {
if (!deleteTaskId) return;
// Optimistic deletion - remove from UI immediately
const taskToDelete = tasks.find(task => task.id === deleteTaskId);
setTasks(prev => prev.filter(task => task.id !== deleteTaskId));
// Close task details modal if the deleted task was open
if (selectedTask?.id === deleteTaskId) {
handleTaskDetailsClose();
}
setDeleteTaskId(null);
try {
const response = await fetch(`/api/tasks/${deleteTaskId}`, {
method: 'DELETE'
});
// 404 is acceptable - task doesn't exist on backend (seeded task)
if (!response.ok && response.status !== 404) {
throw new Error('Failed to delete task');
}
console.log('Task deleted:', deleteTaskId);
} catch (error) {
console.error('Error deleting task:', error);
// Restore task on error
if (taskToDelete) {
setTasks(prev => [...prev, taskToDelete]);
}
}
};
const handleCreateFromTemplate = (templateId: string, startDate: Date, projectName: string) => {
console.log('Creating project from template:', { templateId, startDate, projectName });
// In a real app, this would create multiple tasks based on the template
@@ -242,6 +291,7 @@ function App() {
tasks={tasks}
onTaskUpdate={handleTaskUpdate}
onTaskEdit={handleTaskClick}
onTaskDelete={handleTaskDelete}
onStartTimer={startTimer}
onStopTimer={stopTimer}
/>
@@ -264,6 +314,7 @@ function App() {
onTaskStatusChange={handleTaskStatusChange}
onTaskUpdate={handleTaskUpdate}
onTaskClick={handleTaskClick}
onTaskDelete={handleTaskDelete}
onStartTimer={startTimer}
onStopTimer={stopTimer}
/>
@@ -354,6 +405,30 @@ function App() {
onSave={handleTaskDetailsSave}
labels={labels}
/>
{/* Delete Confirmation Dialog */}
<AlertDialog open={deleteTaskId !== null} onOpenChange={(open) => !open && setDeleteTaskId(null)}>
<AlertDialogContent data-testid="dialog-delete-confirmation">
<AlertDialogHeader>
<AlertDialogTitle>{t('deleteConfirmation.title')}</AlertDialogTitle>
<AlertDialogDescription>
{t('deleteConfirmation.description')}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel data-testid="button-cancel-delete">
{t('deleteConfirmation.cancel')}
</AlertDialogCancel>
<AlertDialogAction
onClick={confirmTaskDelete}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
data-testid="button-confirm-delete"
>
{t('deleteConfirmation.confirm')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
<Toaster />
+6 -1
View File
@@ -15,6 +15,7 @@ interface TasksWithCalendarProps {
tasks: Task[];
onTaskUpdate?: (taskId: string, updates: Partial<Task>) => void;
onTaskEdit?: (task: Task) => void;
onTaskDelete?: (taskId: string) => void;
onStartTimer?: (taskId: string) => void;
onStopTimer?: (taskId: string) => void;
}
@@ -22,7 +23,7 @@ interface TasksWithCalendarProps {
type SortOption = 'dueDate' | 'priority' | 'title' | 'status';
type FilterOption = 'all' | 'todo' | 'inProgress' | 'done' | 'overdue';
export default function TasksWithCalendar({ tasks, onTaskUpdate, onTaskEdit, onStartTimer, onStopTimer }: TasksWithCalendarProps) {
export default function TasksWithCalendar({ tasks, onTaskUpdate, onTaskEdit, onTaskDelete, onStartTimer, onStopTimer }: TasksWithCalendarProps) {
const [searchQuery, setSearchQuery] = useState('');
const [sortBy, setSortBy] = useState<SortOption>('dueDate');
const [filterBy, setFilterBy] = useState<FilterOption>('all');
@@ -304,6 +305,10 @@ export default function TasksWithCalendar({ tasks, onTaskUpdate, onTaskEdit, onS
onTaskEdit?.(task);
console.log(`Edit task ${task.title}`);
}}
onDelete={() => {
onTaskDelete?.(task.id);
console.log(`Delete task ${task.title}`);
}}
onStatusChange={(newStatus) => {
if (newStatus === 'done') {
handleTaskComplete(task);
+6
View File
@@ -173,5 +173,11 @@
"close": "Schließen",
"loading": "Laden...",
"error": "Ein Fehler ist aufgetreten"
},
"deleteConfirmation": {
"title": "Aufgabe löschen",
"description": "Möchten Sie diese Aufgabe wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.",
"cancel": "Abbrechen",
"confirm": "Löschen"
}
}
+6
View File
@@ -173,5 +173,11 @@
"close": "Close",
"loading": "Loading...",
"error": "An error occurred"
},
"deleteConfirmation": {
"title": "Delete Task",
"description": "Are you sure you want to delete this task? This action cannot be undone.",
"cancel": "Cancel",
"confirm": "Delete"
}
}