eletrotupi / tcc/ frontend/app/entry/mood-components.tsx9da626f48d8fc1ad986733b04198d44cf3d40019
6.9 KB | 246 lines Raw
1
import React from "react";
2
import { View, ScrollView, Pressable, StyleSheet } from "react-native";
3
import { Text } from "@/components/ui/Text";
4
import { router } from "expo-router";
5
import { Ionicons } from "@expo/vector-icons";
6

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

19
import { useMoodEntryStore } from "@/stores/moodEntry";
20

21
export default function MoodComponentsScreen() {
22
  const {
23
    selectedMood,
24
    components,
25
    addComponent,
26
    removeComponent,
27
    setComponentIntensity,
28
  } = useMoodEntryStore();
29

30
  const activeIds = components.map((c) => c.id);
31
  const available = MOOD_COMPONENTS.filter((d) => !activeIds.includes(d.id));
32

33
  const handleDone = () => {
34
    router.back();
35
  };
36

37
  return (
38
    <View style={styles.container} testID="screen-mood-components">
39
      {/* Header */}
40
      <View style={styles.header}>
41
        <Pressable onPress={() => router.back()} hitSlop={8}>
42
          <Ionicons name="close" size={24} color={Theme.colors.light.text} />
43
        </Pressable>
44
        <Text style={styles.headerTitle}>Adicionar estados</Text>
45
        <Pressable onPress={handleDone} hitSlop={8} testID="header-done">
46
          <Text style={styles.headerAction}>Pronto</Text>
47
        </Pressable>
48
      </View>
49

50
      <ScrollView
51
        contentContainerStyle={styles.content}
52
        showsVerticalScrollIndicator={false}
53
      >
54
        {/* Subtitle */}
55
        {selectedMood && (
56
          <View style={styles.subtitleBlock}>
57
            <Text style={styles.subtitleSmall}>
58
              Além do humor principal, o que melhor descreve como estou me
59
              sentindo agora?
60
            </Text>
61
          </View>
62
        )}
63

64
        {/* Active components */}
65
        {components.length > 0 && (
66
          <Section>
67
            <SectionHeader
68
              title="Intensidade"
69
              info={`Ativos: ${components.length}`}
70
            />
71

72
            {components.map((comp) => {
73
              const def = getMoodComponent(comp.id);
74
              if (!def) return null;
75

76
              return (
77
                <Card key={comp.id} style={styles.activeCard}>
78
                  {/* Card header */}
79
                  <Row style={styles.activeCardHeader}>
80
                    <View
81
                      style={[styles.dot, { backgroundColor: def.color }]}
82
                    />
83
                    <Text style={styles.activeCardLabel}>{def.label}</Text>
84
                    <Pressable
85
                      onPress={() => removeComponent(comp.id)}
86
                      hitSlop={8}
87
                    >
88
                      <Ionicons
89
                        name="remove-circle-outline"
90
                        size={22}
91
                        color="#F87171"
92
                      />
93
                    </Pressable>
94
                  </Row>
95

96
                  {/* Slider */}
97
                  <ScaleSlider
98
                    variant="compact"
99
                    label="Intensidade"
100
                    value={comp.intensity}
101
                    onValueChange={(v) => setComponentIntensity(comp.id, v)}
102
                    min={1}
103
                    max={3}
104
                    step={1}
105
                    minLabel="Suave"
106
                    maxLabel="Intensa"
107
                    style={styles.slider}
108
                  />
109
                </Card>
110
              );
111
            })}
112
          </Section>
113
        )}
114

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

134
                    <Ionicons
135
                      name="add-circle-outline"
136
                      size={20}
137
                      color={Theme.colors.light.smallAddButtonIcon}
138
                    />
139
                  </Pressable>
140
                </Card>
141
              ))}
142
            </Grid>
143
          </Section>
144
        )}
145

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

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