Files
zn-ai/src-react/components/chat/TaskBoard.tsx
duanshuwen b1dea9a5c2 feat: implement task management store with IPC integration
- Added a new task store in `src-react/stores/task.ts` to manage tasks and their statuses.
- Implemented functions for creating, executing, and retrying tasks, along with handling task progress and completion.
- Introduced persistence for tasks using IPC.
- Created utility functions for normalizing room types and building subtasks.
- Added a new CSS file for global styles in `src-react/styles.css`.
- Created runtime types in `src-react/types/runtime.ts` and exported them.
- Updated the main entry points for Vue and React applications to support dynamic framework loading.
- Refactored chat model interfaces and utility functions into `src/shared/chat-model.ts`.
- Updated TypeScript configuration to include paths for React components and types.
- Enhanced Vite configuration to support both Vue and React frameworks.
2026-04-17 07:09:56 +08:00

115 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { TaskItem } from './types';
type TaskBoardProps = {
activeTab: 'pending' | 'completed';
pendingItems: TaskItem[];
completedItems: TaskItem[];
onTabChange: (tab: 'pending' | 'completed') => void;
onRemoveTask?: (taskId: string) => void;
onRetryTask?: (taskId: string) => void;
currentDateLabel?: string;
currentTime?: string;
};
export default function TaskBoard({
activeTab,
pendingItems,
completedItems,
onTabChange,
onRemoveTask,
onRetryTask,
currentDateLabel,
currentTime,
}: TaskBoardProps) {
const items = activeTab === 'pending' ? pendingItems : completedItems;
return (
<aside className="h-full min-h-0 w-full flex-none lg:w-[288px]">
<div className="flex h-full min-h-0 flex-col rounded-[20px] bg-white p-3 shadow-[0_10px_30px_rgba(15,23,42,0.06)] dark:bg-[#1b1b1d]">
<div className="flex rounded-[10px] border border-[#BEDBFF] bg-[#EFF6FF] p-1 dark:border-[#2a2a2d] dark:bg-[#1f1f22]">
<button
type="button"
className={[
'flex-1 rounded-[8px] px-3 py-2 text-sm transition-colors',
activeTab === 'pending'
? 'bg-white text-[#2B7FFF] shadow-sm dark:bg-[#1f1f22]'
: 'text-[#525866] dark:text-gray-300',
].join(' ')}
onClick={() => onTabChange('pending')}
>
{pendingItems.length > 0 ? `${pendingItems.length}` : ''}
</button>
<button
type="button"
className={[
'flex-1 rounded-[8px] px-3 py-2 text-sm transition-colors',
activeTab === 'completed'
? 'bg-white text-[#2B7FFF] shadow-sm dark:bg-[#1f1f22]'
: 'text-[#525866] dark:text-gray-300',
].join(' ')}
onClick={() => onTabChange('completed')}
>
{completedItems.length > 0 ? `${completedItems.length}` : ''}
</button>
</div>
{(currentDateLabel || currentTime) ? (
<div className="flex items-center justify-between px-1 pb-3 pt-3 text-[13px] text-[#99A0AE] dark:text-gray-500">
<span>{currentDateLabel || '执行时段'}</span>
<span>{currentTime || '--'}</span>
</div>
) : null}
<div className="grid grid-cols-1 gap-3 overflow-y-auto">
{items.length > 0 ? (
items.map((item) => (
<article
key={item.id}
className="relative flex gap-3 rounded-[12px] border border-[#dfeaf6] bg-white p-3.5 transition-colors hover:bg-[#F5F7FA] dark:border-[#2a2a2d] dark:bg-[#1f1f22] dark:hover:bg-[#2a2a2d]"
>
{(onRemoveTask || onRetryTask) ? (
<div className="absolute right-2 top-2 flex items-center gap-2">
{onRetryTask && (item.status === 'failed' || item.status === 'partial_failed') ? (
<button
type="button"
className="text-[11px] text-[#2B7FFF] transition-colors hover:text-[#1d4ed8]"
onClick={() => onRetryTask(item.id)}
>
</button>
) : null}
{onRemoveTask ? (
<button
type="button"
className="text-[11px] text-[#99A0AE] transition-colors hover:text-[#ef4444]"
onClick={() => onRemoveTask(item.removeTaskId || item.id)}
>
</button>
) : null}
</div>
) : null}
<div className="flex h-11 w-11 items-center justify-center rounded-lg border border-dashed border-[#9fc0e8] bg-[#EFF6FF] text-[23px] text-[#3b82f6] dark:border-gray-700 dark:bg-[#222225]">
</div>
<div className="min-w-0 flex-1">
<div className="text-sm font-semibold text-[#171717] dark:text-gray-100">{item.title}</div>
<div className="mt-1.5 text-[13px] leading-6 text-[#9aa5b1] dark:text-gray-400">{item.description}</div>
{item.meta ? (
<div className="mt-1 text-[11px] text-[#99A0AE] dark:text-gray-500">{item.meta}</div>
) : null}
<div className="mt-2 text-xs font-medium text-[#2B7FFF]">{item.status}</div>
</div>
</article>
))
) : (
<div className="rounded-[12px] border border-dashed border-[#dfeaf6] bg-[#f8fbff] px-4 py-6 text-sm text-[#99A0AE] dark:border-[#2a2a2d] dark:bg-[#1f1f22] dark:text-gray-400">
</div>
)}
</div>
</div>
</aside>
);
}