eletrotupi / tcc/ frontend/app/profile.tsx13a51f682d4b5fcaa5c7daa8c766d0d8738d29da
6.6 KB | 253 lines Raw
1
import { useState, useEffect } from 'react';
2
import {
3
  View,
4
  StyleSheet,
5
  ScrollView,
6
  KeyboardAvoidingView,
7
} from 'react-native';
8

9
import { Text } from '@/components/ui/Text';
10
import { ThemedView } from '@/components/ui/themed-view'
11
import { Link, useLocalSearchParams, router } from 'expo-router';
12
import { StatusBar } from 'expo-status-bar';
13
import { SafeAreaView } from 'react-native-safe-area-context';
14
import { useThemeColor } from '@/hooks/use-theme-color';
15
import { Button, Input } from '@/components/ui';
16
import { useAuth } from '@/context/AuthContext';
17
import { apiClient, ApiError, User } from '@/lib/api'
18
import { useToast } from '@/context/ToastContext';
19
import { translateError, translateFields } from '@/lib/errors/translations';
20
import { Card } from '@/components/ui/Cards';
21
import AvatarUpload  from '@/components/ui/AvatarUpload';
22
import { Typography, Spacing, Colors } from '@/constants/theme';
23

24
export default function Profile() {
25
  const { user, updateAuthUser } = useAuth();
26
  const { showToast } = useToast();
27
  const [firstName, setFirstName] = useState(user?.firstName ?? '');
28
  const [lastName, setLastName] = useState(user?.lastName ?? '');
29
  const [email, setEmail] = useState(user?.email ?? '');
30
  const [password, setPassword] = useState('');
31
  const [confirmPassword, setConfirmPassword] = useState('');
32
  const [loading] = useState(false);
33
  const [errors, setErrors] = useState<{
34
    firstName?: string;
35
    lastName?: string;
36
    email?: string;
37
    password?: string;
38
    confirmPassword?: string;
39
  }>({});
40

41
  const backgroundColor = useThemeColor({}, 'background');
42

43
  const validateForm = () => {
44
    const newErrors: {
45
      firstName?: string;
46
      email?: string;
47
      password?: string;
48
      confirmPassword?: string
49
    } = {};
50

51
    if (password) {
52
      if (!confirmPassword) {
53
        newErrors.confirmPassword = 'Precisa confirmar a senha';
54
      } else {
55
        if (password !== confirmPassword) {
56
          newErrors.confirmPassword = "Senhas não conferem";
57
        }
58
      }
59
    }
60

61
    if (!/\S+@\S+\.\S+/.test(email)) {
62
      newErrors.email = 'Por favor, use um email válido';
63
    }
64

65
    if (!firstName) {
66
      newErrors.firstName = 'É necessário ter um nome';
67
    }
68

69
    setErrors(newErrors);
70

71
    return Object.keys(newErrors).length === 0;
72
  }
73

74
  const handleUpdate = async () => {
75
    if (!user) return;
76
    if (!validateForm()) return;
77

78
    try {
79
      const id = user.id;
80

81
      const response = await apiClient.updateUser({
82
        id,
83
        firstName,
84
        lastName,
85
        email,
86
        ...(password ? { password } : {}),
87
      });
88

89
      updateAuthUser(response.user);
90

91
      router.replace('/(tabs)/insights');
92
    } catch (error) {
93
      console.error("[PROFILE]", error)
94
      if (error instanceof ApiError && error.fields) {
95
        setErrors(translateFields(error.fields));
96
      } else {
97
        showToast(translateError(error) || 'Falha ao atualizar o perfil', 'error');
98
      }
99
    }
100
  }
101

102
  const onChangeAvatar = (user: User) => {
103
    updateAuthUser(user);
104
  }
105

106
  return (
107
    <SafeAreaView style={[styles.container, { backgroundColor }]}>
108
      <StatusBar style={'dark'} />
109
      <KeyboardAvoidingView
110
        behavior='height'
111
        style={styles.keyboardView}
112
      >
113
        <ScrollView
114
          contentContainerStyle={styles.scrollContainer}
115
          showsVerticalScrollIndicator={false}
116
        >
117
          <AvatarUpload
118
            currentAvatar={user?.avatarURL}
119
            onUploadSuccess={onChangeAvatar}
120
            onRemoveSuccess={onChangeAvatar} />
121

122
          <View style={styles.form}>
123
            <Card style={styles.cardBlock}>
124
              <Input
125
                label="Primeiro Nome"
126
                type="text"
127
                value={firstName}
128
                onChangeText={setFirstName}
129
                placeholder="Qual seu nome?"
130
                variant="darkGhost"
131
                error={errors.firstName}
132
              />
133

134
              <Input
135
                label="Sobrenome"
136
                type="text"
137
                value={lastName}
138
                variant="darkGhost"
139
                onChangeText={setLastName}
140
                placeholder="Qual seu sobrenome?"
141
                error={errors.lastName}
142
              />
143
            </Card>
144

145
            <Card style={styles.cardBlock}>
146
              <Input
147
                label="Email"
148
                type="text"
149
                value={email}
150
                variant="darkGhost"
151
                onChangeText={setEmail}
152
                placeholder="Qual seu email?"
153
                error={errors.email}
154
              />
155
            </Card>
156

157
            <Card style={styles.cardBlock}>
158
              <Input
159
                label="Nova senha"
160
                type="password"
161
                value={password}
162
                onChangeText={setPassword}
163
                placeholder="Digite uma nova senha"
164
                error={errors.password}
165
                variant="darkGhost"
166
                showPasswordToggle
167
              />
168

169
              <Input
170
                label="Confirmar nova senha"
171
                type="password"
172
                value={confirmPassword}
173
                onChangeText={setConfirmPassword}
174
                placeholder="Confirme a nova senha"
175
                error={errors.confirmPassword}
176
                variant="darkGhost"
177
                showPasswordToggle
178
              />
179

180
              <Text style={styles.cardBlockHint}>
181
                Preencha apenas se quiser mudar a sua senha
182
              </Text>
183
            </Card>
184

185
            <Button
186
              title="Atualizar perfil"
187
              onPress={handleUpdate}
188
              loading={loading}
189
              style={styles.resetButton}
190
            />
191
          </View>
192

193
        </ScrollView>
194
      </KeyboardAvoidingView>
195
    </SafeAreaView>
196
  )
197
}
198

