frontend: display generated trigger pattern insights
Pedro Lucas Porcellis porcellis@eletrotupi.com · 4 months ago
0df6beae35dccec34c54d7848d067aca2070eb29 Browse filesParents
2581519Changed files
3
Additions
+100
Deletions
-6
frontend/app/(tabs)/insights.tsx
1 1
import { View, Text, ScrollView, StyleSheet } from "react-native";
2 2
3 3
import { ThemedText } from "@/components/misc/themed-text";
4 4
import { ThemedView } from "@/components/misc/themed-view";
5 5
import { ScreenLayout } from "@/components/ui/ScreenLayout";
9 9
import { useThemeColor } from "@/hooks/use-theme-color";
10 10
import { Spacing, BorderRadius, Colors, Typography } from "@/constants/theme";
11 11
import { WeeklyResume } from "@/components/insights/WeeklyResume";
12 12
import { InsightCard, Highlight } from "@/components/insights/InsightCard";
13 13
import { MoodTrendWidget } from "@/components/insights/MoodTrendWidget";
14 +
import { TriggerPatternWidget } from "@/components/insights/TriggerPatternWidget";
14 15
15 16
export default function Insights() {
16 17
const { user } = useAuth();
17 18
18 19
return (
44 45
accent="green"
45 46
/>
46 47
47 48
<MoodTrendWidget />
48 49
49 -
<InsightCard
50 -
category="Gatilhos"
51 -
body="Trabalho representa 45% dos registros."
52 -
metric="45%"
53 -
accent="neutral"
54 -
/>
50 +
<TriggerPatternWidget />
55 51
</Section>
56 52
</ScreenLayout>
57 53
);
58 54
}
59 55
144 140
insightMetric: {
145 141
...Typography.headlineLg,
146 142
fontSize: 22,
147 143
},
148 144
});
/dev/null → frontend/components/insights/TriggerPatternWidget.tsx
1 +
import React from "react";
2 +
import { Text, View, StyleSheet, ActivityIndicator } from "react-native";
3 +
import { MaterialIcons } from "@expo/vector-icons";
4 +
import { Card } from "@/components/ui/Cards";
5 +
import { useInsight } from "@/hooks/useInsights.queries";
6 +
import { Colors, Spacing } from "@/constants/theme";
7 +
import { getTriggerCategory } from "@/constants/trigger-categories";
8 +
import { InsightCard } from "./InsightCard";
9 +
10 +
export const TriggerPatternWidget: React.FC = () => {
11 +
const { data: insight, isLoading } = useInsight("TRIGGER_PATTERN");
12 +
13 +
if (isLoading) {
14 +
return (
15 +
<Card style={styles.container}>
16 +
<ActivityIndicator />
17 +
</Card>
18 +
);
19 +
}
20 +
21 +
if (!insight) {
22 +
return (
23 +
<InsightCard
24 +
category="Gatilhos"
25 +
body={
26 +
<Text style={styles.empty}>
27 +
Registre gatilhos com mais frequência para identificar padrões.
28 +
</Text>
29 +
}
30 +
accent="neutral"
31 +
metric="—"
32 +
/>
33 +
);
34 +
}
35 +
36 +
const metadata = insight.metadata as {
37 +
topCategory: string;
38 +
topCount: number;
39 +
total: number;
40 +
distribution: Record<string, number>;
41 +
};
42 +
43 +
const pct =
44 +
metadata.total > 0
45 +
? Math.round((metadata.topCount / metadata.total) * 100)
46 +
: 0;
47 +
48 +
const topMeta = getTriggerCategory(metadata.topCategory);
49 +
50 +
return (
51 +
<InsightCard
52 +
category="Gatilhos"
53 +
body={
54 +
<View style={styles.bodyRow}>
55 +
<Text style={styles.bodyText}>{insight.body}</Text>
56 +
</View>
57 +
}
58 +
metric={`${pct}%`}
59 +
accent="neutral"
60 +
/>
61 +
);
62 +
};
63 +
64 +
const styles = StyleSheet.create({
65 +
container: {
66 +
padding: Spacing.cardGap,
67 +
},
68 +
bodyRow: {
69 +
flexDirection: "row",
70 +
alignItems: "center",
71 +
gap: 6,
72 +
},
73 +
iconWrap: {
74 +
width: 22,
75 +
height: 22,
76 +
borderRadius: 11,
77 +
alignItems: "center",
78 +
justifyContent: "center",
79 +
},
80 +
bodyText: {
81 +
flex: 1,
82 +
fontSize: 14,
83 +
color: Colors.light.text,
84 +
lineHeight: 20,
85 +
},
86 +
empty: {
87 +
color: Colors.light.lightSlateGray,
88 +
marginTop: 4,
89 +
},
90 +
});
frontend/hooks/useTrigger.queries.ts
1 1
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
2 2
3 3
import { apiClient, Trigger, CreateTriggerPayload } from "@/lib/api";
4 +
import { insightKeys } from "@/hooks/useInsights.queries";
4 5
5 6
export const triggerKeys = {
6 7
all: () => ["triggers"] as const,
7 8
lists: () => [...triggerKeys.all(), "list"] as const,
8 9
list: (filters?: TriggerFilters) =>
78 79
},
79 80
80 81
// On success, replace optimistic record with real one from server
81 82
onSuccess: () => {
82 83
queryClient.invalidateQueries({ queryKey: triggerKeys.lists() });
84 +
// Delay to allow the async queue job to finish writing the new insight row
85 +
setTimeout(() => {
86 +
queryClient.invalidateQueries({ queryKey: insightKeys.byType("TRIGGER_PATTERN") });
87 +
}, 2000);
83 88
},
84 89
});
85 90
};
86 91
87 92
// Delete a mood entry with optimistic removal
108 113
}
109 114
},
110 115
111 116
onSuccess: () => {
112 117
queryClient.invalidateQueries({ queryKey: triggerKeys.lists() });
118 +
setTimeout(() => {
119 +
queryClient.invalidateQueries({ queryKey: insightKeys.byType("TRIGGER_PATTERN") });
120 +
}, 2000);
113 121
},
114 122
});
115 123
};