diff --git a/package.json b/package.json
index 44f14fa6..8b9c6f06 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,8 @@
"@heroicons/react": "2.0.18",
"@material-tailwind/react": "2.1.4",
"apexcharts": "3.44.0",
+ "axios": "^1.12.2",
+ "jwt-decode": "^4.0.0",
"prop-types": "15.8.1",
"react": "18.2.0",
"react-apexcharts": "1.4.1",
@@ -27,6 +29,6 @@
"prettier": "3.0.3",
"prettier-plugin-tailwindcss": "0.5.6",
"tailwindcss": "3.3.4",
- "vite": "4.5.0"
+ "vite": "^4.5.14"
}
-}
\ No newline at end of file
+}
diff --git a/public/img/background.jpg b/public/img/background.jpg
new file mode 100644
index 00000000..fb14759e
Binary files /dev/null and b/public/img/background.jpg differ
diff --git a/public/img/pattern.png b/public/img/pattern.png
deleted file mode 100644
index 2b34e46f..00000000
Binary files a/public/img/pattern.png and /dev/null differ
diff --git a/src/App.jsx b/src/App.jsx
index 87826600..4995a483 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,12 +1,25 @@
import { Routes, Route, Navigate } from "react-router-dom";
import { Dashboard, Auth } from "@/layouts";
+import PrivateRoute from "../src/component/PrivateRoute";
function App() {
return (
- } />
+ {/* Dashboard'u sadece giriş yapmış kullanıcı görecek */}
+
+
+
+ }
+ />
+
+ {/* Auth sayfaları (giriş, kayıt) herkes görebilir */}
} />
- } />
+
+ {/* Varsayılan yönlendirme */}
+ } />
);
}
diff --git a/src/api/axiosConfig.js b/src/api/axiosConfig.js
new file mode 100644
index 00000000..8fabae67
--- /dev/null
+++ b/src/api/axiosConfig.js
@@ -0,0 +1,29 @@
+import axios from "axios";
+
+
+
+const apiClient = axios.create({
+ baseURL: "https://localhost:7093/api",
+});
+
+
+apiClient.interceptors.request.use(
+ (config) => {
+
+ const token = localStorage.getItem("authToken");
+
+
+ if (token) {
+
+ config.headers.Authorization = `Bearer ${token}`;
+ }
+
+ return config;
+ },
+ (error) => {
+
+ return Promise.reject(error);
+ }
+);
+
+export default apiClient;
\ No newline at end of file
diff --git a/src/component/PrivateRoute.jsx b/src/component/PrivateRoute.jsx
new file mode 100644
index 00000000..63f8e93b
--- /dev/null
+++ b/src/component/PrivateRoute.jsx
@@ -0,0 +1,15 @@
+import React from "react";
+import { Navigate } from "react-router-dom";
+
+const PrivateRoute = ({ children }) => {
+ const token = localStorage.getItem("authToken");
+
+ if (!token) {
+ // Token yoksa login sayfasına yönlendir
+ return ;
+ }
+
+ return children;
+};
+
+export default PrivateRoute;
diff --git a/src/context/index.jsx b/src/context/index.jsx
index 653a362d..232589ba 100644
--- a/src/context/index.jsx
+++ b/src/context/index.jsx
@@ -23,6 +23,10 @@ export function reducer(state, action) {
}
case "OPEN_CONFIGURATOR": {
return { ...state, openConfigurator: action.value };
+ }
+ // YENİ: Kullanıcı rolünü ayarlamak için yeni case eklendi.
+ case "SET_USER_ROLE": {
+ return { ...state, userRole: action.value };
}
default: {
throw new Error(`Unhandled action type: ${action.type}`);
@@ -38,18 +42,20 @@ export function MaterialTailwindControllerProvider({ children }) {
transparentNavbar: true,
fixedNavbar: false,
openConfigurator: false,
+ // YENİ: userRole state'i eklendi. Sayfa yenilendiğinde rolün kaybolmaması için localStorage'dan okunuyor.
+ userRole: localStorage.getItem("userRole") || null,
};
const [controller, dispatch] = React.useReducer(reducer, initialState);
const value = React.useMemo(
- () => [controller, dispatch],
- [controller, dispatch]
+ () => [controller, dispatch],
+ [controller, dispatch]
);
return (
-
- {children}
-
+
+ {children}
+
);
}
@@ -58,7 +64,7 @@ export function useMaterialTailwindController() {
if (!context) {
throw new Error(
- "useMaterialTailwindController should be used inside the MaterialTailwindControllerProvider."
+ "useMaterialTailwindController should be used inside the MaterialTailwindControllerProvider."
);
}
@@ -72,14 +78,18 @@ MaterialTailwindControllerProvider.propTypes = {
};
export const setOpenSidenav = (dispatch, value) =>
- dispatch({ type: "OPEN_SIDENAV", value });
+ dispatch({ type: "OPEN_SIDENAV", value });
export const setSidenavType = (dispatch, value) =>
- dispatch({ type: "SIDENAV_TYPE", value });
+ dispatch({ type: "SIDENAV_TYPE", value });
export const setSidenavColor = (dispatch, value) =>
- dispatch({ type: "SIDENAV_COLOR", value });
+ dispatch({ type: "SIDENAV_COLOR", value });
export const setTransparentNavbar = (dispatch, value) =>
- dispatch({ type: "TRANSPARENT_NAVBAR", value });
+ dispatch({ type: "TRANSPARENT_NAVBAR", value });
export const setFixedNavbar = (dispatch, value) =>
- dispatch({ type: "FIXED_NAVBAR", value });
+ dispatch({ type: "FIXED_NAVBAR", value });
export const setOpenConfigurator = (dispatch, value) =>
- dispatch({ type: "OPEN_CONFIGURATOR", value });
+ dispatch({ type: "OPEN_CONFIGURATOR", value });
+
+
+export const setUserRole = (dispatch, value) =>
+ dispatch({ type: "SET_USER_ROLE", value });
\ No newline at end of file
diff --git a/src/layouts/dashboard.jsx b/src/layouts/dashboard.jsx
index 888a627a..3041b4a5 100644
--- a/src/layouts/dashboard.jsx
+++ b/src/layouts/dashboard.jsx
@@ -2,53 +2,53 @@ import { Routes, Route } from "react-router-dom";
import { Cog6ToothIcon } from "@heroicons/react/24/solid";
import { IconButton } from "@material-tailwind/react";
import {
- Sidenav,
- DashboardNavbar,
- Configurator,
- Footer,
+ Sidenav,
+ DashboardNavbar,
+ Configurator,
+ Footer,
} from "@/widgets/layout";
import routes from "@/routes";
import { useMaterialTailwindController, setOpenConfigurator } from "@/context";
export function Dashboard() {
- const [controller, dispatch] = useMaterialTailwindController();
- const { sidenavType } = controller;
+ const [controller, dispatch] = useMaterialTailwindController();
+ const { sidenavType } = controller;
- return (
-
-
-
-
-
-
setOpenConfigurator(dispatch, true)}
- >
-
-
-
- {routes.map(
- ({ layout, pages }) =>
- layout === "dashboard" &&
- pages.map(({ path, element }) => (
-
- ))
- )}
-
-
-
-
-
-
- );
+ return (
+
+
+
+
+
+
setOpenConfigurator(dispatch, true)}
+ >
+
+
+
+ {routes.map(
+ ({ layout, pages }) =>
+ layout === "dashboard" &&
+ pages.map(({ path, element }) => (
+
+ ))
+ )}
+
+
+
+
+
+
+ );
}
Dashboard.displayName = "/src/layout/dashboard.jsx";
diff --git a/src/pages/auth/index.js b/src/pages/auth/index.js
index ca1bbcb6..425a5351 100644
--- a/src/pages/auth/index.js
+++ b/src/pages/auth/index.js
@@ -1,2 +1,2 @@
export * from "@/pages/auth/sign-in";
-export * from "@/pages/auth/sign-up";
+
diff --git a/src/pages/auth/sign-in.jsx b/src/pages/auth/sign-in.jsx
index 3b3da41a..9396ea96 100644
--- a/src/pages/auth/sign-in.jsx
+++ b/src/pages/auth/sign-in.jsx
@@ -1,126 +1,122 @@
+import React, { useState } from "react";
+import { Card, Input, Button, Typography } from "@material-tailwind/react";
+import { useNavigate } from "react-router-dom";
+import { jwtDecode } from "jwt-decode";
import {
- Card,
- Input,
- Checkbox,
- Button,
- Typography,
-} from "@material-tailwind/react";
-import { Link } from "react-router-dom";
-
+ useMaterialTailwindController,
+ setUserRole,
+} from "@/context";
+// 1. Kendi oluşturduğunuz apiClient'ı import edin
+import apiClient from "../../api/axiosConfig.js"
export function SignIn() {
+ const [, dispatch] = useMaterialTailwindController();
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+ const navigate = useNavigate();
+
+ const handleSignIn = async () => {
+ const loginData = {
+ username: username,
+ password: password,
+ };
+
+ try {
+
+ const response = await apiClient.post("/Auth/Login", loginData);
+
+
+ const token = response.data;
+ console.log(token);
+
+ if (token) {
+ // 3. Token'ı localStorage'a kaydet. Bu, interceptor'ın çalışması için kritik.
+ localStorage.setItem("authToken", token);
+
+
+ // İYİ BİR PRATİK: Token'ı aldıktan sonra, interceptor'ın bir sonraki
+ // sayfa yenilemesini beklemeden çalışması için anında ayarlayın.
+ apiClient.defaults.headers.common['Authorization'] = `Bearer ${token}`;
+
+ console.log("Giriş Başarılı!");
+ const decodedToken = jwtDecode(token);
+ const userRoleClaim = decodedToken.role || decodedToken["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"];
+ const userRole = userRoleClaim ? userRoleClaim.toLowerCase() : null;
+ console.log("Kullanıcı Rolü:", userRole);
+
+ if (userRole) {
+ setUserRole(dispatch, userRole);
+ localStorage.setItem("userRole", userRole);
+ }
+
+ navigate("/dashboard/home");
+ } else {
+ alert("Giriş başarılı ancak sunucudan geçerli bir token alınamadı.");
+ }
+ } catch (error) {
+ // 4. Geliştirilmiş hata yönetimi.
+ // axios, 4xx veya 5xx gibi başarısız statü kodlarında otomatik olarak hata fırlatır ve bu blok çalışır.
+ console.error("Giriş sırasında hata:", error);
+
+ if (error.response) {
+ // Sunucu bir hata koduyla (401, 404, 500 vb.) yanıt verdi.
+ alert("Kullanıcı adı veya şifre hatalı!");
+ } else if (error.request) {
+ // İstek yapıldı ancak sunucudan yanıt alınamadı (network hatası).
+ alert("Sunucuya bağlanılamadı. Ağ bağlantınızı kontrol edin.");
+ } else {
+ // İsteği hazırlarken bir hata oluştu.
+ alert("Beklenmedik bir hata oluştu.");
+ }
+ }
+ };
+
return (
-
-
-
- Sign In
- Enter your email and password to Sign In.
-
-