Implement live analysis achievements and admin console
这个提交包含在:
@@ -134,6 +134,58 @@ function buildStats(user: MockUser) {
|
||||
shotCount: 18,
|
||||
},
|
||||
],
|
||||
recentLiveSessions: [],
|
||||
dailyTraining: [
|
||||
{
|
||||
trainingDate: "2026-03-13",
|
||||
totalMinutes: 48,
|
||||
sessionCount: 2,
|
||||
effectiveActions: 36,
|
||||
averageScore: 80,
|
||||
},
|
||||
{
|
||||
trainingDate: "2026-03-14",
|
||||
totalMinutes: 32,
|
||||
sessionCount: 1,
|
||||
effectiveActions: 18,
|
||||
averageScore: 84,
|
||||
},
|
||||
],
|
||||
achievements: [
|
||||
{
|
||||
key: "training_day_1",
|
||||
name: "开练",
|
||||
description: "完成首个训练日",
|
||||
progressPct: 100,
|
||||
unlocked: true,
|
||||
},
|
||||
{
|
||||
key: "analyses_1",
|
||||
name: "分析首秀",
|
||||
description: "完成首个分析会话",
|
||||
progressPct: 100,
|
||||
unlocked: true,
|
||||
},
|
||||
{
|
||||
key: "live_analysis_5",
|
||||
name: "实时观察者",
|
||||
description: "完成 5 次实时分析",
|
||||
progressPct: 40,
|
||||
unlocked: false,
|
||||
},
|
||||
],
|
||||
latestNtrpSnapshot: {
|
||||
rating: user.ntrpRating,
|
||||
createdAt: nowIso(),
|
||||
dimensionScores: {
|
||||
poseAccuracy: 82,
|
||||
strokeConsistency: 78,
|
||||
footwork: 74,
|
||||
fluidity: 79,
|
||||
timing: 77,
|
||||
matchReadiness: 70,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -272,6 +324,10 @@ async function handleTrpc(route: Route, state: MockAppState) {
|
||||
return trpcResult(state.videos);
|
||||
case "analysis.list":
|
||||
return trpcResult(state.analyses);
|
||||
case "analysis.liveSessionList":
|
||||
return trpcResult([]);
|
||||
case "analysis.liveSessionSave":
|
||||
return trpcResult({ sessionId: 1, trainingRecordId: 1 });
|
||||
case "task.list":
|
||||
return trpcResult(state.tasks);
|
||||
case "task.get": {
|
||||
@@ -369,6 +425,39 @@ async function handleTrpc(route: Route, state: MockAppState) {
|
||||
];
|
||||
}
|
||||
return trpcResult({ videoId: state.nextVideoId, url: state.mediaSession?.playback.webmUrl });
|
||||
case "achievement.list":
|
||||
return trpcResult(buildStats(state.user).achievements);
|
||||
case "rating.current":
|
||||
return trpcResult({
|
||||
rating: state.user.ntrpRating,
|
||||
latestSnapshot: buildStats(state.user).latestNtrpSnapshot,
|
||||
});
|
||||
case "rating.history":
|
||||
return trpcResult([
|
||||
{
|
||||
id: 1,
|
||||
rating: 2.4,
|
||||
triggerType: "daily",
|
||||
createdAt: nowIso(),
|
||||
dimensionScores: {
|
||||
poseAccuracy: 72,
|
||||
strokeConsistency: 70,
|
||||
footwork: 66,
|
||||
fluidity: 69,
|
||||
timing: 68,
|
||||
matchReadiness: 60,
|
||||
},
|
||||
sourceSummary: { analyses: 1, liveSessions: 0, totalEffectiveActions: 12, totalPk: 0, activeDays: 1 },
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
rating: state.user.ntrpRating,
|
||||
triggerType: "daily",
|
||||
createdAt: nowIso(),
|
||||
dimensionScores: buildStats(state.user).latestNtrpSnapshot.dimensionScores,
|
||||
sourceSummary: { analyses: 2, liveSessions: 1, totalEffectiveActions: 36, totalPk: 0, activeDays: 2 },
|
||||
},
|
||||
]);
|
||||
default:
|
||||
return trpcResult(null);
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户