Compare commits
No commits in common. "saksham" and "main" have entirely different histories.
3
.env
Normal file
3
.env
Normal file
@ -0,0 +1,3 @@
|
||||
GENERATE_SOURCEMAP=false
|
||||
|
||||
REACT_APP_API_URL=http://157.66.191.31:33730/back/
|
||||
12
.github/workflows/main.yml
vendored
Normal file
12
.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
name: Autocloser
|
||||
on: [issues]
|
||||
jobs:
|
||||
autoclose:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Issue auto-closer
|
||||
uses: roots/issue-closer-action@v1.1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-close-message: "@${issue.user.login} this issue was automatically closed because it did not follow our rules:\n\n<pre>\n\n\n\nIMPORTANT: Please use the following link to create a new issue:\n\nhttps://www.creative-tim.com/new-issue/argon-dashboard-react\n\n**If your issue was not created using the app above, it will be closed immediately.**\n\n\n\nLove Creative Tim? Do you need Angular, React, Vuejs or HTML? You can visit:\n👉 https://www.creative-tim.com/bundles\n👉 https://www.creative-tim.com\n\n\n</pre>\n\n"
|
||||
issue-pattern: (\#\#\# Version([\S\s.*]*?)\#\#\# Reproduction link([\S\s.*]*?)\#\#\# Operating System([\S\s.*]*?)\#\#\# Device([\S\s.*]*?)\#\#\# Browser & Version([\S\s.*]*?)\#\#\# Steps to reproduce([\S\s.*]*?)\#\#\# What is expected([\S\s.*]*?)\#\#\# What is actually happening([\S\s.*]*?)---([\S\s.*]*?)\#\#\# Solution([\S\s.*]*?)\#\#\# Additional comments([\S\s.*]*?)\<\!-- generated by creative-tim-issues\. DO NOT REMOVE --\>)|(\#\#\# What is your enhancement([\S\s.*]*?)\<\!-- generated by creative-tim-issues\. DO NOT REMOVE --\>)
|
||||
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<<<<<<< HEAD
|
||||
/build
|
||||
/node_modules
|
||||
package-lock.json
|
||||
/build
|
||||
=======
|
||||
node_modules/
|
||||
node_modules/
|
||||
/build
|
||||
package-lock.json
|
||||
>>>>>>> 1c0592d (commit new code)
|
||||
.eslintcache
|
||||
3
.npmrc
Normal file
3
.npmrc
Normal file
@ -0,0 +1,3 @@
|
||||
legacy-peer-deps=true
|
||||
auto-install-peers=true
|
||||
strict-peer-dependencies=false
|
||||
25036
package-lock.json
generated
25036
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -26,7 +26,7 @@
|
||||
"homepage": "https://demos.creative-tim.com/argon-dashboard-react/",
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "cross-env PUBLIC_URL=/ react-scripts build",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"install:clean": "rm -rf node_modules/ && rm -rf package-lock.json && npm install && npm start",
|
||||
@ -71,6 +71,7 @@
|
||||
"classnames": "2.3.2",
|
||||
"cors-anywhere": "^0.4.4",
|
||||
"file-saver": "^2.0.5",
|
||||
"html2canvas": "^1.4.1",
|
||||
"moment": "2.29.4",
|
||||
"multiselect-react-dropdown": "^2.0.25",
|
||||
"nouislider": "15.4.0",
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -93,7 +93,7 @@ const App = () => {
|
||||
<AdminLayout />
|
||||
</ProtectedRoute>
|
||||
}>
|
||||
<Route path="regform" element={<Regform />} />
|
||||
{/* <Route path="regform" element={<Regform />} /> */}
|
||||
<Route path="error404" element={<Error404 />} />
|
||||
<Route path="resetpassword" element={<ResetPassword />} />
|
||||
|
||||
@ -137,10 +137,11 @@ const App = () => {
|
||||
<Route path="dashboard-new-edit/:id" element={<DashboardNewEdit/>}/>
|
||||
<Route path="edit-new-dash/:id" element={<EditNewDash/>}/>
|
||||
<Route path="dashrunner/:id" element={<DashboardRunner/>}/>
|
||||
|
||||
{/* <Route path="test" element={<Regform />} /> */}
|
||||
{/* buildercomponents */}
|
||||
|
||||
|
||||
|
||||
</Route>
|
||||
|
||||
<Route path="/auth/*" element={<AuthLayout />}>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// 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 : <Navigate to="/auth/login" replace />;
|
||||
};
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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 (
|
||||
<div style={{ marginTop: "1rem" }}>
|
||||
@ -305,6 +318,21 @@ const [visibleColumns, setVisibleColumns] = useState({
|
||||
>
|
||||
{token.isActive ? "Active" : "Inactive"}
|
||||
</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]
|
||||
)}
|
||||
@ -383,93 +411,245 @@ const [visibleColumns, setVisibleColumns] = useState({
|
||||
</PaginationItem>
|
||||
</Pagination>
|
||||
|
||||
{/* Add/Edit Token Modal */}
|
||||
{/* Generate Token Modal */}
|
||||
<Modal show={showGenerateTokenModal} onHide={handleClose}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Generate New Token</Modal.Title>
|
||||
<FontAwesomeIcon
|
||||
icon={faTimes}
|
||||
size="lg"
|
||||
onClick={handleClose}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "25px",
|
||||
right: "25px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
/>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Form>
|
||||
<Form.Group controlId="formNewTokenName" className="mb-3">
|
||||
<Form.Label>Token Name</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
value={newTokenName}
|
||||
onChange={(e) => setNewTokenName(e.target.value)}
|
||||
required
|
||||
className="custom-hover-border"
|
||||
placeholder="Enter a name for the new token"
|
||||
/>
|
||||
</Form.Group>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Generate New Token</Modal.Title>
|
||||
<FontAwesomeIcon
|
||||
icon={faTimes}
|
||||
size="lg"
|
||||
onClick={handleClose}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "25px",
|
||||
right: "25px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
/>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Form>
|
||||
<Form.Group controlId="formNewTokenName" className="mb-3">
|
||||
<Form.Label>Token Name</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
value={newTokenName}
|
||||
onChange={(e) => setNewTokenName(e.target.value)}
|
||||
required
|
||||
className="custom-hover-border"
|
||||
placeholder="Enter a name for the new token"
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="formTokenScopes" className="mb-3">
|
||||
<Form.Label>Token Scopes</Form.Label>
|
||||
<Form.Select
|
||||
multiple
|
||||
value={selectedScopes}
|
||||
onChange={(e) => {
|
||||
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 => (
|
||||
<option key={scope.value} value={scope.value}>
|
||||
{scope.label}
|
||||
</option>
|
||||
))}
|
||||
</Form.Select>
|
||||
<Form.Text className="text-muted">
|
||||
Select the permissions this token should have (hold Ctrl/Cmd to select multiple)
|
||||
</Form.Text>
|
||||
</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={addScope}
|
||||
disabled={!selectedScope}
|
||||
>
|
||||
Add Scope
|
||||
</Button>
|
||||
</div>
|
||||
<Form.Text className="text-muted">
|
||||
Select and add the permissions this token should have
|
||||
</Form.Text>
|
||||
|
||||
{generatedToken && (
|
||||
<Form.Group controlId="formGeneratedToken" className="mb-3">
|
||||
<Form.Label>Generated Token</Form.Label>
|
||||
<Form.Control
|
||||
as="textarea"
|
||||
rows={3}
|
||||
value={generatedToken}
|
||||
readOnly
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
<Form.Text className="text-muted">
|
||||
Copy this token and store it securely. You won't be able to see it again.
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
)}
|
||||
{/* 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>
|
||||
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={handleClose}>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={generateNewToken}
|
||||
disabled={!newTokenName.trim()}
|
||||
>
|
||||
Generate Token
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Form>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
{generatedToken && (
|
||||
<Form.Group controlId="formGeneratedToken" className="mb-3">
|
||||
<Form.Label>Generated Token</Form.Label>
|
||||
<Form.Control
|
||||
as="textarea"
|
||||
rows={3}
|
||||
value={generatedToken}
|
||||
readOnly
|
||||
className="custom-hover-border"
|
||||
/>
|
||||
<Form.Text className="text-muted">
|
||||
Copy this token and store it securely. You won't be able to see it again.
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
)}
|
||||
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={handleClose}>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={generateNewToken}
|
||||
disabled={!newTokenName.trim()}
|
||||
>
|
||||
Generate Token
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Form>
|
||||
</Modal.Body>
|
||||
</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>
|
||||
|
||||
@ -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 <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 (
|
||||
<div className="sidebar-wrapper">
|
||||
<ProSidebar
|
||||
@ -41,21 +107,38 @@ const Sidebar = (props) => {
|
||||
</div>
|
||||
|
||||
<Menu>
|
||||
<SubMenu label="Transactions" icon={<FaExclamationCircle />}>
|
||||
<MenuItem onClick={() => navigate("/admin/regform")}>
|
||||
Regform
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => navigate("/admin/error404")}>
|
||||
Additional container
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
{menuItems.map((menuItem) => {
|
||||
const subItems = subMenuItems[menuItem.menuItemId] || [];
|
||||
|
||||
<MenuItem
|
||||
icon={<FaDatabase />}
|
||||
onClick={() => navigate("/admin/error404")}
|
||||
>
|
||||
Masters
|
||||
</MenuItem>
|
||||
if (subItems.length > 0) {
|
||||
return (
|
||||
<SubMenu
|
||||
key={menuItem.menuItemId}
|
||||
label={menuItem.menuItemDesc}
|
||||
icon={getIconComponent(menuItem.main_menu_icon_name)}
|
||||
>
|
||||
{subItems.map((subItem) => (
|
||||
<MenuItem
|
||||
key={subItem.menuItemId}
|
||||
onClick={() => safeNavigate(subItem.main_menu_action_name)}
|
||||
>
|
||||
{subItem.menuItemDesc}
|
||||
</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>
|
||||
</ProSidebar>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user