eletrotupi / tcc/ frontend/app/entry/post-mood-links.tsxea1475564abe0f26ea6bb9c6c840b714261aad91
6.5 KB | 198 lines Raw
1
import { View, TouchableOpacity, ScrollView, StyleSheet, ActivityIndicator } from 'react-native';
2
import { Text } from '@/components/ui/Text';
3
import { useState } from 'react';
4
import { router, useLocalSearchParams } from 'expo-router';
5
import { Ionicons, MaterialIcons } from '@expo/vector-icons';
6
import { Button } from '@/components/ui/Button';
7
import { Spacing, Typography, Colors, BorderRadius } from '@/constants/theme';
8
import { useTriggers, useLinkMoodToTrigger } from '@/hooks';
9
import { getTriggerCategory } from '@/constants/trigger-categories';
10
import { Trigger } from '@/lib/api';
11

12
type FlowState = 'PICK_TRIGGER' | 'PICK_IMPACT';
13

14
export default function PostMoodLinks() {
15
  const { moodId } = useLocalSearchParams<{ moodId: string }>();
16
  const [flowState, setFlowState] = useState<FlowState>('PICK_TRIGGER');
17
  const [selectedTrigger, setSelectedTrigger] = useState<Trigger | null>(null);
18
  const [impact, setImpact] = useState<number>(3);
19

20
  const { data, isLoading } = useTriggers({ limit: 5 });
21
  const linkMood = useLinkMoodToTrigger();
22

23
  const triggers = data?.entries ?? [];
24

25
  const handleSelectTrigger = (trigger: Trigger) => {
26
    setSelectedTrigger(trigger);
27
    setFlowState('PICK_IMPACT');
28
  };
29

30
  const handleSaveLink = async () => {
31
    if (!selectedTrigger || !moodId) return;
32
    await linkMood.mutateAsync({
33
      triggerId: String(selectedTrigger.id),
34
      moodId,
35
      perceivedImpact: impact,
36
    });
37
    router.back();
38
  };
39

40
  if (flowState === 'PICK_TRIGGER') {
41
    return (
42
      <View style={styles.container}>
43
        <View style={styles.header}>
44
          <Text style={styles.title}>Algo influenciou esse humor?</Text>
45
          <Text style={styles.subtitle}>
46
            Selecione um gatilho recente para vincular
47
          </Text>
48
        </View>
49

50
        <ScrollView contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false}>
51
          {isLoading && <ActivityIndicator style={{ marginTop: 20 }} />}
52

53
          {!isLoading && triggers.length === 0 && (
54
            <Text style={styles.emptyText}>Nenhum gatilho recente</Text>
55
          )}
56

57
          {triggers.map((trigger) => {
58
            const cat = getTriggerCategory(trigger.category);
59
            return (
60
              <TouchableOpacity
61
                key={trigger.id}
62
                style={styles.triggerCard}
63
                activeOpacity={0.7}
64
                onPress={() => handleSelectTrigger(trigger)}
65
              >
66
                <View style={[styles.triggerIcon, { backgroundColor: cat.iconBackground }]}>
67
                  <MaterialIcons name={cat.icon} size={18} color={cat.color} />
68
                </View>
69
                <View style={styles.triggerText}>
70
                  <Text style={styles.triggerCategory}>{cat.label}</Text>
71
                  {trigger.comment ? (
72
                    <Text style={styles.triggerComment} numberOfLines={1}>{trigger.comment}</Text>
73
                  ) : null}
74
                </View>
75
                <Ionicons name="chevron-forward" size={16} color={Colors.light.disabled} />
76
              </TouchableOpacity>
77
            );
78
          })}
79

80
          <Button title="Pular" variant="ghost" onPress={() => router.back()} />
81
        </ScrollView>
82
      </View>
83
    );
84
  }
85

86
  // PICK_IMPACT
87
  const cat = selectedTrigger ? getTriggerCategory(selectedTrigger.category) : null;
