eletrotupi / tcc/ commit / de5e5ec

frontend: rework copy text and general UX flow for mood entries

Pedro Lucas Porcellis porcellis@eletrotupi.com 1 month ago de5e5eca8460913bbc90890ce76ac0b905dae0cd
Parents: ec834db
4 file(s) changed
  • frontend/app/(tabs)/new/entry.tsx +56 -61
  • frontend/app/(tabs)/new/index.tsx +2 -0
  • frontend/app/entry/[id].tsx +98 -65
  • frontend/app/entry/mood-components.tsx +53 -54
frontend/app/(tabs)/new/entry.tsx
1 -
import { useState, useEffect } from 'react';
2 -
import {
3 -
  Link, router, useLocalSearchParams
4 -
} from 'expo-router';
1 +
import { useState, useEffect } from "react";
2 +
import { Link, router, useLocalSearchParams } from "expo-router";
5 3

6 -
import {
7 -
  View,
8 -
  Text,
9 -
  StyleSheet,
10 -
  ScrollView
11 -
} from 'react-native';
4 +
import { View, Text, StyleSheet, ScrollView } from "react-native";
12 5

13 -
import { SafeAreaView } from 'react-native-safe-area-context';
14 -
import { ThemedText } from '@/components/misc/themed-text';
15 -
import { ThemedView } from '@/components/misc/themed-view';
16 -
import { ScreenLayout } from '@/components/ui/ScreenLayout';
17 -
import { useAuth } from '@/context/AuthContext';
18 -
import { Grid, Col, Row, Between, Center } from '@/components/ui/LayoutHelpers';
19 -
import { Card } from '@/components/ui/Cards';
20 -
import { Button } from '@/components/ui/Button';
21 -
import { Input, TextArea } from '@/components/ui/Input';
22 -
import { Section, SectionHeader } from '@/components/ui/Sections';
23 -
import { Spacing, Typography, Colors } from '@/constants/theme';
24 -
import { ScaleSlider } from '@/components/ui/ScaleSlider';
25 -
import { MoodComponentCard } from '@/components/ui/MoodComponentCard';
26 -
import { Ionicons } from '@expo/vector-icons';
27 -
import { useThemeColor, useCreateMoodEntry } from '@/hooks';
28 -
import {
29 -
  useMoodEntryStore,
30 -
} from '@/stores';
6 +
import { SafeAreaView } from "react-native-safe-area-context";
7 +
import { ThemedText } from "@/components/misc/themed-text";
8 +
import { ThemedView } from "@/components/misc/themed-view";
9 +
import { ScreenLayout } from "@/components/ui/ScreenLayout";
10 +
import { useAuth } from "@/context/AuthContext";
11 +
import { Grid, Col, Row, Between, Center } from "@/components/ui/LayoutHelpers";
12 +
import { Card } from "@/components/ui/Cards";
13 +
import { Button } from "@/components/ui/Button";
14 +
import { Input, TextArea } from "@/components/ui/Input";
15 +
import { Section, SectionHeader } from "@/components/ui/Sections";
16 +
import { Spacing, Typography, Colors } from "@/constants/theme";
17 +
import { ScaleSlider } from "@/components/ui/ScaleSlider";
18 +
import { MoodComponentCard } from "@/components/ui/MoodComponentCard";
19 +
import { Ionicons } from "@expo/vector-icons";
20 +
import { useThemeColor, useCreateMoodEntry } from "@/hooks";
21 +
import { useMoodEntryStore } from "@/stores";
31 22

32 -
import {
33 -
  MOODS,
34 -
  getMood
35 -
} from '@/constants/moods';
23 +
import { MOODS, getMood } from "@/constants/moods";
36 24

37 -
import {
38 -
  intensityToValue
39 -
} from '@/constants/mood-components';
25 +
import { intensityToValue } from "@/constants/mood-components";
40 26

41 -
import {
42 -
  apiClient
43 -
} from '@/lib/api';
27 +
import { apiClient } from "@/lib/api";
44 28

