fitaiProto/apps/mobile/src/components/ActivityWidget.tsx
2026-03-11 03:43:34 +01:00

225 lines
6.0 KiB
TypeScript

import React, { useEffect } from "react";
import {
View,
Text,
StyleSheet,
Dimensions,
ActivityIndicator,
} from "react-native";
import { LinearGradient } from "expo-linear-gradient";
import { Ionicons } from "@expo/vector-icons";
import { theme } from "../styles/theme";
import { useStatistics } from "../contexts/StatisticsContext";
const { width } = Dimensions.get("window");
interface ActivityWidgetProps {
steps?: number;
calories: number;
duration?: number; // in minutes
}
export function ActivityWidget({
steps = 0,
calories,
duration = 0,
}: ActivityWidgetProps) {
const { statistics, loading, refetchStatistics } = useStatistics();
useEffect(() => {
refetchStatistics();
}, [refetchStatistics]);
// Calculate weekly activity bars from weekly trend data
const getWeeklyBars = () => {
if (!statistics || statistics.weeklyTrend.length === 0) {
// Fallback mock data
return [0.4, 0.6, 0.3, 0.8, 0.5, 0.9, 0.7];
}
// Get last 7 weeks and normalize to 0-1 scale
const last7Weeks = statistics.weeklyTrend.slice(-7);
const maxCheckIns = Math.max(...last7Weeks.map((w) => w.checkIns), 1);
return last7Weeks.map((week) => {
// Normalize check-ins to 0.2-1.0 range for better visualization
const normalized = week.checkIns / maxCheckIns;
return Math.max(normalized * 0.8 + 0.2, 0.2);
});
};
const weeklyBars = getWeeklyBars();
const checkInsThisWeek = statistics?.attendance.checkInsThisWeek || 0;
const currentStreak = statistics?.attendance.currentStreak || 0;
return (
<View style={styles.container}>
<LinearGradient
colors={theme.gradients.dark}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={[styles.card, theme.shadows.medium]}
>
<View style={styles.header}>
<Text style={styles.title}>Daily Activity</Text>
<Ionicons name="stats-chart" size={20} color={theme.colors.primary} />
</View>
{loading ? (
<View style={styles.loadingContainer}>
<ActivityIndicator size="small" color={theme.colors.primary} />
</View>
) : (
<>
<View style={styles.statsRow}>
<View style={styles.statItem}>
<View
style={[
styles.iconContainer,
{ backgroundColor: "rgba(59, 130, 246, 0.2)" },
]}
>
<Ionicons name="checkmark-circle" size={20} color="#3b82f6" />
</View>
<Text style={styles.statValue}>{checkInsThisWeek}</Text>
<Text style={styles.statLabel}>This Week</Text>
</View>
<View style={styles.divider} />
<View style={styles.statItem}>
<View
style={[
styles.iconContainer,
{ backgroundColor: "rgba(239, 68, 68, 0.2)" },
]}
>
<Ionicons name="flame" size={20} color="#ef4444" />
</View>
<Text style={styles.statValue}>{calories}</Text>
<Text style={styles.statLabel}>Kcal</Text>
</View>
<View style={styles.divider} />
<View style={styles.statItem}>
<View
style={[
styles.iconContainer,
{ backgroundColor: "rgba(16, 185, 129, 0.2)" },
]}
>
<Ionicons name="trophy" size={20} color="#10b981" />
</View>
<Text style={styles.statValue}>{currentStreak}</Text>
<Text style={styles.statLabel}>Day Streak</Text>
</View>
</View>
{/* Weekly Bar Chart */}
<View style={styles.chartContainer}>
{weeklyBars.map((height, index) => (
<View key={index} style={styles.barContainer}>
<LinearGradient
colors={theme.gradients.primaryVertical}
style={[styles.bar, { height: height * 60 }]}
/>
<Text style={styles.dayLabel}>
{["M", "T", "W", "T", "F", "S", "S"][index % 7]}
</Text>
</View>
))}
</View>
</>
)}
</LinearGradient>
</View>
);
}
const styles = StyleSheet.create({
container: {
marginHorizontal: 20,
marginBottom: 20,
},
card: {
borderRadius: 24,
padding: 20,
borderWidth: 1,
borderColor: "rgba(255, 255, 255, 0.1)",
},
header: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 20,
},
title: {
fontSize: 18,
fontWeight: "700",
color: "#fff",
},
loadingContainer: {
paddingVertical: 40,
alignItems: "center",
justifyContent: "center",
},
statsRow: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 24,
},
statItem: {
alignItems: "center",
flex: 1,
},
iconContainer: {
width: 40,
height: 40,
borderRadius: 20,
justifyContent: "center",
alignItems: "center",
marginBottom: 8,
},
statValue: {
fontSize: 20,
fontWeight: "700",
color: "#fff",
marginBottom: 2,
},
statLabel: {
fontSize: 12,
color: theme.colors.gray400,
fontWeight: "500",
},
divider: {
width: 1,
height: 40,
backgroundColor: "rgba(255, 255, 255, 0.1)",
},
chartContainer: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "flex-end",
height: 80,
paddingTop: 10,
borderTopWidth: 1,
borderTopColor: "rgba(255, 255, 255, 0.1)",
},
barContainer: {
alignItems: "center",
gap: 8,
},
bar: {
width: 6,
borderRadius: 3,
opacity: 0.8,
},
dayLabel: {
fontSize: 10,
color: theme.colors.gray500,
fontWeight: "600",
},
});