88
  return (
89
    <View style={styles.container}>
90
      <View style={styles.header}>
91
        <Text style={styles.title}>Qual foi o impacto?</Text>
92
        <Text style={styles.subtitle}>De 1 (leve) a 5 (muito forte)</Text>
93
      </View>
94

95
      {selectedTrigger && cat && (
96
        <View style={styles.selectedTrigger}>
97
          <View style={[styles.triggerIcon, { backgroundColor: cat.iconBackground }]}>
98
            <MaterialIcons name={cat.icon} size={18} color={cat.color} />
99
          </View>
100
          <Text style={styles.selectedTriggerLabel}>{cat.label}</Text>
101
        </View>
102
      )}
103

104
      <View style={styles.impactRow}>
105
        {[1, 2, 3, 4, 5].map((v) => (
106
          <TouchableOpacity
107
            key={v}
108
            style={[styles.impactDot, impact === v && styles.impactDotActive]}
109
            activeOpacity={0.7}
110
            onPress={() => setImpact(v)}
111
          >
112
            <Text style={[styles.impactLabel, impact === v && styles.impactLabelActive]}>{v}</Text>
113
          </TouchableOpacity>
114
        ))}
115
      </View>
116

117
      <Button
118
        title="Vincular"
119
        onPress={handleSaveLink}
120
        loading={linkMood.isPending}
121
        style={{ marginTop: 8 }}
122
      />
123
      <Button
124
        title="Voltar"
125
        variant="ghost"
126
        onPress={() => setFlowState('PICK_TRIGGER')}
127
      />
128
    </View>
129
  );
130
}
131

132
const styles = StyleSheet.create({
133
  container: {
134
    flex: 1,
135
    paddingHorizontal: Spacing.containerPadding,
136
    paddingTop: 24,
137
    paddingBottom: 36,
138
    gap: 16,
139
  },
140
  header: { gap: 6 },
141
  title: { ...Typography.headlineMd },
142
  subtitle: { ...Typography.bodyMd, color: Colors.light.textSecondary },
143
  scrollContent: { gap: 10, paddingBottom: 20 },
144
  emptyText: { ...Typography.bodyMd, color: Colors.light.textSecondary, textAlign: 'center', marginTop: 20 },
145
  triggerCard: {
146
    flexDirection: 'row',
147
    alignItems: 'center',
148
    backgroundColor: '#fff',
149
    borderRadius: BorderRadius.lg,
150
    padding: Spacing.cardGap,
151
    borderWidth: 1,
152
    borderColor: Colors.light.divider,
153
    gap: 12,
154
  },
155
  triggerIcon: {
156
    width: 36,
157
    height: 36,
158
    borderRadius: BorderRadius.md,
159
    alignItems: 'center',
160
    justifyContent: 'center',
161
  },
162
  triggerText: { flex: 1, gap: 2 },
163
  triggerCategory: { ...Typography.bodyMd, fontWeight: '600' },
164
  triggerComment: { ...Typography.labelSm, color: Colors.light.textSecondary },
165
  selectedTrigger: {
166
    flexDirection: 'row',
167
    alignItems: 'center',
168
    gap: 12,
169
    padding: Spacing.cardGap,
170
    backgroundColor: Colors.light.surface,
171
    borderRadius: BorderRadius.lg,
172
    borderWidth: 1,
173
    borderColor: Colors.light.divider,
174
  },
175
  selectedTriggerLabel: { ...Typography.bodyMd, fontWeight: '600' },
176
  impactRow: {
177
    flexDirection: 'row',
178
    justifyContent: 'center',
179
    gap: 12,
180
    marginVertical: 8,
181
  },
182
  impactDot: {
183
    width: 48,
184
    height: 48,
185
    borderRadius: 24,
186
    borderWidth: 2,
187
    borderColor: Colors.light.divider,
188
    alignItems: 'center',
189
    justifyContent: 'center',
190
    backgroundColor: '#fff',
191
  },
192
  impactDotActive: {
193
    borderColor: Colors.light.tint,
194
    backgroundColor: Colors.light.tint + '1A',
195
  },
196
  impactLabel: { fontSize: 18, fontWeight: '600', color: Colors.light.textSecondary },
197
  impactLabelActive: { color: Colors.light.text },
198
});