eletrotupi / tcc/ frontend/app/entry/mood-components.tsxb996777000e11000717aefd4afbdde6ac3c4c587
6.8 KB | 244 lines Raw
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";
5

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";
12
import {
13
  MOOD_COMPONENTS,
14
  getMoodComponent,
15
  intensityLabel,
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}>
38
      {/* Header */}
39
      <View style={styles.header}>
40
        <Pressable onPress={() => router.back()} hitSlop={8}>
41
          <Ionicons name="close" size={24} color={Theme.colors.text} />
42
        </Pressable>
43
        <Text style={styles.headerTitle}>Adicionar estados</Text>
44
        <Pressable onPress={handleDone} hitSlop={8}>
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
                  >
125
                    <Row gap={8} style={styles.availableInner}>
126
                      <View
127
                        style={[styles.dot, { backgroundColor: def.color }]}
128
                      />
129
                      <Text style={styles.availableLabel}>{def.label}</Text>
130
                    </Row>
131

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

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

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