frontend: add daily energy widget
Parents:
05053ef2 file(s) changed
- frontend/app/(tabs)/new/index.tsx +34 -45
- frontend/components/ui/DailyEnergyWidget.tsx +99 -0
frontend/app/(tabs)/new/index.tsx
1 1
import { useEffect, useState } from "react";
2 2
import { StyleSheet, View, Text, ActivityIndicator } from "react-native";
3 3
4 4
import { ThemedText } from "@/components/misc/themed-text";
5 5
import { ThemedView } from "@/components/misc/themed-view";
▸ 17 unchanged lines
23 23
import { MoodDefinition, MOODS } from "@/constants/moods";
24 24
25 25
import { useThemeColor, useMoodEntries } from "@/hooks";
26 26
27 27
import MoodEntryLog from "@/components/ui/MoodEntryLog";
28 +
import { DailyEnergyWidget } from "@/components/ui/DailyEnergyWidget";
28 29
29 30
export default function New() {
30 31
const { user } = useAuth();
31 32
const [mood, setMood] = useState<string>("good");
32 33
const { data, isLoading } = useMoodEntries({ limit: 5 });
▸ 5 unchanged lines
38 39
// TODO: Set down on camelCase vs kebab case
39 40
router.navigate(`/new/entry?initialMood=${mood}`);
40 41
};
41 42
42 43
const onNotificationPress = () => {
43 -
router.push('/notifications');
44 -
}
44 +
router.push("/notifications");
45 +
};
45 46
46 47
return (
47 48
<ScreenLayout
48 49
userName={user.firstName}
49 50
userAvatar={user.avatarURL}
▸ 32 unchanged lines
82 83
</Card>
83 84
</Section>
84 85
85 86
{/* TODO: Check whether we gonna have to generate these on demand */}
86 87
<Section>
87 -
<Grid gap={4}>
88 -
<Card style={{ padding: Spacing.cardGap }}>
89 -
<View
90 -
style={{
91 -
flex: 1,
92 -
flexDirection: "row",
93 -
flexGrow: 0,
94 -
paddingVertical: 10,
95 -
}}
96 -
>
97 -
<Ionicons name="flash" size={20} color="#64748B" />
88 +
<View
89 +
style={[
90 +
{
91 +
flexDirection: "row",
92 +
gap: 16,
93 +
},
94 +
]}
95 +
>
96 +
<View style={{ flex: 1 }}>
97 +
<DailyEnergyWidget />
98 +
</View>
98 99
99 -
<Text style={{ ...Typography.headlineMd, marginBottom: 10 }}>
100 -
Energia
101 -
</Text>
102 -
</View>
100 +
<View style={{ flex: 1 }}>
101 +
<Card style={{ padding: Spacing.cardGap }}>
102 +
<View
103 +
style={{
104 +
flex: 1,
105 +
flexDirection: "row",
106 +
flexGrow: 0,
107 +
paddingVertical: 10,
108 +
}}
109 +
>
110 +
<Ionicons name="moon" size={20} color="#64748B" />
103 111
104 -
<Text style={{ ...Typography.headlineMd, marginBottom: 10 }}>
105 -
Média Alta
106 -
</Text>
107 -
108 -
<View style={styles.lineTrack}>
109 -
<View style={[styles.lineFill, { width: `35%` }]} />
110 -
</View>
111 -
</Card>
112 -
113 -
<Card style={{ padding: Spacing.cardGap }}>
114 -
<View
115 -
style={{
116 -
flex: 1,
117 -
flexDirection: "row",
118 -
flexGrow: 0,
119 -
paddingVertical: 10,
120 -
}}
121 -
>
122 -
<Ionicons name="moon-outline" size={20} color="#64748B" />
112 +
<Text style={{ ...Typography.headlineMd, marginBottom: 10 }}>
113 +
Sono
114 +
</Text>
115 +
</View>
123 116
124 117
<Text style={{ ...Typography.headlineMd, marginBottom: 10 }}>
125 -
Sono
118 +
7h 30m
126 119
</Text>
127 -
</View>
128 120
129 -
<Text style={{ ...Typography.headlineMd, marginBottom: 10 }}>
130 -
7h 30m
131 -
</Text>
132 -
133 -
<Text style={styles.indicatorGreen}>+45min que ontem</Text>
134 -
</Card>
135 -
</Grid>
121 +
<Text style={styles.indicatorGreen}>+45min que ontem</Text>
122 +
</Card>
123 +
</View>
124 +
</View>
136 125
</Section>
137 126
138 127
<Section>
139 128
<SectionHeader title="Registros recentes" />
140 129
▸ 48 unchanged lines
189 178
fontSize: 11,
190 179
fontWeight: 600,
191 180
lineHeight: 16.5,
192 181
},
193 182
});
frontend/components/ui/DailyEnergyWidget.tsx
@@ -0,0 +1,99 @@
1 + import { ActivityIndicator, StyleSheet, Text, View } from "react-native";
2 + import { Card } from "@/components/ui/Cards";
3 + import { useInsight } from "@/hooks/useInsights.queries";
4 + import { Ionicons } from "@expo/vector-icons";
5 + import { Typography, Spacing, Colors, BorderRadius } from "@/constants/theme";
6 +
7 + export const DailyEnergyWidget = () => {
8 + const { data: insight, isLoading } = useInsight("DAILY_ENERGY", 0);
9 +
10 + const metadata = insight?.metadata as {
11 + peak: number;
12 + trough: number;
13 + entryCount: number;
14 + avgEnergy: number;
15 + };
16 +
17 + const avgEnergy = metadata?.avgEnergy;
18 + const peak = metadata?.peak;
19 +
20 + const energyWidth = (avgEnergy / peak) * 100;
21 + const energySummary = () => {
22 + if (avgEnergy < 3) return "Energia baixa";
23 + if (avgEnergy < 5) return "Media baixa";
24 + if (avgEnergy < 7.5) return "Média Alta";
25 + return "Energia alta";
26 + };
27 +
28 + return (
29 + <Card style={styles.container}>
30 + <View
31 + style={{
32 + flex: 1,
33 + flexDirection: "row",
34 + flexGrow: 0,
35 + paddingVertical: 10,
36 + }}
37 + >
38 + <Ionicons name="flash" size={20} color="#64748B" />
39 +
40 + <Text style={{ ...Typography.headlineMd, marginBottom: 10 }}>
41 + Energia
42 + </Text>
43 + </View>
44 +
45 + <View style={{ flex: 1 }}>
46 + {isLoading ? (
47 + <View style={styles.empty}>
48 + <ActivityIndicator size="small" />
49 + </View>
50 + ) : insight ? (
51 + <>
52 + <Text style={{ ...Typography.headlineMd, marginBottom: 10 }}>
53 + {energySummary()}
54 + </Text>
55 +
56 + <View style={styles.lineTrack}>
57 + <View style={[styles.lineFill, { width: `${energyWidth}%` }]} />
58 + </View>
59 + </>
60 + ) : (
61 + <>
62 + <Text style={styles.empty}>
63 + Não há dados disponíveis para o dia de hoje
64 + </Text>
65 + </>
66 + )}
67 + </View>
68 + </Card>
69 + );
70 + };
71 +
72 + const styles = StyleSheet.create({
73 + container: {
74 + flex: 1,
75 + padding: Spacing.cardGap,
76 + },
77 + empty: {
78 + ...Typography.labelSm,
79 + opacity: 0.5,
80 + },
81 + lineTrack: {
82 + height: 6,
83 + backgroundColor: Colors.light.divider,
84 + borderRadius: BorderRadius.full,
85 + marginBottom: 4,
86 + overflow: "hidden",
87 + },
88 + lineFill: {
89 + height: "100%",
90 + backgroundColor: Colors.light.tint,
91 + borderRadius: BorderRadius.full,
92 + },
93 + indicatorGreen: {
94 + color: Colors.light.tint,
95 + fontSize: 11,
96 + fontWeight: 600,
97 + lineHeight: 16.5,
98 + },
99 + });