Update Tasks.tsx
This commit is contained in:
@@ -29,15 +29,9 @@ import { TaskIcon, PlusIcon, DownloadIcon } from '../../icons';
|
||||
import { createTasksPageConfig } from '../../config/pages/tasks.config';
|
||||
import { useSectorStore } from '../../store/sectorStore';
|
||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||
import ViewToggle, { ViewType } from '../../components/common/ViewToggle';
|
||||
import { KanbanBoard, TaskList, Task as KanbanTask } from '../../components/tasks';
|
||||
import WorkflowPipeline, { WorkflowStep } from '../../components/dashboard/WorkflowPipeline';
|
||||
import ComponentCard from '../../components/common/ComponentCard';
|
||||
import { useNavigate } from 'react-router';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
|
||||
export default function Tasks() {
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
const { activeSector } = useSectorStore();
|
||||
const { pageSize } = usePageSizeStore();
|
||||
@@ -65,9 +59,6 @@ export default function Tasks() {
|
||||
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
|
||||
const [showContent, setShowContent] = useState(false);
|
||||
|
||||
// View state
|
||||
const [currentView, setCurrentView] = useState<ViewType>('table');
|
||||
|
||||
// Modal state
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [isEditMode, setIsEditMode] = useState(false);
|
||||
@@ -193,52 +184,6 @@ export default function Tasks() {
|
||||
setCurrentPage(1);
|
||||
}, [pageSize]);
|
||||
|
||||
// Map API Task to KanbanBoard Task
|
||||
const mapTaskToKanbanTask = (task: Task): KanbanTask => {
|
||||
// Map status from API to Kanban status
|
||||
const statusMap: Record<string, "todo" | "in_progress" | "completed"> = {
|
||||
'queued': 'todo',
|
||||
'in_progress': 'in_progress',
|
||||
'generating': 'in_progress',
|
||||
'completed': 'completed',
|
||||
'published': 'completed',
|
||||
'draft': 'todo',
|
||||
'review': 'in_progress',
|
||||
};
|
||||
|
||||
const kanbanStatus = statusMap[task.status] || 'todo';
|
||||
|
||||
// Format due date if available (you might need to add this field to Task)
|
||||
const dueDate = task.created_at ? new Date(task.created_at).toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
}) : undefined;
|
||||
|
||||
// Parse keywords if available (comma-separated string)
|
||||
const keywordNames = task.keywords ? task.keywords.split(',').map(k => k.trim()).filter(k => k) : undefined;
|
||||
|
||||
return {
|
||||
id: String(task.id),
|
||||
title: task.title || 'Untitled Task',
|
||||
status: kanbanStatus,
|
||||
dueDate,
|
||||
commentsCount: 0, // Add if you have this data
|
||||
attachmentsCount: 0, // Add if you have this data
|
||||
tags: task.cluster_name ? [{ label: task.cluster_name, color: 'brand' as const }] : undefined,
|
||||
description: task.description || undefined,
|
||||
assignee: undefined, // Add if you have assignee data
|
||||
// Relationship data
|
||||
clusterId: task.cluster_id || null,
|
||||
clusterName: task.cluster_name || null,
|
||||
ideaId: task.idea_id || null,
|
||||
ideaTitle: task.idea_title || null,
|
||||
keywordIds: undefined, // API doesn't return keyword IDs directly, would need to fetch
|
||||
keywordNames: keywordNames,
|
||||
};
|
||||
};
|
||||
|
||||
const kanbanTasks = useMemo(() => tasks.map(mapTaskToKanbanTask), [tasks]);
|
||||
|
||||
// Debounced search
|
||||
useEffect(() => {
|
||||
@@ -602,115 +547,13 @@ export default function Tasks() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleTaskClick = (kanbanTask: KanbanTask) => {
|
||||
const apiTask = tasks.find(t => String(t.id) === kanbanTask.id);
|
||||
if (apiTask) {
|
||||
setEditingTask(apiTask);
|
||||
setFormData({
|
||||
title: apiTask.title || '',
|
||||
description: apiTask.description || '',
|
||||
keywords: apiTask.keywords || '',
|
||||
cluster_id: apiTask.cluster_id || null,
|
||||
content_structure: apiTask.content_structure || 'blog_post',
|
||||
content_type: apiTask.content_type || 'blog_post',
|
||||
status: apiTask.status || 'queued',
|
||||
word_count: apiTask.word_count || 0,
|
||||
});
|
||||
setIsEditMode(true);
|
||||
setIsModalOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTaskMove = async (taskId: string, newStatus: "todo" | "in_progress" | "completed") => {
|
||||
const apiTask = tasks.find(t => String(t.id) === taskId);
|
||||
if (!apiTask) return;
|
||||
|
||||
// Map Kanban status back to API status
|
||||
const statusMap: Record<string, string> = {
|
||||
'todo': 'queued',
|
||||
'in_progress': 'in_progress',
|
||||
'completed': 'completed',
|
||||
};
|
||||
|
||||
try {
|
||||
await updateTask(apiTask.id, { status: statusMap[newStatus] || 'queued' });
|
||||
toast.success('Task status updated');
|
||||
loadTasks();
|
||||
} catch (error: any) {
|
||||
toast.error(`Failed to update task: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate workflow steps for Tasks page
|
||||
const todoCount = tasks.filter(t => t.status === 'queued' || t.status === 'draft').length;
|
||||
const inProgressCount = tasks.filter(t => t.status === 'in_progress' || t.status === 'generating' || t.status === 'review').length;
|
||||
const completedCount = tasks.filter(t => t.status === 'completed' || t.status === 'published').length;
|
||||
const contentCount = tasks.filter(t => t.content && t.content.length > 0).length;
|
||||
|
||||
const workflowSteps: WorkflowStep[] = [
|
||||
{
|
||||
number: 1,
|
||||
title: "Queue Tasks",
|
||||
status: todoCount > 0 ? "completed" : "pending",
|
||||
count: todoCount,
|
||||
path: "/writer/tasks",
|
||||
description: "Tasks queued for content generation",
|
||||
},
|
||||
{
|
||||
number: 2,
|
||||
title: "Generate Content",
|
||||
status: inProgressCount > 0 ? "in_progress" : contentCount > 0 ? "completed" : "pending",
|
||||
count: contentCount,
|
||||
path: "/writer/tasks",
|
||||
description: "AI content generation",
|
||||
},
|
||||
{
|
||||
number: 3,
|
||||
title: "Review & Edit",
|
||||
status: tasks.filter(t => t.status === 'review').length > 0 ? "in_progress" : "pending",
|
||||
count: tasks.filter(t => t.status === 'review').length,
|
||||
path: "/writer/content",
|
||||
description: "Content review and editing",
|
||||
},
|
||||
{
|
||||
number: 4,
|
||||
title: "Publish",
|
||||
status: completedCount > 0 ? "completed" : "pending",
|
||||
count: completedCount,
|
||||
path: "/writer/content",
|
||||
description: "Published content",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Workflow Pipeline - Show for all views */}
|
||||
<div className="mb-6">
|
||||
<ComponentCard title="Content Creation Workflow" desc="Track your content creation progress">
|
||||
<WorkflowPipeline
|
||||
steps={workflowSteps}
|
||||
onStepClick={(step) => {
|
||||
navigate(step.path);
|
||||
}}
|
||||
showConnections={true}
|
||||
/>
|
||||
</ComponentCard>
|
||||
</div>
|
||||
|
||||
{/* View Toggle - Only show for Kanban/List views */}
|
||||
{currentView !== 'table' && (
|
||||
<div className="mb-4 flex items-center justify-end">
|
||||
<ViewToggle currentView={currentView} onViewChange={setCurrentView} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Table View */}
|
||||
{currentView === 'table' && (
|
||||
<>
|
||||
<div className="mb-4 flex justify-end">
|
||||
<ViewToggle currentView={currentView} onViewChange={setCurrentView} />
|
||||
</div>
|
||||
<TablePageTemplate
|
||||
<PageHeader
|
||||
title="Tasks"
|
||||
badge={{ icon: <TaskIcon />, color: 'indigo' }}
|
||||
/>
|
||||
<TablePageTemplate
|
||||
hideHeader={true}
|
||||
columns={pageConfig.columns}
|
||||
data={tasks}
|
||||
@@ -811,69 +654,6 @@ export default function Tasks() {
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Kanban View */}
|
||||
{currentView === 'kanban' && (
|
||||
<KanbanBoard
|
||||
tasks={kanbanTasks}
|
||||
onTaskClick={handleTaskClick}
|
||||
onTaskMove={handleTaskMove}
|
||||
onAddTask={() => {
|
||||
resetForm();
|
||||
setIsModalOpen(true);
|
||||
}}
|
||||
onFilterChange={(filter) => {
|
||||
// Map filter to status filter
|
||||
const statusMap: Record<string, string> = {
|
||||
'All': '',
|
||||
'Todo': 'queued',
|
||||
'InProgress': 'in_progress',
|
||||
'Completed': 'completed',
|
||||
};
|
||||
setStatusFilter(statusMap[filter] || '');
|
||||
setCurrentPage(1);
|
||||
loadTasks();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* List View */}
|
||||
{currentView === 'list' && (
|
||||
<TaskList
|
||||
tasks={kanbanTasks}
|
||||
onTaskClick={handleTaskClick}
|
||||
onTaskToggle={async (taskId, completed) => {
|
||||
const apiTask = tasks.find(t => String(t.id) === taskId);
|
||||
if (apiTask) {
|
||||
try {
|
||||
await updateTask(apiTask.id, { status: completed ? 'completed' : 'queued' });
|
||||
toast.success('Task updated');
|
||||
loadTasks();
|
||||
} catch (error: any) {
|
||||
toast.error(`Failed to update task: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}}
|
||||
onAddTask={() => {
|
||||
resetForm();
|
||||
setIsModalOpen(true);
|
||||
}}
|
||||
onFilterChange={(filter) => {
|
||||
// Map filter to status filter
|
||||
const statusMap: Record<string, string> = {
|
||||
'All': '',
|
||||
'Todo': 'queued',
|
||||
'InProgress': 'in_progress',
|
||||
'Completed': 'completed',
|
||||
};
|
||||
setStatusFilter(statusMap[filter] || '');
|
||||
setCurrentPage(1);
|
||||
loadTasks();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Progress Modal for AI Functions */}
|
||||
<ProgressModal
|
||||
|
||||
Reference in New Issue
Block a user