45 29
export default function NewMoodEntry() {
46 30
  const { user } = useAuth();
47 -
  const tintColor = useThemeColor({}, 'tint');
31 +
  const tintColor = useThemeColor({}, "tint");
48 32

49 33
  const { initialMood } = useLocalSearchParams();
50 34
  const [annotation, setAnnotation] = useState();
51 35
  const [stress, setStress] = useState(0);
52 36
  const [anxiety, setAnxiety] = useState(0);
53 37
  const [energy, setEnergy] = useState(0);
54 38

55 -
  const {
56 -
    selectedMood,
57 -
    setSelectedMood,
58 -
    components,
59 -
    reset
60 -
  } = useMoodEntryStore();
39 +
  const { selectedMood, setSelectedMood, components, reset } =
40 +
    useMoodEntryStore();
61 41

62 42
  const createMoodEntry = useCreateMoodEntry();
63 43

64 44
  useEffect(() => {
65 45
    setSelectedMood(getMood(initialMood));
▸ 11 unchanged lines
77 57
      anxietyLevel: anxiety,
78 58
      moment: new Date(),
79 59
      annotation: annotation,
80 60
      moodComponents: components.map((c) => ({
81 61
        component: c.id.toUpperCase(),
82 -
        intensity: intensityToValue(c.intensity)
83 -
      }))
62 +
        intensity: intensityToValue(c.intensity),
63 +
      })),
84 64
    };
85 65

86 -
    await createMoodEntry.mutateAsync(data)
66 +
    await createMoodEntry.mutateAsync(data);
87 67

88 68
    reset();
89 -
    router.replace("/")
90 -
  }
69 +
    router.replace("/");
70 +
  };
91 71

92 72
  const editComponents = () => {
93 -
    router.push("/entry/mood-components")
94 -
  }
73 +
    router.push("/entry/mood-components");
74 +
  };
95 75

96 76
  return (
97 77
    <View>
98 -
      <ScrollView
99 -
        scrollEventThrottle={16}>
78 +
      <ScrollView scrollEventThrottle={16}>
100 79
        <View style={styles.container}>
101 80
          <Center>
102 81
            <ThemedText style={styles.statusPrefix}>
103 82
              Como você está se sentindo agora?
104 83
            </ThemedText>
▸ 2 unchanged lines
107 86
              {selectedMood?.label || "Neutro"}
108 87
            </ThemedText>
109 88
          </Center>
110 89

111 90
          <Card style={styles.resumeCard}>
91 +
            <ThemedText style={styles.infoText}>
92 +
              Não existe resposta certa ou errada. Estime, de 0 a 10, como tu
93 +
              percebe tua energia, estresse e ansiedade neste momento. O
94 +
              objetivo é registrar uma impressão geral, não uma medida precisa.
95 +
            </ThemedText>
96 +

112 97
            <Col gap={24}>
113 98
              <ScaleSlider
114 99
                variant="rich"
115 100
                label="Energia"
116 101
                value={energy}
117 102
                onValueChange={setEnergy}
118 -
                icon={<Ionicons name="flower-outline" size={20} color={tintColor} />}
103 +
                icon={
104 +
                  <Ionicons name="flower-outline" size={20} color={tintColor} />
105 +
                }
119 106
                minLabel="Baixa"
120 107
                maxLabel="Alta"
121 108
              />
122 109

123 110
              <ScaleSlider
▸ 30 unchanged lines
154 141
              placeholder="Escreva uma nota rápida sobre o seu dia até o momento..."
155 142
            />
156 143
          </Card>
157 144

158 145
          <Section>
159 -
            <SectionHeader title="Componentes do Humor" info="Editar" />
146 +
            <SectionHeader title="Também estou..." />
160 147
            {components.length > 0 ? (
161 148
              <Grid gap={2} style={{ marginTop: 8 }}>
162 149
                {components.map((comp) => (
163 150
                  <MoodComponentCard
164 151
                    key={comp.id}
▸ 2 unchanged lines
167 154
                  />
168 155
                ))}
169 156
              </Grid>
170 157
            ) : null}
171 158

172 -
            <Button title="Adicionar Componentes"
159 +
            <Button
160 +
              title="Adicionar estados & emoções"
173 161
              onPress={editComponents}
174 162
              textStyle={{ fontWeight: 500 }}
175 163
              variant="dashed"
176 164
            />
177 165
          </Section>
178 166

179 -
          <Button title="Salvar Registro"
167 +
          <Button
168 +
            title="Salvar Registro"
180 169
            onPress={saveMoodEntry}
181 170
            disabled={!components.length}
182 171
          />
183 172
        </View>
184 173
      </ScrollView>
▸ 5 unchanged lines
190 179
  container: {
191 180
    gap: 24,
192 181
    paddingHorizontal: Spacing.containerPadding,
193 182
    paddingVertical: Spacing.sectionGap,
194 183
  },
184 +
  infoText: {
185 +
    ...Typography.labelSm,
186 +
    color: Colors.light.textSecondary,
187 +
    marginBottom: 20,
188 +
    fontWeight: "400" as const,
189 +
  },
195 190
  statusText: {
196 191
    ...Typography.headlineXg,
197 192
  },
198 193
  statusPrefix: {
199 -
    ...Typography.bodyMd
194 +
    ...Typography.bodyMd,
200 195
  },
201 196
  annotationsCard: {
202 -
    padding: Spacing.cardGap
197 +
    padding: Spacing.cardGap,
203 198
  },
204 199
  resumeCard: {
205 200
    padding: Spacing.cardGap,
206 -
  }
201 +
  },
