feat(版本): 完善计划覆盖与工作台待办
关键改动: - 增加计划日志汇总、需求覆盖草稿校验与对应测试 - 抽取工作台工作项 Hook,补充待办计数能力 - 优化版本详情中计划、任务、测试用例和 Bug 的筛选与展示 Co-Authored-By: Codex GPT-5 <codex@openai.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import { DevTaskDetailDrawer } from './DevTaskDetailDrawer';
|
||||
import { calcGroupProgress, DEV_TASK_STATUS_LABEL, aggregateDevTaskHours, devTaskIntervals } from '@/lib/dev-task';
|
||||
import { calcTwoMetrics } from '@/lib/work-hours';
|
||||
import { Pagination, usePagination } from '@/components/Pagination';
|
||||
import { FilterSelect } from '@/components/FilterSelect';
|
||||
import { SearchInput, matchTitleOrNo } from '@/components/SearchInput';
|
||||
import { useDebouncedValue } from '@/hooks/useDebouncedValue';
|
||||
import { orderByRequirementForGrouping } from '@/lib/requirement-grouping';
|
||||
@@ -140,24 +141,36 @@ export function DevTaskTab({ versionId, requirementIds, versionDeadline }: Props
|
||||
<span className="w-px h-4 bg-[var(--line)] mx-1 shrink-0" />
|
||||
|
||||
<SearchInput value={keyword} onChange={(v) => { setKeyword(v); setPage(1); }} placeholder="搜索标题/编号" />
|
||||
<select value={filterAssignee} onChange={(e) => { setFilterAssignee(e.target.value); setPage(1); }} className="h-6 rounded border border-[var(--line)] bg-[var(--bg-card)] px-1.5 text-[11px] text-[var(--ink-soft)] focus:border-[var(--accent)] focus:outline-none">
|
||||
<option value="">负责人</option>
|
||||
{user?.name && <option value={user.name}>我的</option>}
|
||||
{assignees.filter((a) => a !== user?.name).map((a) => <option key={a} value={a}>{a}</option>)}
|
||||
</select>
|
||||
<select value={filterStatus} onChange={(e) => { setFilterStatus(e.target.value); setPage(1); }} className="h-6 rounded border border-[var(--line)] bg-[var(--bg-card)] px-1.5 text-[11px] text-[var(--ink-soft)] focus:border-[var(--accent)] focus:outline-none">
|
||||
<option value="">状态</option>
|
||||
{(Object.entries(DEV_TASK_STATUS_LABEL) as [DevTaskStatus, string][]).map(([k, v]) => <option key={k} value={k}>{v}</option>)}
|
||||
</select>
|
||||
<select value={filterBlocked} onChange={(e) => { setFilterBlocked(e.target.value); setPage(1); }} className="h-6 rounded border border-[var(--line)] bg-[var(--bg-card)] px-1.5 text-[11px] text-[var(--ink-soft)] focus:border-[var(--accent)] focus:outline-none">
|
||||
<option value="">阻塞</option>
|
||||
<option value="yes">阻塞中</option>
|
||||
<option value="no">正常</option>
|
||||
</select>
|
||||
<select value={filterCategory} onChange={(e) => { setFilterCategory(e.target.value); setPage(1); }} className="h-6 rounded border border-[var(--line)] bg-[var(--bg-card)] px-1.5 text-[11px] text-[var(--ink-soft)] focus:border-[var(--accent)] focus:outline-none">
|
||||
<option value="">类型</option>
|
||||
{categories.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
|
||||
</select>
|
||||
<FilterSelect
|
||||
value={filterAssignee || 'all'}
|
||||
onChange={(value) => { setFilterAssignee(value === 'all' ? '' : value); setPage(1); }}
|
||||
options={[
|
||||
...(user?.name ? [{ value: user.name, label: '我的' }] : []),
|
||||
...assignees.filter((a) => a !== user?.name).map((a) => ({ value: a, label: a })),
|
||||
]}
|
||||
allLabel="负责人"
|
||||
/>
|
||||
<FilterSelect
|
||||
value={filterStatus || 'all'}
|
||||
onChange={(value) => { setFilterStatus(value === 'all' ? '' : value); setPage(1); }}
|
||||
options={(Object.entries(DEV_TASK_STATUS_LABEL) as [DevTaskStatus, string][]).map(([value, label]) => ({ value, label }))}
|
||||
allLabel="状态"
|
||||
/>
|
||||
<FilterSelect
|
||||
value={filterBlocked || 'all'}
|
||||
onChange={(value) => { setFilterBlocked(value === 'all' ? '' : value); setPage(1); }}
|
||||
options={[
|
||||
{ value: 'yes', label: '阻塞中' },
|
||||
{ value: 'no', label: '正常' },
|
||||
]}
|
||||
allLabel="阻塞"
|
||||
/>
|
||||
<FilterSelect
|
||||
value={filterCategory || 'all'}
|
||||
onChange={(value) => { setFilterCategory(value === 'all' ? '' : value); setPage(1); }}
|
||||
options={categories.map((category) => ({ value: category.id, label: category.name }))}
|
||||
allLabel="类型"
|
||||
/>
|
||||
{hasFilter && <button onClick={() => { setFilterAssignee(''); setFilterStatus(''); setFilterBlocked(''); setFilterCategory(''); setKeyword(''); setPage(1); }} className="text-[10px] text-[var(--accent)] hover:underline shrink-0">清除</button>}
|
||||
|
||||
<div className="ml-auto flex items-center gap-2 shrink-0">
|
||||
|
||||
Reference in New Issue
Block a user