diff --git a/package.json b/package.json
index 44f14fa6..d31eb950 100644
--- a/package.json
+++ b/package.json
@@ -9,9 +9,13 @@
"preview": "vite preview"
},
"dependencies": {
+ "@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/sortable": "^10.0.0",
"@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 +31,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..3933629b 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,12 +1,28 @@
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.
-
-
+
+ );
}
-export default Home;
+export default Home;
\ No newline at end of file
diff --git a/src/pages/dashboard/index.js b/src/pages/dashboard/index.js
index 7651895e..3b32e950 100644
--- a/src/pages/dashboard/index.js
+++ b/src/pages/dashboard/index.js
@@ -2,3 +2,9 @@ export * from "@/pages/dashboard/home";
export * from "@/pages/dashboard/profile";
export * from "@/pages/dashboard/tables";
export * from "@/pages/dashboard/notifications";
+export * from "@/pages/dashboard/queuemanagementpage.jsx";
+
+
+
+
+
diff --git a/src/pages/dashboard/notifications.jsx b/src/pages/dashboard/notifications.jsx
index f4be88b0..50e1f755 100644
--- a/src/pages/dashboard/notifications.jsx
+++ b/src/pages/dashboard/notifications.jsx
@@ -1,88 +1,151 @@
-import React from "react";
+import React, { useState, useEffect } from "react";
import {
Typography,
- Alert,
Card,
CardHeader,
CardBody,
+ Button,
} from "@material-tailwind/react";
-import { InformationCircleIcon } from "@heroicons/react/24/outline";
+import apiClient from "../../api/axiosConfig.js";
+import { AddRouteModal } from "@/widgets/layout/AddRouteModal";
+import { EditRouteModal } from "@/widgets/layout/EditRouteModal"; // 1. Edit modal'ı import et
export function Notifications() {
- const [showAlerts, setShowAlerts] = React.useState({
- blue: true,
- green: true,
- orange: true,
- red: true,
- });
- const [showAlertsWithIcon, setShowAlertsWithIcon] = React.useState({
- blue: true,
- green: true,
- orange: true,
- red: true,
- });
- const alerts = ["gray", "green", "orange", "red"];
+ const [routes, setRoutes] = useState([]);
+
+ // "Ekle" modalı için state'ler
+ const [addModalOpen, setAddModalOpen] = useState(false);
+ const handleOpenAddModal = () => setAddModalOpen(!addModalOpen);
+
+ // 2. "Düzenle" modalı için yeni state'ler
+ const [editModalOpen, setEditModalOpen] = useState(false);
+ const [currentRoute, setCurrentRoute] = useState(null); // Düzenlenecek güzergahı tutar
+
+ // Düzenleme modal'ını açan fonksiyon
+ const handleOpenEditModal = (route) => {
+ setCurrentRoute(route);
+ setEditModalOpen(true);
+ };
+
+ // Düzenleme modal'ını kapatan fonksiyon
+ const handleCloseEditModal = () => {
+ setEditModalOpen(false);
+ setCurrentRoute(null);
+ };
+
+ // Güzergah listesini API'den çeken ana fonksiyon
+ const fetchRoutes = async () => {
+ try {
+ const response = await apiClient.get("/admin/routes");
+ setRoutes(response.data);
+ } catch (error) {
+ console.error("Güzergahları çekerken hata oluştu:", error);
+ }
+ };
+
+ useEffect(() => {
+ fetchRoutes();
+ }, []);
+
+ // Ekleme, silme veya güncelleme sonrası listeyi yeniden çekmek için
+ const handleDataChange = () => {
+ fetchRoutes();
+ };
+
+ const handleDelete = async (routeId) => {
+ if (window.confirm("Bu güzergahı silmek istediğinizden emin misiniz?")) {
+ try {
+ await apiClient.delete(`/admin/routes/${routeId}`);
+ handleDataChange(); // Silme sonrası listeyi yenile
+ } catch (error) {
+ console.error("Güzergah silinirken hata oluştu:", error);
+ }
+ }
+ };
return (
-
-
-
-
- Alerts
-
-
-
- {alerts.map((color) => (
- setShowAlerts((current) => ({ ...current, [color]: false }))}
- >
- A simple {color} alert with an example link. Give
- it a click if you like.
-
- ))}
-
-
-
-
-
- Alerts with Icon
-
-
-
- {alerts.map((color) => (
-
- }
- onClose={() => setShowAlertsWithIcon((current) => ({
- ...current,
- [color]: false,
- }))}
+ <>
+
+
+ {/* 3. Yeni düzenleme modalını sayfaya ekle */}
+
+
+
+
+
- A simple {color} alert with an example link. Give
- it a click if you like.
-
- ))}
-
-
-
+
+ Güzergahlar
+
+
+
+
+
+
+
+ {["Güzergah", "İşlem"].map((el) => (
+ |
+
+ {el}
+
+ |
+ ))}
+
+
+
+ {routes.map((route) => (
+
+ |
+
+ {route.routeName}
+
+ |
+
+ {/* 4. Düzenle butonunun onClick olayını güncelle */}
+
+
+ |
+
+ ))}
+
+
+
+
+
+ >
);
}
-export default Notifications;
+export default Notifications;
\ No newline at end of file
diff --git a/src/pages/dashboard/profile.jsx b/src/pages/dashboard/profile.jsx
index 0d9f0115..bc40433a 100644
--- a/src/pages/dashboard/profile.jsx
+++ b/src/pages/dashboard/profile.jsx
@@ -1,221 +1,122 @@
-import {
- Card,
- CardBody,
- CardHeader,
- CardFooter,
- Avatar,
- Typography,
- Tabs,
- TabsHeader,
- Tab,
- Switch,
- Tooltip,
- Button,
-} from "@material-tailwind/react";
-import {
- HomeIcon,
- ChatBubbleLeftEllipsisIcon,
- Cog6ToothIcon,
- PencilIcon,
-} from "@heroicons/react/24/solid";
-import { Link } from "react-router-dom";
-import { ProfileInfoCard, MessageCard } from "@/widgets/cards";
-import { platformSettingsData, conversationsData, projectsData } from "@/data";
+import React, { useState, useEffect } from "react";
+import { Card, CardHeader, CardBody, Typography, Button } from "@material-tailwind/react";
+import apiClient from "../../api/axiosConfig.js";
+import { AddUserModal } from "@/widgets/layout/AddUserModal";
+import { EditUserModal } from "@/widgets/layout/EditUserModal";
export function Profile() {
+ const [users, setUsers] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ // Ekleme ve Düzenleme Modalları için state'ler
+ const [openAddModal, setOpenAddModal] = useState(false);
+ const [openEditModal, setOpenEditModal] = useState(false);
+ const [currentUser, setCurrentUser] = useState(null);
+
+ const handleOpenAddModal = () => setOpenAddModal(true);
+ const handleCloseAddModal = () => setOpenAddModal(false);
+
+ // Düzenleme modalını açan fonksiyon
+ const handleOpenEditModal = (user) => {
+ setCurrentUser(user);
+ setOpenEditModal(true);
+ };
+ // Düzenleme modalını kapatan fonksiyon
+ const handleCloseEditModal = () => {
+ setCurrentUser(null);
+ setOpenEditModal(false);
+ };
+
+ const fetchUsers = async () => {
+ try {
+ setLoading(true);
+ const response = await apiClient.get("/Users");
+ setUsers(response.data);
+ setError(null);
+ } catch (err) {
+ setError("Kullanıcı verileri yüklenirken bir hata oluştu.");
+ console.error(err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchUsers();
+ }, []);
+
+ // Ekleme, silme veya güncelleme sonrası listeyi yeniden çek
+ const handleDataChange = () => {
+ fetchUsers();
+ };
+
+ const handleDeleteUser = async (userId, userName) => {
+ if (window.confirm(`'${userName}' adlı kullanıcıyı silmek istediğinizden emin misiniz?`)) {
+ try {
+ await apiClient.delete(`/Users/${userId}`);
+ handleDataChange(); // Silme sonrası listeyi yenile
+ } catch (err) {
+ alert("Kullanıcı silinirken bir hata oluştu.");
+ console.error(err);
+ }
+ }
+ };
+
+ if (loading) return Kullanıcılar Yükleniyor...
;
+ if (error) return {error}
;
+
return (
- <>
-
-
-
-
-
-
-
-
- Richard Davis
-
-
- CEO / Co-Founder
-
-
-
-
-
-
-
-
- App
-
-
-
- Message
-
-
-
- Settings
-
-
-
-
-
-
-
-
- Platform Settings
-
-
- {platformSettingsData.map(({ title, options }) => (
-
-
- {title}
-
-
- {options.map(({ checked, label }) => (
-
- ))}
-
-
- ))}
-
-
-
-
-
-
-
- ),
- }}
- action={
-
-
-
- }
- />
-
-
- Platform Settings
-
-
- {conversationsData.map((props) => (
-
- reply
-
- }
- />
+ <>
+
+
+
+
+
+
+ Kullanıcılar
+
+
+
+
+
+
+ {["Tam Ad", "Plaka", "Telefon", "İşlem"].map((el) => (
+ |
+ {el}
+ |
+ ))}
+
+
+
+ {users.map((user) => (
+
+ |
+ {user.fullName}
+ |
+
+ {user.licensePlate}
+ |
+
+ {user.phoneNumber}
+ |
+
+ {/* 3. Düzenle butonunun onClick olayı güncellendi */}
+
+ {/* 4. Buton boyutu `xs`'den `sm`'ye çevrildi */}
+
+ |
+
))}
-
-
-
-
-
- Projects
-
-
- Architects design houses
-
-
- {projectsData.map(
- ({ img, title, description, tag, route, members }) => (
-
-
-
-
-
-
- {tag}
-
-
- {title}
-
-
- {description}
-
-
-
-
-
-
-
- {members.map(({ img, name }, key) => (
-
-
-
- ))}
-
-
-
- )
- )}
-
-
-
-
- >
+
+
+
+
+
+ >
);
}
-export default Profile;
+export default Profile;
\ No newline at end of file
diff --git a/src/pages/dashboard/queuemanagementpage.jsx b/src/pages/dashboard/queuemanagementpage.jsx
new file mode 100644
index 00000000..f6151c3e
--- /dev/null
+++ b/src/pages/dashboard/queuemanagementpage.jsx
@@ -0,0 +1,187 @@
+import React, { useState, useEffect, useMemo } from "react";
+import {
+ Typography, Card, CardHeader, CardBody, Button, Select, Option, List, ListItem,
+} from "@material-tailwind/react";
+import apiClient from "../../api/axiosConfig.js";
+
+export function QueueManagementPage() {
+ // --- STATE TANIMLAMALARI ---
+ const [routes, setRoutes] = useState([]); // Tüm güzergahların listesi (sol panel)
+ const [allVehicles, setAllVehicles] = useState([]); // Tüm araçların listesi (dropdown için)
+ const [selectedRoute, setSelectedRoute] = useState(null); // Seçili olan güzergah
+ const [queuedVehicles, setQueuedVehicles] = useState([]); // Seçili güzergahın sırasındaki araçlar
+ const [vehicleToAdd, setVehicleToAdd] = useState(""); // Dropdown'dan seçilen araç ID'si
+ const [loadingQueue, setLoadingQueue] = useState(false); // Sıra yüklenirken gösterilecek spinner için
+
+ // --- VERİ ÇEKME İŞLEMLERİ ---
+
+ // 1. Bileşen ilk yüklendiğinde tüm güzergahları ve tüm araçları çek
+ useEffect(() => {
+ const fetchInitialData = async () => {
+ try {
+ const routesPromise = apiClient.get("/admin/routes");
+ const vehiclesPromise = apiClient.get("/admin/vehicles");
+
+ const [routesResponse, vehiclesResponse] = await Promise.all([routesPromise, vehiclesPromise]);
+
+ setRoutes(routesResponse.data);
+ setAllVehicles(vehiclesResponse.data);
+ } catch (error) {
+ console.error("Ana veriler çekilirken hata oluştu:", error);
+ }
+ };
+ fetchInitialData();
+ }, []);
+
+ // 2. Bir güzergah seçildiğinde, o güzergahın sırasını API'den çek
+ useEffect(() => {
+ if (selectedRoute) {
+ setLoadingQueue(true);
+ const fetchQueue = async () => {
+ try {
+ const response = await apiClient.get(`/routes/${selectedRoute.id}/queue`);
+ setQueuedVehicles(response.data);
+ } catch (error) {
+ console.error(`Sıra çekilirken hata (Güzergah ID: ${selectedRoute.id}):`, error);
+ setQueuedVehicles([]); // Hata durumunda listeyi boşalt
+ } finally {
+ setLoadingQueue(false);
+ }
+ };
+ fetchQueue();
+ }
+ }, [selectedRoute]); // selectedRoute her değiştiğinde bu blok çalışır
+
+
+
+ const handleAddVehicleToQueue = async () => {
+ if (!vehicleToAdd || !selectedRoute) {
+ alert("Lütfen bir araç seçin.");
+ return;
+ }
+ try {
+ await apiClient.post(`/routes/${selectedRoute.id}/queue`, { vehicleId: vehicleToAdd });
+ // Başarılı ekleme sonrası sırayı anında güncelle (Refetch)
+ const response = await apiClient.get(`/routes/${selectedRoute.id}/queue`);
+ setQueuedVehicles(response.data);
+ setVehicleToAdd(""); // Dropdown'ı temizle
+ } catch (error) {
+ console.error("Sıraya araç eklenirken hata:", error);
+ alert(error.response?.data || "Araç sıraya eklenemedi.");
+ }
+ };
+
+ const handleRemoveVehicleFromQueue = async (vehicleId) => {
+ if (window.confirm("Bu aracı sıradan çıkarmak istediğinizden emin misiniz?")) {
+ try {
+ await apiClient.delete(`/routes/${selectedRoute.id}/queue/${vehicleId}`);
+ // Başarılı silme sonrası sırayı anında güncelle (Refetch)
+ const response = await apiClient.get(`/routes/${selectedRoute.id}/queue`);
+ setQueuedVehicles(response.data);
+ } catch (error) {
+ console.error("Araç sıradan çıkarılırken hata:", error);
+ alert("Araç sıradan çıkarılamadı.");
+ }
+ }
+ };
+
+ // KULLANICI DENEYİMİ İYİLEŞTİRMESİ: Mevcut sırada olan araçları, ekleme dropdown'ında gösterme.
+ const availableVehicles = useMemo(() => {
+ const queuedVehicleIds = new Set(queuedVehicles.map(v => v.id));
+ return allVehicles.filter(v => !queuedVehicleIds.has(v.id));
+ }, [allVehicles, queuedVehicles]);
+
+ return (
+
+
+
+
+ Güzergah Sıra Yönetimi
+
+
+
+ {/* SOL PANEL: Güzergah Listesi */}
+
+ Güzergah Seçin
+
+
+ {routes.map(route => (
+ setSelectedRoute(route)}
+ selected={selectedRoute?.id === route.id}
+ >
+ {route.routeName}
+
+ ))}
+
+
+
+
+ {/* SAĞ PANEL: Sıra ve Ekleme Formu */}
+
+ {!selectedRoute ? (
+
+ Lütfen bir güzergah seçin
+
+ ) : (
+
+ {/* Sıradaki Araçlar Tablosu */}
+
+ Sıradaki Araçlar: {selectedRoute.routeName}
+
+
+
+
+
+ {["Sıra", "Plaka", "Şoför", "İşlem"].map(el => (
+ |
+ {el}
+ |
+ ))}
+
+
+
+ {loadingQueue ? (
+ | Yükleniyor... |
+ ) : (
+ queuedVehicles.map((vehicle, index) => (
+
+ | #{index + 1} |
+ {vehicle.licensePlate} |
+ {vehicle.userFullName} |
+
+
+ |
+
+ ))
+ )}
+
+
+
+
+ {/* Sıraya Araç Ekleme Formu */}
+
+
+
+
+
+
+
+ )}
+
+
+
+
+ );
+}
+
+export default QueueManagementPage;
\ No newline at end of file
diff --git a/src/pages/dashboard/tables.jsx b/src/pages/dashboard/tables.jsx
index 3d453ed7..0b583910 100644
--- a/src/pages/dashboard/tables.jsx
+++ b/src/pages/dashboard/tables.jsx
@@ -1,221 +1,129 @@
-import {
- Card,
- CardHeader,
- CardBody,
- Typography,
- Avatar,
- Chip,
- Tooltip,
- Progress,
-} from "@material-tailwind/react";
-import { EllipsisVerticalIcon } from "@heroicons/react/24/outline";
-import { authorsTableData, projectsTableData } from "@/data";
+import React, { useState, useEffect } from "react";
+import { Card, CardHeader, CardBody, Typography, Button } from "@material-tailwind/react";
+import apiClient from "../../api/axiosConfig.js";
+import { AddVehicleModal } from "@/widgets/layout/AddVehicleModal";
+import { EditVehicleModal } from "@/widgets/layout/EditVehicleModal";
export function Tables() {
+ const [vehicles, setVehicles] = useState([]);
+
+ // "Ekle" modalı için state'ler
+ const [addModalOpen, setAddModalOpen] = useState(false);
+ const handleOpenAddModal = () => setAddModalOpen(!addModalOpen);
+
+ // "Düzenle" modalı için state'ler
+ const [editModalOpen, setEditModalOpen] = useState(false);
+ const [currentVehicle, setCurrentVehicle] = useState(null); // Düzenlenecek aracı tutar
+
+ // Düzenleme modal'ını açan fonksiyon
+ const handleOpenEditModal = (vehicle) => {
+ setCurrentVehicle(vehicle); // Hangi aracın düzenleneceğini state'e ata
+ setEditModalOpen(true); // Modal'ı aç
+ };
+
+ // Düzenleme modal'ını kapatan fonksiyon
+ const handleCloseEditModal = () => {
+ setEditModalOpen(false);
+ setCurrentVehicle(null); // Modal kapanınca seçili aracı temizle
+ }
+
+ // Araç listesini API'den çeken ana fonksiyon
+ const fetchVehicles = async () => {
+ try {
+ const response = await apiClient.get("/admin/vehicles");
+ setVehicles(response.data);
+ } catch (error) {
+ console.error("Araçlar çekilirken bir hata oluştu:", error);
+ }
+ };
+
+ // Bileşen ilk yüklendiğinde araçları çek
+ useEffect(() => {
+ fetchVehicles();
+ }, []);
+
+ // Ekleme, silme veya güncelleme sonrası listeyi yeniden çekmek için
+ const handleDataChange = () => {
+ fetchVehicles();
+ };
+
+ // Araç silme fonksiyonu
+ const handleDelete = async (id) => {
+ if (window.confirm("Bu aracı silmek istediğinizden emin misiniz?")) {
+ try {
+ await apiClient.delete(`/admin/vehicles/${id}`);
+ handleDataChange(); // Silme sonrası listeyi yenile
+ } catch (error) {
+ alert("Araç silinirken bir sorun oluştu.");
+ console.error("Araç silinirken hata:", error);
+ }
+ }
+ };
+
return (
-
-
-
-
- Authors Table
-
-
-
-
-
-
- {["author", "function", "status", "employed", ""].map((el) => (
- |
-
- {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"
- }`;
+ <>
+
- return (
-
-
-
-
-
-
- {name}
-
-
- {email}
-
-
-
- |
-
-
- {job[0]}
-
-
- {job[1]}
-
- |
-
-
- |
-
-
- {date}
-
- |
-
-
- Edit
-
- |
-
- );
- }
- )}
-
-
-
-
-
-
-
- Projects Table
-
-
-
-
-
-
- {["companies", "members", "budget", "completion", ""].map(
- (el) => (
- |
-
- {el}
-
- |
- )
- )}
-
-
-
- {projectsTableData.map(
- ({ img, name, members, budget, completion }, key) => {
- const className = `py-3 px-5 ${
- key === projectsTableData.length - 1
- ? ""
- : "border-b border-blue-gray-50"
- }`;
+
+
+
+
+ Araçlar
+
+
+
+
+
+
+ {["Plaka", "Şoför Adı", "Telefon", "İşlem"].map((el) => (
+ |
+ {el}
+ |
+ ))}
+
+
+
+ {vehicles.map((vehicle, key) => {
+ const className = `py-3 px-5 ${key === vehicles.length - 1 ? "" : "border-b border-blue-gray-50"}`;
return (
-
- |
-
- |
-
- {members.map(({ img, name }, key) => (
-
-
-
- ))}
- |
-
-
- {budget}
-
- |
-
-
- |
-
-
-
-
- |
-
+
+ |
+ {vehicle.licensePlate}
+ |
+
+ {vehicle.userFullName}
+ |
+
+ {vehicle.phoneNumber || "-"}
+ |
+
+
+
+ |
+
);
- }
- )}
-
-
-
-
-
+ })}
+
+
+
+
+
+ >
);
}
-export default Tables;
+export default Tables;
\ No newline at end of file
diff --git a/src/routes.jsx b/src/routes.jsx
index 3a5a8da0..e076afca 100644
--- a/src/routes.jsx
+++ b/src/routes.jsx
@@ -5,9 +5,25 @@ import {
InformationCircleIcon,
ServerStackIcon,
RectangleStackIcon,
+ TruckIcon,
+ MapIcon,
+ Squares2X2Icon,
+ QueueListIcon, // 1. YENİ: Sıra Yönetimi sayfası için gerekli ikonu import edelim
} from "@heroicons/react/24/solid";
-import { Home, Profile, Tables, Notifications } from "@/pages/dashboard";
-import { SignIn, SignUp } from "@/pages/auth";
+
+// 1. YENİ: Yeni sayfa bileşenimizi de diğerlerinin yanına import edelim
+import {
+ Home,
+ Profile,
+ Tables,
+ Notifications,
+ QueueManagementPage
+} from "@/pages/dashboard";
+
+
+import { SignIn } from "@/pages/auth";
+
+
const icon = {
className: "w-5 h-5 text-inherit",
@@ -18,28 +34,40 @@ export const routes = [
layout: "dashboard",
pages: [
{
- icon: ,
- name: "dashboard",
+ icon: ,
+ name: "Araç Sıraları",
path: "/home",
element: ,
},
+ // 2. YENİ: "Sıra Yönetimi" sayfasını menüye ekleyelim
+ // Bu, feature branch'inden gelen ana özelliktir.
+ {
+ icon: ,
+ name: "Sıra Yönetimi",
+ path: "/queue-management",
+ element: ,
+ roles: ['admin'], // Sadece adminler görebilir
+ },
{
icon: ,
- name: "profile",
+ name: "Kullanıcılar",
path: "/profile",
element: ,
+ roles: ['admin'],
},
{
- icon: ,
- name: "tables",
+ icon: ,
+ name: "Araçlar",
path: "/tables",
element: ,
+ roles: ['admin'],
},
{
- icon: ,
- name: "notifications",
+ icon: ,
+ name: "Güzergahlar",
path: "/notifications",
element: ,
+ roles: ['admin'],
},
],
},
@@ -53,14 +81,8 @@ export const routes = [
path: "/sign-in",
element: ,
},
- {
- icon: ,
- name: "sign up",
- path: "/sign-up",
- element: ,
- },
],
},
];
-export default routes;
+export default routes;
\ No newline at end of file
diff --git a/src/widgets/layout/AddRouteModal.jsx b/src/widgets/layout/AddRouteModal.jsx
new file mode 100644
index 00000000..18446dba
--- /dev/null
+++ b/src/widgets/layout/AddRouteModal.jsx
@@ -0,0 +1,68 @@
+import React, { useState } from "react";
+import {
+ Button,
+ Dialog,
+ DialogHeader,
+ DialogBody,
+ DialogFooter,
+ Input,
+ Typography,
+} from "@material-tailwind/react";
+import apiClient from "../../api/axiosConfig.js";
+
+export function AddRouteModal({ open, handleOpen, onRouteAdded }) {
+ const [routeName, setRouteName] = useState("");
+ const [error, setError] = useState("");
+
+ const clearForm = () => {
+ setRouteName("");
+ setError("");
+ };
+
+ const handleClose = () => {
+ clearForm();
+ handleOpen();
+ };
+
+ const handleSubmit = async () => {
+ if (!routeName.trim()) {
+ setError("Güzergah adı boş olamaz.");
+ return;
+ }
+
+ try {
+ // CreateRouteDto'ya uygun şekilde { "routeName": "değer" } gönderiyoruz
+ const response = await apiClient.post("/admin/routes", { routeName });
+ alert("Güzergah başarıyla eklendi!");
+ onRouteAdded(response.data); // Ana listeyi anında güncellemek için
+ handleClose(); // Formu temizle ve modal'ı kapat
+ } catch (err) {
+ console.error("Güzergah eklenirken hata:", err);
+ // Backend'den gelen "Bu rota adı zaten mevcut." gibi hataları göster
+ setError(err.response?.data || "Bir hata oluştu.");
+ }
+ };
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/src/widgets/layout/AddUserModal.jsx b/src/widgets/layout/AddUserModal.jsx
new file mode 100644
index 00000000..46c9286e
--- /dev/null
+++ b/src/widgets/layout/AddUserModal.jsx
@@ -0,0 +1,81 @@
+import React, { useState } from "react";
+import {
+ Button,
+ Dialog,
+ DialogHeader,
+ DialogBody,
+ DialogFooter,
+ Input,
+ Typography,
+} from "@material-tailwind/react";
+import apiClient from "../../api/axiosConfig.js";
+
+export function AddUserModal({ open, handleOpen, onUserAdded }) {
+ const [formData, setFormData] = useState({
+ fullName: "",
+ userName: "",
+ password: "",
+ confirmPassword: "",
+ });
+ const [error, setError] = useState("");
+
+ const clearForm = () => {
+ setFormData({ fullName: "", userName: "", password: "", confirmPassword: "" });
+ setError("");
+ };
+
+ const handleClose = () => {
+ clearForm();
+ handleOpen();
+ };
+
+ const handleSubmit = async () => {
+ if (!formData.fullName || !formData.userName || !formData.password) {
+ setError("Tüm zorunlu alanları doldurunuz.");
+ return;
+ }
+ if (formData.password !== formData.confirmPassword) {
+ setError("Şifreler uyuşmuyor.");
+ return;
+ }
+
+ try {
+ await apiClient.post("/Users", formData);
+ alert("Kullanıcı başarıyla eklendi!");
+
+ // Başarılı ekleme sonrası ana sayfadaki listeyi yenilemesi için sinyal gönderiyoruz.
+ onUserAdded();
+
+ handleClose();
+ } catch (err) {
+ console.error("Kullanıcı eklenirken hata:", err);
+ setError(err.response?.data || "Bir hata oluştu.");
+ }
+ };
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setFormData(prev => ({ ...prev, [name]: value }));
+ };
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/src/widgets/layout/AddVehicleModal.jsx b/src/widgets/layout/AddVehicleModal.jsx
new file mode 100644
index 00000000..0f0d14ae
--- /dev/null
+++ b/src/widgets/layout/AddVehicleModal.jsx
@@ -0,0 +1,93 @@
+import React, { useState, useEffect } from "react";
+import {
+ Button,
+ Dialog,
+ DialogHeader,
+ DialogBody,
+ DialogFooter,
+ Input,
+ Select,
+ Option,
+ Typography,
+} from "@material-tailwind/react";
+import apiClient from "../../api/axiosConfig.js";
+
+export function AddVehicleModal({ open, handleOpen, onVehicleAdded }) {
+ const [users, setUsers] = useState([]);
+ const [formData, setFormData] = useState({
+ licensePlate: "",
+ driverName: "",
+ phoneNumber: "",
+ appUserId: "",
+ });
+ const [error, setError] = useState("");
+
+ useEffect(() => {
+ if (open) {
+ // DEĞİŞİKLİK: Backend'de oluşturduğumuz yeni ve filtrelenmiş endpoint'i kullanıyoruz.
+ apiClient.get("/Users/without-vehicle")
+ .then(response => {
+ setUsers(response.data);
+ })
+ .catch(err => {
+ console.error("Kullanıcılar çekilirken hata oluştu:", err);
+ setError("Atanabilir kullanıcı listesi yüklenemedi.");
+ });
+ }
+ }, [open]);
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setFormData(prev => ({ ...prev, [name]: value }));
+ };
+
+ const handleSelectChange = (value) => {
+ setFormData(prev => ({ ...prev, appUserId: value }));
+ };
+
+ const clearForm = () => {
+ setFormData({ licensePlate: "", driverName: "", phoneNumber: "", appUserId: "" });
+ setError("");
+ };
+
+ const handleClose = () => {
+ clearForm();
+ handleOpen();
+ }
+
+ const handleSubmit = async () => {
+ if (!formData.licensePlate || !formData.appUserId) {
+ setError("Plaka ve Kullanıcı alanları zorunludur.");
+ return;
+ }
+ try {
+ await apiClient.post("/admin/vehicles", formData);
+ alert("Araç başarıyla eklendi!");
+ onVehicleAdded();
+ handleClose();
+ } catch (err) {
+ setError(err.response?.data || "Bir hata oluştu.");
+ }
+ };
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/src/widgets/layout/EditRouteModal.jsx b/src/widgets/layout/EditRouteModal.jsx
new file mode 100644
index 00000000..67629bb0
--- /dev/null
+++ b/src/widgets/layout/EditRouteModal.jsx
@@ -0,0 +1,96 @@
+import React, { useState, useEffect } from "react";
+import {
+ Button,
+ Dialog,
+ DialogHeader,
+ DialogBody,
+ DialogFooter,
+ Input,
+ Typography,
+ Checkbox,
+} from "@material-tailwind/react";
+import apiClient from "../../api/axiosConfig.js";
+
+export function EditRouteModal({ open, handleOpen, routeToEdit, onRouteUpdated }) {
+ const [formData, setFormData] = useState({
+ routeName: "",
+ isActive: true,
+ });
+ const [error, setError] = useState("");
+
+ // Modal'a düzenlenecek güzergah bilgisi geldiğinde formu doldur
+ useEffect(() => {
+ if (routeToEdit) {
+ setFormData({
+ routeName: routeToEdit.routeName,
+ isActive: routeToEdit.isActive,
+ });
+ setError("");
+ }
+ }, [routeToEdit]);
+
+ const handleChange = (e) => {
+ const { name, value, type, checked } = e.target;
+ setFormData(prev => ({
+ ...prev,
+ [name]: type === "checkbox" ? checked : value,
+ }));
+ };
+
+ const clearForm = () => {
+ setFormData({ routeName: "", isActive: true });
+ setError("");
+ };
+
+ const handleClose = () => {
+ clearForm();
+ handleOpen();
+ };
+
+ const handleSubmit = async () => {
+ if (!formData.routeName.trim()) {
+ setError("Güzergah adı boş olamaz.");
+ return;
+ }
+
+ try {
+ // PUT isteği ile güzergahı güncelle
+ await apiClient.put(`/admin/routes/${routeToEdit.id}`, formData);
+ alert("Güzergah başarıyla güncellendi!");
+ onRouteUpdated(); // Ana listeyi yenilemesi için sinyal gönder
+ handleClose();
+ } catch (err) {
+ console.error("Güzergah güncellenirken hata:", err);
+ setError(err.response?.data || "Bir hata oluştu.");
+ }
+ };
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/src/widgets/layout/EditUserModal.jsx b/src/widgets/layout/EditUserModal.jsx
new file mode 100644
index 00000000..0f377bc4
--- /dev/null
+++ b/src/widgets/layout/EditUserModal.jsx
@@ -0,0 +1,115 @@
+import React, { useState, useEffect } from "react";
+import {
+ Button, Dialog, DialogHeader, DialogBody, DialogFooter, Input, Typography,
+} from "@material-tailwind/react";
+import apiClient from "../../api/axiosConfig.js";
+
+export function EditUserModal({ open, handleOpen, userToEdit, onUserUpdated }) {
+ const [formData, setFormData] = useState({
+ fullName: "",
+ userName: "",
+ password: "",
+ confirmPassword: "",
+ });
+ const [error, setError] = useState("");
+
+
+ useEffect(() => {
+ if (userToEdit) {
+ setFormData({
+ fullName: userToEdit.fullName || "",
+ userName: userToEdit.userName || "",
+ password: "",
+ confirmPassword: "",
+ });
+ setError("");
+ }
+ }, [userToEdit]);
+
+ const handleChange = (e) => {
+ setFormData({ ...formData, [e.target.name]: e.target.value });
+ };
+
+ const handleClose = () => {
+ setError("");
+ handleOpen();
+ };
+
+ const handleSubmit = async () => {
+
+ if (formData.password && formData.password !== formData.confirmPassword) {
+ setError("Girilen şifreler uyuşmuyor.");
+ return;
+ }
+
+
+ const payload = {
+ fullName: formData.fullName,
+ userName: formData.userName,
+ };
+ if (formData.password) {
+ payload.password = formData.password;
+ payload.confirmPassword = formData.confirmPassword;
+ }
+
+ try {
+ await apiClient.put(`/Users/${userToEdit.id}`, payload);
+ alert("Kullanıcı başarıyla güncellendi!");
+ onUserUpdated();
+ handleClose();
+ } catch (err) {
+ console.error("Kullanıcı güncellenirken hata:", err);
+
+ setError(err.response?.data?.title || err.response?.data || "Bir hata oluştu.");
+ }
+ };
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/src/widgets/layout/EditVehicleModal.jsx b/src/widgets/layout/EditVehicleModal.jsx
new file mode 100644
index 00000000..12b14a96
--- /dev/null
+++ b/src/widgets/layout/EditVehicleModal.jsx
@@ -0,0 +1,75 @@
+import React, { useState, useEffect } from "react";
+import {
+ Button, Dialog, DialogHeader, DialogBody, DialogFooter, Input, Checkbox, Typography
+} from "@material-tailwind/react";
+import apiClient from "../../api/axiosConfig.js";
+
+export function EditVehicleModal({ open, handleOpen, onVehicleUpdated, vehicleToEdit }) {
+ const [formData, setFormData] = useState({
+ licensePlate: "",
+ driverName: "",
+ phoneNumber: "",
+ isActive: true,
+ });
+ const [error, setError] = useState("");
+
+ // Bu useEffect, modal'a düzenlenecek araç bilgisi geldiğinde formu doldurur.
+ useEffect(() => {
+ if (vehicleToEdit) {
+ setFormData({
+ licensePlate: vehicleToEdit.licensePlate || "",
+ driverName: vehicleToEdit.driverName || "",
+ phoneNumber: vehicleToEdit.phoneNumber || "",
+ isActive: vehicleToEdit.isActive,
+ });
+ } else {
+
+ setFormData({ licensePlate: "", driverName: "", phoneNumber: "", isActive: true });
+ setError("");
+ }
+ }, [vehicleToEdit]);
+
+ const handleChange = (e) => {
+ const { name, value, type, checked } = e.target;
+ setFormData(prev => ({
+ ...prev,
+ [name]: type === 'checkbox' ? checked : value
+ }));
+ };
+
+ const handleSubmit = async () => {
+ if (!formData.licensePlate) {
+ setError("Plaka alanı zorunludur.");
+ return;
+ }
+ if (!vehicleToEdit) return;
+
+ try {
+ // Backend'deki PUT endpoint'ine istek gönderiyoruz.
+ await apiClient.put(`/admin/vehicles/${vehicleToEdit.id}`, formData);
+ alert("Araç başarıyla güncellendi!");
+ onVehicleUpdated();
+ handleOpen();
+ } catch (err) {
+ console.error("Araç güncellenirken hata:", err);
+ setError(err.response?.data || "Güncelleme sırasında bir hata oluştu.");
+ }
+ };
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/src/widgets/layout/VehicleQueueCard.jsx b/src/widgets/layout/VehicleQueueCard.jsx
new file mode 100644
index 00000000..396d996a
--- /dev/null
+++ b/src/widgets/layout/VehicleQueueCard.jsx
@@ -0,0 +1,29 @@
+import React from "react";
+import { Typography } from "@material-tailwind/react";
+
+export function VehicleQueueCard({ vehicle, index }) {
+ const isFirstThree = index < 3;
+ const cardBgColor = isFirstThree ? "bg-green-100" : "bg-blue-gray-50/70";
+ const textColor = isFirstThree ? "text-green-900" : "text-blue-gray-700";
+ const borderColor = isFirstThree ? "border-green-300" : "border-transparent";
+
+ return (
+
+
+
+ #{index + 1}
+
+
+
+ {vehicle.licensePlate}
+
+
+ {vehicle.userFullName}
+
+
+
+
+ );
+}
+
+export default VehicleQueueCard;
\ 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..e389bbdb 100644
--- a/src/widgets/layout/sidenav.jsx
+++ b/src/widgets/layout/sidenav.jsx
@@ -11,7 +11,8 @@ 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",
@@ -19,93 +20,74 @@ export function Sidenav({ brandImg, brandName, routes }) {
};
return (
-
+
+
+
+
+ {routes
+ .filter((route) => route.layout === "dashboard") // Sadece dashboard layout'unu al
+ .map(({ layout, pages }, key) => (
+
+ {pages
+ // BU SATIR SAYESİNDE OTOMATİK FİLTRELEME YAPILIYOR
+ .filter((page) => !page.roles || page.roles.includes(userRole))
+ .map(({ icon, name, path }) => (
+ -
+
+ {({ isActive }) => (
+
+ )}
+
+
+ ))}
+
+ ))}
+
+
);
}
-Sidenav.defaultProps = {
- brandImg: "/img/logo-ct.png",
- brandName: "Material Tailwind React",
-};
-
-Sidenav.propTypes = {
- brandImg: PropTypes.string,
- brandName: PropTypes.string,
- routes: PropTypes.arrayOf(PropTypes.object).isRequired,
-};
-Sidenav.displayName = "/src/widgets/layout/sidnave.jsx";
-export default Sidenav;
+export default Sidenav;
\ No newline at end of file