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, type ContinuousUsageBlock,
} from '../training/continuousUsage'; } from '../training/continuousUsage';
import { TrainingChart } from '../components/TrainingChart'; import { TrainingChart } from '../components/TrainingChart';
import { TrackRow } from '../components/TrackRow';
import { EmptyState } from '../components/EmptyState';
import { colors, spacing, typography } from '../theme'; import { colors, spacing, typography } from '../theme';
type HistoryMode = 'windows' | 'continuous' | 'range'; type HistoryMode = 'windows' | 'continuous' | 'range';
@@ -360,46 +362,25 @@ function WindowHistory({
</View> </View>
<View style={styles.timeline}> <View style={styles.timeline}>
{sessions.map(session => { {sessions.map((session, index) => {
const isOpen = selectedId === session.id; const isOpen = selectedId === session.id;
return ( return (
<View key={session.id} style={styles.timelineNode}> <View key={session.id} style={styles.timelineNode}>
<Pressable <TrackRow
accessibilityRole="button" accessibilityLabel={`Janela ${formatSessionDay(session.startedAt)} ${formatSessionHour(session.startedAt)}`}
onPress={() => onOpenSession(session)} day={formatSessionDay(session.startedAt)}
style={({ pressed }) => [ hour={formatSessionHour(session.startedAt)}
styles.sessionStrip, isOpen={isOpen}
pressed ? styles.pressed : null, 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 ? ( {isOpen ? (
<WindowDetail <WindowDetail
@@ -409,6 +390,7 @@ function WindowHistory({
zones={zones} zones={zones}
/> />
) : null} ) : null}
</TrackRow>
</View> </View>
); );
})} })}
@@ -465,50 +447,30 @@ function ContinuousHistory({
</View> </View>
<View style={styles.timeline}> <View style={styles.timeline}>
{blocks.map(block => { {blocks.map((block, index) => {
const isOpen = selectedUsageId === block.id; const isOpen = selectedUsageId === block.id;
return ( return (
<View key={block.id} style={styles.timelineNode}> <View key={block.id} style={styles.timelineNode}>
<Pressable <TrackRow
accessibilityRole="button" accessibilityLabel={`Uso contínuo ${formatSessionDay(block.startedAt)}`}
onPress={() => onOpenBlock(block)} day={formatSessionDay(block.startedAt)}
style={({ pressed }) => [ hour={formatSessionHour(block.startedAt)}
styles.sessionStrip, isOpen={isOpen}
pressed ? styles.pressed : null, 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 ? ( {isOpen && selectedBlock ? (
<UsageDetail block={selectedBlock} /> <UsageDetail block={selectedBlock} />
) : null} ) : null}
</TrackRow>
</View> </View>
); );
})} })}
@@ -591,11 +553,12 @@ function EmptyHistory({
readonly title: string; readonly title: string;
}) { }) {
return ( return (
<View style={styles.emptyBlock}> <EmptyState
<Text style={styles.eyebrow}>{eyebrow}</Text> copy={copy}
<Text style={styles.emptyTitle}>{title}</Text> eyebrow={eyebrow}
<Text style={styles.copy}>{copy}</Text> title={title}
</View> tone={eyebrow === 'ANÁLISE BLOQUEADA' ? 'red' : 'amber'}
/>
); );
} }