Files
agent/apps/senlinai-acro-react/src/pages/projects/project-tasks.tsx

166 lines
6.5 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 { useState } from 'react'
import { Button, Card, Descriptions, Input, Modal, Progress, Space, Tag, Typography } from '@arco-design/web-react'
import { IconArrowLeft, IconCalendar, IconCheckCircle, IconCheckCircleFill, IconClockCircle, IconPlus } from '@arco-design/web-react/icon'
import type { ProjectWorkspace, TaskItem } from './project-types'
const { Title, Text, Paragraph } = Typography
export function ProjectTasks({
activeWorkspace,
activeTaskID,
onOpenTask,
onCloseTask,
onCreateTask,
onCreateProjectTag,
}: {
activeWorkspace: ProjectWorkspace
activeTaskID: string | null
onOpenTask: (taskID: string) => void
onCloseTask: () => void
onSelectItem: (title: string) => void
onCreateTask: () => void
onCreateProjectTag: (name: string) => void
}) {
const { tasks, project, tags } = activeWorkspace
const activeTask = tasks.find((task) => task.id === activeTaskID)
const pendingTasks = tasks.filter((task) => !task.completed)
const completedTasks = tasks.filter((task) => task.completed)
const projectTags = tags.filter((tag) => tag !== 'all' && tag !== '全部')
const [tagModalOpen, setTagModalOpen] = useState(false)
const [tagName, setTagName] = useState('')
if (activeTask) {
return <TaskDetail activeWorkspace={activeWorkspace} task={activeTask} onBack={onCloseTask} />
}
function submitTag() {
const nextTag = tagName.trim()
if (!nextTag) return
onCreateProjectTag(nextTag)
setTagName('')
setTagModalOpen(false)
}
return (
<div className="project-channel-page project-tasks-page overview-page">
<div className="overview-head">
<div>
<Title heading={4}></Title>
<Text type="secondary">{project.name} </Text>
</div>
<Button type="primary" icon={<IconPlus />} onClick={onCreateTask}></Button>
</div>
<Card className="queue-section task-tag-section" bordered>
<div className="section-header">
<Title heading={6}></Title>
<Button className="tag-create-button" size="mini" type="text" icon={<IconPlus />} onClick={() => setTagModalOpen(true)}></Button>
</div>
<Space wrap>
<Tag color="arcoblue"></Tag>
{projectTags.map((tag) => (
<Tag key={tag} color="gray">{tag}</Tag>
))}
</Space>
</Card>
<Card className="queue-section" bordered>
<div className="section-header">
<Title heading={6}></Title>
<Tag color="arcoblue">{pendingTasks.length}</Tag>
</div>
<div className="task-plan-card-grid">
{pendingTasks.map((task) => (
<button className="task-plan-card" key={task.id} onClick={() => onOpenTask(task.id)}>
<span className="task-plan-title"><IconCheckCircleFill />{task.title}</span>
<Paragraph ellipsis={{ rows: 2 }} type="secondary">{task.summary || '暂无正文'}</Paragraph>
<div className="task-plan-meta">
<Tag color="arcoblue">{task.tag || '未设置标签'}</Tag>
<span><IconCalendar /> {task.createdAt}</span>
</div>
</button>
))}
{pendingTasks.length === 0 && <Text type="secondary"></Text>}
</div>
</Card>
<Card className="queue-section" bordered>
<div className="section-header">
<Title heading={6}></Title>
<Tag color="green">{completedTasks.length}</Tag>
</div>
{completedTasks.map((task) => (
<button className="completed-plan-row" key={task.id} onClick={() => onOpenTask(task.id)}>
<span className="completed-plan-main">
<Text className="completed-plan-title"><IconCheckCircle />{task.title}</Text>
<Text type="secondary" ellipsis={{ showTooltip: true }}>{task.summary || '暂无正文'}</Text>
</span>
<span>{task.createdAt}</span>
<span>{task.completedAt || '未知'}</span>
<span><IconClockCircle /> {task.duration || '未知'}</span>
</button>
))}
{completedTasks.length === 0 && <Text type="secondary"></Text>}
</Card>
<Modal
className="action-modal"
title="新建标签"
visible={tagModalOpen}
onCancel={() => {
setTagModalOpen(false)
setTagName('')
}}
onOk={submitTag}
>
<Space direction="vertical" size={12} className="action-form">
<label>
<Text></Text>
<Input autoFocus placeholder="例如:设计、客户、重要" value={tagName} onChange={setTagName} onPressEnter={submitTag} />
</label>
</Space>
</Modal>
</div>
)
}
function TaskDetail({ activeWorkspace, task, onBack }: { activeWorkspace: ProjectWorkspace; task: TaskItem; onBack: () => void }) {
const percent = Number.parseInt(task.progress, 10)
return (
<div className="project-channel-page project-task-detail-page overview-page">
<div className="overview-head">
<div>
<Button type="text" icon={<IconArrowLeft />} onClick={onBack}></Button>
<Title heading={4}>{task.title}</Title>
<Text type="secondary">{activeWorkspace.project.name} </Text>
</div>
<Tag color={task.completed ? 'green' : 'arcoblue'}>{task.tag || '未设置标签'}</Tag>
</div>
<Card className="queue-section task-detail-card" bordered>
<Descriptions
colon=""
column={2}
data={[
{ label: '所属项目', value: activeWorkspace.project.name },
{ label: '负责人', value: task.owner },
{ label: '创建时间', value: task.createdAt },
{ label: '完成状态', value: task.completed ? '已完成' : '未完成' },
{ label: '完成时间', value: task.completedAt || '未完成' },
{ label: '耗时', value: task.duration || '未完成' },
]}
/>
<div className="task-detail-progress">
<Text type="secondary"></Text>
<Progress percent={Number.isNaN(percent) ? 0 : percent} />
</div>
<div className="task-detail-summary">
<Title heading={6}></Title>
<Text>{task.summary || '暂无任务说明'}</Text>
</div>
</Card>
</div>
)
}