diff --git a/frontend/src/Dashboard.tsx b/frontend/src/Dashboard.tsx index e8a5e693adc2997828526087e3d8a7b3489d6f57..4e7461a72116555f37d76955e2b13e83fa599ef4 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 cba0b2d4301f82948ec18f0fb8adb0630dc9e34b..9b65f5fd8a002e146c2ae424128fcac7f863e72f 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 b85d7f803ab214b92206abb9afac4f9c578ff4eb..7a221ca0260c7256726a207a8d84278cece82b38 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 3b24d3b8b24961d95d42ecb4bfbb2711530b352d..ce35417f8abd88c40b15d5abef20c0ca3cd42eaa 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 37aaa36ec6b22184556cec8b4aec2616f2dca3dc..91e785426a736b2feef86256c63589120e33463a 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 07dc82a95ea321c07877f3476681cc5bf5028136..5e7fafd12b807ccc7e8b3bd795244b836c9a56f1 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 b48a513da1d77c2f87ee22a12f25e119d8e336b7..96c9ade911e1fcf41328f5f4dfa5a6a9f8b0d78c 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 61fd298aaa219424d228f1452bf3e8a9cc9966f3..6b9a1f760042df24ecf65d18293d0b10a7d03789 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 df2913cbfe2d75fafa330c80445852eff5a49127..e33ab269054f497ee5b0d1f99d5ad51e9dc87443 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 aed586291ca44129c45470e7dc53061798861eff..abbb580dd0715fa7954769a21d7bb48e99177433 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 c2818b28411165a2158e7822e36db954b19efff6..1ab9bdf44f67ca22f51f8d17192c83f7171ce32a 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 68f219805065b470ac17447c55e016024b8052d9..49d568d913a737998d217acf4a4cd63d1953d619 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 5c0cb34911a924e8ca454b6e19c870f80f9f2bb3..97c43fc81591084ffa0efa5407d3a615a9997755 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 f07890fe7f814828aeb8187ae69abbb5ceabac2c..99ae655c053b8fbf98fa0dde5ec73019c0791f25 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 a356bfc189fb432180abc01ae311a9dc9f31688a..58fb7d6d2ba6c63dcc38af4cd7e4154059ace558 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 5d20afccf8b0460df50a521b2a66452861076e56..dcbb79eff1c6b75ca67a9da2f23b2f1f006e567b 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>