fix(小宝预警): 剩余工作量展示天数换算
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { CalendarClock, ChevronRight, Sparkles, TrendingDown, TrendingUp, TriangleAlert } from 'lucide-react';
|
||||
import type { XiaobaoRiskLevel, XiaobaoVersionRisk } from '@/lib/xiaobao-risk';
|
||||
import { formatDateTime } from '@/lib/format';
|
||||
import { formatRemainingWork } from '@/lib/xiaobao-warning-view';
|
||||
|
||||
const RISK_LEVEL_LABEL: Record<XiaobaoRiskLevel, string> = {
|
||||
on_track: '按期',
|
||||
@@ -50,7 +51,7 @@ export function XiaobaoWarningCard({ risk, onClick }: { risk: XiaobaoVersionRisk
|
||||
<div className="mt-3 grid grid-cols-4 gap-2">
|
||||
<Metric label="风险分" value={String(risk.riskScore)} tone={risk.riskScore >= 70 ? 'danger' : risk.riskScore >= 45 ? 'warn' : 'ok'} />
|
||||
<Metric label="延期" value={risk.delayDays > 0 ? `${risk.delayDays}天` : '0天'} tone={risk.delayDays > 0 ? 'danger' : 'ok'} />
|
||||
<Metric label="剩余" value={`${risk.remainingWorkHours}h`} tone={risk.remainingWorkHours > 0 ? 'warn' : 'ok'} />
|
||||
<Metric label="剩余" value={formatRemainingWork(risk.remainingWorkHours)} tone={risk.remainingWorkHours > 0 ? 'warn' : 'ok'} />
|
||||
<Metric label="置信" value={`${risk.confidence}%`} tone={risk.confidence < 50 ? 'danger' : risk.confidence < 75 ? 'warn' : 'ok'} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { CalendarClock, Sparkles, X } from 'lucide-react';
|
||||
import type { XiaobaoVersionRisk } from '@/lib/xiaobao-risk';
|
||||
import { formatDateTime } from '@/lib/format';
|
||||
import { formatRemainingWork } from '@/lib/xiaobao-warning-view';
|
||||
|
||||
export function XiaobaoWarningDrawer({
|
||||
risk,
|
||||
@@ -46,7 +47,7 @@ export function XiaobaoWarningDrawer({
|
||||
<div className="mb-5 grid grid-cols-2 gap-3">
|
||||
<Summary label="风险分" value={`${risk.riskScore}`} />
|
||||
<Summary label="置信度" value={`${risk.confidence}%`} />
|
||||
<Summary label="剩余工作量" value={`${risk.remainingWorkHours}h`} />
|
||||
<Summary label="剩余工作量" value={formatRemainingWork(risk.remainingWorkHours)} />
|
||||
<Summary label="预计延期" value={risk.delayDays > 0 ? `${risk.delayDays}天` : '0天'} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import type { VersionWithContext } from './derive';
|
||||
import { filterXiaobaoWarningVersions } from './xiaobao-warning-view';
|
||||
import { filterXiaobaoWarningVersions, formatRemainingWork } from './xiaobao-warning-view';
|
||||
|
||||
function version(patch: Partial<VersionWithContext> = {}): VersionWithContext {
|
||||
return {
|
||||
@@ -45,3 +45,10 @@ test('filterXiaobaoWarningVersions limits non-managers to versions where they ar
|
||||
|
||||
assert.deepEqual(result.map((item) => item.id), ['ver-1']);
|
||||
});
|
||||
|
||||
test('formatRemainingWork keeps hours and adds work-day conversion', () => {
|
||||
assert.equal(formatRemainingWork(0), '0h / 0天');
|
||||
assert.equal(formatRemainingWork(8), '8h / 1天');
|
||||
assert.equal(formatRemainingWork(12), '12h / 1.5天');
|
||||
assert.equal(formatRemainingWork(0.5), '0.5h / 0.1天');
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { VersionWithContext } from './derive';
|
||||
import { WORK_HOURS } from './work-hours';
|
||||
|
||||
const UNFINISHED_VERSION_STATUSES = new Set(['planned', 'developing', 'paused']);
|
||||
|
||||
@@ -18,3 +19,14 @@ export function filterXiaobaoWarningVersions(
|
||||
return (version.members ?? []).some((member) => member.name === filter.userName);
|
||||
});
|
||||
}
|
||||
|
||||
export function formatRemainingWork(hours: number): string {
|
||||
const safeHours = Number.isFinite(hours) && hours > 0 ? hours : 0;
|
||||
const days = safeHours / WORK_HOURS.hoursPerDay;
|
||||
return `${formatNumber(safeHours)}h / ${formatNumber(days)}天`;
|
||||
}
|
||||
|
||||
function formatNumber(value: number): string {
|
||||
if (Number.isInteger(value)) return String(value);
|
||||
return value.toFixed(1).replace(/\.0$/, '');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user