eletrotupi / tcc/ commit / a13a1c0

frontend: introduce a empty state component

Pedro Lucas Porcellis porcellis@eletrotupi.com · 4 months ago a13a1c0382a8d1ae8d553f0a91d6e5716c967b31 Browse files
Parents d28d4c0
Changed files
3
Additions
+66
Deletions
-10
frontend/app/(tabs)/history.tsx
1 1
import { useState } from "react";
2 2
import { ScrollView, View, Text } from "react-native";
3 3
import { ScreenLayout } from "@/components/ui/ScreenLayout";
4 4
import { Section, SectionHeader } from "@/components/ui/Sections";
5 5
import { FilterPills } from "@/components/ui/FilterPills";
10 10
import { SleepRecordCard } from "@/components/history/SleepRecordCard";
11 11
import { TriggerHistoryCard } from "@/components/history/TriggerCard";
12 12
import { MedicineHistoryCard } from "@/components/history/MedicineHistoryCard";
13 13
import { AppointmentHistoryCard } from "@/components/history/AppointmentHistoryCard";
14 14
import { ActivityHistoryCard } from "@/components/history/ActivityHistoryCard";
15 +
import { EmptyState } from "@/components/ui/EmptyState";
15 16

16 17
function HistoryCardRenderer({ card }: { card: HistoryCard }) {
17 18
  switch (card.category) {
18 19
    case "mood":
19 20
      return <MoodHistoryCard card={card as any} />;
49 50
      showNotificationBadge={true}
50 51
    >
51 52
      <ScrollView>
52 53
        <FilterPills active={activeFilter} onChange={setActiveFilter} />
53 54

54 -
        {Object.entries(grouped).map(([date, cards]) => (
55 -
          <Section key={date}>
56 -
            <SectionHeader title={date} />
57 -
            <View>
58 -
              {cards.map((card) => (
59 -
                <HistoryCardRenderer key={card.id} card={card} />
60 -
              ))}
61 -
            </View>
62 -
          </Section>
63 -
        ))}
55 +
        {Object.keys(grouped).length === 0 ? (
56 +
          <EmptyState
57 +
            title="Nenhum registro encontrado"
58 +
            subtitle="Tente mudar o filtro ou adicione um novo registro."
59 +
            icon="time-outline"
60 +
          />
61 +
        ) : (
62 +
          Object.entries(grouped).map(([date, cards]) => (
63 +
            <Section key={date}>
64 +
              <SectionHeader title={date} />
65 +
              <View>
66 +
                {cards.map((card) => (
67 +
                  <HistoryCardRenderer key={card.id} card={card} />
68 +
                ))}
69 +
              </View>
70 +
            </Section>
71 +
          ))
72 +
        )}
64 73
      </ScrollView>
65 74
    </ScreenLayout>
66 75
  );
67 76
}
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";
25 25
import { useThemeColor, useMoodEntries } from "@/hooks";
26 26

27 27
import MoodEntryLog from "@/components/ui/MoodEntryLog";
28 28
import { DailyEnergyWidget } from "@/components/ui/DailyEnergyWidget";
29 29
import { DailySleepWidget } from "@/components/ui/DailySleepWidget";
30 +
import { EmptyState } from "@/components/ui/EmptyState";
30 31

31 32
export default function New() {
32 33
  const { user } = useAuth();
33 34
  const [mood, setMood] = useState<string>("good");
34 35
  const { data, isLoading } = useMoodEntries({ limit: 5 });
104 105
      <Section>
105 106
        <SectionHeader title="Registros recentes" />
106 107

107 108
        {isLoading ? (
108 109
          <ActivityIndicator />
110 +
        ) : recentEntries.length === 0 ? (
111 +
          <EmptyState
112 +
            title="Nenhum registro ainda"
113 +
            subtitle="Seus registros de humor aparecerão aqui."
114 +
          />
109 115
        ) : (
110 116
          <Col gap={8}>
111 117
            {recentEntries.map((entry) => (
112 118
              <MoodEntryLog key={entry.id} entry={entry} />
113 119
            ))}
155 161
    fontSize: 11,
156 162
    fontWeight: 600,
157 163
    lineHeight: 16.5,
158 164
  },
159 165
});
/dev/null → frontend/components/ui/EmptyState.tsx
1 +
import { View, Text, StyleSheet } from "react-native";
2 +
import { Ionicons } from "@expo/vector-icons";
3 +
import { Colors, Typography, Spacing } from "@/constants/theme";
4 +

5 +
interface EmptyStateProps {
6 +
  title: string;
7 +
  subtitle?: string;
8 +
  icon?: keyof typeof Ionicons.glyphMap;
9 +
}
10 +

11 +
export function EmptyState({
12 +
  title,
13 +
  subtitle,
14 +
  icon = "leaf-outline",
15 +
}: EmptyStateProps) {
16 +
  return (
17 +
    <View style={styles.container}>
18 +
      <Ionicons name={icon} size={40} color={Colors.light.textSecondary} />
19 +
      <Text style={styles.title}>{title}</Text>
20 +
      {subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
21 +
    </View>
22 +
  );
23 +
}
24 +

25 +
const styles = StyleSheet.create({
26 +
  container: {
27 +
    alignItems: "center",
28 +
    paddingVertical: 32,
29 +
    gap: Spacing.inlineGapSm,
30 +
  },
31 +
  title: {
32 +
    ...Typography.bodyLg,
33 +
    color: Colors.light.text,
34 +
    textAlign: "center",
35 +
  },
36 +
  subtitle: {
37 +
    ...Typography.bodyMd,
38 +
    color: Colors.light.textSecondary,
39 +
    textAlign: "center",
40 +
  },
41 +
});