Compare commits
	
		
			No commits in common. "main" and "saksham" have entirely different histories.
		
	
	
		
	
		
							
								
								
									
										3
									
								
								.env
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								.env
									
									
									
									
									
								
							| @ -1,3 +0,0 @@ | ||||
| GENERATE_SOURCEMAP=false | ||||
| 
 | ||||
| REACT_APP_API_URL=http://157.66.191.31:33730/back/ | ||||
							
								
								
									
										12
									
								
								.github/workflows/main.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/main.yml
									
									
									
									
										vendored
									
									
								
							| @ -1,12 +0,0 @@ | ||||
| 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
									
									
								
							
							
						
						
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,12 +0,0 @@ | ||||
| <<<<<<< HEAD | ||||
| /build | ||||
| /node_modules | ||||
| package-lock.json | ||||
| /build | ||||
| ======= | ||||
| node_modules/ | ||||
| node_modules/ | ||||
| /build | ||||
| package-lock.json | ||||
| >>>>>>> 1c0592d (commit new code) | ||||
| .eslintcache | ||||
							
								
								
									
										3
									
								
								.npmrc
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								.npmrc
									
									
									
									
									
								
							| @ -1,3 +0,0 @@ | ||||
| legacy-peer-deps=true | ||||
| auto-install-peers=true | ||||
| strict-peer-dependencies=false | ||||
							
								
								
									
										25036
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										25036
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												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": "react-scripts build", | ||||
|     "build": "cross-env PUBLIC_URL=/ 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,7 +71,6 @@ | ||||
|     "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,7 +21,6 @@ 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,13 +137,12 @@ 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 */} | ||||
|                | ||||
|              {/* buildercomponents */} | ||||
|             | ||||
|                | ||||
|           </Route> | ||||
|              | ||||
| 
 | ||||
|           <Route path="/auth/*" element={<AuthLayout />}> | ||||
|             <Route path="login" element={<Login />} /> | ||||
|             <Route path="register" element={<Register />} /> | ||||
|  | ||||
| @ -1,11 +1,11 @@ | ||||
| // ProtectedRoute.js
 | ||||
| import React from "react"; | ||||
| import { Navigate } from "react-router-dom"; | ||||
| import { getToken } from "utils/tokenService"; | ||||
| 
 | ||||
| const ProtectedRoute = ({ children }) => { | ||||
|   const isAuthenticated = getToken() !== null; | ||||
|     console.log(`token for checking whether authenticated: ${getToken()}`); | ||||
|   const isAuthenticated = getToken() !== null; // Check if user is authenticated
 | ||||
|   return isAuthenticated ? children : <Navigate to="/auth/login" replace />; | ||||
| }; | ||||
| 
 | ||||
| export default ProtectedRoute; | ||||
| export default ProtectedRoute; | ||||
|  | ||||
| @ -57,7 +57,7 @@ function AccessTypeManagement() { | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     const fetchAccessTypes = async () => { | ||||
|       const apiUrl = `${process.env.REACT_APP_API_URL}token/access_type/Accesstype`; | ||||
|       const apiUrl = `${process.env.REACT_APP_API_URL}api/getAllAccessTypes`; | ||||
|       const token = localStorage.getItem("authToken"); | ||||
| 
 | ||||
|       if (!token) { | ||||
|  | ||||
| @ -20,31 +20,31 @@ function TOKENRegistry() { | ||||
|     id: "", | ||||
|     tokenName: "", | ||||
|     tokenValue: "", | ||||
|     isActive: true, | ||||
|     scopes: [] | ||||
|     isActive: true | ||||
|   }); | ||||
|   const [currentPage, setCurrentPage] = useState(1); | ||||
|   const [searchQuery, setSearchQuery] = useState(""); | ||||
|   const [isEditing, setIsEditing] = useState(false); | ||||
|   const [recordsPerPage, setRecordsPerPage] = useState(10); | ||||
|   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' }, | ||||
|   ]); | ||||
|   // Add to your state:
 | ||||
| const [visibleColumns, setVisibleColumns] = useState({ | ||||
|   id: true, | ||||
|   tokenName: true, | ||||
|   tokenValue: true, | ||||
|   scopes: true, | ||||
|   isActive: true, | ||||
|   actions: true | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
|   const [loading, setLoading] = useState(true); | ||||
|   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' }, | ||||
|   ]); | ||||
|   useEffect(() => { | ||||
|     fetchTokens(); | ||||
|   }, []); | ||||
| @ -95,7 +95,6 @@ function TOKENRegistry() { | ||||
|     setShowGenerateTokenModal(false); | ||||
|     setGeneratedToken(""); | ||||
|     setNewTokenName(""); | ||||
|     setSelectedScopes([]); | ||||
|   }; | ||||
| 
 | ||||
