feat(stats): recovery 통계를 서버 계약으로 연결

This commit is contained in:
2026-03-15 19:18:05 +09:00
parent 1b01ceaa8b
commit 3aba789c97
9 changed files with 122 additions and 42 deletions

View File

@@ -21,6 +21,12 @@ export interface FocusStatsSummary {
completedSessions: number;
carriedOverCount: number;
};
recovery: {
pausedSessions: number;
resumedSessions: number;
pauseRecoveryRate: number;
awayRecoveryReady: boolean;
};
trend: FocusTrendPoint[];
}

View File

@@ -49,6 +49,12 @@ const buildMockSummary = (): FocusStatsSummary => {
completedSessions: 4,
carriedOverCount: 1,
},
recovery: {
pausedSessions: 3,
resumedSessions: 2,
pauseRecoveryRate: 2 / 3,
awayRecoveryReady: true,
},
trend: [],
};
};
@@ -145,6 +151,83 @@ const buildCompletionSummary = (summary: FocusStatsSummary) => {
);
};
const buildRecoverySummary = (summary: FocusStatsSummary, source: StatsSource) => {
if (source === 'mock') {
return copy.stats.reviewRecoveryMockSummary;
}
if (summary.recovery.pausedSessions === 0) {
return copy.stats.reviewRecoveryNoPauseSummary;
}
return copy.stats.reviewRecoveryApiSummary(
formatPercent(summary.recovery.pauseRecoveryRate),
summary.recovery.resumedSessions,
summary.recovery.pausedSessions,
);
};
const buildRecoverySection = (
summary: FocusStatsSummary,
source: StatsSource,
): WeeklyReviewSection => {
if (source === 'mock') {
return {
title: copy.stats.reviewRecoveryTitle,
summary: copy.stats.reviewRecoveryMockSummary,
availability: 'ready',
metrics: [
{
id: 'recovery-pause',
label: copy.stats.reviewPauseRecovery,
value: '67%',
hint: copy.stats.reviewPauseRecoveryHint,
},
{
id: 'recovery-away',
label: copy.stats.reviewAwayRecovery,
value: '50%',
hint: copy.stats.reviewAwayRecoveryHint,
},
],
note: copy.stats.reviewRecoveryMockNote,
};
}
const availability = summary.recovery.awayRecoveryReady ? 'ready' : 'limited';
const metrics =
summary.recovery.pausedSessions > 0
? [
{
id: 'recovery-pause-rate',
label: copy.stats.reviewPauseRecovery,
value: formatPercent(summary.recovery.pauseRecoveryRate),
hint: copy.stats.reviewPauseRecoveryHint,
},
{
id: 'recovery-resumed-sessions',
label: copy.stats.reviewResumedSessions,
value: `${summary.recovery.resumedSessions}${copy.stats.countUnit}`,
hint: copy.stats.reviewResumedSessionsHint,
},
{
id: 'recovery-paused-sessions',
label: copy.stats.reviewPausedSessions,
value: `${summary.recovery.pausedSessions}${copy.stats.countUnit}`,
hint: copy.stats.reviewPausedSessionsHint,
},
]
: [];
return {
title: copy.stats.reviewRecoveryTitle,
summary: buildRecoverySummary(summary, source),
availability,
metrics,
note: availability === 'limited' ? copy.stats.reviewRecoveryPartialNote : undefined,
};
};
const buildCarryForward = (summary: FocusStatsSummary): WeeklyReviewViewModel['carryForward'] => {
const completionRate =
summary.last7Days.startedSessions > 0
@@ -255,35 +338,7 @@ const buildReviewFromSummary = (
},
],
},
recoveryQuality: {
title: copy.stats.reviewRecoveryTitle,
summary:
source === 'mock'
? copy.stats.reviewRecoveryMockSummary
: copy.stats.reviewRecoveryLimitedSummary,
availability: source === 'mock' ? 'ready' : 'limited',
metrics:
source === 'mock'
? [
{
id: 'recovery-pause',
label: copy.stats.reviewPauseRecovery,
value: '67%',
hint: copy.stats.reviewPauseRecoveryHint,
},
{
id: 'recovery-away',
label: copy.stats.reviewAwayRecovery,
value: '50%',
hint: copy.stats.reviewAwayRecoveryHint,
},
]
: [],
note:
source === 'mock'
? copy.stats.reviewRecoveryMockNote
: copy.stats.reviewRecoveryLimitedNote,
},
recoveryQuality: buildRecoverySection(summary, source),
completionQuality: {
title: copy.stats.reviewCompletionTitle,
summary: buildCompletionSummary(summary),