eletrotupi / tcc/ frontend/app/profile.tsxbb31fc84a30face6fb284d7f0c7785eab1b7ec23
6.5 KB | 252 lines Raw
1
import { useState } 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 { router } from 'expo-router';
11
import { StatusBar } from 'expo-status-bar';
12
import { SafeAreaView } from 'react-native-safe-area-context';
13
import { useThemeColor } from '@/hooks/use-theme-color';
14
import { Button, Input } from '@/components/ui';
15
import { useAuth } from '@/context/AuthContext';
16
import { apiClient, ApiError, User } from '@/lib/api'
17
import { useToast } from '@/context/ToastContext';
18
import { translateError, translateFields } from '@/lib/errors/translations';
19
import { Card } from '@/components/ui/Cards';
20
import AvatarUpload  from '@/components/ui/AvatarUpload';
21
import { Typography, Spacing, Colors } from '@/constants/theme';
22

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

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

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

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

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

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

68
    setErrors(newErrors);
69

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

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

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

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

88
      updateAuthUser(response.user);
89

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

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

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

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

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

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

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

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

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

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

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

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