eletrotupi / tcc/ frontend/app/auth/signup.tsx019b3e18cceed747855fe5c1507f5876171dc12b
5.9 KB | 219 lines Raw
1
import React, { useState } from 'react';
2
import {
3
  View,
4
  StyleSheet,
5
  ScrollView,
6
  KeyboardAvoidingView,
7
} from 'react-native';
8
import { Text } from '@/components/ui/Text';
9
import { Link, router } from 'expo-router';
10
import { SafeAreaView } from 'react-native-safe-area-context';
11
import { useThemeColor } from '@/hooks/use-theme-color';
12
import { Button, Input } from '@/components/ui';
13
import { useAuth } from '@/context/AuthContext';
14
import { ApiError } from '@/lib/api';
15
import { useToast } from '@/context/ToastContext';
16
import { translateError, translateFields } from '@/lib/errors/translations';
17

18
export default function SignupScreen() {
19
  const [firstName, setFirstName] = useState('');
20
  const [lastName, setLastName] = useState('');
21
  const [email, setEmail] = useState('');
22
  const [password, setPassword] = useState('');
23
  const [confirmPassword, setConfirmPassword] = useState('');
24
  const [errors, setErrors] = useState<{
25
    firstName?: string;
26
    email?: string;
27
    password?: string;
28
    confirmPassword?: string;
29
  }>({});
30

31
  const [loading, setLoading] = useState(false);
32
  const { signup } = useAuth();
33
  const { showToast } = useToast();
34
  const textColor = useThemeColor({}, 'text');
35
  const backgroundColor = useThemeColor({}, 'background');
36
  const tintColor = useThemeColor({}, 'tint');
37

38
  const validateForm = () => {
39
    const newErrors: typeof errors = {};
40

41
    if (!firstName.trim()) {
42
      newErrors.firstName = 'O nome é obrigatório';
43
    }
44

45
    if (!email) {
46
      newErrors.email = 'Email é obrigatório';
47
    } else if (!/\S+@\S+\.\S+/.test(email)) {
48
      newErrors.email = 'Por favor entre um email válido';
49
    }
50

51
    if (!password) {
52
      newErrors.password = 'Senha é obrigatório';
53
    } else if (password.length < 6) {
54
      newErrors.password = 'A senha deve conter pelo menos 6 caracteres';
55
    }
56

57
    if (!confirmPassword) {
58
      newErrors.confirmPassword = 'Por favor confirme sua senha';
59
    } else if (password !== confirmPassword) {
60
      newErrors.confirmPassword = 'As senhas não coincidem';
61
    }
62

63
    setErrors(newErrors);
64
    return Object.keys(newErrors).length === 0;
65
  };
66

67
  const handleSignup = async () => {
68
    if (!validateForm()) return;
69

70
    setLoading(true);
71
    try {
72
      await signup({ firstName, lastName, email, password });
73
      router.replace('/');
74
    } catch (error: any) {
75
      if (error instanceof ApiError && error.fields) {
76
        setErrors(translateFields(error.fields));
77
      } else {
78
        showToast(translateError(error.message) || 'Falha no cadastro. Tente novamente.', 'error');
79
      }
80
    } finally {
81
      setLoading(false);
82
    }
83
  };
84

85
  return (
86
    <SafeAreaView style={[styles.container, { backgroundColor }]}>
87
      <KeyboardAvoidingView
88
        behavior='height'
89
        style={styles.keyboardView}
90
      >
91
        <ScrollView
92
          contentContainerStyle={styles.scrollContainer}
93
          showsVerticalScrollIndicator={false}
94
        >
95
          <View style={styles.header}>
96
            <Text style={[styles.title, { color: textColor }]}>Criar conta</Text>
97
            <Text style={[styles.subtitle, { color: textColor, opacity: 0.7 }]}>
98
              Cadastre-se para começar a monitorar o seu humor.
99
            </Text>
100
          </View>
101

102
          <View style={styles.form}>
103
            <Input
104
              label="Nome"
105
              value={firstName}
106
              onChangeText={setFirstName}
107
              placeholder="Digite seu nome"
108
              error={errors.firstName}
109
            />
110

111
            <Input
112
              label="Sobrenome (opcional)"
113
              value={lastName}
114
              onChangeText={setLastName}
115
              placeholder="Digite seu sobrenome"
116
            />
117

118
            <Input
119
              label="Email"
120
              type="email"
121
              value={email}
122
              onChangeText={setEmail}
123
              placeholder="Qual seu email?"
124
              error={errors.email}
125
              autoCapitalize="none"
126
            />
127

128
            <Input
129
              label="Senha"
130
              type="password"
131
              value={password}
132
              onChangeText={setPassword}
133
              placeholder="Deve ter no min. 6 caracteres"
134
              error={errors.password}
135
              showPasswordToggle
136
            />
137

138
            <Input
139
              label="Confirme a senha"
140
              type="password"
141
              value={confirmPassword}
142
              onChangeText={setConfirmPassword}
143
              placeholder="Confirme sua senha"
144
              error={errors.confirmPassword}
145
              showPasswordToggle
146
            />
147

148
            <Button
149
              title="Criar conta"
150
              onPress={handleSignup}
151
              loading={loading}
152
              style={styles.signupButton}
153
            />
154
          </View>
155

156
          <View style={styles.footer}>
157
            <Text style={[styles.footerText, { color: textColor, opacity: 0.7 }]}>
158
              Já tem uma conta?{' '}
159
            </Text>
160
            <Link href="/auth/login" asChild style={[styles.link, { color: tintColor }]}>
161
              <Text style={[styles.link, { color: tintColor }]}>
162
                Faça login
163
              </Text>
164
            </Link>
165
          </View>
166
        </ScrollView>
167
      </KeyboardAvoidingView>
168
    </SafeAreaView>
169
  );
170
}
171

172
const styles = StyleSheet.create({
173
  container: {
174
    flex: 1,
175
  },
176
  keyboardView: {
177
    flex: 1,
178
  },
179
  scrollContainer: {
180
    flexGrow: 1,
181
    paddingHorizontal: 24,
182
    justifyContent: 'center',
183
    minHeight: '100%',
184
  },
185
  header: {
186
    alignItems: 'center',
187
    marginBottom: 40,
188
  },
189
  title: {
190
    fontSize: 28,
191
    fontWeight: 'bold',
192
    marginBottom: 8,
193
  },
194
  subtitle: {
195
    fontSize: 16,
196
    textAlign: 'center',
197
  },
198
  form: {
199
    marginBottom: 32,
200
  },
201
  signupButton: {
202
    marginTop: 8,
203
  },
204
  link: {
205
    textAlign: 'center',
206
    fontSize: 14,
207
    fontWeight: '500',
208
  },
209
  footer: {
210
    flexDirection: 'row',
211
    justifyContent: 'center',
212
    alignItems: 'center',
213
    marginTop: 'auto',
214
    paddingBottom: 20,
215
  },
216
  footerText: {
217
    fontSize: 14,
218
  },
219
});