207 202
});
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";
▸ 78 unchanged lines
84 84
        </Card>
85 85
      </Section>
86 86

87 87
      {/* TODO: Check whether we gonna have to generate these on demand */}
88 88
      <Section>
89 +
        <SectionHeader title="Entre hoje e ontem" />
90 +

89 91
        <View
90 92
          style={[
91 93
            {
92 94
              flexDirection: "row",
93 95
              gap: 16,
▸ 64 unchanged lines
158 160
    fontSize: 11,
159 161
    fontWeight: 600,
160 162
    lineHeight: 16.5,
161 163
  },
162 164
});
frontend/app/entry/[id].tsx
1 -
import { useState, useEffect } from 'react';
2 -
import { StyleSheet, Text, View, ScrollView, Pressable, ActivityIndicator } from 'react-native';
3 -
import { useLocalSearchParams, router, Stack } from 'expo-router';
4 -
import { format } from 'date-fns';
5 -
import { ptBR } from 'date-fns/locale';
1 +
import { useState, useEffect } from "react";
2 +
import {
3 +
  StyleSheet,
4 +
  Text,
5 +
  View,
6 +
  ScrollView,
7 +
  Pressable,
8 +
  ActivityIndicator,
9 +
} from "react-native";
10 +
import { useLocalSearchParams, router, Stack } from "expo-router";
11 +
import { format } from "date-fns";
12 +
import { ptBR } from "date-fns/locale";
6 13

7 -
import { Card, SubtleInfoCard } from '@/components/ui/Cards';
8 -
import { Badge } from '@/components/ui/Badge';
9 -
import { Button } from '@/components/ui/Button';
10 -
import { Col, Between, Row } from '@/components/ui/LayoutHelpers';
11 -
import { Section, SectionHeader } from '@/components/ui/Sections';
12 -
import { Theme, Colors, Spacing } from '@/constants/theme';
13 -
import { getMood } from '@/constants/moods';
14 -
import { getMoodComponent, intensityLabel, intensityToValue } from '@/constants/mood-components';
15 -
import { useMoodEntry, useDeleteMoodEntry } from '@/hooks';
16 -
import { getTimeBadge, formatMoment } from '@/lib/utils/time';
17 -
import { MoodComponentCard } from '@/components/ui/MoodComponentCard';
14 +
import { Card, SubtleInfoCard } from "@/components/ui/Cards";
15 +
import { Badge } from "@/components/ui/Badge";
16 +
import { Button } from "@/components/ui/Button";
17 +
import { Col, Between, Row } from "@/components/ui/LayoutHelpers";
18 +
import { Section, SectionHeader } from "@/components/ui/Sections";
19 +
import { Theme, Colors, Spacing } from "@/constants/theme";
20 +
import { getMood } from "@/constants/moods";
21 +
import {
22 +
  getMoodComponent,
23 +
  intensityLabel,
24 +
  intensityToValue,
25 +
} from "@/constants/mood-components";
26 +
import { useMoodEntry, useDeleteMoodEntry } from "@/hooks";
27 +
import { getTimeBadge, formatMoment } from "@/lib/utils/time";
28 +
import { MoodComponentCard } from "@/components/ui/MoodComponentCard";
18 29