|   const handleRecordsPerPageChange = (number) => { | ||||
| @ -136,10 +135,9 @@ function TOKENRegistry() { | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const openModal = (token = { id: "", tokenName: "", tokenValue: "", isActive: false, scopes: [] }) => { | ||||
|   const openModal = (token = { id: "", tokenName: "", tokenValue: "", isActive: false }) => { | ||||
|     setIsEditing(!!token.id); | ||||
|     setCurrentToken(token); | ||||
|     setSelectedScopes(token.scopes || []); | ||||
|     setShowAddEditModal(true); | ||||
|   }; | ||||
| 
 | ||||
| @ -155,35 +153,24 @@ function TOKENRegistry() { | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const addScope = () => { | ||||
|     if (selectedScope && !selectedScopes.includes(selectedScope)) { | ||||
|       setSelectedScopes([...selectedScopes, selectedScope]); | ||||
|       setSelectedScope(""); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const removeScope = (scopeToRemove) => { | ||||
|     setSelectedScopes(selectedScopes.filter(scope => scope !== scopeToRemove)); | ||||
|   }; | ||||
| 
 | ||||
|   const generateNewToken = async () => { | ||||
|     if (!newTokenName.trim()) { | ||||
|       toast.error("Please enter a token name"); | ||||
|       return; | ||||
|     } | ||||
|   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"); | ||||
|     } | ||||
|   }; | ||||
|   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" }}> | ||||
| @ -318,21 +305,6 @@ function TOKENRegistry() { | ||||
|                             > | ||||
|                               {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] | ||||
|                           )} | ||||
| @ -411,245 +383,93 @@ function TOKENRegistry() { | ||||
|             </PaginationItem> | ||||
|           </Pagination> | ||||
| 
 | ||||
|           {/* 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> | ||||
| 
 | ||||
|                 <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> | ||||
|                    | ||||
|                   {/* 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> | ||||
| 
 | ||||
|                 {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> | ||||
|           <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> | ||||
| 
 | ||||
|       <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> | ||||
| 
 | ||||
|       {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> | ||||
|         </div> | ||||
|       )} | ||||
|     </div> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { useState, useEffect } from "react"; | ||||
| import { useState } from "react"; | ||||
| import { useNavigate } from "react-router-dom"; | ||||
| import PropTypes from "prop-types"; | ||||
| import { | ||||
| @ -7,86 +7,20 @@ import { | ||||
|   MenuItem, | ||||
|   SubMenu, | ||||
| } from "react-pro-sidebar"; | ||||
| import { FaChevronLeft, FaChevronRight, FaDatabase, FaExclamationCircle, FaCircle } from "react-icons/fa"; | ||||
| import { fetchMenuItems, getSubmenuItems } from "../../APIServices/MenuMaintenanceAPI"; | ||||
| import { FaChevronLeft, FaChevronRight, FaDatabase, FaExclamationCircle } from "react-icons/fa"; | ||||
| 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); | ||||
|     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"); | ||||
|     if (props.onSidebarToggle) { | ||||
|       props.onSidebarToggle(!collapsed); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   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 | ||||
| @ -107,38 +41,21 @@ const Sidebar = (props) => { | ||||
|         </div> | ||||
| 
 | ||||
|         <Menu> | ||||
|           {menuItems.map((menuItem) => { | ||||
|             const subItems = subMenuItems[menuItem.menuItemId] || []; | ||||
|              | ||||
|             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> | ||||
|             ); | ||||
|           })} | ||||
|           <SubMenu label="Transactions" icon={<FaExclamationCircle />}> | ||||
|             <MenuItem onClick={() => navigate("/admin/regform")}> | ||||
|               Regform | ||||
|             </MenuItem> | ||||
|             <MenuItem onClick={() => navigate("/admin/error404")}> | ||||
|               Additional container | ||||
|             </MenuItem> | ||||
|           </SubMenu> | ||||
| 
 | ||||
|           <MenuItem | ||||
|             icon={<FaDatabase />} | ||||
|             onClick={() => navigate("/admin/error404")} | ||||
|           > | ||||
|             Masters | ||||
|           </MenuItem> | ||||
|         </Menu> | ||||
|       </ProSidebar> | ||||
|     </div> | ||||
| @ -154,4 +71,4 @@ Sidebar.propTypes = { | ||||
|   onSidebarToggle: PropTypes.func, | ||||
| }; | ||||
| 
 | ||||
| export default Sidebar; | ||||
| export default Sidebar; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user