feat: initial project scaffold with Watch companion app
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { View, Text, ScrollView } from "react-native";
|
||||
import { getWeeklyStats, getTotalStats, getPersonalBests } from "@db/operations";
|
||||
import { formatDistance } from "@utils/formatDistance";
|
||||
import { formatTime } from "@utils/formatTime";
|
||||
|
||||
type WeeklyStats = {
|
||||
count: number;
|
||||
totalDistance: number;
|
||||
totalDuration: number;
|
||||
avgCalories: number;
|
||||
};
|
||||
|
||||
type TotalStats = {
|
||||
totalWorkouts: number;
|
||||
totalDistance: number;
|
||||
totalDuration: number;
|
||||
};
|
||||
|
||||
type PersonalBest = {
|
||||
distance: number;
|
||||
bestTime: number;
|
||||
bestSwolf: number | null;
|
||||
};
|
||||
|
||||
export function StatsScreen() {
|
||||
const [weekly, setWeekly] = useState<WeeklyStats | null>(null);
|
||||
const [total, setTotal] = useState<TotalStats | null>(null);
|
||||
const [bests, setBests] = useState<PersonalBest[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
loadStats();
|
||||
}, []);
|
||||
|
||||
const loadStats = async () => {
|
||||
const [w, t, b] = await Promise.all([
|
||||
getWeeklyStats(),
|
||||
getTotalStats(),
|
||||
getPersonalBests(),
|
||||
]);
|
||||
setWeekly(w as WeeklyStats);
|
||||
setTotal(t as TotalStats);
|
||||
setBests(b as PersonalBest[]);
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollView className="flex-1 bg-white p-4">
|
||||
<Text className="text-2xl font-bold text-slate-900 mb-6">Statistics</Text>
|
||||
|
||||
<Text className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2">
|
||||
This Week
|
||||
</Text>
|
||||
<View className="bg-sky-50 rounded-2xl p-4 mb-6">
|
||||
{weekly ? (
|
||||
<View className="flex-row justify-between">
|
||||
<View className="items-center flex-1">
|
||||
<Text className="text-2xl font-bold text-sky-600">{weekly.count}</Text>
|
||||
<Text className="text-xs text-slate-500">Workouts</Text>
|
||||
</View>
|
||||
<View className="items-center flex-1">
|
||||
<Text className="text-2xl font-bold text-sky-600">
|
||||
{formatDistance(weekly.totalDistance)}
|
||||
</Text>
|
||||
<Text className="text-xs text-slate-500">Distance</Text>
|
||||
</View>
|
||||
<View className="items-center flex-1">
|
||||
<Text className="text-2xl font-bold text-sky-600">
|
||||
{formatTime(weekly.totalDuration)}
|
||||
</Text>
|
||||
<Text className="text-xs text-slate-500">Duration</Text>
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<Text className="text-slate-400 text-center py-4">No data yet</Text>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<Text className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2">
|
||||
All Time
|
||||
</Text>
|
||||
<View className="bg-slate-50 rounded-2xl p-4 mb-6">
|
||||
{total ? (
|
||||
<View className="flex-row justify-between">
|
||||
<View className="items-center flex-1">
|
||||
<Text className="text-2xl font-bold text-slate-700">{total.totalWorkouts}</Text>
|
||||
<Text className="text-xs text-slate-500">Workouts</Text>
|
||||
</View>
|
||||
<View className="items-center flex-1">
|
||||
<Text className="text-2xl font-bold text-slate-700">
|
||||
{formatDistance(total.totalDistance)}
|
||||
</Text>
|
||||
<Text className="text-xs text-slate-500">Distance</Text>
|
||||
</View>
|
||||
<View className="items-center flex-1">
|
||||
<Text className="text-2xl font-bold text-slate-700">
|
||||
{formatTime(total.totalDuration)}
|
||||
</Text>
|
||||
<Text className="text-xs text-slate-500">Duration</Text>
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<Text className="text-slate-400 text-center py-4">No data yet</Text>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{bests.length > 0 && (
|
||||
<>
|
||||
<Text className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2">
|
||||
Personal Bests
|
||||
</Text>
|
||||
{bests.map((best) => (
|
||||
<View
|
||||
key={best.distance}
|
||||
className="bg-green-50 rounded-2xl p-4 mb-2 flex-row justify-between items-center"
|
||||
>
|
||||
<Text className="text-green-700 font-medium">{best.distance}m</Text>
|
||||
<View className="items-end">
|
||||
<Text className="text-green-800 font-bold">{formatTime(best.bestTime)}</Text>
|
||||
{best.bestSwolf && (
|
||||
<Text className="text-green-600 text-xs">SWOLF: {best.bestSwolf}</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user