19 30
export default function MoodDetailScreen() {
20 31
  const { id } = useLocalSearchParams<{ id: string }>();
21 32
  const { data: entry, isLoading, isError } = useMoodEntry(id);
22 33
  const deleteMoodEntry = useDeleteMoodEntry();
▸ 6 unchanged lines
29 40
      const createdAt = new Date(entry?.moment);
30 41
      const msSinceCreation = Date.now() - createdAt.getTime();
31 42
      const deletionWindowMs = 5 * 60 * 1000;
32 43

33 44
      const newCanDelete = msSinceCreation < deletionWindowMs;
34 -
      const newMinutesLeft = Math.max(0, Math.ceil((deletionWindowMs - msSinceCreation) / 60000));
45 +
      const newMinutesLeft = Math.max(
46 +
        0,
47 +
        Math.ceil((deletionWindowMs - msSinceCreation) / 60000),
48 +
      );
35 49

36 50
      setCanDelete(newCanDelete);
37 51
      setMinutesLeft(newMinutesLeft);
38 52
    };
39 53

▸ 33 unchanged lines
73 87
    router.back();
74 88
  };
75 89

76 90
  const deleteButtonMsg = () => {
77 91
    const msg = "Excluir Registro";
78 -
    const timeLeftStr = `(Disponível por ${minutesLeft < 1 ? '<1' : minutesLeft}:00)`;
79 -
    const suffix = canDelete ? timeLeftStr : "(Expirado)"
92 +
    const timeLeftStr = `(Disponível por ${minutesLeft < 1 ? "<1" : minutesLeft}:00)`;
93 +
    const suffix = canDelete ? timeLeftStr : "(Expirado)";
80 94

81 -
    return `${msg} ${suffix}`
82 -
  }
95 +
    return `${msg} ${suffix}`;
96 +
  };
83 97

