diff --git a/dashboard/src/states/Login/tabs/LoginTab/LoginTab.jsx b/dashboard/src/states/Login/tabs/LoginTab/LoginTab.jsx
new file mode 100644
index 0000000..ee8cf9f
--- /dev/null
+++ b/dashboard/src/states/Login/tabs/LoginTab/LoginTab.jsx
@@ -0,0 +1,60 @@
+import {Alert, Button, CircularProgress, Stack} from "@mui/material";
+import LoginFields from "@/states/Login/tabs/LoginTab/components/LoginFields";
+import {useContext, useState} from "react";
+import {request} from "@/common/utils/RequestUtil.js";
+import {UserContext} from "@contexts/User";
+import TotpForm from "@/states/Login/tabs/LoginTab/components/TotpForm";
+
+export const LoginTab = () => {
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(false);
+
+ const [totpInfo, setTotpInfo] = useState(null);
+
+ const {updateSessionToken} = useContext(UserContext);
+
+ const login = (event) => {
+ event.preventDefault();
+
+ setError(false);
+ setLoading(true);
+
+ setTimeout(async () => {
+ try {
+ const data = await request("/auth/login", "POST", {username, password});
+
+ if (data.totpRequired) {
+ setTotpInfo(data.token);
+ return setLoading(false);
+ }
+
+ updateSessionToken(data.token);
+ } catch (e) {
+ setLoading(false);
+ setError(true);
+ }
+ }, 500);
+ }
+
+ if (totpInfo) return ;
+
+ return (
+
+
+ {error && Wrong username or password!}
+
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/dashboard/src/states/Login/tabs/LoginTab/index.js b/dashboard/src/states/Login/tabs/LoginTab/index.js
new file mode 100644
index 0000000..43f8f64
--- /dev/null
+++ b/dashboard/src/states/Login/tabs/LoginTab/index.js
@@ -0,0 +1 @@
+export {LoginTab as default} from "./LoginTab.jsx";
\ No newline at end of file