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..65555782 100644
--- a/src/pages/auth/sign-in.jsx
+++ b/src/pages/auth/sign-in.jsx
@@ -1,126 +1,129 @@
+import React, { useState } from "react";
+import { Card, Input, Button, Typography } from "@material-tailwind/react";
+import { Link, 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";
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 fetch("https://localhost:7093/api/Auth/Login", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(loginData),
+ });
+
+ if (response.ok) {
+ const token = await response.text();
+
+
+ localStorage.setItem("authToken", 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(userRole);
+
+ if (userRole) {
+ setUserRole(dispatch, userRole);
+ localStorage.setItem("userRole", userRole);
+ }
+
+
+ navigate("/dashboard/home");
+ } else {
+ alert("Kullanıcı adı veya şifre hatalı!");
+ }
+ } catch (error) {
+ console.error("Sunucuya bağlanırken bir hata oluştu:", error);
+ alert("Sunucuya bağlanılamadı.");
+ }
+ };
+
+
+
return (
-
-
-
- Sign In
- Enter your email and password to Sign In.
-
-
-
-
- Projects
+
+
+
+
+ Kullanıcılar
-
- Architects design houses
-
-
- {projectsData.map(
- ({ img, title, description, tag, route, members }) => (
-
-
+ Kullanıcı Ekle
+
+
+
+
+
+
+ {["Ad Soyad", "Araç Plakası", "Telefon", "İşlem"].map((el) => (
+
-
-
-
- {tag}
+ {el}
-
- {title}
-
-
- {description}
-
-
-
-
-
-
-
- {members.map(({ img, name }, key) => (
-
-
+ ))}
+ |
+
+
+ {/* Statik veri yerine backend'den gelen 'users' state'ini map'liyoruz */}
+ {users.map((user, key) => {
+ const isLast = key === users.length - 1;
+ const className = `py-3 px-5 ${
+ isLast ? "" : "border-b border-blue-gray-50"
+ }`;
+
+ return (
+ // React'te listeler için 'key' prop'u benzersiz olmalıdır, ID kullanmak en iyisidir.
+
+ |
+
+ {user.fullName}
+
+ |
+
+
+ {user.licensePlate}
+
+ |
+
+
+ {user.phoneNumber}
+
+ |
+
+
+
+ {/* YENİ: Sil butonuna onClick olayı eklendi */}
+
-
- ))}
-
-
-
- )
- )}
-
-
-
-
- >
+ variant="gradient"
+ color="red"
+ className="normal-case text-[10px] py-1 px-2"
+ onClick={() => handleDelete(user.id, user.fullName)}
+ >
+ Sil
+
+
+ |
+
+ );
+ })}
+
+
+
+
+
);
}
-export default Profile;
+export default Profile;
\ No newline at end of file
diff --git a/src/pages/dashboard/tables.jsx b/src/pages/dashboard/tables.jsx
index 3d453ed7..531a557b 100644
--- a/src/pages/dashboard/tables.jsx
+++ b/src/pages/dashboard/tables.jsx
@@ -3,31 +3,37 @@ import {
CardHeader,
CardBody,
Typography,
- Avatar,
- Chip,
- Tooltip,
- Progress,
+ Button,
} from "@material-tailwind/react";
-import { EllipsisVerticalIcon } from "@heroicons/react/24/outline";
-import { authorsTableData, projectsTableData } from "@/data";
+import { authorsTableData } from "@/data";
export function Tables() {
return (
-
+
- Authors Table
+ Araçlar
+
+
-
+
- {["author", "function", "status", "employed", ""].map((el) => (
+ {["Araçlar", "Şoför", "İşlem"].map((el) => (
|
|
+
- {authorsTableData.map(
- ({ img, name, email, job, online, date }, key) => {
- const className = `py-3 px-5 ${
- key === authorsTableData.length - 1
- ? ""
- : "border-b border-blue-gray-50"
- }`;
+ {authorsTableData.map(({ plaka, driver }, key) => {
+ const className = `py-3 px-5 ${
+ key === authorsTableData.length - 1 ? "" : "border-b border-blue-gray-50"
+ }`;
- return (
-
-
-
-
-
-
- {name}
-
-
- {email}
-
-
-
- |
-
-
- {job[0]}
-
-
- {job[1]}
-
- |
-
-
- |
-
-
- {date}
-
- |
-
-
- Edit
-
- |
-
- );
- }
- )}
-
-
-
-
-
-
-
- Projects Table
-
-
-
-
-
-
- {["companies", "members", "budget", "completion", ""].map(
- (el) => (
-
-
- {el}
+ return (
+
+ {/* Araç Plaka */}
+ |
+
+ {plaka}
-
- )
- )}
- |
- |
-
- {projectsTableData.map(
- ({ img, name, members, budget, completion }, key) => {
- const className = `py-3 px-5 ${
- key === projectsTableData.length - 1
- ? ""
- : "border-b border-blue-gray-50"
- }`;
+
+
+ {/* Şoför */}
+
+
+ {driver}
+
+ |
- return (
-
- |
-
- |
-
- {members.map(({ img, name }, key) => (
-
-
-
- ))}
- |
-
-
+
+ |
-
-
- |
-
-
+
- |
-
- );
- }
- )}
+ Sil
+
+
+
+
+ );
+ })}
diff --git a/src/routes.jsx b/src/routes.jsx
index 3a5a8da0..c2c7cfbe 100644
--- a/src/routes.jsx
+++ b/src/routes.jsx
@@ -1,66 +1,139 @@
import {
- HomeIcon,
- UserCircleIcon,
- TableCellsIcon,
- InformationCircleIcon,
- ServerStackIcon,
- RectangleStackIcon,
+
+ HomeIcon,
+
+ UserCircleIcon,
+
+ TableCellsIcon,
+
+ InformationCircleIcon,
+
+ ServerStackIcon,
+
+ RectangleStackIcon,
+
+ TruckIcon,
+
+ MapIcon,
+
+ Squares2X2Icon,
+
} from "@heroicons/react/24/solid";
+
import { Home, Profile, Tables, Notifications } from "@/pages/dashboard";
-import { SignIn, SignUp } from "@/pages/auth";
+
+import { SignIn } from "@/pages/auth";
+
+
+
+
+
+
const icon = {
- className: "w-5 h-5 text-inherit",
+
+ className: "w-5 h-5 text-inherit",
+
};
+
+
export const routes = [
- {
- layout: "dashboard",
- pages: [
- {
- icon: ,
- name: "dashboard",
- path: "/home",
- element: ,
- },
- {
- icon: ,
- name: "profile",
- path: "/profile",
- element: ,
- },
- {
- icon: ,
- name: "tables",
- path: "/tables",
- element: ,
- },
- {
- icon: ,
- name: "notifications",
- path: "/notifications",
- element: ,
- },
- ],
- },
- {
- title: "auth pages",
- layout: "auth",
- pages: [
- {
- icon: ,
- name: "sign in",
- path: "/sign-in",
- element: ,
- },
- {
- icon: ,
- name: "sign up",
- path: "/sign-up",
- element: ,
- },
- ],
- },
+
+ {
+
+ layout: "dashboard",
+
+ pages: [
+
+ {
+
+ icon: ,
+
+ name: "Araç Sıraları",
+
+ path: "/home",
+
+ element: ,
+
+ // roles eklenmediği için giriş yapan herkes görebilir.
+
+ // İsterseniz roles: ['admin', 'user'] de ekleyebilirsiniz.
+
+ },
+
+ {
+
+ icon: ,
+
+ name: "Kullanıcılar",
+
+ path: "/profile",
+
+ element: ,
+
+ roles: ['admin'],
+
+ },
+
+ {
+
+ icon: ,
+
+ name: "Araçlar",
+
+ path: "/tables",
+
+ element: ,
+
+ roles: ['admin'], // YENİ: Sadece adminler görebilir.
+
+ },
+
+ {
+
+ icon: ,
+
+ name: "Güzergahlar",
+
+ path: "/notifications",
+
+ element: ,
+
+ roles: ['admin'], // YENİ: Sadece adminler görebilir.
+
+ },
+
+ ],
+
+ },
+
+ {
+
+ title: "auth pages",
+
+ layout: "auth",
+
+ pages: [
+
+ {
+
+ icon: ,
+
+ name: "sign in",
+
+ path: "/sign-in",
+
+ element: ,
+
+ },
+
+ ],
+
+ },
+
];
-export default routes;
+
+
+export default routes;
\ No newline at end of file
diff --git a/src/widgets/layout/dashboard-navbar.jsx b/src/widgets/layout/dashboard-navbar.jsx
index d91e23f7..00eaa89b 100644
--- a/src/widgets/layout/dashboard-navbar.jsx
+++ b/src/widgets/layout/dashboard-navbar.jsx
@@ -1,36 +1,23 @@
-import { useLocation, Link } from "react-router-dom";
-import {
- Navbar,
- Typography,
- Button,
- IconButton,
- Breadcrumbs,
- Input,
- Menu,
- MenuHandler,
- MenuList,
- MenuItem,
- Avatar,
-} from "@material-tailwind/react";
-import {
- UserCircleIcon,
- Cog6ToothIcon,
- BellIcon,
- ClockIcon,
- CreditCardIcon,
- Bars3Icon,
-} from "@heroicons/react/24/solid";
-import {
- useMaterialTailwindController,
- setOpenConfigurator,
- setOpenSidenav,
-} from "@/context";
+import { ArrowRightOnRectangleIcon, Bars3Icon } from "@heroicons/react/24/solid";
+import { IconButton, Input, Navbar, Typography, Breadcrumbs } from "@material-tailwind/react";
+import { useMaterialTailwindController, setOpenSidenav } from "@/context";
+import { useLocation, Link, useNavigate } from "react-router-dom"; // 🔹 useNavigate eklendi
export function DashboardNavbar() {
const [controller, dispatch] = useMaterialTailwindController();
const { fixedNavbar, openSidenav } = controller;
const { pathname } = useLocation();
const [layout, page] = pathname.split("/").filter((el) => el !== "");
+
+
+ const navigate = useNavigate();
+
+const handleLogout = () => {
+ localStorage.removeItem("authToken"); // Token sil
+ localStorage.removeItem("userRole"); // varsa rol bilgisini de sil
+ navigate("/auth/sign-in"); // Login ekranına yönlendir
+};
+
return (
-
+
+ {/* Breadcrumbs ve sayfa başlığı */}
- {layout}
+ {layout || "Home"}
-
- {page}
+
+ {page || "Dashboard"}
-
- {page}
+
+ {page || "Dashboard"}
-
+
+ {/* Arama ve ikonlar */}
+
-
+
+
+ {/* Sidenav toggle */}
-
-
-
-
-
-
-
+
+ {/* Çıkış butonu */}
setOpenConfigurator(dispatch, true)}
+ color="red"
+ onClick={handleLogout} // 🔹 logout çağrıldı
+ className="rounded-full hover:bg-red-100 transition-colors"
>
-
+
);
}
-
-DashboardNavbar.displayName = "/src/widgets/layout/dashboard-navbar.jsx";
-
-export default DashboardNavbar;
diff --git a/src/widgets/layout/footer.jsx b/src/widgets/layout/footer.jsx
index 1ea98e53..f2c59643 100644
--- a/src/widgets/layout/footer.jsx
+++ b/src/widgets/layout/footer.jsx
@@ -8,33 +8,7 @@ export function Footer({ brandName, brandLink, routes }) {
return (
);
diff --git a/src/widgets/layout/sidenav.jsx b/src/widgets/layout/sidenav.jsx
index cc7e6ffe..091be2b8 100644
--- a/src/widgets/layout/sidenav.jsx
+++ b/src/widgets/layout/sidenav.jsx
@@ -11,22 +11,24 @@ import { useMaterialTailwindController, setOpenSidenav } from "@/context";
export function Sidenav({ brandImg, brandName, routes }) {
const [controller, dispatch] = useMaterialTailwindController();
- const { sidenavColor, sidenavType, openSidenav } = controller;
+
+ const { sidenavColor, sidenavType, openSidenav, userRole } = controller;
const sidenavTypes = {
dark: "bg-gradient-to-br from-gray-800 to-gray-900",
white: "bg-white shadow-sm",
transparent: "bg-transparent",
};
+ // Debug: Rolün doğru gelip gelmediğini kontrol etmek için
+ console.log("SIDEBAR'DAKİ ROL:", userRole);
+
return (