Skip to content

Commit

Permalink
Merge pull request #26 from christianstubbe/feature-purpose-tree
Browse files Browse the repository at this point in the history
Feature: Purpose tree
  • Loading branch information
ehourdebaigt authored Jul 21, 2023
2 parents cea7f90 + f02c9bb commit f5b1a7b
Show file tree
Hide file tree
Showing 39 changed files with 2,400 additions and 890 deletions.
664 changes: 555 additions & 109 deletions frontend/package-lock.json

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "privavy-engineering-toolbox",
"name": "peng-demo-app",
"homepage": ".",
"version": "0.1.0",
"private": true,
Expand All @@ -8,16 +8,20 @@
"@emotion/styled": "^11.11.0",
"@headlessui/react": "^1.7.15",
"@heroicons/react": "^2.0.18",
"@mui/icons-material": "^5.11.16",
"@mui/material": "^5.13.5",
"@mui/icons-material": "^5.14.0",
"@mui/lab": "^5.0.0-alpha.136",
"@mui/material": "^5.14.0",
"@mui/styles": "^5.14.0",
"@mui/x-data-grid": "^6.10.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.4.0",
"create-create-app": "^7.3.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.9.0",
"react-router-dom": "^6.13.0",
"react-router-dom": "^6.14.1",
"react-scripts": "^5.0.1",
"tw-elements": "^1.0.0-beta2",
"web-vitals": "^2.1.4"
Expand Down
37 changes: 19 additions & 18 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
import * as React from "react";

import CssBaseline from '@mui/material/CssBaseline';
import { Grid, Container } from "@mui/material";

import UploadData from "./components/UploadData";
import RetrieveData from "./components/RetrieveData";
import Navbar from "./components/Navbar.js";
import {BrowserRouter, Routes, Route} from 'react-router-dom';
import Home from "./components/Home";
import Settings from "./components/Settings";


function App() {
return (
<>
<CssBaseline />
<Navbar />
<Container maxWidth="lg">
<Grid container spacing={6}>
<Grid item xs={6}>
<UploadData/>
</Grid>
<Grid item xs={6}>
<RetrieveData />
</Grid>
</Grid>
</Container>
</>
);

return (
<>
<CssBaseline/>
<BrowserRouter>
<Navbar/>
<Routes>
<Route path='/' element={<Home/>}/>
<Route path='/retrieve' element={<RetrieveData/>}/>
<Route path='/upload' element={<UploadData/>}/>
<Route path='/settings' element={<Settings/>}/>
</Routes>
</BrowserRouter>
</>
);
}

export default App;
183 changes: 183 additions & 0 deletions frontend/src/TreeContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import React, {createContext, useReducer, useEffect} from "react";
import {CircularProgress} from "@mui/material";
import axios from "axios";

const reducer = (state, action) => {
switch (action.type) {
case "FETCH_SUCCESS":
return {
isLoading: false,
treeData: action.payload,
error: null,
};
case "FETCH_ERROR":
return {
isLoading: false,
treeData: [],
error: "Something went wrong!",
};
case "UPDATE_TREE":
return {
...state,
treeData: action.payload
};
case "TOGGLE_CHECKBOX":
return {
...state,
treeData: toggleNodeAndChildren(state.treeData, action.payload)
};

case "ADD_NODE":
return {
...state,
treeData: addNodeToTree(state.treeData, action.payload),
};
case "DELETE_NODE":
return {
...state,
treeData: deleteNodeFromTree(state.treeData, action.payload),
};
default:
return state;
}
};

const initialState = {
treeData: [],
isLoading: true,
error: null,
};

const deleteNodeFromTree = (treeData, nodeId) => {
return treeData.filter(node => {
if (node.id === nodeId) {
return false;
}

if (node.children) {
node.children = deleteNodeFromTree(node.children, nodeId);
}

return true;
});
};


const addNodeToTree = (treeData, newNode) => {
return treeData.map((node) => {
if (node.id === newNode.parent_id) {
return {...node, children: [...(node.children || []), newNode]};
} else if (node.children && node.children.length > 0) {
return {
...node,
children: addNodeToTree(node.children, newNode),
};
} else {
return node;
}
});
};


const toggleNodeAndChildren = (treeData, id, selected) => {
return treeData.map((node) => {
if (node.id === id) {
const newNode = {...node, selected};
if (node.children) {
newNode.children = toggleNodeAndChildren(node.children, id, selected);
}
return newNode;
} else if (node.children) {
return {...node, children: toggleNodeAndChildren(node.children, id, selected)};
} else {
return node;
}
});
};


