diff --git a/src/APIServices/ReportRunnerAPI.js b/src/APIServices/ReportRunnerAPI.js index 0f4ac94..8b2a2f4 100644 --- a/src/APIServices/ReportRunnerAPI.js +++ b/src/APIServices/ReportRunnerAPI.js @@ -21,6 +21,7 @@ export const fetchAllReportsApi = async () => { try { const response = await apiService.get('/Rpt_builder2/Rpt_builder2'); // API call to fetch all reports console.log("Fetch all reports response:", response.data); + return response.data; // Assuming response data comes in `data` field } catch (error) { console.error("Error while fetching all reports:", error); diff --git a/src/App.js b/src/App.js index 772d707..43680cf 100644 --- a/src/App.js +++ b/src/App.js @@ -43,7 +43,7 @@ import Login from "views/examples/Login"; import ResetPassword from "views/examples/resetPassword"; import ReportBuild2All from "components/Dashboard/Reportbuild2/Report-build2all/ReportBuild2All"; import ReportBuild2Add from "components/Dashboard/Reportbuild2/Report-build2add/ReportBuild2Add"; -import ReportBuild2Edit from "./components/Dashboard/Reportbuild2/Report-build2edit/ReportBuildEdit"; +import ReportBuild2Edit from "./components/Dashboard/Reportbuild2/Report-build2edit/ReportBuildEdit"; import ReportQuery from "components/Dashboard/Reportbuilder/reportQuery"; import DynamicForm from "components/Dashboard/DynamicForm/DynamicForm2"; import DynamicFormAdd from "components/Dashboard/DynamicForm/DynamicFormAdd"; @@ -67,90 +67,90 @@ import SequenceGenerator from "components/Dashboard/sequencegenerator"; const App = () => { return ( -
- {/* ToastContainer should be placed in the root component */} - +
+ {/* ToastContainer should be placed in the root component */} + - - - } /> - } /> + + + } /> + } /> - {/* ptotecting the admin page */} +{/* ptotecting the admin page */} - - - + + + }> - } /> - } /> - } /> - - } /> - } /> - } /> - } /> - } /> - - {/* Dynamic Routes */} - } /> - } /> - } /> - } /> - } /> - {/* Static Routes */} - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - - - - {/* buildercomponents */} - - }> - } /> - } /> - - - -
+ {/* } /> */} + } /> + } /> + + }/> + } /> + } /> + } /> + } /> + + {/* Dynamic Routes */} + } /> + } /> + } /> + } /> + } /> + {/* Static Routes */} + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + }/> + }/> + }/> + }/> + }/> + }/> + {/* } /> */} + {/* buildercomponents */} + + + + + + }> + } /> + } /> + + + +
); }; diff --git a/src/ProtectedRoute.js b/src/ProtectedRoute.js index a3e042f..5419a7e 100644 --- a/src/ProtectedRoute.js +++ b/src/ProtectedRoute.js @@ -1,11 +1,11 @@ +// ProtectedRoute.js import React from "react"; import { Navigate } from "react-router-dom"; import { getToken } from "utils/tokenService"; const ProtectedRoute = ({ children }) => { - console.log(`token for checking whether authenticated: ${getToken()}`); - const isAuthenticated = getToken() !== null; // Check if user is authenticated + const isAuthenticated = getToken() !== null; return isAuthenticated ? children : ; }; -export default ProtectedRoute; +export default ProtectedRoute; \ No newline at end of file diff --git a/src/components/Dashboard/AccessType.js b/src/components/Dashboard/AccessType.js index b8b57e1..e0fc575 100644 --- a/src/components/Dashboard/AccessType.js +++ b/src/components/Dashboard/AccessType.js @@ -57,7 +57,7 @@ function AccessTypeManagement() { useEffect(() => { const fetchAccessTypes = async () => { - const apiUrl = `${process.env.REACT_APP_API_URL}api/getAllAccessTypes`; + const apiUrl = `${process.env.REACT_APP_API_URL}token/access_type/Accesstype`; const token = localStorage.getItem("authToken"); if (!token) { diff --git a/src/components/Dashboard/TOKENRegistry.js b/src/components/Dashboard/TOKENRegistry.js index b246ede..47412d7 100644 --- a/src/components/Dashboard/TOKENRegistry.js +++ b/src/components/Dashboard/TOKENRegistry.js @@ -20,31 +20,31 @@ function TOKENRegistry() { id: "", tokenName: "", tokenValue: "", - isActive: true + isActive: true, + scopes: [] }); const [currentPage, setCurrentPage] = useState(1); const [searchQuery, setSearchQuery] = useState(""); const [isEditing, setIsEditing] = useState(false); const [recordsPerPage, setRecordsPerPage] = useState(10); - // Add to your state: -const [visibleColumns, setVisibleColumns] = useState({ - id: true, - tokenName: true, - tokenValue: true, - scopes: true, - isActive: true, - actions: true -}); - - + const [visibleColumns, setVisibleColumns] = useState({ + id: true, + tokenName: true, + tokenValue: true, + scopes: true, + isActive: true, + actions: true + }); const [loading, setLoading] = useState(true); + const [selectedScope, setSelectedScope] = useState(""); const [selectedScopes, setSelectedScopes] = useState([]); - const [availableScopes] = useState([ - { value: 'read', label: 'Read Access' }, - { value: 'write', label: 'Write Access' }, - { value: 'delete', label: 'Delete Access' }, - { value: 'admin', label: 'Admin Access' }, + const [availableScopes] = useState([ + { value: 'read', label: 'Read Access' }, + { value: 'write', label: 'Write Access' }, + { value: 'delete', label: 'Delete Access' }, + { value: 'admin', label: 'Admin Access' }, ]); + useEffect(() => { fetchTokens(); }, []); @@ -95,6 +95,7 @@ const [visibleColumns, setVisibleColumns] = useState({ setShowGenerateTokenModal(false); setGeneratedToken(""); setNewTokenName(""); + setSelectedScopes([]); }; const handleRecordsPerPageChange = (number) => { @@ -135,9 +136,10 @@ const [visibleColumns, setVisibleColumns] = useState({ } }; - const openModal = (token = { id: "", tokenName: "", tokenValue: "", isActive: false }) => { + const openModal = (token = { id: "", tokenName: "", tokenValue: "", isActive: false, scopes: [] }) => { setIsEditing(!!token.id); setCurrentToken(token); + setSelectedScopes(token.scopes || []); setShowAddEditModal(true); }; @@ -153,24 +155,35 @@ const [visibleColumns, setVisibleColumns] = useState({ } }; - const generateNewToken = async () => { - if (!newTokenName.trim()) { - toast.error("Please enter a token name"); - return; - } + const addScope = () => { + if (selectedScope && !selectedScopes.includes(selectedScope)) { + setSelectedScopes([...selectedScopes, selectedScope]); + setSelectedScope(""); + } + }; - try { - const data = await tokenRegistryAPI.generateToken({ - name: newTokenName, - scopes: selectedScopes - }); - setGeneratedToken(data.tokenValue); - toast.success("Token generated successfully!"); - fetchTokens(); - } catch (error) { - handleApiError(error, "generate token"); - } -}; + const removeScope = (scopeToRemove) => { + setSelectedScopes(selectedScopes.filter(scope => scope !== scopeToRemove)); + }; + + const generateNewToken = async () => { + if (!newTokenName.trim()) { + toast.error("Please enter a token name"); + return; + } + + try { + const data = await tokenRegistryAPI.generateToken({ + name: newTokenName, + scopes: selectedScopes + }); + setGeneratedToken(data.tokenValue); + toast.success("Token generated successfully!"); + fetchTokens(); + } catch (error) { + handleApiError(error, "generate token"); + } + }; return (
@@ -305,6 +318,21 @@ const [visibleColumns, setVisibleColumns] = useState({ > {token.isActive ? "Active" : "Inactive"} + ) : key === "scopes" ? ( +
+ {token.scopes && token.scopes.map(scope => { + const scopeInfo = availableScopes.find(s => s.value === scope); + return ( + + {scopeInfo?.label || scope} + + ); + })} +
) : ( token[key] )} @@ -383,93 +411,245 @@ const [visibleColumns, setVisibleColumns] = useState({ - {/* Add/Edit Token Modal */} + {/* Generate Token Modal */} - - Generate New Token - - - -
- - Token Name - setNewTokenName(e.target.value)} - required - className="custom-hover-border" - placeholder="Enter a name for the new token" - /> - + + Generate New Token + + + + + + Token Name + setNewTokenName(e.target.value)} + required + className="custom-hover-border" + placeholder="Enter a name for the new token" + /> + - - Token Scopes - { - const options = [...e.target.options]; - const selectedValues = options - .filter(option => option.selected) - .map(option => option.value); - setSelectedScopes(selectedValues); - }} - className="custom-hover-border" - style={{ height: 'auto' }} - > - {availableScopes.map(scope => ( - - ))} - - - Select the permissions this token should have (hold Ctrl/Cmd to select multiple) - - + + Token Scopes +
+ setSelectedScope(e.target.value)} + className="custom-hover-border me-2" + > + + {availableScopes.map(scope => ( + + ))} + + +
+ + Select and add the permissions this token should have + + + {/* Display selected scopes as chips */} + {selectedScopes.length > 0 && ( +
+ {selectedScopes.map(scope => { + const scopeInfo = availableScopes.find(s => s.value === scope); + return ( + + {scopeInfo?.label || scope} + removeScope(scope)} + /> + + ); + })} +
+ )} +
- {generatedToken && ( - - Generated Token - - - Copy this token and store it securely. You won't be able to see it again. - - - )} - - - - - - -
-
+ {generatedToken && ( + + Generated Token + + + Copy this token and store it securely. You won't be able to see it again. + + + )} + + + + + + + + + + {/* Add/Edit Token Modal */} + + + {isEditing ? "Edit Token" : "Add Token"} + + + +
+ + Token Name + + + + Token Value + + + + Token Scopes +
+ setSelectedScope(e.target.value)} + className="custom-hover-border me-2" + > + + {availableScopes.map(scope => ( + + ))} + + +
+ + Select and add the permissions this token should have + + + {/* Display selected scopes as chips */} + {currentToken.scopes && currentToken.scopes.length > 0 && ( +
+ {currentToken.scopes.map(scope => { + const scopeInfo = availableScopes.find(s => s.value === scope); + return ( + + {scopeInfo?.label || scope} + { + setCurrentToken(prev => ({ + ...prev, + scopes: prev.scopes.filter(s => s !== scope) + })); + }} + /> + + ); + })} +
+ )} +
+ + + + + + + +
+
+
)} diff --git a/src/components/Sidebar/Sidebar.js b/src/components/Sidebar/Sidebar.js index bc78fa8..b35c8e6 100644 --- a/src/components/Sidebar/Sidebar.js +++ b/src/components/Sidebar/Sidebar.js @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import PropTypes from "prop-types"; import { @@ -7,20 +7,86 @@ import { MenuItem, SubMenu, } from "react-pro-sidebar"; -import { FaChevronLeft, FaChevronRight, FaDatabase, FaExclamationCircle } from "react-icons/fa"; +import { FaChevronLeft, FaChevronRight, FaDatabase, FaExclamationCircle, FaCircle } from "react-icons/fa"; +import { fetchMenuItems, getSubmenuItems } from "../../APIServices/MenuMaintenanceAPI"; import "../Sidebar/Sidebar.css"; const Sidebar = (props) => { const [collapsed, setCollapsed] = useState(true); + const [menuItems, setMenuItems] = useState([]); + const [subMenuItems, setSubMenuItems] = useState({}); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); const navigate = useNavigate(); + useEffect(() => { + const loadMenuData = async () => { + try { + const mainMenuItems = await fetchMenuItems(); + setMenuItems(mainMenuItems); + + const subMenuPromises = mainMenuItems.map(item => + getSubmenuItems(item.menuItemId) + .then(subItems => subItems || []) + .catch(() => []) + ); + + const subMenus = await Promise.all(subMenuPromises); + const subMenuMap = {}; + mainMenuItems.forEach((item, index) => { + subMenuMap[item.menuItemId] = subMenus[index]; + }); + setSubMenuItems(subMenuMap); + } catch (err) { + console.error("Failed to load menu data:", err); + setError(err.message); + } finally { + setLoading(false); + } + }; + loadMenuData(); + }, []); + const toggleSidebar = () => { setCollapsed(!collapsed); - if (props.onSidebarToggle) { - props.onSidebarToggle(!collapsed); + props.onSidebarToggle?.(!collapsed); + }; + + const validatePath = (path) => { + if (!path || path === "#" || path === "null" || path === "undefined") { + return false; + } + return true; + }; + + const safeNavigate = (path) => { + if (!validatePath(path)) { + navigate("/admin/error404"); + return; + } + + // Ensure path starts with /admin/ + const adminPath = path.startsWith("/admin/") ? path : `/admin/${path.replace(/^\//, '')}`; + + try { + navigate(adminPath); + } catch (error) { + console.error(`Navigation failed:`, error); + navigate("/admin/error404"); } }; + const getIconComponent = (iconName) => { + switch (iconName) { + case "fa-database": return ; + case "fa-exclamation-circle": return ; + default: return ; + } + }; + + if (loading) return
Loading...
; + if (error) return
Error: {error}
; + return (
{
- }> - navigate("/admin/regform")}> - Regform - - navigate("/admin/error404")}> - Additional container - - - - } - onClick={() => navigate("/admin/error404")} - > - Masters - + {menuItems.map((menuItem) => { + const subItems = subMenuItems[menuItem.menuItemId] || []; + + if (subItems.length > 0) { + return ( + + {subItems.map((subItem) => ( + safeNavigate(subItem.main_menu_action_name)} + > + {subItem.menuItemDesc} + + ))} + + ); + } + + return ( + safeNavigate(menuItem.main_menu_action_name)} + > + {menuItem.menuItemDesc} + + ); + })} @@ -71,4 +154,4 @@ Sidebar.propTypes = { onSidebarToggle: PropTypes.func, }; -export default Sidebar; +export default Sidebar; \ No newline at end of file