eletrotupi / tcc/ frontend/app/entry/mood-components.tsx28784494c2bb40db043631f5ffbb25eebc00273a
6.8 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
  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}>
39
      {/* Header */}
40
      <View style={styles.header}>
41
        <Pressable onPress={() => router.back()} hitSlop={8}>
42
          <Ionicons name="close" size={24} color={Theme.colors.text} />
43
        </Pressable>
44
        <Text style={styles.headerTitle}>Adicionar estados</Text>
45
        <Pressable onPress={handleDone} hitSlop={8}>
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
                  >
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
});