Files
SeaGateway-App/app/auth/login.tsx
2025-11-07 15:28:55 +07:00

267 lines
6.7 KiB
TypeScript

import { ThemedText } from "@/components/themed-text";
import { ThemedView } from "@/components/themed-view";
import { TOKEN } from "@/constants";
import { login } from "@/controller/AuthController";
import { showErrorToast } from "@/services/toast_service";
import {
getStorageItem,
removeStorageItem,
setStorageItem,
} from "@/utils/storage";
import { parseJwtToken } from "@/utils/token";
import { useRouter } from "expo-router";
import { useCallback, useEffect, useState } from "react";
import {
ActivityIndicator,
Image,
KeyboardAvoidingView,
Platform,
ScrollView,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
} from "react-native";
export default function LoginScreen() {
const router = useRouter();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const checkLogin = useCallback(async () => {
const token = await getStorageItem(TOKEN);
console.log("Token:", token);
if (!token) {
return;
}
const parsed = parseJwtToken(token);
console.log("Parse Token: ", parsed);
const { exp } = parsed;
const now = Math.floor(Date.now() / 1000);
const oneHour = 60 * 60;
if (exp - now < oneHour) {
await removeStorageItem(TOKEN);
} else {
router.replace("/(tabs)");
}
}, [router]);
useEffect(() => {
checkLogin();
}, [checkLogin]);
const handleLogin = async () => {
// Validate input
if (!username.trim() || !password.trim()) {
showErrorToast("Vui lòng nhập tài khoản và mật khẩu");
return;
}
setLoading(true);
try {
const body: Model.LoginRequestBody = {
username: username,
password: password,
};
const response = await login(body);
// Nếu thành công, lưu token và chuyển sang (tabs)
console.log("Login thành công với data:", response.data);
if (response?.data.token) {
// Lưu token vào storage nếu cần (thêm logic này sau)
console.log("Login Token ");
await setStorageItem(TOKEN, response.data.token);
console.log("Token:", response.data.token);
router.replace("/(tabs)");
}
} catch (error) {
showErrorToast(
error instanceof Error ? error.message : "Đăng nhập thất bại"
);
} finally {
setLoading(false);
}
};
return (
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
style={{ flex: 1 }}
>
<ScrollView contentContainerStyle={styles.scrollContainer}>
<ThemedView style={styles.container}>
{/* Header */}
<View style={styles.headerContainer}>
{/* Logo */}
<Image
source={require("@/assets/images/logo.png")}
style={styles.logo}
resizeMode="contain"
/>
<ThemedText type="title" style={styles.title}>
Hệ thống giám sát tàu
</ThemedText>
<ThemedText style={styles.subtitle}>
Đăng nhập đ tiếp tục
</ThemedText>
</View>
{/* Form */}
<View style={styles.formContainer}>
{/* Username Input */}
<View style={styles.inputGroup}>
<ThemedText style={styles.label}>Tài khoản</ThemedText>
<TextInput
style={styles.input}
placeholder="Nhập tài khoản"
placeholderTextColor="#999"
value={username}
onChangeText={setUsername}
editable={!loading}
autoCapitalize="none"
/>
</View>
{/* Password Input */}
<View style={styles.inputGroup}>
<ThemedText style={styles.label}>Mật khẩu</ThemedText>
<TextInput
style={styles.input}
placeholder="Nhập mật khẩu"
placeholderTextColor="#999"
value={password}
onChangeText={setPassword}
secureTextEntry
editable={!loading}
autoCapitalize="none"
/>
</View>
{/* Login Button */}
<TouchableOpacity
style={[
styles.loginButton,
loading && styles.loginButtonDisabled,
]}
onPress={handleLogin}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#fff" size="small" />
) : (
<Text style={styles.loginButtonText}>Đăng nhập</Text>
)}
</TouchableOpacity>
{/* Footer text */}
<View style={styles.footerContainer}>
<ThemedText style={styles.footerText}>
Chưa tài khoản?{" "}
<Text style={styles.linkText}>Đăng ngay</Text>
</ThemedText>
</View>
{/* Copyright */}
<View style={styles.copyrightContainer}>
<ThemedText style={styles.copyrightText}>
© {new Date().getFullYear()} - Sản phẩm của Mobifone
</ThemedText>
</View>
</View>
</ThemedView>
</ScrollView>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
scrollContainer: {
flexGrow: 1,
justifyContent: "center",
},
container: {
flex: 1,
paddingHorizontal: 20,
justifyContent: "center",
},
headerContainer: {
marginBottom: 40,
alignItems: "center",
},
logo: {
width: 120,
height: 120,
marginBottom: 20,
},
title: {
fontSize: 28,
fontWeight: "bold",
marginBottom: 8,
},
subtitle: {
fontSize: 16,
opacity: 0.7,
},
formContainer: {
gap: 16,
},
inputGroup: {
gap: 8,
},
label: {
fontSize: 14,
fontWeight: "600",
},
input: {
borderWidth: 1,
borderColor: "#ddd",
borderRadius: 8,
paddingHorizontal: 12,
paddingVertical: 12,
fontSize: 16,
backgroundColor: "#f5f5f5",
color: "#000",
},
loginButton: {
backgroundColor: "#007AFF",
paddingVertical: 14,
borderRadius: 8,
alignItems: "center",
marginTop: 16,
},
loginButtonDisabled: {
opacity: 0.6,
},
loginButtonText: {
color: "#fff",
fontSize: 16,
fontWeight: "600",
},
footerContainer: {
marginTop: 16,
alignItems: "center",
},
footerText: {
fontSize: 14,
},
linkText: {
color: "#007AFF",
fontWeight: "600",
},
copyrightContainer: {
marginTop: 20,
alignItems: "center",
},
copyrightText: {
fontSize: 12,
opacity: 0.6,
textAlign: "center",
},
});