eletrotupi / tcc/ frontend/app/entry/mood-components.tsxc901c35af9c84b6a29e5185545ced1129f33b8f4
6.9 KB | 245 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
} from "@/constants/mood-components";
17

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

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

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

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

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

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

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

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

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

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

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

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

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

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