199
const styles = StyleSheet.create({
200
  container: {
201
    flex: 1,
202
  },
203
  keyboardView: {
204
    flex: 1,
205
  },
206
  scrollContainer: {
207
    flexGrow: 1,
208
    paddingHorizontal: 24,
209
    justifyContent: 'start',
210
    minHeight: '100%',
211
  },
212
  header: {
213
    alignItems: 'left',
214
    marginBottom: 40,
215
  },
216
  title: {
217
    fontSize: 28,
218
    fontWeight: 'bold',
219
    marginBottom: 8,
220
  },
221
  subtitle: {
222
    fontSize: 16,
223
    textAlign: 'left',
224
  },
225
  form: {
226
    marginBottom: 32,
227
  },
228
  resetButton: {
229
    marginTop: 8,
230
  },
231
  link: {
232
    textAlign: 'center',
233
    fontSize: 14,
234
    fontWeight: '500',
235
  },
236
  footer: {
237
    flexDirection: 'row',
238
    justifyContent: 'center',
239
    alignItems: 'center',
240
    marginTop: 'auto',
241
    paddingBottom: 20,
242
  },
243
  footerText: {
244
    fontSize: 14,
245
  },
246
  cardBlock: {
247
    padding: Spacing.cardGap
248
  },
249
  cardBlockHint: {
250
    ...Typography.cardHint,
251
    color: Colors.light.textSecondary
252
  }
253
});