eletrotupi / tcc/ frontend/app/auth/login.tsxc901c35af9c84b6a29e5185545ced1129f33b8f4
5.7 KB | 221 lines Raw
1
import React, { useState, useEffect } from "react";
2
import { Animated, Easing, Image, StyleSheet, View } from "react-native";
3
import { StatusBar } from "expo-status-bar";
4
import { Link, router } from "expo-router";
5
import { useThemeColor } from "@/hooks/use-theme-color";
6
import { Button, Input } from "@/components/ui";
7
import { Text } from "@/components/ui/Text";
8
import { AuthScreen } from "@/components/auth/AuthScreen";
9
import { useAuth } from "@/context/AuthContext";
10
import { apiClient, ApiError } from "@/lib/api";
11
import { useToast } from "@/context/ToastContext";
12
import { translateError, translateFields } from "@/lib/errors/translations";
13
import { registerForPushNotifications } from "@/hooks/useRegisterPushToken";
14

15
export default function LoginScreen() {
16
  const [email, setEmail] = useState("");
17
  const [password, setPassword] = useState("");
18
  const [errors, setErrors] = useState<{ email?: string; password?: string }>(
19
    {},
20
  );
21
  const [loading, setLoading] = useState(false);
22

23
  const { login, updateAuthUser } = useAuth();
24
  const { showToast } = useToast();
25
  const textColor = useThemeColor({}, "text");
26
  const tintColor = useThemeColor({}, "tint");
27

28
  const [spinValue] = useState(() => new Animated.Value(0));
29
  useEffect(() => {
30
    const animation = Animated.loop(
31
      Animated.timing(spinValue, {
32
        toValue: 1,
33
        duration: 12000, // 12s per full turn (slow)
34
        easing: Easing.linear,
35
        useNativeDriver: true,
36
      })
37
    );
38

39
    animation.start();
40

41
    return () => {
42
      animation.stop();
43
    };
44
  }, [spinValue]);
45

46
  const spin = spinValue.interpolate({
47
    inputRange: [0, 1],
48
    outputRange: ["0deg", "360deg"],
49
  });
50

51
  const validateForm = () => {
52
    const newErrors: { email?: string; password?: string } = {};
53

54
    if (!email) {
55
      newErrors.email = "Email é obrigatório";
56
    } else if (!/\S+@\S+\.\S+/.test(email)) {
57
      newErrors.email = "Por favor, use um email válido";
58
    }
59

60
    if (!password) {
61
      newErrors.password = "Senha é obrigatória";
62
    }
63

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

68
  const handleLogin = async () => {
69
    if (!validateForm()) return;
70

71
    setLoading(true);
72
    try {
73
      const response = await login(email, password);
74

75
      if (response?.user) {
76
        const token = await registerForPushNotifications();
77
        if (token) {
78
          const updateResponse = await apiClient.updateUser({
79
            id: response.user.id,
80
            pushToken: token,
81
          });
82

83
          if (updateResponse) {
84
            updateAuthUser(updateResponse.user);
85
          }
86
        }
87
      }
88

89
      router.replace("/");
90
    } catch (error) {
91
      if (error instanceof ApiError && error.fields) {
92
        setErrors(translateFields(error.fields));
93
      } else {
94
        showToast(translateError(error) || "Falha ao fazer login", "error");
95
      }
96
    } finally {
97
      setLoading(false);
98
    }
99
  };
100

101
  return (
102
    <>
103
      <StatusBar style="dark" />
104
      <AuthScreen
105
        header={
106
          <Animated.View
107
            style={[styles.logoContainer, { transform: [{ rotate: spin }] }]}
108
          >
109
            <Image
110
              source={require("@/assets/images/logo.png")}
111
              style={styles.logo}
112
              resizeMode="contain"
113
            />
114
          </Animated.View>
115
        }
116
        title="Bem vindo de volta"
117
        titleTestID="text-welcome"
118
        subtitle="Logue na sua conta nexo"
119
        footer={
120
          <View style={styles.footer}>
121
            <Text style={[styles.footerText, { color: textColor, opacity: 0.7 }]}>
122
              Não tem uma conta ainda?{" "}
123
            </Text>
124
            <Link
125
              href="/auth/signup"
126
              asChild
127
              style={StyleSheet.flatten([styles.link, { color: tintColor }])}
128
            >
129
              <Text
130
                style={StyleSheet.flatten([
131
                  styles.footerText,
132
                  { color: tintColor },
133
                ])}
134
              >
135
                Cadastre-se
136
              </Text>
137
            </Link>
138
          </View>
139
        }
140
      >
141
        <View style={styles.form}>
142
          <Input
143
            label="Email"
144
            type="email"
145
            value={email}
146
            onChangeText={setEmail}
147
            placeholder="Qual seu email?"
148
            error={errors.email}
149
            autoCapitalize="none"
150
          />
151

152
          <Input
153
            label="Senha"
154
            type="password"
155
            value={password}
156
            onChangeText={setPassword}
157
            placeholder="Qual sua senha?"
158
            error={errors.password}
159
            showPasswordToggle
160
            testID="input-password"
161
          />
162

163
          <Button
164
            title="Entrar"
165
            onPress={handleLogin}
166
            loading={loading}
167
            style={styles.loginButton}
168
            testID="button-login"
169
          />
170

171
          <Link
172
            href="/auth/forgot-password"
173
            style={StyleSheet.flatten([styles.link, { color: tintColor }])}
174
            asChild
175
          >
176
            <Text
177
              style={StyleSheet.flatten([
178
                styles.footerText,
179
                { color: textColor, opacity: 0.7 },
180
              ])}
181
            >
182
              Esqueceu a senha?
183
            </Text>
184
          </Link>
185
        </View>
186
      </AuthScreen>
187
    </>
188
  );
189
}
190

191
const styles = StyleSheet.create({
192
  logoContainer: {
193
    alignItems: "center",
194
    marginBottom: 40,
195
    marginTop: 10,
196
  },
197
  logo: {
198
    width: 120,
199
    height: 120,
200
  },
201
  form: {
202
    marginBottom: 32,
203
  },
204
  loginButton: {
205
    marginTop: 8,
206
    marginBottom: 16,
207
  },
208
  link: {
209
    textAlign: "center",
210
    fontSize: 14,
211
    fontWeight: "500",
212
  },
213
  footer: {
214
    flexDirection: "row",
215
    justifyContent: "center",
216
    alignItems: "center",
217
  },
218
  footerText: {
219
    fontSize: 14,
220
  },
221
});