From 6db2e47b3425df6d80e2520a86cccfc50b625b49 Mon Sep 17 00:00:00 2001 From: Laura Henning <laura-marie.henning@stud.tu-darmstadt.de> Date: Mon, 6 May 2024 13:57:28 +0000 Subject: [PATCH] feat: Add configurable page auto-refresh to all pages (in Dashboard) --- frontend/src/Dashboard.tsx | 96 +++++++++++++++++-- frontend/src/SimpleListMenu.tsx | 48 +++------- frontend/src/pages/AnalysisList.tsx | 7 +- frontend/src/pages/ChangePassword.tsx | 2 +- frontend/src/pages/Charging/ChargingTable.tsx | 27 +----- frontend/src/pages/StatusList.tsx | 7 +- frontend/src/pages/StatusRead.tsx | 5 +- frontend/src/pages/SubscriberCreate.tsx | 3 +- frontend/src/pages/SubscriberList.tsx | 15 ++- frontend/src/pages/SubscriberRead.tsx | 2 +- frontend/src/pages/TenantCreate.tsx | 2 +- frontend/src/pages/TenantList.tsx | 4 +- frontend/src/pages/TenantUpdate.tsx | 2 +- frontend/src/pages/UserCreate.tsx | 2 +- frontend/src/pages/UserList.tsx | 4 +- frontend/src/pages/UserUpdate.tsx | 2 +- 16 files changed, 141 insertions(+), 87 deletions(-) diff --git a/frontend/src/Dashboard.tsx b/frontend/src/Dashboard.tsx index e8a5e69..4e7461a 100644 --- a/frontend/src/Dashboard.tsx +++ b/frontend/src/Dashboard.tsx @@ -1,4 +1,4 @@ -import React, { useContext } from "react"; +import React, { useContext, useState, useEffect } from "react"; import { styled, createTheme, ThemeProvider } from "@mui/material/styles"; import CssBaseline from "@mui/material/CssBaseline"; import MuiDrawer from "@mui/material/Drawer"; @@ -17,6 +17,7 @@ import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; import { MainListItems } from "./ListItems"; import { LoginContext } from "./LoginContext"; import SimpleListMenu from "./SimpleListMenu"; +import { useNavigate } from "react-router-dom"; const drawerWidth = 300; @@ -73,6 +74,7 @@ const mdTheme = createTheme(); export interface DashboardProps { children: React.ReactNode; title: string; + refreshAction: () => void; } function Dashboard(props: DashboardProps) { @@ -80,7 +82,76 @@ function Dashboard(props: DashboardProps) { const toggleDrawer = () => { setOpen(!open); }; - const { user } = useContext(LoginContext); + const { user, setUser } = useContext(LoginContext); + const navigation = useNavigate(); + + const [time, setTime] = useState(Date.now()); + const [refreshInterval, setRefreshInterval] = useState(0); + const [refreshString, setRefreshString] = useState("manual"); + + // execute every time the refreshInterval changes to set the interval correctly + // update the time value every x ms, which triggers refresh (see below) + useEffect(() => { + if (refreshInterval === 0) { + console.log("refreshInterval is 0") + return; + } + const interval = setInterval(() => setTime(Date.now()), refreshInterval); + return () => { + console.log("clear refreshInterval") + clearInterval(interval); + }; + }, [refreshInterval]); + + // refresh every time the 'time' value changes + useEffect(() => { + console.log("reload page at", time); + //setChildKey(prev => prev + 1); + props.refreshAction(); + }, [time]); + + const handleUserNameClick = (event: React.MouseEvent<HTMLElement>, index: number) => { + switch (index) { + case 0: + navigation("/password"); + break; + case 1: + setUser(null); + navigation("/login"); + break; + default: + break; + } + }; + + const refreshStrings = ["manual", "1s", "5s", "10s", "30s"]; + + const handleRefreshClick = (event: React.MouseEvent<HTMLElement>, index: number) => { + switch (index) { + case 0: // manual + setRefreshInterval(0); + setRefreshString(refreshStrings.at(index)!); + break; + case 1: // 1s + setRefreshInterval(1000); + setRefreshString(refreshStrings.at(index)!); + break; + case 2: // 5s + setRefreshInterval(5000); + setRefreshString(refreshStrings.at(index)!); + break; + case 3: // 10s + setRefreshInterval(10000); + setRefreshString(refreshStrings.at(index)!); + break; + case 4: // 30s + setRefreshInterval(30000); + setRefreshString(refreshStrings.at(index)!); + break; + default: + break; + } + }; return ( <ThemeProvider theme={mdTheme}> @@ -104,10 +175,23 @@ function Dashboard(props: DashboardProps) { > <MenuIcon /> </IconButton> - <Typography component="h1" variant="h6" color="inherit" noWrap sx={{ flexGrow: 1 }}> - {props.title} - </Typography> - <SimpleListMenu title={user?.username} /> + <Box sx={{ display: "flex", alignItems: "center", flexGrow: 1 }}> + <Typography component="h1" variant="h6" color="inherit" noWrap> + {props.title} + </Typography> + <Divider + orientation="vertical" + flexItem + sx={{ + //height: "100%", + //alignSelf: "center", + mx: 2, + borderColor: "white", + }} + /> + <SimpleListMenu title={`Refresh: ${refreshString}`} options={refreshStrings} handleMenuItemClick={handleRefreshClick} /> + </Box> + <SimpleListMenu title={user?.username} options={["Change Password", "Logout"]} handleMenuItemClick={handleUserNameClick} /> </Toolbar> </AppBar> <Drawer variant="permanent" open={open}> diff --git a/frontend/src/SimpleListMenu.tsx b/frontend/src/SimpleListMenu.tsx index cba0b2d..9b65f5f 100644 --- a/frontend/src/SimpleListMenu.tsx +++ b/frontend/src/SimpleListMenu.tsx @@ -1,54 +1,24 @@ -import React, { useContext } from "react"; -import { useNavigate } from "react-router-dom"; +import React, { useState } from "react"; import List from "@mui/material/List"; import ListItem from "@mui/material/ListItem"; import ListItemText from "@mui/material/ListItemText"; import MenuItem from "@mui/material/MenuItem"; import Menu from "@mui/material/Menu"; -import { LoginContext } from "./LoginContext"; - -const options = ["Change Password", "Logout"]; - export interface SimpleListMenuProps { title: string | undefined; + options: string[]; + handleMenuItemClick: (event: React.MouseEvent<HTMLElement>, index: number) => void; } export default function SimpleListMenu(props: SimpleListMenuProps) { - const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null); - const [selectedIndex, setSelectedIndex] = React.useState(1); + const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); + const [selectedIndex, setSelectedIndex] = useState(0); const open = Boolean(anchorEl); const handleClickListItem = (event: React.MouseEvent<HTMLElement>) => { setAnchorEl(event.currentTarget); }; - const navigation = useNavigate(); - const { setUser } = useContext(LoginContext); - - function onChangePassword() { - navigation("/password"); - } - - function onLogout() { - setUser(null); - navigation("/login"); - } - - const handleMenuItemClick = (event: React.MouseEvent<HTMLElement>, index: number) => { - setSelectedIndex(index); - setAnchorEl(null); - switch (index) { - case 0: - onChangePassword(); - break; - case 1: - onLogout(); - break; - default: - break; - } - }; - const handleClose = () => { setAnchorEl(null); }; @@ -78,11 +48,15 @@ export default function SimpleListMenu(props: SimpleListMenuProps) { role: "listbox", }} > - {options.map((option, index) => ( + {props.options.map((option, index) => ( <MenuItem key={option} selected={index === selectedIndex} - onClick={(event) => handleMenuItemClick(event, index)} + onClick={(event) => { + setSelectedIndex(index); + setAnchorEl(null); + props.handleMenuItemClick(event, index); + }} > {option} </MenuItem> diff --git a/frontend/src/pages/AnalysisList.tsx b/frontend/src/pages/AnalysisList.tsx index b85d7f8..7a221ca 100644 --- a/frontend/src/pages/AnalysisList.tsx +++ b/frontend/src/pages/AnalysisList.tsx @@ -21,6 +21,7 @@ export default function AnalysisList() { const [data, setData] = useState<UeContext[]>([]); const [limit, setLimit] = useState(50); const [page, setPage] = useState(0); + const [refresh, setRefresh] = useState<boolean>(false); useEffect(() => { axios @@ -29,9 +30,9 @@ export default function AnalysisList() { setData(res.data); }) .catch((e) => { - console.log(e); + console.log(e.message); }); - }, [limit, page]); + }, [refresh, limit, page]); const handlePageChange = ( _event: React.MouseEvent<HTMLButtonElement> | null, @@ -103,7 +104,7 @@ export default function AnalysisList() { ); return ( - <Dashboard title="Analysis"> + <Dashboard title="Analysis" refreshAction={() => setRefresh(!refresh)}> <br /> {data == null || data.length === 0 ? <div>No App Data</div> : tableView} </Dashboard> diff --git a/frontend/src/pages/ChangePassword.tsx b/frontend/src/pages/ChangePassword.tsx index 3b24d3b..ce35417 100644 --- a/frontend/src/pages/ChangePassword.tsx +++ b/frontend/src/pages/ChangePassword.tsx @@ -79,7 +79,7 @@ export default function ChangePassword() { }; return ( - <Dashboard title="Change Password"> + <Dashboard title="Change Password" refreshAction={() => {}}> <Table> <TableBody> <TableRow> diff --git a/frontend/src/pages/Charging/ChargingTable.tsx b/frontend/src/pages/Charging/ChargingTable.tsx index 37aaa36..91e7854 100644 --- a/frontend/src/pages/Charging/ChargingTable.tsx +++ b/frontend/src/pages/Charging/ChargingTable.tsx @@ -9,7 +9,7 @@ import { Button, Grid } from "@mui/material"; export default function ChargingTable() { const [expand, setExpand] = useState(true); - const [currentTime, setCurrentTime] = useState<Date>(new Date()); + const [refresh, setRefresh] = useState<boolean>(false); const [onlineChargingData, setOnlineChargingData] = useState<ChargingData[]>([]); const [offlineChargingData, setOfflineChargingData] = useState<ChargingData[]>([]); @@ -44,37 +44,23 @@ export default function ChargingTable() { }; const onRefresh = () => { + console.log("refreshing charging data"); fetchChargingData("Online", setOnlineChargingData); fetchChargingData("Offline", setOfflineChargingData); fetchChargingRecord(); - setCurrentTime(new Date()); }; useEffect(() => { onRefresh(); - const id = setInterval(() => { - onRefresh(); - }, 10000); - return () => clearInterval(id); - }, []); + }, [refresh]); const onExpand = () => { setExpand(!expand); }; return ( - <Dashboard title="UE CHARGING"> + <Dashboard title="UE CHARGING" refreshAction={() => onRefresh()}> <Grid container spacing="2"> - <Grid item> - <Button - color="secondary" - variant="contained" - onClick={() => onRefresh()} - sx={{ m: 2, backgroundColor: "blue", "&:hover": { backgroundColor: "blue" } }} - > - Refresh - </Button> - </Grid> <Grid item> <Button color="secondary" @@ -85,11 +71,6 @@ export default function ChargingTable() { {expand ? "Fold" : "Expand"} </Button> </Grid> - <Grid item> - <Button color="success" variant="contained" sx={{ m: 2 }} disabled> - Last update: {currentTime.toISOString().slice(0, 19).replace("T", " ")} - </Button> - </Grid> </Grid> <br /> <ChargingList diff --git a/frontend/src/pages/StatusList.tsx b/frontend/src/pages/StatusList.tsx index 07dc82a..5e7fafd 100644 --- a/frontend/src/pages/StatusList.tsx +++ b/frontend/src/pages/StatusList.tsx @@ -19,6 +19,7 @@ import { export default function StatusList() { const navigation = useNavigate(); const [data, setData] = useState<UeContext[]>([]); + const [refresh, setRefresh] = useState<boolean>(false); const [limit, setLimit] = useState(50); const [page, setPage] = useState(0); @@ -33,9 +34,9 @@ export default function StatusList() { } }) .catch((e) => { - console.log(e); + console.log(e.message); }); - }, [limit, page]); + }, [refresh, limit, page]); const handlePageChange = ( _event: React.MouseEvent<HTMLButtonElement> | null, @@ -109,7 +110,7 @@ export default function StatusList() { ); return ( - <Dashboard title="Registered UEs"> + <Dashboard title="Registered UEs" refreshAction={() => setRefresh(!refresh)}> <br /> {data == null || data.length === 0 ? <div>No App Data</div> : tableView} </Dashboard> diff --git a/frontend/src/pages/StatusRead.tsx b/frontend/src/pages/StatusRead.tsx index b48a513..96c9ade 100644 --- a/frontend/src/pages/StatusRead.tsx +++ b/frontend/src/pages/StatusRead.tsx @@ -11,6 +11,7 @@ import { Card, Table, TableBody, TableCell, TableRow } from "@mui/material"; export default function StatusRead() { const location = useLocation(); const ueContext = location.state as UeContext; + const [refresh, setRefresh] = useState<boolean>(false); const { id } = useParams<{ id: string; @@ -28,10 +29,10 @@ export default function StatusRead() { const pdus: PduSessionInfo[] = res.map((item) => item.data); setData(pdus); }); - }, [id]); + }, [id, refresh]); return ( - <Dashboard title="Registered UEs"> + <Dashboard title="Registered UEs" refreshAction={() => setRefresh(!refresh)}> <h3>AMF Information [SUPI:{ueContext.Supi}]</h3> <Card variant="outlined"> <Table> diff --git a/frontend/src/pages/SubscriberCreate.tsx b/frontend/src/pages/SubscriberCreate.tsx index 61fd298..6b9a1f7 100644 --- a/frontend/src/pages/SubscriberCreate.tsx +++ b/frontend/src/pages/SubscriberCreate.tsx @@ -302,6 +302,7 @@ export default function SubscriberCreate() { if (!isNewSubscriber) { useEffect(() => { axios.get("/api/subscriber/" + id + "/" + plmn).then((res) => { + console.log('loaded existing subscriber', res.data); setData(res.data); }); }, [id]); @@ -1577,7 +1578,7 @@ export default function SubscriberCreate() { }; return ( - <Dashboard title="Subscription"> + <Dashboard title="Subscription" refreshAction={() => {}}> <Card variant="outlined"> <Table> <TableBody id="Subscriber Data Number"> diff --git a/frontend/src/pages/SubscriberList.tsx b/frontend/src/pages/SubscriberList.tsx index df2913c..e33ab26 100644 --- a/frontend/src/pages/SubscriberList.tsx +++ b/frontend/src/pages/SubscriberList.tsx @@ -25,13 +25,14 @@ export default function SubscriberList() { const [page, setPage] = useState(0); useEffect(() => { + console.log("get subscribers"); axios .get("/api/subscriber") .then((res) => { setData(res.data); }) .catch((e) => { - console.log(e); + console.log(e.message); }); }, [refresh, limit, page]); @@ -94,6 +95,10 @@ export default function SubscriberList() { navigation("/subscriber/" + subscriber.ueId + "/" + subscriber.plmnID); }; + const handleDuplicate = (subscriber: Subscriber) => { + navigation("/subscriber/create/" + subscriber.ueId + "/" + subscriber.plmnID) + }; + const tableView = ( <React.Fragment> <Table> @@ -103,6 +108,7 @@ export default function SubscriberList() { <TableCell>UE ID</TableCell> <TableCell>Delete</TableCell> <TableCell>View</TableCell> + <TableCell>Duplicate</TableCell> </TableRow> </TableHead> <TableBody> @@ -124,6 +130,11 @@ export default function SubscriberList() { VIEW </Button> </TableCell> + <TableCell> + <Button color="primary" variant="contained" onClick={() => handleDuplicate(row)}> + DUPLICATE + </Button> + </TableCell> </TableRow> ))} </TableBody> @@ -138,7 +149,7 @@ export default function SubscriberList() { ); return ( - <Dashboard title="SUBSCRIBER"> + <Dashboard title="SUBSCRIBER" refreshAction={() => setRefresh(!refresh)}> <br /> {data == null || data.length === 0 ? ( <div> diff --git a/frontend/src/pages/SubscriberRead.tsx b/frontend/src/pages/SubscriberRead.tsx index aed5862..abbb580 100644 --- a/frontend/src/pages/SubscriberRead.tsx +++ b/frontend/src/pages/SubscriberRead.tsx @@ -272,7 +272,7 @@ export default function SubscriberRead() { }; return ( - <Dashboard title="Subscription"> + <Dashboard title="Subscription" refreshAction={() => {}}> <Card variant="outlined"> <Table> <TableBody> diff --git a/frontend/src/pages/TenantCreate.tsx b/frontend/src/pages/TenantCreate.tsx index c2818b2..1ab9bdf 100644 --- a/frontend/src/pages/TenantCreate.tsx +++ b/frontend/src/pages/TenantCreate.tsx @@ -32,7 +32,7 @@ export default function TenantCreate() { }; return ( - <Dashboard title="Tenant"> + <Dashboard title="Tenant" refreshAction={() => {}}> <Table> <TableBody> <TableRow> diff --git a/frontend/src/pages/TenantList.tsx b/frontend/src/pages/TenantList.tsx index 68f2198..49d568d 100644 --- a/frontend/src/pages/TenantList.tsx +++ b/frontend/src/pages/TenantList.tsx @@ -31,7 +31,7 @@ export default function TenantList() { setData(res.data); }) .catch((e) => { - console.log(e); + console.log(e.message); }); }, [limit, page, refresh]); @@ -142,7 +142,7 @@ export default function TenantList() { ); return ( - <Dashboard title="Tenants"> + <Dashboard title="Tenants" refreshAction={() => setRefresh(!refresh)}> <br /> {data == null || data.length === 0 ? ( <div> diff --git a/frontend/src/pages/TenantUpdate.tsx b/frontend/src/pages/TenantUpdate.tsx index 5c0cb34..97c43fc 100644 --- a/frontend/src/pages/TenantUpdate.tsx +++ b/frontend/src/pages/TenantUpdate.tsx @@ -36,7 +36,7 @@ export default function TenantUpdate() { }; return ( - <Dashboard title="Tenant"> + <Dashboard title="Tenant" refreshAction={() => {}}> <Table> <TableBody> <TableRow> diff --git a/frontend/src/pages/UserCreate.tsx b/frontend/src/pages/UserCreate.tsx index f07890f..99ae655 100644 --- a/frontend/src/pages/UserCreate.tsx +++ b/frontend/src/pages/UserCreate.tsx @@ -87,7 +87,7 @@ export default function UserCreate() { }; return ( - <Dashboard title="User"> + <Dashboard title="User" refreshAction={() => {}}> <Table> <TableBody> <TableRow> diff --git a/frontend/src/pages/UserList.tsx b/frontend/src/pages/UserList.tsx index a356bfc..58fb7d6 100644 --- a/frontend/src/pages/UserList.tsx +++ b/frontend/src/pages/UserList.tsx @@ -35,7 +35,7 @@ export default function UserList() { setData(res.data); }) .catch((e) => { - console.log(e); + console.log(e.message); }); }, [limit, page, refresh]); @@ -144,7 +144,7 @@ export default function UserList() { ); return ( - <Dashboard title="Users"> + <Dashboard title="Users" refreshAction={() => {}}> <br /> {data == null || data.length === 0 ? ( <div> diff --git a/frontend/src/pages/UserUpdate.tsx b/frontend/src/pages/UserUpdate.tsx index 5d20afc..dcbb79e 100644 --- a/frontend/src/pages/UserUpdate.tsx +++ b/frontend/src/pages/UserUpdate.tsx @@ -89,7 +89,7 @@ export default function UserUpdate() { }; return ( - <Dashboard title="User"> + <Dashboard title="User" refreshAction={() => {}}> <Table> <TableBody> <TableRow> -- GitLab