Show precise record times and action summaries

这个提交包含在:
cryptocommuniums-afk
2026-03-15 17:34:24 +08:00
父节点 67b27e3551
当前提交 71caf0de19
修改 4 个文件,包含 103 行新增19 行删除

查看文件

@@ -13,6 +13,28 @@ import {
} from "recharts";
import { useLocation } from "wouter";
const ACTION_LABEL_MAP: Record<string, string> = {
forehand: "正手挥拍",
backhand: "反手挥拍",
serve: "发球",
volley: "截击",
overhead: "高压",
slice: "切削",
lob: "挑高球",
unknown: "未知动作",
};
function getRecordMetadata(record: any) {
if (!record?.metadata || typeof record.metadata !== "object") {
return null;
}
return record.metadata as Record<string, any>;
}
function getActionLabel(actionType: string) {
return ACTION_LABEL_MAP[actionType] || actionType;
}
export default function Progress() {
const { user } = useAuth();
const { data: records, isLoading } = trpc.record.list.useQuery({ limit: 100 });
@@ -181,7 +203,16 @@ export default function Progress() {
<CardContent>
{(records?.length || 0) > 0 ? (
<div className="space-y-2">
{(records || []).slice(0, 20).map((record: any) => (
{(records || []).slice(0, 20).map((record: any) => {
const metadata = getRecordMetadata(record);
const actionSummary = metadata?.actionSummary && typeof metadata.actionSummary === "object"
? Object.entries(metadata.actionSummary as Record<string, number>).filter(([, count]) => Number(count) > 0)
: [];
const topActions = actionSummary
.sort((left, right) => Number(right[1]) - Number(left[1]))
.slice(0, 3);
return (
<div key={record.id} className="border-b py-2 last:border-0">
<div className="flex items-start justify-between gap-3">
<div className="flex items-start gap-3">
@@ -193,15 +224,27 @@ export default function Progress() {
<div>
<p className="text-sm font-medium">{record.exerciseName}</p>
<p className="text-xs text-muted-foreground">
{formatDateTimeShanghai(record.trainingDate || record.createdAt)}
{formatDateTimeShanghai(record.trainingDate || record.createdAt, { second: "2-digit" })}
{record.durationMinutes ? ` · ${record.durationMinutes}分钟` : ""}
{record.sourceType ? ` · ${record.sourceType}` : ""}
</p>
{record.actionCount ? (
<p className="mt-1 text-xs text-muted-foreground">
{record.actionCount}
</p>
) : null}
<div className="mt-1 flex flex-wrap items-center gap-2">
{record.actionCount ? (
<Badge variant="outline" className="text-[11px]">
{record.actionCount}
</Badge>
) : null}
{metadata?.dominantAction ? (
<Badge variant="secondary" className="text-[11px]">
{getActionLabel(String(metadata.dominantAction))}
</Badge>
) : null}
{topActions.map(([actionType, count]) => (
<Badge key={`${record.id}-${actionType}`} variant="secondary" className="text-[11px]">
{getActionLabel(actionType)} {count}
</Badge>
))}
</div>
</div>
</div>
<div className="flex items-center gap-2">
@@ -236,36 +279,36 @@ export default function Progress() {
</div>
</div>
{record.metadata ? (
{metadata ? (
<div className="mt-4 space-y-3">
{record.metadata.dominantAction ? (
{metadata.dominantAction ? (
<div>
<div className="text-xs uppercase tracking-[0.16em] text-muted-foreground"></div>
<div className="mt-1 font-medium">{String(record.metadata.dominantAction)}</div>
<div className="mt-1 font-medium">{getActionLabel(String(metadata.dominantAction))}</div>
</div>
) : null}
{record.metadata.actionSummary && Object.keys(record.metadata.actionSummary).length > 0 ? (
{metadata.actionSummary && Object.keys(metadata.actionSummary).length > 0 ? (
<div>
<div className="text-xs uppercase tracking-[0.16em] text-muted-foreground"></div>
<div className="mt-2 flex flex-wrap gap-2">
{Object.entries(record.metadata.actionSummary as Record<string, number>)
{Object.entries(metadata.actionSummary as Record<string, number>)
.filter(([, count]) => Number(count) > 0)
.map(([actionType, count]) => (
<Badge key={actionType} variant="secondary">
{actionType} {count}
{getActionLabel(actionType)} {count}
</Badge>
))}
</div>
</div>
) : null}
{record.metadata.validityStatus ? (
{metadata.validityStatus ? (
<div>
<div className="text-xs uppercase tracking-[0.16em] text-muted-foreground"></div>
<div className="mt-1 font-medium">{String(record.metadata.validityStatus)}</div>
{record.metadata.invalidReason ? (
<div className="mt-1 text-xs text-muted-foreground">{String(record.metadata.invalidReason)}</div>
<div className="mt-1 font-medium">{String(metadata.validityStatus)}</div>
{metadata.invalidReason ? (
<div className="mt-1 text-xs text-muted-foreground">{String(metadata.invalidReason)}</div>
) : null}
</div>
) : null}
@@ -281,7 +324,8 @@ export default function Progress() {
</div>
) : null}
</div>
))}
);
})}
</div>
) : (
<div className="py-8 text-center text-muted-foreground text-sm">