summaryrefslogtreecommitdiff
path: root/app/login
diff options
context:
space:
mode:
authorMiqdad <ahmadmiqdad27@gmail.com>2025-09-25 08:22:51 +0700
committerMiqdad <ahmadmiqdad27@gmail.com>2025-09-25 08:22:51 +0700
commit3f5c6e86c57f3dbcc76a12294cbad94e8b130443 (patch)
treeb9602314767a387666e460f870fb26f28c254325 /app/login
parent3190b87c73b35b50681c91e9bc080f9ce5bc2b47 (diff)
<Miqdad> Fix login
Diffstat (limited to 'app/login')
-rw-r--r--app/login/page.tsx220
1 files changed, 164 insertions, 56 deletions
diff --git a/app/login/page.tsx b/app/login/page.tsx
index 9e0a5fe..3ea2190 100644
--- a/app/login/page.tsx
+++ b/app/login/page.tsx
@@ -1,84 +1,192 @@
"use client";
-
-import { useRouter } from "next/navigation";
-import { useState } from "react";
+import {
+ Box,
+ Button,
+ FormControl,
+ FormLabel,
+ TextField,
+ Typography,
+} from "@mui/material";
+import Header from "../lib/camera/component/hedear";
import odooApi from "../lib/api/odooApi";
+import { getAuth, setAuth } from "../lib/api/auth";
+import { useRouter } from "next/navigation";
+import { useEffect, useState } from "react";
+
+// Ambil tipe parameter untuk setAuth agar sesuai tepat dengan definisinya
+type AuthProps = Parameters<typeof setAuth>[0];
type LoginStatus = { code?: number; description?: string };
-type LoginResult = { token?: string; email?: string; [k: string]: unknown };
+type LoginResult = {
+ is_auth?: boolean;
+ reason?: "NOT_FOUND" | "NOT_ACTIVE" | string;
+ user?: unknown; // akan dicast ke AuthProps jika lolos
+};
type LoginResponse = { status?: LoginStatus; result?: LoginResult };
-export default function LoginPage() {
+const Login = () => {
const router = useRouter();
+
+ // state untuk validasi MUI helperText
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+
+ const [emailError, setEmailError] = useState(false);
+ const [emailErrorMessage, setEmailErrorMessage] = useState("");
+ const [passwordError, setPasswordError] = useState(false);
+ const [passwordErrorMessage, setPasswordErrorMessage] = useState("");
const [loading, setLoading] = useState(false);
- const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
- e.preventDefault();
- setLoading(true);
+ useEffect(() => {
+ const token = getAuth();
+ if (token) router.push("/");
+ }, [router]);
- const fd = new FormData(e.currentTarget);
+ const validateInputs = (e: string, p: string) => {
+ let ok = true;
+
+ if (!e || !/\S+@\S+\.\S+/.test(e)) {
+ setEmailError(true);
+ setEmailErrorMessage("Please enter a valid email address.");
+ ok = false;
+ } else {
+ setEmailError(false);
+ setEmailErrorMessage("");
+ }
+
+ if (!p || p.length < 6) {
+ setPasswordError(true);
+ setPasswordErrorMessage("Password must be at least 6 characters long.");
+ ok = false;
+ } else {
+ setPasswordError(false);
+ setPasswordErrorMessage("");
+ }
+
+ return ok;
+ };
- // Narrowing ke string, bukan File/null
+ const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
+ event.preventDefault();
+
+ // Ambil dari FormData agar sesuai dengan form yang disubmit
+ const fd = new FormData(event.currentTarget);
const rawEmail = fd.get("email");
const rawPassword = fd.get("password");
- const email = typeof rawEmail === "string" ? rawEmail.trim() : "";
- const password = typeof rawPassword === "string" ? rawPassword : "";
+ const emailStr = typeof rawEmail === "string" ? rawEmail.trim() : "";
+ const passwordStr = typeof rawPassword === "string" ? rawPassword : "";
- if (!email || !password) {
- alert("Email dan password wajib diisi.");
- setLoading(false);
- return;
- }
+ if (!validateInputs(emailStr, passwordStr)) return;
try {
+ setLoading(true);
+
const res = (await odooApi("POST", "/api/v1/user/login", {
- email,
- password,
+ email: emailStr,
+ password: passwordStr,
})) as unknown as LoginResponse;
- if (res?.status?.code === 200) {
- // Jika kamu punya util setAuth(res.result), panggil di sini.
+ const auth = res?.result;
+
+ if (res?.status?.code === 200 && auth?.is_auth) {
+ // Cast auth.user ke AuthProps → cocok dengan setAuth
+ if (auth.user && typeof auth.user === "object") {
+ setAuth(auth.user as AuthProps);
+ }
router.push("/");
- } else {
- alert(res?.status?.description || "Login gagal. Periksa email/password.");
+ return;
}
- } catch (err) {
- console.error(err);
- alert("Terjadi kesalahan saat login.");
+
+ // Tangani alasan gagal umum
+ switch (auth?.reason) {
+ case "NOT_FOUND":
+ alert("Email tidak ditemukan");
+ break;
+ case "NOT_ACTIVE":
+ alert("Akun anda belum aktif");
+ break;
+ default:
+ alert(res?.status?.description || "Login gagal. Periksa email/password.");
+ }
+ } catch (error) {
+ console.error(error);
+ alert("Gagal login, silahkan coba lagi");
} finally {
setLoading(false);
}
};
return (
- <main className="min-h-screen flex items-center justify-center">
- <form onSubmit={onSubmit} className="w-full max-w-sm p-6 space-y-3 border rounded">
- <h1 className="text-xl font-semibold">Login</h1>
-
- <input
- name="email"
- type="email"
- placeholder="Email"
- className="w-full border rounded px-3 py-2"
- required
- />
-
- <input
- name="password"
- type="password"
- placeholder="Password"
- className="w-full border rounded px-3 py-2"
- required
- />
-
- <button
- type="submit"
- disabled={loading}
- className="w-full bg-red-600 text-white rounded py-2"
- >
- {loading ? "Loading..." : "Login"}
- </button>
- </form>
- </main>
+ <div className="bg-[#fafeff] h-screen overflow-auto">
+ <Header />
+ <div className="py-4 px-4 sm:px-96 pt-20">
+ <div className="bg-white py-6 px-4 sm:px-96 shadow-md rounded-sm">
+ <Typography
+ component="h1"
+ variant="h4"
+ sx={{ width: "100%", fontSize: "clamp(2rem, 10vw, 2.15rem)", mb: 4 }}
+ >
+ Sign in
+ </Typography>
+
+ <Box
+ component="form"
+ onSubmit={handleSubmit}
+ noValidate
+ sx={{ display: "flex", flexDirection: "column", width: "100%", gap: 2 }}
+ >
+ <FormControl>
+ <FormLabel htmlFor="email">Email</FormLabel>
+ <TextField
+ error={emailError}
+ helperText={emailErrorMessage}
+ id="email"
+ type="email"
+ name="email"
+ placeholder="your@email.com"
+ autoComplete="email"
+ autoFocus
+ required
+ fullWidth
+ variant="outlined"
+ color={emailError ? "error" : "primary"}
+ size="small"
+ inputProps={{ "aria-label": "email" }}
+ value={email}
+ onChange={(e) => setEmail(e.target.value)}
+ />
+ </FormControl>
+
+ <FormControl>
+ <Box sx={{ display: "flex", justifyContent: "space-between" }}>
+ <FormLabel htmlFor="password">Password</FormLabel>
+ </Box>
+ <TextField
+ error={passwordError}
+ helperText={passwordErrorMessage}
+ name="password"
+ placeholder="••••••"
+ type="password"
+ id="password"
+ autoComplete="current-password"
+ required
+ fullWidth
+ variant="outlined"
+ color={passwordError ? "error" : "primary"}
+ size="small"
+ value={password}
+ onChange={(e) => setPassword(e.target.value)}
+ />
+ </FormControl>
+
+ <Button type="submit" fullWidth variant="contained" disabled={loading}>
+ {loading ? "Loading..." : "Sign in"}
+ </Button>
+ </Box>
+ </div>
+ </div>
+ </div>
);
-} \ No newline at end of file
+};
+
+export default Login;