eletrotupi / tcc/ commit / 0df6bea

frontend: display generated trigger pattern insights

Pedro Lucas Porcellis porcellis@eletrotupi.com 1 month ago 0df6beae35dccec34c54d7848d067aca2070eb29
Parents: 2581519
3 file(s) changed
  • frontend/app/(tabs)/insights.tsx +2 -6
  • frontend/components/insights/TriggerPatternWidget.tsx +90 -0
  • frontend/hooks/useTrigger.queries.ts +8 -0
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";
▸ 3 unchanged lines
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 (
▸ 25 unchanged lines
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

▸ 84 unchanged lines
144 140
  insightMetric: {
145 141
    ...Typography.headlineLg,
146 142
    fontSize: 22,
147 143
  },
148 144
});
frontend/components/insights/TriggerPatternWidget.tsx
@@ -0,0 +1,90 @@
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) =>
▸ 69 unchanged lines
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
▸ 20 unchanged lines
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
};