summaryrefslogtreecommitdiff
path: root/app/lib
diff options
context:
space:
mode:
Diffstat (limited to 'app/lib')
-rw-r--r--app/lib/api/odooApi.ts78
-rw-r--r--app/lib/camera/component/hedear.tsx54
2 files changed, 78 insertions, 54 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<string, string>;
+type PayloadMap = Record<string, string>;
+
+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<string> => {
+ 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<string> => {
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<string, any> = {}, 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<any>(null);
+ const [auth, setAuth] = useState<AuthPayload | string | null>(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 (
- <nav className="fixed top-0 left-0 w-full bg-white border-b-2 border-red-500 py-4 px-4 sm:px-96 z-50 shadow-md">
- <div className="flex justify-between items-center">
- <div className="flex items-center">
- <Image
- src="/images/indoteknik-logo.png"
- alt="Logo"
- width={120}
- height={60}
- className="rounded-full"
- priority
- />
+ <header className="fixed top-0 left-0 right-0 z-50 bg-white shadow-sm">
+ <div className="max-w-screen-xl mx-auto flex items-center justify-between p-3">
+ <div className="flex items-center gap-2">
+ <Image src="/favicon.ico" width={32} height={32} alt="logo" />
+ <span className="font-semibold">indoteknik</span>
</div>
- <div>
- {mounted && auth ? (
- <Button size="small" onClick={handleSignOut}>
- Logout
- </Button>
- ) : (
- <span className="inline-block h-8 w-16" aria-hidden />
- )}
+ <div className="flex items-center gap-3">
+ <span className="text-sm text-gray-600">
+ {typeof auth === "object" && auth?.email ? auth.email : ""}
+ </span>
+ <Button size="small" variant="text" color="error" onClick={onLogout}>
+ Logout
+ </Button>
</div>
</div>
- </nav>
+ </header>
);
}