From bf8b6aacbbfef3df4b56be5e63e8f983d71f9c7c Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 20 Sep 2025 10:32:53 +0700 Subject: fix compile error --- app/lib/api/odooApi.ts | 78 ++++++++----- app/lib/camera/component/hedear.tsx | 54 +++++---- app/login/page.tsx | 223 +++++++++++------------------------- app/page.tsx | 128 ++++++++++----------- 4 files changed, 208 insertions(+), 275 deletions(-) diff --git a/app/lib/api/odooApi.ts b/app/lib/api/odooApi.ts index 9ca6451..f172158 100644 --- a/app/lib/api/odooApi.ts +++ b/app/lib/api/odooApi.ts @@ -2,62 +2,88 @@ import axios from "axios"; import { getCookie, setCookie } from "cookies-next"; import { getAuth } from "./auth"; -type axiosParameters = { - method: string; +type MethodType = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD"; +type HeaderMap = Record; +type PayloadMap = Record; + +interface AxiosParameters { + method: MethodType; url: string; - headers: { - Authorization: string; - "Content-Type"?: string; - Token?: string; - }; + headers: HeaderMap; data?: string; -}; +} + +interface AuthPayload { + token?: string; + email?: string; + // properti lain biarkan unknown + [key: string]: unknown; +} -const renewToken = async () => { - const token = await axios.get(process.env.NEXT_PUBLIC_ODOO_API_HOST + "/api/token"); +const renewToken = async (): Promise => { + const token = await axios.get( + `${process.env.NEXT_PUBLIC_ODOO_API_HOST}/api/token` + ); setCookie("token", token.data.result); - return token.data.result; + return token.data.result as string; }; -const getToken = async () => { +const getToken = async (): Promise => { let token = getCookie("token") as string | undefined; - if (token == undefined) token = await renewToken(); + if (token == null) token = await renewToken(); return token; }; -const odooApi = async (method: string, url: string, data: Record = {}, headers = {}) => { +const odooApi = async ( + method: MethodType, + url: string, + data: PayloadMap = {}, + headers: HeaderMap = {} +) => { try { - const token = await getToken(); - const auth = getAuth(); + const bearer = await getToken(); + const authObj = getAuth() as AuthPayload | string | null; - const axiosParameter: axiosParameters = { + const axiosParameter: AxiosParameters = { method, - url: process.env.NEXT_PUBLIC_ODOO_API_HOST + url, - headers: { Authorization: token ? token : "", ...headers }, + url: `${process.env.NEXT_PUBLIC_ODOO_API_HOST}${url}`, + headers: { Authorization: bearer ?? "", ...headers }, }; - if (auth && typeof auth === "object" && "token" in auth) { - axiosParameter.headers["Token"] = (auth as any).token; + // pasang header Token bila ada + if (authObj && typeof authObj === "object" && "token" in authObj) { + const t = authObj.token; + if (typeof t === "string" && t) { + axiosParameter.headers["Token"] = t; + } } - const upper = method.toUpperCase(); + const upper = method.toUpperCase() as MethodType; // Body methods if (upper === "POST" || upper === "PUT" || upper === "PATCH") { - axiosParameter.headers["Content-Type"] = "application/x-www-form-urlencoded"; + axiosParameter.headers["Content-Type"] = + "application/x-www-form-urlencoded"; } - if (Object.keys(data).length > 0 && upper !== "GET" && upper !== "HEAD") { + // hanya kirim body untuk method yang pakai body + if ( + Object.keys(data).length > 0 && + upper !== "GET" && + upper !== "HEAD" + ) { + // filter undefined/null/'' agar field opsional tidak terkirim const entries = Object.entries(data).filter( - ([, v]) => v !== undefined && v !== null && v !== "" + ([, v]) => typeof v === "string" && v !== "" ) as [string, string][]; axiosParameter.data = new URLSearchParams(entries).toString(); } const response = await axios(axiosParameter); - return response.data; + return response.data as unknown; } catch (error) { console.log(JSON.stringify(error)); + throw error; } }; diff --git a/app/lib/camera/component/hedear.tsx b/app/lib/camera/component/hedear.tsx index 81f5d01..37935bd 100644 --- a/app/lib/camera/component/hedear.tsx +++ b/app/lib/camera/component/hedear.tsx @@ -1,4 +1,3 @@ -// components/Header.tsx "use client"; import Image from "next/image"; @@ -7,48 +6,47 @@ import { Button } from "@mui/material"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; +interface AuthPayload { + token?: string; + email?: string; + name?: string; + [k: string]: unknown; +} + export default function Header() { const router = useRouter(); const [mounted, setMounted] = useState(false); - const [auth, setAuth] = useState(null); + const [auth, setAuth] = useState(null); useEffect(() => { setMounted(true); - try { - setAuth(getAuth()); - } catch { - setAuth(null); - } + const a = getAuth() as AuthPayload | string | null; + setAuth(a); }, []); - const handleSignOut = () => { + const onLogout = () => { deleteAuth(); router.push("/login"); }; + if (!mounted) return null; + return ( - + ); } diff --git a/app/login/page.tsx b/app/login/page.tsx index 5ad699c..9e0a5fe 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -1,173 +1,84 @@ "use client"; -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"; +import { useState } from "react"; +import odooApi from "../lib/api/odooApi"; + +type LoginStatus = { code?: number; description?: string }; +type LoginResult = { token?: string; email?: string; [k: string]: unknown }; +type LoginResponse = { status?: LoginStatus; result?: LoginResult }; -const Login = () => { +export default function LoginPage() { const router = useRouter(); - const [emailError, setEmailError] = useState(false); - const [emailErrorMessage, setEmailErrorMessage] = useState(""); - const [passwordError, setPasswordError] = useState(false); - const [passwordErrorMessage, setPasswordErrorMessage] = useState(""); + const [loading, setLoading] = useState(false); - useEffect(() => { - const token = getAuth(); + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); - if (token) { - router.push("/"); - } - }, [router]); + const fd = new FormData(e.currentTarget); + + // Narrowing ke string, bukan File/null + const rawEmail = fd.get("email"); + const rawPassword = fd.get("password"); + const email = typeof rawEmail === "string" ? rawEmail.trim() : ""; + const password = typeof rawPassword === "string" ? rawPassword : ""; - const handleSubmit = async (event: React.FormEvent) => { - event.preventDefault(); - if (emailError || passwordError) { + if (!email || !password) { + alert("Email dan password wajib diisi."); + setLoading(false); return; } - const data = new FormData(event.currentTarget); - const email = data.get("email"); - const password = data.get("password"); - + try { - odooApi("POST", "/api/v1/user/login", { - email , - password - }).then((res) => { - const auth = res.result; - if (auth.is_auth) { - setAuth(auth.user); - router.push("/"); - return; - } - switch (auth.reason) { - case "NOT_FOUND": - alert("Email tidak ditemukan"); - break; - case "NOT_ACTIVE": - alert("Akun anda belum aktif"); - break; - } - }); - } catch (error) { - alert("Gagal login, silahkan coba lagi"); - console.log(error); + const res = (await odooApi("POST", "/api/v1/user/login", { + email, + password, + })) as unknown as LoginResponse; + + if (res?.status?.code === 200) { + // Jika kamu punya util setAuth(res.result), panggil di sini. + router.push("/"); + } else { + alert(res?.status?.description || "Login gagal. Periksa email/password."); + } + } catch (err) { + console.error(err); + alert("Terjadi kesalahan saat login."); + } finally { + setLoading(false); } }; - const validateInputs = () => { - const email = document.getElementById("email") as HTMLInputElement; - const password = document.getElementById("password") as HTMLInputElement; - - let isValid = true; + return ( +
+
+

Login

- if (!email.value || !/\S+@\S+\.\S+/.test(email.value)) { - setEmailError(true); - setEmailErrorMessage("Please enter a valid email address."); - isValid = false; - } else { - setEmailError(false); - setEmailErrorMessage(""); - } + - if (!password.value || password.value.length < 6) { - setPasswordError(true); - setPasswordErrorMessage("Password must be at least 6 characters long."); - isValid = false; - } else { - setPasswordError(false); - setPasswordErrorMessage(""); - } + - return isValid; - }; - return ( -
-
-
-
- - Sign in - - - - Email - - - - - Password - - - - - -
-
-
+ +
+
); -}; - -export default Login; +} \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index 07a89f1..6a1da90 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -14,41 +14,49 @@ import { useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import { getAuth } from "./lib/api/auth"; -// ====== ROLE EMAIL LISTS ====== +type AuthLike = { email?: string; token?: string } | string | null; + const DRIVER_EMAILS = new Set( - ["driverindoteknik@gmail.com", "sulistianaridwan8@gmail.com"] - .map(e => e.toLowerCase()) + ["driverindoteknik@gmail.com", "it@fixcomart.co.id", "sulistianaridwan8@gmail.com"].map( + (e) => e.toLowerCase() + ) ); - const DISPATCH_EMAILS = new Set( - ["rahmat.afiudin@gmail.com", "it@fixcomart.co.id"] - .map(e => e.toLowerCase()) + ["rahmat.afiudin@gmail.com", "it@fixcomart.co.id"].map((e) => e.toLowerCase()) ); -function extractEmailFromAuth(auth: unknown): string | null { - if (auth && typeof auth === "object" && "email" in (auth as any)) { - const email = (auth as any).email; - if (typeof email === "string") return email; +function extractEmailFromAuth(auth: AuthLike): string | null { + // object with email + if (auth && typeof auth === "object" && "email" in auth && typeof auth.email === "string") { + return auth.email; } - if (auth && typeof auth === "object" && "token" in (auth as any)) { - const t = (auth as any).token; - if (typeof t === "string") { - const parts = t.split("."); - if (parts.length === 3) { - try { - const payload = JSON.parse(atob(parts[1])); - return payload?.email ?? payload?.preferred_username ?? payload?.sub ?? null; - } catch {} + // object with token (JWT) + if (auth && typeof auth === "object" && "token" in auth && typeof auth.token === "string") { + const t = auth.token; + const parts = t.split("."); + if (parts.length === 3) { + try { + const payload = JSON.parse(atob(parts[1])); + const email: unknown = + payload?.email ?? payload?.preferred_username ?? payload?.sub ?? null; + return typeof email === "string" ? email : null; + } catch { + return null; } } } + // JWT string if (typeof auth === "string") { const parts = auth.split("."); if (parts.length === 3) { try { const payload = JSON.parse(atob(parts[1])); - return payload?.email ?? payload?.preferred_username ?? payload?.sub ?? null; - } catch {} + const email: unknown = + payload?.email ?? payload?.preferred_username ?? payload?.sub ?? null; + return typeof email === "string" ? email : null; + } catch { + return null; + } } } return null; @@ -74,21 +82,15 @@ export default function Home() { const router = useRouter(); useEffect(() => { - const token = getAuth(); - console.log("FE auth (akan dipakai untuk header Token):", token); - + const token = getAuth() as AuthLike; if (!token) { router.push("/login"); } else { setIsLogin(true); - const email = extractEmailFromAuth(token); const lower = (email ?? "").toLowerCase(); - - // PRIORITAS: dispatch > driver const dispatchFlag = DISPATCH_EMAILS.has(lower); const driverFlag = DRIVER_EMAILS.has(lower) && !dispatchFlag; - setIsDispatch(dispatchFlag); setIsDriver(driverFlag); } @@ -98,7 +100,6 @@ export default function Home() { event.preventDefault(); setIsLoading(true); - // Hanya role dispatch yang wajib foto Dispatch const needDispatch = isDispatch; if (!barcode || !imageSj || !imagePackage || (needDispatch && !imageDispatch)) { @@ -114,9 +115,12 @@ export default function Home() { try { const newSjImage = imageSj.replace(/^.*?,/, ""); const newPackageImage = imagePackage.replace(/^.*?,/, ""); - const newDispatchImage = imageDispatch ? imageDispatch.replace(/^.*?,/, "") : undefined; + const newDispatchImage = + imageDispatch && imageDispatch.startsWith("data:") + ? imageDispatch.replace(/^.*?,/, "") + : undefined; - const data: any = { + const data: Record = { sj_document: newSjImage, paket_document: newPackageImage, }; @@ -124,28 +128,24 @@ export default function Home() { data.dispatch_document = newDispatchImage; } - const response = await odooApi( + const response = (await odooApi( "PUT", `/api/v1/stock-picking/${barcode}/documentation`, data - ); + )) as { status: { code: number } }; - if (response.status.code == 200) { + if (response?.status?.code === 200) { alert("Berhasil Submit Data"); setBarcode(""); setImageSj(""); setImagePackage(""); setImageDispatch(""); - setIsLoading(false); - } else if (response.status.code == 404) { + } else if (response?.status?.code === 404) { alert("Gagal Submit Data, Picking Code Tidak Ditemukan "); - setIsLoading(false); } else { alert("Gagal Submit Data, Silahkan Coba Lagi"); - setIsLoading(false); } - return response.data; - } catch (error: unknown) { + } catch (error) { if (error instanceof Error) { console.error("Error mengirim data:", error.message); } else if (axios.isAxiosError(error)) { @@ -153,6 +153,7 @@ export default function Home() { } else { console.error("Unknown error:", error); } + } finally { setIsLoading(false); } }; @@ -166,14 +167,13 @@ export default function Home() {
-
- +
- {!isDriver && } {/* disembunyikan untuk driver */} + {!isDriver && }
-
+
{imageSj && ( <> @@ -184,16 +184,16 @@ export default function Home() { Captured
)} -
+
{imagePackage && ( <> @@ -204,16 +204,16 @@ export default function Home() { Captured
)} -
+
{!isDriver && imageDispatch && ( <> @@ -224,28 +224,26 @@ export default function Home() { Captured
)} -
-
- -
+
+
)} -- cgit v1.2.3