const getSelectedNodeIds = (treeData) => {
let selectedIds = [];

const traverseTree = (node) => {
if (node.selected) {
selectedIds.push(node.id);
}

if (node.children) {
node.children.forEach(childNode => traverseTree(childNode));
}
}

treeData.forEach(rootNode => traverseTree(rootNode));

return selectedIds;
}


export const TreeContext = createContext();

const TreeContextProvider = ({children}) => {
const [state, dispatch] = useReducer(reducer, initialState);

useEffect(() => {
axios
.get("http://localhost:8000/api/v1/pap/purposes")
.then((response) => {
dispatch({type: "FETCH_SUCCESS", payload: response.data});
})
.catch(() => {
dispatch({type: "FETCH_ERROR"});
});
}, []);

const handleCheckboxChange = (nodeId) => {
const findNode = (treeData, id) => {
for (const node of treeData) {
if (node.id === id) return node;
if (node.children) {
const result = findNode(node.children, id);
if (result) return result;
}
}
return null;
}

const nodeToChange = findNode(state.treeData, nodeId);

if (!nodeToChange) {
console.error(`Node with id ${nodeId} not found`);
return;
}

const newTreeData = toggleNodeAndChildren(state.treeData, nodeId, !nodeToChange.selected);
dispatch({type: "UPDATE_TREE", payload: newTreeData});
};


const handleAddNodeContext = (newNode) => {
dispatch({type: "ADD_NODE", payload: newNode});
};

const handleDeleteNodeContext = (nodeId) => {
dispatch({type: "DELETE_NODE", payload: nodeId});
};


return (
<TreeContext.Provider
value={{
treeData: state.treeData,
isLoading: state.isLoading,
handleCheckboxChange,
getSelectedNodeIds,
handleAddNodeContext,
handleDeleteNodeContext
}}
>
{state.isLoading ? <CircularProgress/> : children}
</TreeContext.Provider>
);
};

export default TreeContextProvider;
34 changes: 34 additions & 0 deletions frontend/src/components/Home.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { Container, Typography, Box } from '@mui/material';

function Home() {
return (
<Container>
<Box
display="flex"
flexDirection="column"
justifyContent="center"
alignItems="center"
minHeight="100vh"
textAlign="center"
>
<Typography variant="h2" gutterBottom>
Welcome to Our Company
</Typography>
<Typography variant="h5">
We're committed to providing our customers with the best services.
</Typography>
<Box mt={4}>
<Typography variant="body1">
Our company was founded with a mission to make life better. We're dedicated to providing
innovative solutions that improve the quality of life. Our team is passionate about
creating products that make a difference. We believe in putting our customers first
and are proud of the work we do each day.
</Typography>
</Box>
</Box>
</Container>
);
}

export default Home;
50 changes: 50 additions & 0 deletions frontend/src/components/Navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,52 @@ import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";

import AccountCircleIcon from '@mui/icons-material/AccountCircle';
import {Drawer, IconButton, List, ListItem, ListItemText} from "@mui/material";
import {Link} from "react-router-dom";
import MenuIcon from "@mui/icons-material/Menu";
import { makeStyles } from '@mui/styles';

const useStyles = makeStyles({
list: {
width: 250,
},
fullList: {
width: 'auto',
},
});

function Navbar() {
const classes = useStyles();
const [state, setState] = React.useState({ left: false });

const toggleDrawer = (anchor, open) => (event) => {
setState({ ...state, [anchor]: open });
};

const list = (anchor) => (
<div
className={classes.list}
role="presentation"
onClick={toggleDrawer(anchor, false)}
onKeyDown={toggleDrawer(anchor, false)}
>
<List>
<ListItem button key={'Home'} component={Link} to="/">
<ListItemText primary={'Home'} />
</ListItem>
<ListItem button key={'RetrieveData'} component={Link} to="/retrieve">
<ListItemText primary={'Retrieve Images'} />
</ListItem>
<ListItem button key={'UploadData'} component={Link} to="/upload">
<ListItemText primary={'Upload Images'} />
</ListItem>
<ListItem button key={'Settings'} component={Link} to="/settings">
<ListItemText primary={'Settings'} />
</ListItem>
</List>
</div>
);

return (
<>
<Box sx={{ flexGrow: 1, marginBottom: 0 }}>
Expand All @@ -18,6 +62,12 @@ function Navbar() {
position="static"
>
<Toolbar>
<IconButton edge="start" color="inherit" aria-label="menu" onClick={toggleDrawer('left', true)}>
<MenuIcon />
</IconButton>
<Drawer anchor={'left'} open={state['left']} onClose={toggleDrawer('left', false)}>
{list('left')}
</Drawer>
<img className="logo" alt="Logo TU Berlin" src={LogoTU} />
<Typography
variant="h6"
Expand Down
Loading

0 comments on commit f5b1a7b

Please sign in to comment.