eletrotupi / tcc/ frontend/app/auth/login.tsx8b7a4383c6c39de79612d786a2c836f1b01221b8
6.3 KB | 254 lines Raw
1
import React, { useState, useEffect, useRef } from "react";
2
import {
3
  View,
4
  Text,
5
  StyleSheet,
6
  ScrollView,
7
  KeyboardAvoidingView,
8
  Platform,
9
  Alert,
10
  Animated,
11
  Easing
12
} from "react-native";
13
import { Link, router } from "expo-router";
14
import { Image } from "react-native";
15
import { SafeAreaView } from "react-native-safe-area-context";
16
import { StatusBar } from "expo-status-bar";
17
import { useThemeColor } from "@/hooks/use-theme-color";
18
import { Button, Input } from "@/components/ui";
19
import { useAuth } from "@/context/AuthContext";
20
import { apiClient, User } from "@/lib/api";
21
import { registerForPushNotifications } from "@/hooks/useRegisterPushToken";
22

23
export default function LoginScreen() {
24
  const [email, setEmail] = useState("");
25
  const [password, setPassword] = useState("");
26
  const [errors, setErrors] = useState<{ email?: string; password?: string }>(
27
    {},
28
  );
29

30
  const { login, updateAuthUser, isLoading } = useAuth();
31
  const textColor = useThemeColor({}, "text");
32
  const backgroundColor = useThemeColor({}, "background");
33
  const tintColor = useThemeColor({}, "tint");
34

35
  const spinValue = useRef(new Animated.Value(0)).current;
36
  useEffect(() => {
37
    const animation = Animated.loop(
38
      Animated.timing(spinValue, {
39
        toValue: 1,
40
        duration: 12000, // 12s per full turn (slow)
41
        easing: Easing.linear,
42
        useNativeDriver: true,
43
      })
44
    );
45

46
    animation.start();
47

48
    return () => {
49
      animation.stop();
50
    };
51
  }, [spinValue]);
52

53
  const spin = spinValue.interpolate({
54
    inputRange: [0, 1],
55
    outputRange: ["0deg", "360deg"],
56
  });
57

58
  const validateForm = () => {
59
    const newErrors: { email?: string; password?: string } = {};
60

61
    if (!email) {
62
      newErrors.email = "Email é obrigatório";
63
    } else if (!/\S+@\S+\.\S+/.test(email)) {
64
      newErrors.email = "Por favor, use um email válido";
65
    }
66

67
    if (!password) {
68
      newErrors.password = "Senha é obrigatória";
69
    }
70

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

75
  const handleLogin = async () => {
76
    if (!validateForm()) return;
77

78
    try {
79
      const response = await login(email, password);
80

81
      if (response?.user) {
82
        const token = await registerForPushNotifications();
83
        if (token) {
84
          const updateResponse = await apiClient.updateUser({
85
            id: response.user.id,
86
            pushToken: token,
87
          });
88

89
          console.log("UPDATE USER response: ", updateResponse);
90

91
          if (updateResponse) {
92
            updateAuthUser(updateResponse.user);
93
          }
94
        }
95
      }
96

97
      router.replace("/");
98
    } catch (error: any) {
99
      console.log("LOGIN error: ", error);
100
      Alert.alert("Erro: ", "Falha ao fazer login");
101
    }
102
  };
103

104
  return (
105
    <SafeAreaView style={[styles.container, { backgroundColor }]}>
106
      <KeyboardAvoidingView behavior="height" style={styles.keyboardView}>
107
        <StatusBar style={'dark'} />
108
        <ScrollView
109
          contentContainerStyle={styles.scrollContainer}
110
          showsVerticalScrollIndicator={false}
111
        >
112
          <View style={styles.logoContainer}>
113
            <Animated.View style={{ transform: [{ rotate: spin }] }}>
114
              <Image
115
                source={require("@/assets/images/logo.png")}
116
                style={styles.logo}
117
                resizeMode="contain"
118
              />
119
            </Animated.View>
120
          </View>
121

122
          <View style={styles.header}>
123
            <Text style={[styles.title, { color: textColor }]}>
124
              Bem vindo de volta
125
            </Text>
126
            <Text style={[styles.subtitle, { color: textColor, opacity: 0.7 }]}>
127
              Logue na sua conta nexo
128
            </Text>
129
          </View>
130

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

142
            <Input
143
              label="Senha"
144
              type="password"
145
              value={password}
146
              onChangeText={setPassword}
147
              placeholder="Qual sua senha?"
148
              error={errors.password}
149
              showPasswordToggle
150
            />
151

152
            <Button
153
              title="Entrar"
154
              onPress={handleLogin}
155
              loading={isLoading}
156
              style={styles.loginButton}
157
            />
158

159
            <Link
160
              href="/auth/forgot-password"
161
              style={[styles.link, { color: tintColor }]}
162
              asChild
163
            >
164
              <Text
165
                style={[styles.footerText, { color: textColor, opacity: 0.7 }]}
166
              >
167
                Esqueceu a senha?
168
              </Text>
169
            </Link>
170
          </View>
171

172
          <View style={styles.footer}>
173
            <Text
174
              style={[styles.footerText, { color: textColor, opacity: 0.7 }]}
175
            >
176
              Não tem uma conta ainda?{" "}
177
            </Text>
178

179
            <Link
180
              href="/auth/signup"
181
              asChild
182
              style={[styles.link, { color: tintColor }]}
183
            >
184
              <Text
185
                style={[styles.footerText, { color: textColor, opacity: 0.7 }]}
186
              >
187
                Cadastre-se
188
              </Text>
189
            </Link>
190
          </View>
191
        </ScrollView>
192
      </KeyboardAvoidingView>
193
    </SafeAreaView>
194
  );
195
}
196

197
const styles = StyleSheet.create({
198
  container: {
199
    flex: 1,
200
  },
201
  keyboardView: {
202
    flex: 1,
203
  },
204
  logoContainer: {
205
    alignItems: "center",
206
    marginBottom: 40,
207
    marginTop: 10,
208
  },
209
  logo: {
210
    width: 120,
211
    height: 120,
212
  },
213
  scrollContainer: {
214
    flexGrow: 1,
215
    paddingHorizontal: 24,
216
    justifyContent: "center",
217
    minHeight: "100%",
218
  },
219
  header: {
220
    alignItems: "center",
221
    marginBottom: 40,
222
  },
223
  title: {
224
    fontSize: 28,
225
    fontWeight: "bold",
226
    marginBottom: 8,
227
  },
228
  subtitle: {
229
    fontSize: 16,
230
    textAlign: "center",
231
  },
232
  form: {
233
    marginBottom: 32,
234
  },
235
  loginButton: {
236
    marginTop: 8,
237
    marginBottom: 16,
238
  },
239
  link: {
240
    textAlign: "center",
241
    fontSize: 14,
242
    fontWeight: "500",
243
  },
244
  footer: {
245
    flexDirection: "row",
246
    justifyContent: "center",
247
    alignItems: "center",
248
    marginTop: "auto",
249
    paddingBottom: 40,
250
  },
251
  footerText: {
252
    fontSize: 14,
253
  },
254
});