mirror of
https://github.com/coleam00/Archon.git
synced 2026-01-03 05:10:27 -05:00
UI improvement, Add delete confirm,
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||||
import { Table, LayoutGrid, Plus, Wifi, WifiOff, List } from 'lucide-react';
|
import { Table, LayoutGrid, Plus, Wifi, WifiOff, List, Trash2 } from 'lucide-react';
|
||||||
import { DndProvider } from 'react-dnd';
|
import { DndProvider } from 'react-dnd';
|
||||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
import { Toggle } from '../ui/Toggle';
|
import { Toggle } from '../ui/Toggle';
|
||||||
import { projectService } from '../../services/projectService';
|
import { projectService } from '../../services/projectService';
|
||||||
import { getGlobalOperationTracker } from '../../utils/operationTracker';
|
import { getGlobalOperationTracker } from '../../utils/operationTracker';
|
||||||
|
import { Card } from '../ui/card';
|
||||||
|
|
||||||
import { useTaskSocket } from '../../hooks/useTaskSocket';
|
import { useTaskSocket } from '../../hooks/useTaskSocket';
|
||||||
import type { CreateTaskRequest, UpdateTaskRequest, DatabaseTaskStatus } from '../../types/project';
|
import type { CreateTaskRequest, UpdateTaskRequest, DatabaseTaskStatus } from '../../types/project';
|
||||||
@@ -16,6 +17,71 @@ import { EditTaskModal } from './EditTaskModal';
|
|||||||
// Assignee utilities
|
// Assignee utilities
|
||||||
const ASSIGNEE_OPTIONS = ['User', 'Archon', 'AI IDE Agent'] as const;
|
const ASSIGNEE_OPTIONS = ['User', 'Archon', 'AI IDE Agent'] as const;
|
||||||
|
|
||||||
|
// Delete confirmation modal component
|
||||||
|
interface DeleteConfirmModalProps {
|
||||||
|
onConfirm: () => void;
|
||||||
|
onCancel: () => void;
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
confirmText?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DeleteConfirmModal = ({
|
||||||
|
onConfirm,
|
||||||
|
onCancel,
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
confirmText = 'Archive'
|
||||||
|
}: DeleteConfirmModalProps) => {
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50">
|
||||||
|
<div className="relative p-6 rounded-md backdrop-blur-md w-full max-w-md
|
||||||
|
bg-gradient-to-b from-white/80 to-white/60 dark:from-white/10 dark:to-black/30
|
||||||
|
border border-gray-200 dark:border-zinc-800/50
|
||||||
|
shadow-[0_10px_30px_-15px_rgba(0,0,0,0.1)] dark:shadow-[0_10px_30px_-15px_rgba(0,0,0,0.7)]
|
||||||
|
before:content-[''] before:absolute before:top-0 before:left-0 before:right-0 before:h-[2px]
|
||||||
|
before:rounded-t-[4px] before:bg-red-500
|
||||||
|
before:shadow-[0_0_10px_2px_rgba(239,68,68,0.4)] dark:before:shadow-[0_0_20px_5px_rgba(239,68,68,0.7)]">
|
||||||
|
|
||||||
|
<div className="relative z-10">
|
||||||
|
<div className="flex items-center gap-3 mb-4">
|
||||||
|
<div className="w-12 h-12 rounded-full bg-red-100 dark:bg-red-900/30 flex items-center justify-center">
|
||||||
|
<Trash2 className="w-6 h-6 text-red-600 dark:text-red-400" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-semibold text-gray-800 dark:text-white">
|
||||||
|
{title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
This action cannot be undone
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-gray-700 dark:text-gray-300 mb-6">
|
||||||
|
{message}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-3">
|
||||||
|
<button
|
||||||
|
onClick={onCancel}
|
||||||
|
className="px-4 py-2 text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 transition-colors"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={onConfirm}
|
||||||
|
className="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg transition-colors shadow-lg shadow-red-600/25 hover:shadow-red-700/25"
|
||||||
|
>
|
||||||
|
{confirmText}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// Mapping functions for status conversion
|
// Mapping functions for status conversion
|
||||||
const mapUIStatusToDBStatus = (uiStatus: Task['status']): DatabaseTaskStatus => {
|
const mapUIStatusToDBStatus = (uiStatus: Task['status']): DatabaseTaskStatus => {
|
||||||
switch (uiStatus) {
|
switch (uiStatus) {
|
||||||
@@ -71,6 +137,8 @@ export const TasksTab = ({
|
|||||||
const [isLoadingFeatures, setIsLoadingFeatures] = useState(false);
|
const [isLoadingFeatures, setIsLoadingFeatures] = useState(false);
|
||||||
const [isSavingTask, setIsSavingTask] = useState<boolean>(false);
|
const [isSavingTask, setIsSavingTask] = useState<boolean>(false);
|
||||||
const [isWebSocketConnected, setIsWebSocketConnected] = useState(false);
|
const [isWebSocketConnected, setIsWebSocketConnected] = useState(false);
|
||||||
|
const [taskToDelete, setTaskToDelete] = useState<Task | null>(null);
|
||||||
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||||
|
|
||||||
// Track recently deleted tasks to prevent race conditions
|
// Track recently deleted tasks to prevent race conditions
|
||||||
const [recentlyDeletedIds, setRecentlyDeletedIds] = useState<Set<string>>(new Set());
|
const [recentlyDeletedIds, setRecentlyDeletedIds] = useState<Set<string>>(new Set());
|
||||||
@@ -480,16 +548,27 @@ export const TasksTab = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deleteTask = async (task: Task) => {
|
const deleteTask = async (task: Task) => {
|
||||||
|
// Set the task to delete and show confirmation modal
|
||||||
|
setTaskToDelete(task);
|
||||||
|
setShowDeleteConfirm(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmDeleteTask = async () => {
|
||||||
|
if (!taskToDelete) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Delete the task - backend will emit socket event
|
// Delete (actually archives) the task - backend will emit socket event
|
||||||
await projectService.deleteTask(task.id);
|
await projectService.deleteTask(taskToDelete.id);
|
||||||
console.log(`[TasksTab] Task ${task.id} deletion sent to backend`);
|
console.log(`[TasksTab] Task ${taskToDelete.id} archival sent to backend`);
|
||||||
|
|
||||||
// Don't update local state - let socket handle it
|
// Don't update local state - let socket handle it
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to delete task:', error);
|
console.error('Failed to archive task:', error);
|
||||||
// Note: The toast notification for deletion is now handled by TaskBoardView and TaskTableView
|
// Note: The toast notification for deletion is now handled by TaskBoardView and TaskTableView
|
||||||
|
} finally {
|
||||||
|
setTaskToDelete(null);
|
||||||
|
setShowDeleteConfirm(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -699,6 +778,20 @@ export const TasksTab = ({
|
|||||||
onSave={saveTask}
|
onSave={saveTask}
|
||||||
getTasksForPrioritySelection={memoizedGetTasksForPrioritySelection}
|
getTasksForPrioritySelection={memoizedGetTasksForPrioritySelection}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Delete Confirmation Modal */}
|
||||||
|
{showDeleteConfirm && taskToDelete && (
|
||||||
|
<DeleteConfirmModal
|
||||||
|
onConfirm={confirmDeleteTask}
|
||||||
|
onCancel={() => {
|
||||||
|
setTaskToDelete(null);
|
||||||
|
setShowDeleteConfirm(false);
|
||||||
|
}}
|
||||||
|
title="Archive Task"
|
||||||
|
message={`Are you sure you want to archive the task "${taskToDelete.title}"? You can restore it from the archived tasks view.`}
|
||||||
|
confirmText="Archive Task"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</DndProvider>
|
</DndProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user