84 98
  return (
85 99
    <>
86 -
      <Stack.Screen options={{ title: 'Ver detalhes' }} />
100 +
      <Stack.Screen options={{ title: "Ver detalhes" }} />
87 101

88 102
      <ScrollView
89 103
        style={styles.container}
90 104
        contentContainerStyle={styles.content}
91 105
        showsVerticalScrollIndicator={false}
92 106
      >
93 107
        {/* Header card */}
94 108
        <Card style={styles.headerCard}>
95 109
          <Between>
96 -
            <Row gap={12} style={{ alignItems: 'center' }}>
97 -
              <Text style={styles.moodIcon}>{moodDef?.icon ?? '😐'}</Text>
110 +
            <Row gap={12} style={{ alignItems: "center" }}>
111 +
              <Text style={styles.moodIcon}>{moodDef?.icon ?? "😐"}</Text>
98 112
              <Col gap={2}>
99 -
                <Text style={styles.moodLabel}>{moodDef?.label ?? entry.selectedMood}</Text>
113 +
                <Text style={styles.moodLabel}>
114 +
                  {moodDef?.label ?? entry.selectedMood}
115 +
                </Text>
100 116
                <Text style={styles.moodTime}>{formatMoment(moment)}</Text>
101 117
              </Col>
102 118
            </Row>
103 119
            <Badge
104 120
              label="Registro Ativo"
▸ 3 unchanged lines
108 124
          </Between>
109 125
        </Card>
110 126

111 127
        {/* Core levels */}
112 128
        <Section>
113 -
          <SectionHeader title="Níveis" variant="subtle" />
114 -
            {[
115 -
              { id: 'energy', label: 'Energia',  value: entry.energyLevel  },
116 -
              { id: 'stress', label: 'Estresse', value: entry.stressLevel  },
117 -
              { id: 'anxiety', label: 'Ansiedade', value: entry.anxietyLevel },
118 -
            ].map((item, index, arr) => (
119 -
              <Card key={item.id} style={{ padding: Spacing.cardGap }}>
120 -
                <View key={item.label}>
121 -
                  <View style={styles.componentRow}>
122 -
                    <Text style={styles.componentLabel}>{item.label}</Text>
123 -
                    <Text style={styles.componentIntensityGreen}>{item.value}/10</Text>
124 -
                  </View>
129 +
          <SectionHeader title="Níveis emocionais" variant="subtle" />
130 +
          {[
131 +
            { id: "energy", label: "Energia", value: entry.energyLevel },
132 +
            { id: "stress", label: "Estresse", value: entry.stressLevel },
133 +
            { id: "anxiety", label: "Ansiedade", value: entry.anxietyLevel },
134 +
          ].map((item, index, arr) => (
135 +
            <Card key={item.id} style={{ padding: Spacing.cardGap }}>
136 +
              <View key={item.label}>
137 +
                <View style={styles.componentRow}>
138 +
                  <Text style={styles.componentLabel}>{item.label}</Text>
139 +
                  <Text style={styles.componentIntensityGreen}>
140 +
                    {item.value}/10
141 +
                  </Text>
142 +
                </View>
125 143

126 -
                  <View style={styles.progressTrack}>
127 -
                    <View style={[styles.progressFill, { width: `${(item.value / 10) * 100}%` }]} />
128 -
                  </View>
144 +
                <View style={styles.progressTrack}>
145 +
                  <View
146 +
                    style={[
147 +
                      styles.progressFill,
148 +
                      { width: `${(item.value / 10) * 100}%` },
149 +
                    ]}
150 +
                  />
129 151
                </View>
130 -
              </Card>
131 -
            ))}
152 +
              </View>
153 +
            </Card>
154 +
          ))}
132 155
        </Section>
133 156

134 157
        <Section>
135 -
          <SectionHeader title="Componentes" variant="subtle" />
158 +
          <SectionHeader title="Além disso, sinto..." variant="subtle" />
136 159
          <Col gap={0}>
137 160
            {entry.moodComponents.map((comp, index) => {
138 161
              const def = getMoodComponent(comp.component.toLowerCase());
139 -
              const numericIntensity = comp.intensity === 'LIGHT' ? 2 : comp.intensity === 'MODERATE' ? 5 : 8;
162 +
              const numericIntensity =
163 +
                comp.intensity === "LIGHT"
164 +
                  ? 2
165 +
                  : comp.intensity === "MODERATE"
166 +
                    ? 5
167 +
                    : 8;
140 168

141 169
              return (
142 170
                <MoodComponentCard
143 171
                  key={comp.id}
144 172
                  id={comp.component.toLowerCase()}
▸ 5 unchanged lines
150 178
        </Section>
151 179

152 180
        {/* Annotation / notes */}
153 181
        {entry.annotation && (
154 182
          <Section>
155 -
            <SectionHeader title="Notas" variant="subtle" />
183 +
            <SectionHeader title="Anotações" variant="subtle" />
156 184
            <Card style={{ padding: Spacing.cardGap }}>
157 -
              <Text style={styles.annotation}>"{entry.annotation?.trim()}"</Text>
185 +
              <Text style={styles.annotation}>
186 +
                "{entry.annotation?.trim()}"
187 +
              </Text>
158 188
            </Card>
159 189
          </Section>
160 190
        )}
161 191

162 192
        {/* Delete */}
163 193
        <View style={styles.section}>
164 194
          <Text style={styles.sectionLabel}>GESTÃO DO REGISTRO</Text>
165 -
          <Button variant="danger"
195 +
          <Button
196 +
            variant="danger"
166 197
            isLoading={deleteMoodEntry.isPending}
167 198
            disabled={!canDelete || deleteMoodEntry.isPending}
168 199
            onPress={handleDelete}
169 -
            title={deleteButtonMsg()} />
200 +
            title={deleteButtonMsg()}
201 +
          />
170 202

171 -
          <SubtleInfoCard style={styles.deleteInfo}
203 +
          <SubtleInfoCard
204 +
            style={styles.deleteInfo}
172 205
            text={`
173 206
              A exclusão de registros é permitida apenas nos primeiros 5 minutos
174 207
              após o envio para garantir a integridade do histórico emocional.
175 -
              `} />
208 +
              `}
209 +
          />
176 210
        </View>
177 -

178 211
      </ScrollView>
179 212
    </>
180 213
  );
181 214
}
182 215

▸ 7 unchanged lines
190 223
    gap: Spacing.sectionGap,
191 224
    paddingBottom: 48,
192 225
  },
193 226
  centered: {
194 227
    flex: 1,
195 -
    justifyContent: 'center',
196 -
    alignItems: 'center',
228 +
    justifyContent: "center",
229 +
    alignItems: "center",
197 230
    gap: 12,
198 231
  },
199 232
  errorText: {
200 233
    fontSize: 15,
201 234
    color: Colors.light.textSecondary,
▸ 3 unchanged lines
205 238
    paddingVertical: 10,
206 239
    backgroundColor: Theme.colors.tint,
207 240
    borderRadius: Theme.borderRadius.md,
208 241
  },
209 242
  backButtonText: {
210 -
    fontWeight: '700',
243 +
    fontWeight: "700",
211 244
    color: Theme.colors.text,
212 245
  },
213 246

214 247
  // Header
215 248
  headerCard: {
▸ 2 unchanged lines
218 251
  moodIcon: {
219 252
    fontSize: 40,
220 253
  },
221 254
  moodLabel: {
222 255
    fontSize: 17,
223 -
    fontWeight: '700',
256 +
    fontWeight: "700",
224 257
    color: Colors.light.text,
225 258
  },
226 259
  moodTime: {
227 260
    fontSize: 13,
228 261
    color: Colors.light.textSecondary,
▸ 3 unchanged lines
232 265
  section: {
233 266
    gap: 8,
234 267
  },
235 268
  sectionLabel: {
236 269
    fontSize: 11,
237 -
    fontWeight: '600',
270 +
    fontWeight: "600",
238 271
    letterSpacing: 0.8,
239 272
    color: Colors.light.textSecondary,
240 -
    textTransform: 'uppercase',
273 +
    textTransform: "uppercase",
241 274
  },
242 275

243 276
  // Components
244 277
  componentRow: {
245 -
    flexDirection: 'row',
246 -
    justifyContent: 'space-between',
247 -
    alignItems: 'center',
278 +
    flexDirection: "row",
279 +
    justifyContent: "space-between",
280 +
    alignItems: "center",
248 281
    paddingVertical: 10,
249 282
  },
250 283
  componentLabel: {
251 284
    fontSize: 15,
252 -
    fontWeight: '700',
285 +
    fontWeight: "700",
253 286
    color: Colors.light.text,
254 287
  },
255 288
  componentIntensity: {
256 289
    fontSize: 13,
257 290
    color: Colors.light.textSecondary,
258 291
  },
259 292
  componentIntensityGreen: {
260 293
    fontSize: 13,
261 -
    fontWeight: '700',
294 +
    fontWeight: "700",
262 295
    color: Theme.colors.tint,
263 296
  },
264 297
  progressTrack: {
265 298
    height: 6,
266 299
    backgroundColor: Theme.colors.light.divider,
267 300
    borderRadius: Theme.borderRadius.full,
268 301
    marginBottom: 4,
269 -
    overflow: 'hidden',
302 +
    overflow: "hidden",
270 303
  },
271 304
  progressFill: {
272 -
    height: '100%',
305 +
    height: "100%",
273 306
    backgroundColor: Theme.colors.light.tint,
274 307
    borderRadius: Theme.borderRadius.full,
275 308
  },
276 309
  // Annotation
277 310
  annotation: {
278 311
    fontSize: 14,
279 312
    lineHeight: 22,
280 313
    color: Colors.light.textSecondary,
281 -
    fontStyle: 'italic',
314 +
    fontStyle: "italic",
282 315
    padding: 4,
283 316
  },
284 317
});
frontend/app/entry/mood-components.tsx
1 -
import React from 'react';
2 -
import {
3 -
  View,
4 -
  Text,
5 -
  ScrollView,
6 -
  Pressable,
7 -
  StyleSheet,
8 -
} from 'react-native';
9 -
import { router } from 'expo-router';
10 -
import { Ionicons } from '@expo/vector-icons';
1 +
import React from "react";
2 +
import { View, Text, ScrollView, Pressable, StyleSheet } from "react-native";
3 +
import { router } from "expo-router";
4 +
import { Ionicons } from "@expo/vector-icons";
11 5

12 -
import { ScaleSlider } from '@/components/ui/ScaleSlider';
13 -
import { Row, Grid } from '@/components/ui/LayoutHelpers';
14 -
import { Section, SectionHeader } from '@/components/ui/Sections';
15 -
import { Button } from '@/components/ui/Button';
16 -
import { Card } from '@/components/ui/Cards';
17 -
import { Theme } from '@/constants/theme';
6 +
import { ScaleSlider } from "@/components/ui/ScaleSlider";
7 +
import { Row, Grid } from "@/components/ui/LayoutHelpers";
8 +
import { Section, SectionHeader } from "@/components/ui/Sections";
9 +
import { Button } from "@/components/ui/Button";
10 +
import { Card } from "@/components/ui/Cards";
11 +
import { Theme } from "@/constants/theme";
18 12
import {
19 13
  MOOD_COMPONENTS,
20 14
  getMoodComponent,
21 15
  intensityLabel,
22 -
} from '@/constants/mood-components';
16 +
} from "@/constants/mood-components";
23 17

24 -
import {
25 -
  useMoodEntryStore,
26 -
} from '@/stores/moodEntry';
18 +
import { useMoodEntryStore } from "@/stores/moodEntry";
27 19

28 20
export default function MoodComponentsScreen() {
29 21
  const {
30 22
    selectedMood,
31 23
    components,
▸ 9 unchanged lines
41 33
    router.back();
42 34
  };
43 35

44 36
  return (
45 37
    <View style={styles.container}>
46 -

47 38
      {/* Header */}
48 39
      <View style={styles.header}>
49 40
        <Pressable onPress={() => router.back()} hitSlop={8}>
50 41
          <Ionicons name="close" size={24} color={Theme.colors.text} />
51 42
        </Pressable>
52 -
        <Text style={styles.headerTitle}>Editar Componentes</Text>
43 +
        <Text style={styles.headerTitle}>Adicionar estados</Text>
53 44
        <Pressable onPress={handleDone} hitSlop={8}>
54 45
          <Text style={styles.headerAction}>Pronto</Text>
55 46
        </Pressable>
56 47
      </View>
57 48

58 49
      <ScrollView
59 50
        contentContainerStyle={styles.content}
60 51
        showsVerticalScrollIndicator={false}
61 52
      >
62 -

63 53
        {/* Subtitle */}
64 54
        {selectedMood && (
65 55
          <View style={styles.subtitleBlock}>
66 -
            <Text style={styles.subtitleSmall}>Baseado no seu humor selecionado</Text>
67 -
            <Text style={styles.subtitleLarge}>{selectedMood.label}</Text>
56 +
            <Text style={styles.subtitleSmall}>
57 +
              Além do humor principal, o que melhor descreve como estou me
58 +
              sentindo agora?
59 +
            </Text>
68 60
          </View>
69 61
        )}
70 62

71 63
        {/* Active components */}
72 64
        {components.length > 0 && (
73 65
          <Section>
74 66
            <SectionHeader
75 -
              title="Intensidade dos Componentes"
76 -
              info={`Ativos: ${components.length}`} />
67 +
              title="Intensidade"
68 +
              info={`Ativos: ${components.length}`}
69 +
            />
77 70

78 71
            {components.map((comp) => {
79 72
              const def = getMoodComponent(comp.id);
80 73
              if (!def) return null;
81 74

82 75
              return (
83 76
                <Card key={comp.id} style={styles.activeCard}>
84 77
                  {/* Card header */}
85 78
                  <Row style={styles.activeCardHeader}>
86 -
                    <View style={[styles.dot, { backgroundColor: def.color }]} />
79 +
                    <View
80 +
                      style={[styles.dot, { backgroundColor: def.color }]}
81 +
                    />
87 82
                    <Text style={styles.activeCardLabel}>{def.label}</Text>
88 83
                    <Pressable
89 84
                      onPress={() => removeComponent(comp.id)}
90 85
                      hitSlop={8}
91 86
                    >
▸ 19 unchanged lines
111 106
                    style={styles.slider}
112 107
                  />
113 108
                </Card>
114 109
              );
115 110
            })}
116 -

117 111
          </Section>
118 112
        )}
119 113

120 114
        {/* Available to add */}
121 115
        {available.length > 0 && (
122 116
          <Section>
123 -
            <SectionHeader title="Adicionar outros" />
117 +
            <SectionHeader title="Estado & Emoções" />
124 118
            <Grid gap={2}>
125 119
              {available.map((def) => (
126 120
                <Card key={def.id} style={styles.availableItem}>
127 121
                  <Pressable
128 122
                    onPress={() => addComponent(def.id)}
129 123
                    style={styles.availableItemInternalWrapper}
130 124
                  >
131 125
                    <Row gap={8} style={styles.availableInner}>
132 -
                      <View style={[styles.dot, { backgroundColor: def.color }]} />
126 +
                      <View
127 +
                        style={[styles.dot, { backgroundColor: def.color }]}
128 +
                      />
133 129
                      <Text style={styles.availableLabel}>{def.label}</Text>
134 130
                    </Row>
135 131

136 132
                    <Ionicons
137 133
                      name="add-circle-outline"
▸ 4 unchanged lines
142 138
                </Card>
143 139
              ))}
144 140
            </Grid>
145 141
          </Section>
146 142
        )}
143 +

144 +
        <Button title="Pronto" onPress={handleDone} />
147 145
      </ScrollView>
148 146
    </View>
149 147
  );
150 148
}
151 149

152 150
const styles = StyleSheet.create({
153 151
  container: {
154 152
    flex: 1,
155 153
  },
156 154
  header: {
157 -
    flexDirection: 'row',
158 -
    justifyContent: 'space-between',
159 -
    alignItems: 'center',
155 +
    flexDirection: "row",
156 +
    justifyContent: "space-between",
157 +
    alignItems: "center",
160 158
    paddingHorizontal: Theme.spacing.containerPadding,
161 159
    paddingVertical: 16,
162 160
    borderBottomWidth: 1,
163 161
    borderBottomColor: Theme.colors.light.divider,
164 162
  },
165 163
  headerTitle: {
166 164
    fontSize: Theme.typography.bodyLg.fontSize,
167 -
    fontWeight: '700',
165 +
    fontWeight: "700",
168 166
    color: Theme.colors.light.text,
169 167
  },
170 168
  headerAction: {
171 169
    fontSize: Theme.typography.bodyMd.fontSize,
172 -
    fontWeight: '600',
170 +
    fontWeight: "600",
173 171
    color: Theme.colors.light.tint,
174 172
  },
175 173
  content: {
176 174
    padding: Theme.spacing.containerPadding,
177 175
    gap: Theme.spacing.sectionGap,
178 176
    paddingBottom: 120,
179 177
  },
180 178
  subtitleBlock: {
181 -
    alignItems: 'center',
179 +
    alignItems: "center",
182 180
    gap: 4,
183 181
  },
184 182
  subtitleSmall: {
185 183
    fontSize: Theme.typography.bodyMd.fontSize,
186 184
    color: Theme.colors.light.textSecondary,
185 +
    textAlign: "center",
187 186
  },
188 187
  subtitleLarge: {
189 188
    fontSize: 28,
190 -
    fontWeight: '700',
189 +
    fontWeight: "700",
191 190
    color: Theme.colors.light.text,
192 191
  },
193 192
  section: {
194 193
    gap: Theme.spacing.cardGap,
195 194
  },
196 195
  sectionHeader: {
197 -
    justifyContent: 'space-between',
198 -
    alignItems: 'center',
196 +
    justifyContent: "space-between",
197 +
    alignItems: "center",
199 198
  },
200 199
  activeCard: {
201 200
    padding: Theme.spacing.cardGap,
202 201
  },
203 202
  activeCardHeader: {
204 -
    alignItems: 'center',
205 -
    justifyContent: 'space-between',
206 -
    marginBottom: 16
203 +
    alignItems: "center",
204 +
    justifyContent: "space-between",
205 +
    marginBottom: 16,
207 206
  },
208 207
  activeCardLabel: {
209 208
    flex: 1,
210 -
    ... { ...Theme.typography.bodyLg, lineHeight: 24 },
209 +
    ...{ ...Theme.typography.bodyLg, lineHeight: 24 },
211 210
    color: Theme.colors.light.text,
212 211
    marginLeft: 4,
213 212
  },
214 213
  dot: {
215 214
    width: 12,
▸ 3 unchanged lines
219 218
  },
220 219
  slider: {
221 220
    marginTop: 4,
222 221
  },
223 222
  availableItem: {
224 -
    flexDirection: 'row',
225 -
    justifyContent: 'space-between',
226 -
    alignItems: 'center',
223 +
    flexDirection: "row",
224 +
    justifyContent: "space-between",
225 +
    alignItems: "center",
227 226
    paddingHorizontal: 12,
228 227
    paddingVertical: 10,
229 228
  },
230 229
  availableItemInternalWrapper: {
231 -
    flexDirection: 'row',
232 -
    width: '100%',
233 -
    justifyContent: 'space-between',
234 -
    alignItems: 'center',
230 +
    flexDirection: "row",
231 +
    width: "100%",
232 +
    justifyContent: "space-between",
233 +
    alignItems: "center",
235 234
  },
236 235
  availableInner: {
237 -
    alignItems: 'center',
236 +
    alignItems: "center",
238 237
    flexShrink: 1,
239 238
  },
240 239
  availableLabel: {
241 240
    fontSize: Theme.typography.bodyMd.fontSize,
242 -
    fontWeight: '500',
241 +
    fontWeight: "500",
243 242
    color: Theme.colors.light.text,
244 -
  }
243 +
  },
245 244
});