77 lines
2.6 KiB
TypeScript
77 lines
2.6 KiB
TypeScript
import { CheckCircle2, Circle, Loader2, MinusCircle } from 'lucide-react'
|
|
import { useState } from 'react'
|
|
import type { TaskItem } from '../types/chat'
|
|
|
|
function StatusIcon({ status }: { status: TaskItem['status'] }) {
|
|
if (status === 'completed') return <CheckCircle2 className="h-4 w-4 shrink-0 text-success" />
|
|
if (status === 'in_progress') return <Loader2 className="h-4 w-4 shrink-0 animate-spin text-accent" />
|
|
if (status === 'cancelled') return <MinusCircle className="h-4 w-4 shrink-0 text-ink-subtle" />
|
|
return <Circle className="h-4 w-4 shrink-0 text-ink-subtle" />
|
|
}
|
|
|
|
export function visibleTasks(tasks: TaskItem[]): TaskItem[] {
|
|
return tasks.filter((task) => task.status !== 'cancelled')
|
|
}
|
|
|
|
export function TaskPlanList({
|
|
tasks,
|
|
compact = false,
|
|
}: {
|
|
tasks: TaskItem[]
|
|
compact?: boolean
|
|
}) {
|
|
const items = visibleTasks(tasks)
|
|
if (!items.length) return null
|
|
|
|
return (
|
|
<ol className={compact ? 'space-y-1.5' : 'space-y-1'}>
|
|
{items.map((task, index) => {
|
|
const active = task.status === 'in_progress'
|
|
const done = task.status === 'completed'
|
|
return (
|
|
<li
|
|
key={`${index}-${task.content}`}
|
|
className={`flex items-start gap-2 ${
|
|
active ? 'text-ink' : done ? 'text-ink-muted' : 'text-ink-subtle'
|
|
}`}
|
|
>
|
|
<span className="mt-0.5">
|
|
<StatusIcon status={task.status} />
|
|
</span>
|
|
<span className={`${compact ? 'text-[13px] leading-6' : 'text-sm'} ${done ? 'line-through decoration-line' : ''}`}>
|
|
{task.content}
|
|
</span>
|
|
</li>
|
|
)
|
|
})}
|
|
</ol>
|
|
)
|
|
}
|
|
|
|
export function TaskPlanCard({ tasks }: { tasks: TaskItem[] }) {
|
|
const [collapsed, setCollapsed] = useState(false)
|
|
const items = visibleTasks(tasks)
|
|
if (!items.length) return null
|
|
const done = items.filter((task) => task.status === 'completed').length
|
|
|
|
return (
|
|
<div className="my-3 overflow-hidden rounded-xl border border-line bg-panel">
|
|
<button
|
|
type="button"
|
|
onClick={() => setCollapsed((value) => !value)}
|
|
className="flex w-full items-center justify-between px-3 py-2.5 text-sm font-medium text-ink hover:bg-surface-subtle"
|
|
>
|
|
<span>
|
|
برنامه کار ({done}/{items.length})
|
|
</span>
|
|
<span className="text-xs text-ink-muted">{collapsed ? 'نمایش' : 'پنهان'}</span>
|
|
</button>
|
|
{!collapsed ? (
|
|
<div className="border-t border-line px-3 py-2">
|
|
<TaskPlanList tasks={items} />
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|