feat(history): HistoryScreen with TrackRow + EmptyState

This commit is contained in:
fightrate-bot committed 2026-06-16 01:56:15 -03:00
1 parent cd278aa2b1
commit 139294a6bd
1 file changed
+38 -75
+38 -75
View File
@@ -16,6 +16,8 @@ import {
type ContinuousUsageBlock,
} from '../training/continuousUsage';
import { TrainingChart } from '../components/TrainingChart';
import { TrackRow } from '../components/TrackRow';
import { EmptyState } from '../components/EmptyState';
import { colors, spacing, typography } from '../theme';
type HistoryMode = 'windows' | 'continuous' | 'range';
@@ -360,46 +362,25 @@ function WindowHistory({
</View>
<View style={styles.timeline}>
{sessions.map(session => {
{sessions.map((session, index) => {
const isOpen = selectedId === session.id;
return (
<View key={session.id} style={styles.timelineNode}>
<Pressable
accessibilityRole="button"
onPress={() => onOpenSession(session)}
style={({ pressed }) => [
styles.sessionStrip,
pressed ? styles.pressed : null,
<TrackRow
accessibilityLabel={`Janela ${formatSessionDay(session.startedAt)} ${formatSessionHour(session.startedAt)}`}
day={formatSessionDay(session.startedAt)}
hour={formatSessionHour(session.startedAt)}
isOpen={isOpen}
meta={`${formatDuration(session.durationMs)} · ${session.sampleCount} amostras`}
metrics={[
{ label: `MÉD ${session.averageBpm}` },
{ label: `PICO ${session.peakBpm}`, tone: 'hot' as const },
{ label: `MIN ${session.minBpm}` },
]}
onPress={() => onOpenSession(session)}
title={session.deviceName || 'Cinta cardíaca BLE'}
trackNumber={index + 1}
>
<View style={styles.dateBlock}>
<Text style={styles.sessionDay}>
{formatSessionDay(session.startedAt)}
</Text>
<Text style={styles.sessionHour}>
{formatSessionHour(session.startedAt)}
</Text>
</View>
<View style={styles.sessionBody}>
<Text style={styles.sessionTitle}>
{session.deviceName || 'Cinta cardíaca BLE'}
</Text>
<Text style={styles.sessionMeta}>
{formatDuration(session.durationMs)} · {session.sampleCount}{' '}
amostras · toque para detalhes
</Text>
<View style={styles.metricLine}>
<Text style={styles.metricText}>
MÉD {session.averageBpm}
</Text>
<Text style={styles.metricTextHot}>
PICO {session.peakBpm}
</Text>
<Text style={styles.metricText}>MIN {session.minBpm}</Text>
</View>
</View>
{isOpen ? <OpenIcon /> : <ClosedIcon />}
</Pressable>
{isOpen ? (
<WindowDetail
@@ -409,6 +390,7 @@ function WindowHistory({
zones={zones}
/>
) : null}
</TrackRow>
</View>
);
})}
@@ -465,50 +447,30 @@ function ContinuousHistory({
</View>
<View style={styles.timeline}>
{blocks.map(block => {
{blocks.map((block, index) => {
const isOpen = selectedUsageId === block.id;
return (
<View key={block.id} style={styles.timelineNode}>
<Pressable
accessibilityRole="button"
onPress={() => onOpenBlock(block)}
style={({ pressed }) => [
styles.sessionStrip,
pressed ? styles.pressed : null,
<TrackRow
accessibilityLabel={`Uso contínuo ${formatSessionDay(block.startedAt)}`}
day={formatSessionDay(block.startedAt)}
hour={formatSessionHour(block.startedAt)}
isOpen={isOpen}
meta={`${formatUsageRange(block)} · ${block.sampleCount} amostras`}
metrics={[
{ label: `MÉD ${block.averageBpm}` },
{ label: `PICO ${block.peakBpm}`, tone: 'hot' as const },
{ label: `MIN ${block.minBpm}` },
]}
onPress={() => onOpenBlock(block)}
title={block.deviceName || 'Uso contínuo'}
trackNumber={index + 1}
>
<View style={styles.dateBlock}>
<Text style={styles.sessionDay}>
{formatSessionDay(block.startedAt)}
</Text>
<Text style={styles.sessionHour}>
{formatSessionHour(block.startedAt)}
</Text>
</View>
<View style={styles.sessionBody}>
<Text style={styles.sessionTitle}>
{block.deviceName || 'Uso contínuo'}
</Text>
<Text style={styles.sessionMeta}>
{formatUsageRange(block)} · {block.sampleCount} amostras ·
toque para detalhes
</Text>
<View style={styles.metricLine}>
<Text style={styles.metricText}>
MÉD {block.averageBpm}
</Text>
<Text style={styles.metricTextHot}>
PICO {block.peakBpm}
</Text>
<Text style={styles.metricText}>MIN {block.minBpm}</Text>
</View>
</View>
{isOpen ? <OpenIcon /> : <ClosedIcon />}
</Pressable>
{isOpen && selectedBlock ? (
<UsageDetail block={selectedBlock} />
) : null}
</TrackRow>
</View>
);
})}
@@ -591,11 +553,12 @@ function EmptyHistory({
readonly title: string;
}) {
return (
<View style={styles.emptyBlock}>
<Text style={styles.eyebrow}>{eyebrow}</Text>
<Text style={styles.emptyTitle}>{title}</Text>
<Text style={styles.copy}>{copy}</Text>
</View>
<EmptyState
copy={copy}
eyebrow={eyebrow}
title={title}
tone={eyebrow === 'ANÁLISE BLOQUEADA' ? 'red' : 'amber'}
/>
);
}