diff --git a/package.json b/package.json
index 32687b9..bd7ea3b 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
+ "@ant-design/icons": "^5.2.6",
"@tanstack/react-query": "^5.14.6",
"antd": "^5.12.5",
"axios": "^1.6.2",
diff --git a/src/assets/hoc/withTooltip.tsx b/src/assets/hoc/withTooltip.tsx
new file mode 100644
index 0000000..e133cc1
--- /dev/null
+++ b/src/assets/hoc/withTooltip.tsx
@@ -0,0 +1,8 @@
+import { Tooltip } from "antd";
+import React from "react";
+
+const withTooltip = (component: React.ReactNode, title: React.ReactNode) => {
+ return {component};
+};
+
+export default withTooltip;
diff --git a/src/components/layout/Avatar.tsx b/src/components/layout/Avatar.tsx
new file mode 100644
index 0000000..05c5953
--- /dev/null
+++ b/src/components/layout/Avatar.tsx
@@ -0,0 +1,47 @@
+import { colorConfig } from "@/themes/config";
+import { LogoutOutlined, UserOutlined } from "@ant-design/icons";
+import { Avatar as AvatarAntd, Dropdown, Space } from "antd";
+import { ItemType } from "antd/es/menu/hooks/useItems";
+import React from "react";
+
+const Avatar: React.FC = () => {
+ const items: ItemType[] = [
+ {
+ key: "profile",
+ label: (
+
+
+ Profile
+
+ ),
+ },
+ {
+ key: "logout",
+ label: (
+
+
+ Logout
+
+ ),
+ },
+ ];
+
+ return (
+
+ }
+ style={{ cursor: "pointer" }}
+ />
+
+ );
+};
+
+export default Avatar;
diff --git a/src/index.css b/src/index.css
index 41b591a..67075e3 100644
--- a/src/index.css
+++ b/src/index.css
@@ -7,5 +7,5 @@
}
body{
- background-color: rgb(242, 242, 242);
+ background-color:#E7E7E7;
}
\ No newline at end of file
diff --git a/src/layouts/MainLayout.tsx b/src/layouts/MainLayout.tsx
new file mode 100644
index 0000000..6a079f9
--- /dev/null
+++ b/src/layouts/MainLayout.tsx
@@ -0,0 +1,155 @@
+import { Button, Grid, Layout, Menu, Row } from "antd";
+import Sider from "antd/es/layout/Sider";
+import { Content, Header } from "antd/es/layout/layout";
+import React from "react";
+import {
+ DatabaseOutlined,
+ LineChartOutlined,
+ MenuFoldOutlined,
+ MenuUnfoldOutlined,
+ PieChartOutlined,
+} from "@ant-design/icons";
+import { ItemType, MenuItemType } from "antd/es/menu/hooks/useItems";
+import { colorConfig } from "@/themes/config";
+import Avatar from "@/components/layout/Avatar";
+import { useLocation, useNavigate } from "react-router-dom";
+import { Routes } from "@/routes/routes";
+
+interface IProps {
+ children: React.ReactNode;
+}
+
+const iconSidebarStyle = {
+ fontSize: 20,
+ color: colorConfig.neutral.lightGray,
+};
+
+const menuKey = {
+ Dashboard: Routes.Dashboard,
+ VariableInput: Routes.VariableInputIndex,
+ VariableOutput: Routes.VariableOutputIndex,
+ Result: Routes.ResultIndex,
+};
+
+const MainLayout: React.FC = ({ children }) => {
+ const [isSiderCollapsed, setIsSiderCollapsed] = React.useState(false);
+ const { md } = Grid.useBreakpoint();
+ const navigate = useNavigate();
+ const location = useLocation();
+
+ const [activeMenuKey, setActiveMenuKey] = React.useState(null);
+
+ const menuItems = React.useMemo[]>(
+ () => [
+ {
+ key: menuKey.Dashboard,
+ label: "Dashboard",
+ icon: ,
+ onClick: () => navigate(Routes.Dashboard),
+ },
+ {
+ key: menuKey.VariableInput,
+ label: "Variable Input",
+ icon: ,
+ onClick: () => navigate(Routes.VariableInputIndex),
+ },
+ {
+ key: menuKey.VariableOutput,
+ label: "Variable Output",
+ icon: ,
+ onClick: () => navigate(Routes.VariableOutputIndex),
+ },
+ {
+ key: menuKey.Result,
+ label: "Result Predict",
+ icon: ,
+ onClick: () => navigate(Routes.ResultIndex),
+ },
+ ],
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ []
+ );
+
+ React.useEffect(() => {
+ setIsSiderCollapsed(!md);
+ }, [md]);
+
+ React.useEffect(() => {
+ setActiveMenuKey(location.pathname);
+ }, [location]);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ ) : (
+
+ )
+ }
+ onClick={() =>
+ setIsSiderCollapsed(!isSiderCollapsed)
+ }
+ />
+
+
+
+
+ {children}
+
+
+
+ );
+};
+
+export default MainLayout;
diff --git a/src/main.tsx b/src/main.tsx
index a04471a..5e5fb45 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -7,6 +7,8 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import "./index.css";
import { AuthContextProvider } from "./contexts/AuthContext";
import LoaderFullscreen from "./components/shared/Loader/LoaderFullscreen";
+import { ConfigProvider } from "antd";
+import { colorConfig } from "./themes/config";
const queryClient = new QueryClient();
@@ -14,9 +16,17 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
}>
-
-
-
+
+
+
+
+
diff --git a/src/pages/Dashboard/index.tsx b/src/pages/Dashboard/index.tsx
index 724ce96..831d889 100644
--- a/src/pages/Dashboard/index.tsx
+++ b/src/pages/Dashboard/index.tsx
@@ -1,9 +1,13 @@
-import { useAuthContext } from "@/contexts/AuthContext";
+import MainLayout from "@/layouts/MainLayout";
+import { Card } from "antd";
import React from "react";
const Dashboard: React.FC = () => {
- const { user } = useAuthContext();
- return JSON.stringify(user);
+ return (
+
+
+
+ );
};
export default Dashboard;
diff --git a/src/pages/Result/index.tsx b/src/pages/Result/index.tsx
new file mode 100644
index 0000000..8cbaf19
--- /dev/null
+++ b/src/pages/Result/index.tsx
@@ -0,0 +1,13 @@
+import MainLayout from "@/layouts/MainLayout";
+import { Card } from "antd";
+import React from "react";
+
+const ResultIndex: React.FC = () => {
+ return (
+
+
+
+ );
+};
+
+export default ResultIndex;
diff --git a/src/pages/VariableInput/index.tsx b/src/pages/VariableInput/index.tsx
new file mode 100644
index 0000000..9f16feb
--- /dev/null
+++ b/src/pages/VariableInput/index.tsx
@@ -0,0 +1,13 @@
+import MainLayout from "@/layouts/MainLayout";
+import { Card } from "antd";
+import React from "react";
+
+const VariableInputIndex: React.FC = () => {
+ return (
+
+
+
+ );
+};
+
+export default VariableInputIndex;
diff --git a/src/pages/VariableOutput/index.tsx b/src/pages/VariableOutput/index.tsx
new file mode 100644
index 0000000..81d09de
--- /dev/null
+++ b/src/pages/VariableOutput/index.tsx
@@ -0,0 +1,13 @@
+import MainLayout from "@/layouts/MainLayout";
+import { Card } from "antd";
+import React from "react";
+
+const VariableOutputIndex: React.FC = () => {
+ return (
+
+
+
+ );
+};
+
+export default VariableOutputIndex;
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
index f8629fa..2d62b0b 100644
--- a/src/routes/index.tsx
+++ b/src/routes/index.tsx
@@ -9,20 +9,19 @@ import PrivateRoute from "./PrivateRoute";
const LoginPage = React.lazy(() => import("@/pages/Auth/Login"));
const RegisterPage = React.lazy(() => import("@/pages/Auth/Register"));
const DashboardPage = React.lazy(() => import("@/pages/Dashboard"));
+const VariableInputIndexPage = React.lazy(
+ () => import("@/pages/VariableInput")
+);
+const VariableOutputIndexPage = React.lazy(
+ () => import("@/pages/VariableOutput")
+);
+const ResultIndexPage = React.lazy(() => import("@/pages/Result"));
export const withSuspense = (component: ReactNode) => {
return }>{component};
};
export const router = createBrowserRouter([
- {
- path: Routes.Dashboard,
- element: withSuspense(
-
-
-
- ),
- },
{
path: Routes.Login,
element: withSuspense(
@@ -39,4 +38,36 @@ export const router = createBrowserRouter([
),
},
+ {
+ path: Routes.Dashboard,
+ element: withSuspense(
+
+
+
+ ),
+ },
+ {
+ path: Routes.VariableInputIndex,
+ element: withSuspense(
+
+
+
+ ),
+ },
+ {
+ path: Routes.VariableOutputIndex,
+ element: withSuspense(
+
+
+
+ ),
+ },
+ {
+ path: Routes.ResultIndex,
+ element: withSuspense(
+
+
+
+ ),
+ },
]);
diff --git a/src/routes/routes.ts b/src/routes/routes.ts
index fcb732c..7755c2d 100644
--- a/src/routes/routes.ts
+++ b/src/routes/routes.ts
@@ -2,6 +2,10 @@ export enum Routes {
Login = "/login",
Register = "/register",
Dashboard = "/dashboard",
+ Profile = "/profile",
+ VariableInputIndex = "/variable-input",
+ VariableOutputIndex = "/variable-output",
+ ResultIndex = "/result",
}
export enum EndpointApi {
diff --git a/src/themes/config.ts b/src/themes/config.ts
new file mode 100644
index 0000000..81dc2d6
--- /dev/null
+++ b/src/themes/config.ts
@@ -0,0 +1,13 @@
+export const colorConfig = {
+ primary: {
+ main: "#1677FF",
+ lighter: "#BAE0FF",
+ darker: "#0958d9",
+ },
+ neutral: {
+ black: "#000000",
+ gray: "#C4C4C4",
+ lightGray: "#E7E7E7",
+ white: "#FFFFFF",
+ },
+};
diff --git a/yarn.lock b/yarn.lock
index aff309d..c21ee24 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -34,7 +34,7 @@
"@ant-design/icons@^5.2.6":
version "5.2.6"
- resolved "https://registry.npmjs.org/@ant-design/icons/-/icons-5.2.6.tgz"
+ resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.2.6.tgz#2d4a9a37f531eb2a20cebec01d6fb69cf593900d"
integrity sha512-4wn0WShF43TrggskBJPRqCD0fcHbzTYjnaoskdiJrVHg86yxoZ8ZUqsXvyn4WUqehRiFKnaclOhqk9w4Ui2KVw==
dependencies:
"@ant-design/colors" "^7.0.0"