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 { createTasksPageConfig } from '../../config/pages/tasks.config';
|
||||||
import { useSectorStore } from '../../store/sectorStore';
|
import { useSectorStore } from '../../store/sectorStore';
|
||||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
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';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
|
|
||||||
export default function Tasks() {
|
export default function Tasks() {
|
||||||
const navigate = useNavigate();
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const { activeSector } = useSectorStore();
|
const { activeSector } = useSectorStore();
|
||||||
const { pageSize } = usePageSizeStore();
|
const { pageSize } = usePageSizeStore();
|
||||||
@@ -65,9 +59,6 @@ export default function Tasks() {
|
|||||||
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
|
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
|
||||||
const [showContent, setShowContent] = useState(false);
|
const [showContent, setShowContent] = useState(false);
|
||||||
|
|
||||||
// View state
|
|
||||||
const [currentView, setCurrentView] = useState<ViewType>('table');
|
|
||||||
|
|
||||||
// Modal state
|
// Modal state
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [isEditMode, setIsEditMode] = useState(false);
|
const [isEditMode, setIsEditMode] = useState(false);
|
||||||
@@ -193,52 +184,6 @@ export default function Tasks() {
|
|||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
}, [pageSize]);
|
}, [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
|
// Debounced search
|
||||||
useEffect(() => {
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Workflow Pipeline - Show for all views */}
|
<PageHeader
|
||||||
<div className="mb-6">
|
title="Tasks"
|
||||||
<ComponentCard title="Content Creation Workflow" desc="Track your content creation progress">
|
badge={{ icon: <TaskIcon />, color: 'indigo' }}
|
||||||
<WorkflowPipeline
|
/>
|
||||||
steps={workflowSteps}
|
<TablePageTemplate
|
||||||
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
|
|
||||||
hideHeader={true}
|
hideHeader={true}
|
||||||
columns={pageConfig.columns}
|
columns={pageConfig.columns}
|
||||||
data={tasks}
|
data={tasks}
|
||||||
@@ -811,69 +654,6 @@ export default function Tasks() {
|
|||||||
setCurrentPage(1);
|
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 */}
|
{/* Progress Modal for AI Functions */}
|
||||||
<ProgressModal
|
<ProgressModal
|
||||||
|
|||||||
Reference in New Issue
Block a user