eletrotupi / tcc/ frontend/app/auth/forgot-password.tsxe5e48b601d05f0310a1af266e6b76e13a705622b
6.6 KB | 252 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 { useToast } from '@/context/ToastContext';
15
import { translateError } from '@/lib/errors/translations';
16

17
export default function ForgotPasswordScreen() {
18
  const [email, setEmail] = useState('');
19
  const [emailSent, setEmailSent] = useState(false);
20
  const [resetToken, setResetToken] = useState(''); // Only for development
21
  const [errors, setErrors] = useState<{ email?: string }>({});
22

23
  const { forgotPassword } = useAuth();
24
  const { showToast } = useToast();
25
  const [loading, setLoading] = useState(false);
26
  const textColor = useThemeColor({}, 'text');
27
  const backgroundColor = useThemeColor({}, 'background');
28
  const tintColor = useThemeColor({}, 'tint');
29

30
  const validateForm = () => {
31
    const newErrors: { email?: string } = {};
32

33
    if (!email) {
34
      newErrors.email = 'Email é obrigatório';
35
    } else if (!/\S+@\S+\.\S+/.test(email)) {
36
      newErrors.email = 'Por favor entre um email válido';
37
    }
38

39
    setErrors(newErrors);
40
    return Object.keys(newErrors).length === 0;
41
  };
42

43
  const handleForgotPassword = async () => {
44
    if (!validateForm()) return;
45

46
    setLoading(true);
47
    try {
48
      const data = await forgotPassword(email);
49
      setEmailSent(true);
50
      // In development, show the reset token
51
      if (data.token) {
52
        setResetToken(data.token);
53
      }
54
    } catch (error: any) {
55
      showToast(translateError(error.message) || 'Falha ao enviar link de recuperação', 'error');
56
    } finally {
57
      setLoading(false);
58
    }
59
  };
60

61
  const handleUseToken = () => {
62
    if (resetToken) {
63
      router.push({
64
        pathname: '/auth/reset-password',
65
        params: { token: resetToken },
66
      });
67
    }
68
  };
69

70
  if (emailSent) {
71
    return (
72
      <SafeAreaView style={[styles.container, { backgroundColor }]}>
73
        <View style={styles.successContainer}>
74
          <Text style={[styles.title, { color: textColor }]}>Verifique seu email</Text>
75
          <Text style={[styles.message, { color: textColor, opacity: 0.7 }]}>
76
            Enviamos as instruções para resetar sua senha para {email}
77
          </Text>
78

79
          {/* Development only - show reset token */}
80
          {resetToken && (
81
            <View style={styles.devSection}>
82
              <Text style={[styles.devTitle, { color: tintColor }]}>
83
                Dev mode:
84
              </Text>
85
              <Text style={[styles.devText, { color: textColor, opacity: 0.7 }]}>
86
                Token: {resetToken}
87
              </Text>
88
              <Button
89
                title="Usar token para resetar senha"
90
                onPress={handleUseToken}
91
                style={styles.devButton}
92
              />
93
            </View>
94
          )}
95

96
          <View style={styles.actions}>
97
            <Button
98
              title="Reenviar email"
99
              variant="outline"
100
              onPress={() => setEmailSent(false)}
101
              style={styles.resendButton}
102
            />
103

104
            <Link href="/auth/login" asChild style={[styles.link, { color: tintColor }]}>
105
              <Text style={[styles.link, { color: tintColor }]}>
106
                Voltar para login
107
              </Text>
108
            </Link>
109
          </View>
110
        </View>
111
      </SafeAreaView>
112
    );
113
  }
114

115
  return (
116
    <SafeAreaView style={[styles.container, { backgroundColor }]}>
117
      <KeyboardAvoidingView
118
        behavior='height'
119
        style={styles.keyboardView}
120
      >
121
        <ScrollView
122
          contentContainerStyle={styles.scrollContainer}
123
          showsVerticalScrollIndicator={false}
124
        >
125
          <View style={styles.header}>
126
            <Text style={[styles.title, { color: textColor }]}>Esqueceu a senha?</Text>
127
            <Text style={[styles.subtitle, { color: textColor, opacity: 0.7 }]}>
128
              Insira seu email para receber as instruções de recuperação
129
            </Text>
130
          </View>
131

132
          <View style={styles.form}>
133
            <Input
134
              label="Email"
135
              type="email"
136
              value={email}
137
              onChangeText={setEmail}
138
              placeholder="Qual seu email?"
139
              error={errors.email}
140
              autoCapitalize="none"
141
            />
142

143
            <Button
144
              title="Enviar link de recuperação"
145
              onPress={handleForgotPassword}
146
              loading={loading}
147
              style={styles.resetButton}
148
            />
149
          </View>
150

151
          <View style={styles.footer}>
152
            <Text style={[styles.footerText, { color: textColor, opacity: 0.7 }]}>
153
              Lembra tua senha?{' '}
154
            </Text>
155
            <Link href="/auth/login" asChild style={[styles.link, { color: tintColor }]}>
156
              <Text style={[styles.link, { color: tintColor }]}>
157
                Faça login
158
              </Text>
159
            </Link>
160
          </View>
161
        </ScrollView>
162
      </KeyboardAvoidingView>
163
    </SafeAreaView>
164
  );
165
}
166

167
const styles = StyleSheet.create({
168
  container: {
169
    flex: 1,
170
  },
171
  keyboardView: {
172
    flex: 1,
173
  },
174
  scrollContainer: {
175
    flexGrow: 1,
176
    paddingHorizontal: 24,
177
    justifyContent: 'center',
178
    minHeight: '100%',
179
  },
180
  successContainer: {
181
    flex: 1,
182
    justifyContent: 'center',
183
    paddingHorizontal: 24,
184
  },
185
  header: {
186
    alignItems: 'center',
187
    marginBottom: 40,
188
  },
189
  title: {
190
    fontSize: 28,
191
    fontWeight: 'bold',
192
    marginBottom: 8,
193
    textAlign: 'center',
194
  },
195
  subtitle: {
196
    fontSize: 16,
197
    textAlign: 'center',
198
    lineHeight: 24,
199
  },
200
  message: {
201
    fontSize: 16,
202
    textAlign: 'center',
203
    lineHeight: 24,
204
    marginBottom: 32,
205
  },
206
  form: {
207
    marginBottom: 32,
208
  },
209
  resetButton: {
210
    marginTop: 8,
211
  },
212
  actions: {
213
    gap: 16,
214
  },
215
  resendButton: {
216
    marginBottom: 8,
217
  },
218
  devSection: {
219
    marginBottom: 32,
220
    padding: 16,
221
    borderRadius: 8,
222
    backgroundColor: 'rgba(74, 144, 226, 0.1)',
223
  },
224
  devTitle: {
225
    fontSize: 14,
226
    fontWeight: '600',
227
    marginBottom: 8,
228
  },
229
  devText: {
230
    fontSize: 12,
231
    marginBottom: 12,
232
    fontFamily: 'monospace',
233
  },
234
  devButton: {
235
    marginTop: 8,
236
  },
237
  link: {
238
    textAlign: 'center',
239
    fontSize: 14,
240
    fontWeight: '500',
241
  },
242
  footer: {
243
    flexDirection: 'row',
244
    justifyContent: 'center',
245
    alignItems: 'center',
246
    marginTop: 'auto',
247
    paddingBottom: 20,
248
  },
249
  footerText: {
250
    fontSize: 14,
251
  },
252
});