This commit is contained in:
string 2025-06-16 09:50:35 +05:30
parent 5311701872
commit f3a1c75f26
6 changed files with 488 additions and 224 deletions

View File

@ -21,6 +21,7 @@ export const fetchAllReportsApi = async () => {
try { try {
const response = await apiService.get('/Rpt_builder2/Rpt_builder2'); // API call to fetch all reports const response = await apiService.get('/Rpt_builder2/Rpt_builder2'); // API call to fetch all reports
console.log("Fetch all reports response:", response.data); console.log("Fetch all reports response:", response.data);
return response.data; // Assuming response data comes in `data` field return response.data; // Assuming response data comes in `data` field
} catch (error) { } catch (error) {
console.error("Error while fetching all reports:", error); console.error("Error while fetching all reports:", error);

View File

@ -93,7 +93,7 @@ const App = () => {
<AdminLayout /> <AdminLayout />
</ProtectedRoute> </ProtectedRoute>
}> }>
<Route path="regform" element={<Regform />} /> {/* <Route path="regform" element={<Regform />} /> */}
<Route path="error404" element={<Error404 />} /> <Route path="error404" element={<Error404 />} />
<Route path="resetpassword" element={<ResetPassword />} /> <Route path="resetpassword" element={<ResetPassword />} />
@ -137,12 +137,12 @@ const App = () => {
<Route path="dashboard-new-edit/:id" element={<DashboardNewEdit/>}/> <Route path="dashboard-new-edit/:id" element={<DashboardNewEdit/>}/>
<Route path="edit-new-dash/:id" element={<EditNewDash/>}/> <Route path="edit-new-dash/:id" element={<EditNewDash/>}/>
<Route path="dashrunner/:id" element={<DashboardRunner/>}/> <Route path="dashrunner/:id" element={<DashboardRunner/>}/>
{/* <Route path="test" element={<Regform />} /> */}
{/* buildercomponents */}
</Route> </Route>
{/* buildercomponents */}
<Route path="/auth/*" element={<AuthLayout />}> <Route path="/auth/*" element={<AuthLayout />}>
<Route path="login" element={<Login />} /> <Route path="login" element={<Login />} />

View File

@ -1,10 +1,10 @@
// ProtectedRoute.js
import React from "react"; import React from "react";
import { Navigate } from "react-router-dom"; import { Navigate } from "react-router-dom";
import { getToken } from "utils/tokenService"; import { getToken } from "utils/tokenService";
const ProtectedRoute = ({ children }) => { const ProtectedRoute = ({ children }) => {
console.log(`token for checking whether authenticated: ${getToken()}`); const isAuthenticated = getToken() !== null;
const isAuthenticated = getToken() !== null; // Check if user is authenticated
return isAuthenticated ? children : <Navigate to="/auth/login" replace />; return isAuthenticated ? children : <Navigate to="/auth/login" replace />;
}; };

View File

@ -57,7 +57,7 @@ function AccessTypeManagement() {
useEffect(() => { useEffect(() => {
const fetchAccessTypes = async () => { 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"); const token = localStorage.getItem("authToken");
if (!token) { if (!token) {

View File

@ -20,13 +20,13 @@ function TOKENRegistry() {
id: "", id: "",
tokenName: "", tokenName: "",
tokenValue: "", tokenValue: "",
isActive: true isActive: true,
scopes: []
}); });
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
const [recordsPerPage, setRecordsPerPage] = useState(10); const [recordsPerPage, setRecordsPerPage] = useState(10);
// Add to your state:
const [visibleColumns, setVisibleColumns] = useState({ const [visibleColumns, setVisibleColumns] = useState({
id: true, id: true,
tokenName: true, tokenName: true,
@ -35,9 +35,8 @@ const [visibleColumns, setVisibleColumns] = useState({
isActive: true, isActive: true,
actions: true actions: true
}); });
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [selectedScope, setSelectedScope] = useState("");
const [selectedScopes, setSelectedScopes] = useState([]); const [selectedScopes, setSelectedScopes] = useState([]);
const [availableScopes] = useState([ const [availableScopes] = useState([
{ value: 'read', label: 'Read Access' }, { value: 'read', label: 'Read Access' },
@ -45,6 +44,7 @@ const [visibleColumns, setVisibleColumns] = useState({
{ value: 'delete', label: 'Delete Access' }, { value: 'delete', label: 'Delete Access' },
{ value: 'admin', label: 'Admin Access' }, { value: 'admin', label: 'Admin Access' },
]); ]);
useEffect(() => { useEffect(() => {
fetchTokens(); fetchTokens();
}, []); }, []);
@ -95,6 +95,7 @@ const [visibleColumns, setVisibleColumns] = useState({
setShowGenerateTokenModal(false); setShowGenerateTokenModal(false);
setGeneratedToken(""); setGeneratedToken("");
setNewTokenName(""); setNewTokenName("");
setSelectedScopes([]);
}; };
const handleRecordsPerPageChange = (number) => { 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); setIsEditing(!!token.id);
setCurrentToken(token); setCurrentToken(token);
setSelectedScopes(token.scopes || []);
setShowAddEditModal(true); setShowAddEditModal(true);
}; };
@ -153,6 +155,17 @@ const [visibleColumns, setVisibleColumns] = useState({
} }
}; };
const addScope = () => {
if (selectedScope && !selectedScopes.includes(selectedScope)) {
setSelectedScopes([...selectedScopes, selectedScope]);
setSelectedScope("");
}
};
const removeScope = (scopeToRemove) => {
setSelectedScopes(selectedScopes.filter(scope => scope !== scopeToRemove));
};
const generateNewToken = async () => { const generateNewToken = async () => {
if (!newTokenName.trim()) { if (!newTokenName.trim()) {
toast.error("Please enter a token name"); toast.error("Please enter a token name");
@ -305,6 +318,21 @@ const [visibleColumns, setVisibleColumns] = useState({
> >
{token.isActive ? "Active" : "Inactive"} {token.isActive ? "Active" : "Inactive"}
</span> </span>
) : key === "scopes" ? (
<div>
{token.scopes && token.scopes.map(scope => {
const scopeInfo = availableScopes.find(s => s.value === scope);
return (
<span
key={scope}
className="badge bg-primary me-1 mb-1"
style={{ fontSize: '0.8rem', padding: '3px 6px' }}
>
{scopeInfo?.label || scope}
</span>
);
})}
</div>
) : ( ) : (
token[key] token[key]
)} )}
@ -383,7 +411,7 @@ const [visibleColumns, setVisibleColumns] = useState({
</PaginationItem> </PaginationItem>
</Pagination> </Pagination>
{/* Add/Edit Token Modal */} {/* Generate Token Modal */}
<Modal show={showGenerateTokenModal} onHide={handleClose}> <Modal show={showGenerateTokenModal} onHide={handleClose}>
<Modal.Header> <Modal.Header>
<Modal.Title>Generate New Token</Modal.Title> <Modal.Title>Generate New Token</Modal.Title>
@ -415,28 +443,54 @@ const [visibleColumns, setVisibleColumns] = useState({
<Form.Group controlId="formTokenScopes" className="mb-3"> <Form.Group controlId="formTokenScopes" className="mb-3">
<Form.Label>Token Scopes</Form.Label> <Form.Label>Token Scopes</Form.Label>
<div className="d-flex">
<Form.Select <Form.Select
multiple value={selectedScope}
value={selectedScopes} onChange={(e) => setSelectedScope(e.target.value)}
onChange={(e) => { className="custom-hover-border me-2"
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' }}
> >
<option value="">Select a scope</option>
{availableScopes.map(scope => ( {availableScopes.map(scope => (
<option key={scope.value} value={scope.value}> <option key={scope.value} value={scope.value}>
{scope.label} {scope.label}
</option> </option>
))} ))}
</Form.Select> </Form.Select>
<Button
variant="outline-primary"
onClick={addScope}
disabled={!selectedScope}
>
Add Scope
</Button>
</div>
<Form.Text className="text-muted"> <Form.Text className="text-muted">
Select the permissions this token should have (hold Ctrl/Cmd to select multiple) Select and add the permissions this token should have
</Form.Text> </Form.Text>
{/* Display selected scopes as chips */}
{selectedScopes.length > 0 && (
<div className="mt-2">
{selectedScopes.map(scope => {
const scopeInfo = availableScopes.find(s => s.value === scope);
return (
<span
key={scope}
className="badge bg-primary me-2 mb-2"
style={{ fontSize: '0.9rem', padding: '5px 10px' }}
>
{scopeInfo?.label || scope}
<FontAwesomeIcon
icon={faTimes}
className="ms-2"
style={{ cursor: 'pointer' }}
onClick={() => removeScope(scope)}
/>
</span>
);
})}
</div>
)}
</Form.Group> </Form.Group>
{generatedToken && ( {generatedToken && (
@ -470,6 +524,132 @@ const [visibleColumns, setVisibleColumns] = useState({
</Form> </Form>
</Modal.Body> </Modal.Body>
</Modal> </Modal>
{/* Add/Edit Token Modal */}
<Modal show={showAddEditModal} onHide={handleClose}>
<Modal.Header>
<Modal.Title>{isEditing ? "Edit Token" : "Add Token"}</Modal.Title>
<FontAwesomeIcon
icon={faTimes}
size="lg"
onClick={handleClose}
style={{
position: "absolute",
top: "25px",
right: "25px",
cursor: "pointer",
}}
/>
</Modal.Header>
<Modal.Body>
<Form onSubmit={handleSubmit}>
<Form.Group controlId="formTokenName" className="mb-3">
<Form.Label>Token Name</Form.Label>
<Form.Control
type="text"
name="tokenName"
value={currentToken.tokenName}
onChange={handleInputChange}
required
className="custom-hover-border"
/>
</Form.Group>
<Form.Group controlId="formTokenValue" className="mb-3">
<Form.Label>Token Value</Form.Label>
<Form.Control
type="text"
name="tokenValue"
value={currentToken.tokenValue}
onChange={handleInputChange}
required
className="custom-hover-border"
/>
</Form.Group>
<Form.Group controlId="formTokenScopes" className="mb-3">
<Form.Label>Token Scopes</Form.Label>
<div className="d-flex">
<Form.Select
value={selectedScope}
onChange={(e) => setSelectedScope(e.target.value)}
className="custom-hover-border me-2"
>
<option value="">Select a scope</option>
{availableScopes.map(scope => (
<option key={scope.value} value={scope.value}>
{scope.label}
</option>
))}
</Form.Select>
<Button
variant="outline-primary"
onClick={() => {
if (selectedScope && !currentToken.scopes.includes(selectedScope)) {
setCurrentToken(prev => ({
...prev,
scopes: [...prev.scopes, selectedScope]
}));
setSelectedScope("");
}
}}
disabled={!selectedScope}
>
Add Scope
</Button>
</div>
<Form.Text className="text-muted">
Select and add the permissions this token should have
</Form.Text>
{/* Display selected scopes as chips */}
{currentToken.scopes && currentToken.scopes.length > 0 && (
<div className="mt-2">
{currentToken.scopes.map(scope => {
const scopeInfo = availableScopes.find(s => s.value === scope);
return (
<span
key={scope}
className="badge bg-primary me-2 mb-2"
style={{ fontSize: '0.9rem', padding: '5px 10px' }}
>
{scopeInfo?.label || scope}
<FontAwesomeIcon
icon={faTimes}
className="ms-2"
style={{ cursor: 'pointer' }}
onClick={() => {
setCurrentToken(prev => ({
...prev,
scopes: prev.scopes.filter(s => s !== scope)
}));
}}
/>
</span>
);
})}
</div>
)}
</Form.Group>
<Form.Group controlId="formActive" className="mb-3">
<Form.Check
type="checkbox"
label="Active?"
name="isActive"
checked={currentToken.isActive}
onChange={handleInputChange}
className="custom-checkbox"
/>
</Form.Group>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" type="submit">
{isEditing ? "Update Token" : "Add Token"}
</Button>
</Modal.Footer>
</Form>
</Modal.Body>
</Modal>
</div> </div>
)} )}
</div> </div>

View File

@ -1,4 +1,4 @@
import { useState } from "react"; import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { import {
@ -7,20 +7,86 @@ import {
MenuItem, MenuItem,
SubMenu, SubMenu,
} from "react-pro-sidebar"; } 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"; import "../Sidebar/Sidebar.css";
const Sidebar = (props) => { const Sidebar = (props) => {
const [collapsed, setCollapsed] = useState(true); 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(); 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 = () => { const toggleSidebar = () => {
setCollapsed(!collapsed); 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 <FaDatabase />;
case "fa-exclamation-circle": return <FaExclamationCircle />;
default: return <FaCircle />;
}
};
if (loading) return <div className="sidebar-wrapper">Loading...</div>;
if (error) return <div className="sidebar-wrapper">Error: {error}</div>;
return ( return (
<div className="sidebar-wrapper"> <div className="sidebar-wrapper">
<ProSidebar <ProSidebar
@ -41,21 +107,38 @@ const Sidebar = (props) => {
</div> </div>
<Menu> <Menu>
<SubMenu label="Transactions" icon={<FaExclamationCircle />}> {menuItems.map((menuItem) => {
<MenuItem onClick={() => navigate("/admin/regform")}> const subItems = subMenuItems[menuItem.menuItemId] || [];
Regform
</MenuItem>
<MenuItem onClick={() => navigate("/admin/error404")}>
Additional container
</MenuItem>
</SubMenu>
<MenuItem if (subItems.length > 0) {
icon={<FaDatabase />} return (
onClick={() => navigate("/admin/error404")} <SubMenu
key={menuItem.menuItemId}
label={menuItem.menuItemDesc}
icon={getIconComponent(menuItem.main_menu_icon_name)}
> >
Masters {subItems.map((subItem) => (
<MenuItem
key={subItem.menuItemId}
onClick={() => safeNavigate(subItem.main_menu_action_name)}
>
{subItem.menuItemDesc}
</MenuItem> </MenuItem>
))}
</SubMenu>
);
}
return (
<MenuItem
key={menuItem.menuItemId}
icon={getIconComponent(menuItem.main_menu_icon_name)}
onClick={() => safeNavigate(menuItem.main_menu_action_name)}
>
{menuItem.menuItemDesc}
</MenuItem>
);
})}
</Menu> </Menu>
</ProSidebar> </ProSidebar>
</div> </div>