225 lines
6.0 KiB
TypeScript
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",
|
|
},
|
|
});
|