Files
wajs-server/src/pages/Login.tsx
2026-02-06 07:02:41 +08:00

150 lines
4.0 KiB
TypeScript

import {
Button,
Container,
Paper,
Text,
TextInput,
PasswordInput,
Group,
Stack,
Title,
Center,
Box,
ThemeIcon,
} from "@mantine/core";
import { useEffect, useState } from "react";
import { useForm } from "@mantine/form";
import { notifications } from "@mantine/notifications";
import { IconAt, IconLock, IconLogin, IconBrandWhatsapp } from "@tabler/icons-react";
import apiFetch from "../lib/apiFetch";
import clientRoutes from "@/clientRoutes";
import { Navigate } from "react-router-dom";
export default function Login() {
const [loading, setLoading] = useState(false);
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);
const form = useForm({
initialValues: {
email: "",
password: "",
},
validate: {
email: (value) => (/^\S+@\S+$/.test(value) ? null : "Invalid email"),
password: (value) => (value.length < 1 ? "Password is required" : null),
},
});
useEffect(() => {
async function checkSession() {
try {
const res = await apiFetch.api.user.find.get();
setIsAuthenticated(res.status === 200);
} catch {
setIsAuthenticated(false);
}
}
checkSession();
}, []);
const handleSubmit = async (values: typeof form.values) => {
setLoading(true);
try {
const response = await apiFetch.auth.login.post({
email: values.email,
password: values.password,
});
if (response.data?.token) {
localStorage.setItem("token", response.data.token);
notifications.show({
title: "Login Successful",
message: "Welcome back!",
color: "green",
});
window.location.href = clientRoutes["/sq/dashboard"];
return;
}
if (response.error) {
notifications.show({
title: "Login Failed",
message: (response.error as any)?.value?.message || "Invalid credentials",
color: "red",
});
}
} catch (error) {
console.error(error);
notifications.show({
title: "Error",
message: "An unexpected error occurred",
color: "red",
});
} finally {
setLoading(false);
}
};
if (isAuthenticated === null) return null;
if (isAuthenticated)
return <Navigate to={clientRoutes["/sq/dashboard"]} replace />;
return (
<Box
style={{
height: "100vh",
display: "flex",
flexDirection: "column",
justifyContent: "center",
background: "var(--mantine-color-body)",
}}
>
<Container size={420} my={40}>
<Stack gap="xs" mb={30} align="center">
<ThemeIcon size={60} radius="xl" color="green" variant="light">
<IconBrandWhatsapp size={40} />
</ThemeIcon>
<Title ta="center" order={2} fw={900}>
Welcome Back!
</Title>
<Text c="dimmed" size="sm" ta="center">
Login to manage your WhatsApp integration
</Text>
</Stack>
<Paper withBorder shadow="md" p={30} radius="md">
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack>
<TextInput
label="Email address"
placeholder="hello@gmail.com"
required
leftSection={<IconAt size={16} />}
{...form.getInputProps("email")}
/>
<PasswordInput
label="Password"
placeholder="Your password"
required
leftSection={<IconLock size={16} />}
{...form.getInputProps("password")}
/>
<Group justify="space-between" mt="lg">
<Button
fullWidth
type="submit"
loading={loading}
leftSection={<IconLogin size={18} />}
radius="md"
>
Sign in
</Button>
</Group>
</Stack>
</form>
</Paper>
</Container>
</Box>
);
}