eletrotupi / tcc/ commit / 0df6bea

frontend: display generated trigger pattern insights

Pedro Lucas Porcellis porcellis@eletrotupi.com · 4 months ago 0df6beae35dccec34c54d7848d067aca2070eb29 Browse files
Parents 2581519
Changed 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
};