61 lines
2.3 KiB
TypeScript
61 lines
2.3 KiB
TypeScript
import { Plus, Settings } from './Icons';
|
|
import { STATUS_LABEL, type TaskSummary } from '../types';
|
|
|
|
function shortTime(value: string): string {
|
|
const date = new Date(value.replace(' ', 'T'));
|
|
if (Number.isNaN(date.getTime())) return value;
|
|
const now = new Date();
|
|
const hm = date.toTimeString().slice(0, 5);
|
|
return date.toDateString() === now.toDateString() ? hm : `${date.getMonth() + 1}/${date.getDate()} ${hm}`;
|
|
}
|
|
|
|
export function Sidebar({ tasks, activeId, backendLabel, backendOk, onSelect, onNew, onSettings }: {
|
|
tasks: TaskSummary[];
|
|
activeId: string | null;
|
|
backendLabel: string;
|
|
backendOk: boolean;
|
|
onSelect: (id: string) => void;
|
|
onNew: () => void;
|
|
onSettings: () => void;
|
|
}) {
|
|
return (
|
|
<aside className="sidebar" aria-label="任务列表">
|
|
<div className="sidebar-head">
|
|
<div className="brand">智能办事助手</div>
|
|
<button type="button" className="new-task" onClick={onNew} data-testid="new-task">
|
|
<Plus size={16} />新任务
|
|
</button>
|
|
</div>
|
|
<div className="section-label">最近任务</div>
|
|
<ul className="task-list">
|
|
{tasks.length === 0 && <li className="list-empty">还没有任务</li>}
|
|
{tasks.map(task => (
|
|
<li key={task.id}>
|
|
<button
|
|
type="button"
|
|
className={`task-item${task.id === activeId ? ' active' : ''}`}
|
|
onClick={() => onSelect(task.id)}
|
|
title={task.goal}
|
|
data-status={task.status}
|
|
>
|
|
<span className={`status-dot ${task.status}`} aria-hidden="true" />
|
|
<span className="task-item-body">
|
|
<span className="task-item-title">{task.goal || task.business_name || task.id}</span>
|
|
<span className="task-item-meta">
|
|
{task.business_name ? `${task.business_name} · ` : ''}{STATUS_LABEL[task.status] ?? task.status} · {shortTime(task.updated_at)}
|
|
</span>
|
|
</span>
|
|
</button>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<div className="sidebar-foot">
|
|
<span className={`backend-badge${backendOk ? ' ok' : ' err'}`} role="status">{backendLabel}</span>
|
|
<button type="button" className="icon-button" aria-label="设置" title="设置" onClick={onSettings}>
|
|
<Settings size={16} />
|
|
</button>
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|