diff --git a/visualization_tool/src/App.tsx b/visualization_tool/src/App.tsx index 4bd58053..7914aac2 100644 --- a/visualization_tool/src/App.tsx +++ b/visualization_tool/src/App.tsx @@ -13,6 +13,8 @@ function App() { const [searchQuery, setSearchQuery] = useState(''); const [sortAttribute, setSortAttribute] = useState('date'); const [resources, setResources] = useState([]); + const [projects, setProjects] = useState([]); + const [allowedProjects, setAllowedProjects] = useState(projects); const [allowedTypes, setAllowedTypes] = useState( availableResourceTypes ); @@ -22,9 +24,12 @@ function App() { } /> } + element={ + + } /> diff --git a/visualization_tool/src/components/ControlMenu/ControlMenu.css b/visualization_tool/src/components/ControlMenu/ControlMenu.css index d89c7f31..5f64dd6b 100644 --- a/visualization_tool/src/components/ControlMenu/ControlMenu.css +++ b/visualization_tool/src/components/ControlMenu/ControlMenu.css @@ -1,14 +1,16 @@ +.control-menu-container { + position: fixed; +} + .control-menu { background-color: #f8f9fc; position: fixed; - height: 100%; width: 350px; padding-block: 30px; padding-inline: 20px; - display: flex; - flex-direction: column; - align-items: center; + height: calc(100vh - 70px); + overflow-y: auto; } .menu-item { @@ -41,6 +43,7 @@ .menu-item h3 { font-weight: 400; font-size: 18px; + margin-bottom: 5px; } .menu-item img { diff --git a/visualization_tool/src/components/ControlMenu/ControlMenu.tsx b/visualization_tool/src/components/ControlMenu/ControlMenu.tsx index 1ddd8000..50b0e558 100644 --- a/visualization_tool/src/components/ControlMenu/ControlMenu.tsx +++ b/visualization_tool/src/components/ControlMenu/ControlMenu.tsx @@ -3,6 +3,7 @@ import {useLocation} from 'react-router-dom'; import UploadMenu from './partials/UploadMenu'; import SortMenu from './partials/SortMenu'; import FilterMenu from './partials/FilterMenu'; +import FilterProjects from './partials/FilterProjects'; import {Resource} from '../../types/resources'; import {IAMRole} from '../../types/IAMPolicy'; @@ -14,6 +15,9 @@ type ControlMenuProps = { setSortAttribute: React.Dispatch>; setAllowedTypes: React.Dispatch>; setRoles: React.Dispatch>; + projects: string[]; + setProjects: React.Dispatch>; + setAllowedProjects: React.Dispatch>; }; const ControlMenu = ({ @@ -21,18 +25,32 @@ const ControlMenu = ({ setSortAttribute, setAllowedTypes, setRoles, + projects, + setProjects, + setAllowedProjects, }: ControlMenuProps) => { const location = useLocation(); return ( -
- - {location.pathname === '/static/' && ( - <> - - - - )} +
+
+ + + {location.pathname === '/static/' && ( + <> + + + + )} +
); }; diff --git a/visualization_tool/src/components/ControlMenu/partials/FilterProjects.tsx b/visualization_tool/src/components/ControlMenu/partials/FilterProjects.tsx new file mode 100644 index 00000000..528fc35e --- /dev/null +++ b/visualization_tool/src/components/ControlMenu/partials/FilterProjects.tsx @@ -0,0 +1,53 @@ +type FilterProjectsProps = { + projects: string[]; + setAllowedProjects: React.Dispatch>; +}; + +const FilterProjects = ({ + projects, + setAllowedProjects, +}: FilterProjectsProps) => { + return ( +
+
+ + + +

Projects

+
+ {projects.length > 0 && ( +
+ {projects.map(project => { + return ( +
+ { + if (e.target.checked) { + setAllowedProjects(prevProjects => [ + ...prevProjects, + project, + ]); + } else { + setAllowedProjects(prevProjects => { + return prevProjects.filter( + prevProject => prevProject !== project + ); + }); + } + }} + /> + +
+ ); + })} +
+ )} +
+ ); +}; + +export default FilterProjects; diff --git a/visualization_tool/src/components/ControlMenu/partials/UploadMenu.tsx b/visualization_tool/src/components/ControlMenu/partials/UploadMenu.tsx index 23873ebb..b67155e3 100644 --- a/visualization_tool/src/components/ControlMenu/partials/UploadMenu.tsx +++ b/visualization_tool/src/components/ControlMenu/partials/UploadMenu.tsx @@ -9,11 +9,23 @@ import {parseData, parseIAMData} from '../Controller'; type UploadMenuProps = { setResources: React.Dispatch>; setRoles: React.Dispatch>; + setProjects: React.Dispatch>; + setAllowedProjects: React.Dispatch>; }; -const UploadMenu = ({setResources, setRoles}: UploadMenuProps) => { +type File = { + name: string; + projects: string[]; +}; + +const UploadMenu = ({ + setResources, + setRoles, + setProjects, + setAllowedProjects, +}: UploadMenuProps) => { const fileInput = useRef(null); - const [files, setFiles] = useState([]); + const [files, setFiles] = useState([]); const [error, setError] = useState(null); return (
@@ -35,7 +47,7 @@ const UploadMenu = ({setResources, setRoles}: UploadMenuProps) => { return; } // check if file is already uploaded - if (files.includes(file.name)) { + if (files.find(prevFile => prevFile.name === file.name)) { setError('File already uploaded'); return; } @@ -47,6 +59,14 @@ const UploadMenu = ({setResources, setRoles}: UploadMenuProps) => { try { const data = JSON.parse(result); + setProjects(prevProjects => [ + ...prevProjects, + ...Object.keys(data.projects), + ]); + setAllowedProjects(prevProjects => [ + ...prevProjects, + ...Object.keys(data.projects), + ]); const resources = parseData(data, file.name); setResources((prevResources: Resource[]) => [ ...prevResources, @@ -56,7 +76,10 @@ const UploadMenu = ({setResources, setRoles}: UploadMenuProps) => { const roles = parseIAMData(data, file.name); setRoles((prevRoles: IAMRole[]) => [...prevRoles, ...roles]); - setFiles([...files, file.name]); + setFiles([ + ...files, + {name: file.name, projects: Object.keys(data.projects)}, + ]); } catch (err) { setError('Invalid file'); return; @@ -85,21 +108,32 @@ const UploadMenu = ({setResources, setRoles}: UploadMenuProps) => { {files.length > 0 && (
{files.map(file => ( -
-

{file}

+
+

{file.name}