diff --git a/csharp/App/Backend/Controller.cs b/csharp/App/Backend/Controller.cs index 406223442..bca9d21b5 100644 --- a/csharp/App/Backend/Controller.cs +++ b/csharp/App/Backend/Controller.cs @@ -468,6 +468,19 @@ public class Controller : ControllerBase .ToList(); } + [HttpGet(nameof(GetAllSodioHomeInstallations))] + public ActionResult> GetAllSodioHomeInstallations(Token authToken) + { + var user = Db.GetSession(authToken)?.User; + + if (user is null) + return Unauthorized(); + + return user + .AccessibleInstallations(product:(int)ProductType.SodioHome) + .ToList(); + } + [HttpGet(nameof(GetAllFolders))] diff --git a/csharp/App/Backend/DataTypes/Methods/Session.cs b/csharp/App/Backend/DataTypes/Methods/Session.cs index 3d044b728..ff6fa8090 100644 --- a/csharp/App/Backend/DataTypes/Methods/Session.cs +++ b/csharp/App/Backend/DataTypes/Methods/Session.cs @@ -232,6 +232,17 @@ public static class SessionMethods && await installation.CreateBucket() && await installation.RenewS3Credentials(); } + + if (installation.Product == (int)ProductType.SodioHome) + { + return user is not null + && user.UserType != 0 + && user.HasAccessToParentOf(installation) + && Db.Create(installation); + } + + + return false; } diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx new file mode 100644 index 000000000..98a003a5a --- /dev/null +++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx @@ -0,0 +1,390 @@ +import { I_Installation } from 'src/interfaces/InstallationTypes'; +import React from 'react'; +import { Grid } from '@mui/material'; + +interface singleInstallationProps { + current_installation?: I_Installation; + type?: string; +} + +function SodioHomeInstallation(props: singleInstallationProps) { + // const context = useContext(UserContext); + // const { currentUser } = context; + // const location = useLocation().pathname; + // const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false); + // const [currentTab, setCurrentTab] = useState(undefined); + // const [values, setValues] = useState(null); + // const status = props.current_installation.status; + // const [ + // failedToCommunicateWithInstallation, + // setFailedToCommunicateWithInstallation + // ] = useState(0); + // const [connected, setConnected] = useState(true); + // const [loading, setLoading] = useState(true); + // + // if (props.current_installation == undefined) { + // return null; + // } + // + // const S3data = { + // s3Region: props.current_installation.s3Region, + // s3Provider: props.current_installation.s3Provider, + // s3Key: props.current_installation.s3Key, + // s3Secret: props.current_installation.s3Secret, + // s3BucketId: props.current_installation.s3BucketId + // }; + // + // const s3Bucket = + // props.current_installation.s3BucketId.toString() + + // '-' + + // 'c0436b6a-d276-4cd8-9c44-1eae86cf5d0e'; + // + // const [fetchFunctionCalled, setFetchFunctionCalled] = useState(false); + // const s3Credentials = { s3Bucket, ...S3data }; + // + // function timeout(delay: number) { + // return new Promise((res) => setTimeout(res, delay)); + // } + // + // const continueFetching = useRef(false); + // + // const fetchDataPeriodically = async () => { + // var timeperiodToSearch = 30; + // let res; + // let timestampToFetch; + // + // for (var i = 0; i < timeperiodToSearch; i += 1) { + // if (!continueFetching.current) { + // return false; + // } + // timestampToFetch = UnixTime.now().earlier(TimeSpan.fromMinutes(i)); + // console.log('timestamp to fetch is ' + timestampToFetch); + // + // try { + // res = await fetchData(timestampToFetch, s3Credentials, true); + // if (res !== FetchResult.notAvailable && res !== FetchResult.tryLater) { + // break; + // } + // } catch (err) { + // console.error('Error fetching data:', err); + // return false; + // } + // } + // + // if (i >= timeperiodToSearch) { + // setConnected(false); + // setLoading(false); + // return false; + // } + // setConnected(true); + // setLoading(false); + // console.log('NUMBER OF FILES=' + Object.keys(res).length); + // + // while (continueFetching.current) { + // for (const timestamp of Object.keys(res)) { + // if (!continueFetching.current) { + // setFetchFunctionCalled(false); + // return false; + // } + // console.log(`Timestamp: ${timestamp}`); + // console.log(res[timestamp]); + // + // // Set values asynchronously with delay + // setValues( + // extractValues({ + // time: UnixTime.fromTicks(parseInt(timestamp, 10)), + // value: res[timestamp] + // }) + // ); + // // Wait for 2 seconds before processing next timestamp + // await timeout(2000); + // } + // + // timestampToFetch = timestampToFetch.later(TimeSpan.fromMinutes(20)); + // console.log('NEW TIMESTAMP TO FETCH IS ' + timestampToFetch); + // + // for (i = 0; i < 10; i++) { + // if (!continueFetching.current) { + // return false; + // } + // + // try { + // console.log('Trying to fetch timestamp ' + timestampToFetch); + // res = await fetchData(timestampToFetch, s3Credentials, true); + // if ( + // res !== FetchResult.notAvailable && + // res !== FetchResult.tryLater + // ) { + // break; + // } + // } catch (err) { + // console.error('Error fetching data:', err); + // return false; + // } + // timestampToFetch = timestampToFetch.later(TimeSpan.fromMinutes(1)); + // } + // } + // }; + // useEffect(() => { + // let path = location.split('/'); + // setCurrentTab(path[path.length - 1]); + // }, [location]); + // + // useEffect(() => { + // if (location.includes('batteryview')) { + // if (location.includes('batteryview') && !location.includes('mainstats')) { + // if (!continueFetching.current) { + // continueFetching.current = true; + // if (!fetchFunctionCalled) { + // setFetchFunctionCalled(true); + // fetchDataPeriodically(); + // } + // } + // } + // + // return () => { + // continueFetching.current = false; + // }; + // } else { + // continueFetching.current = false; + // } + // }, [currentTab, location]); + // + // useEffect(() => { + // if (status === null) { + // setConnected(false); + // } + // }, [status]); + // + + return ; + + // return ( + // <> + // + //
+ // + // + // + // + // {props.current_installation.name} + // + //
+ //
+ // + // Status: + // + //
+ // {status === -1 ? ( + // + // ) : ( + // '' + // )} + // + // {status === -2 ? ( + // + // ) : ( + // '' + // )} + // + //
+ // + // {props.current_installation.testingMode && ( + // + // )} + //
+ //
+ // {loading && + // currentTab != 'information' && + // currentTab != 'overview' && + // currentTab != 'manage' && + // currentTab != 'history' && + // currentTab != 'log' && ( + // + // + // + // Connecting to the device... + // + // + // )} + // + // + // + // + // + // } + // /> + // + // + // } + // /> + // + // + // } + // /> + // + // + // } + // > + // + // {currentUser.userType == UserType.admin && ( + // + // } + // /> + // )} + // + // {currentUser.userType == UserType.admin && ( + // + // + // + // } + // /> + // )} + // + // } + // /> + // + // + // + // + // + // ); +} + +export default SodioHomeInstallation; diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/InstallationSearch.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/InstallationSearch.tsx new file mode 100644 index 000000000..62ca20dc5 --- /dev/null +++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/InstallationSearch.tsx @@ -0,0 +1,100 @@ +import React, { useMemo, useState } from 'react'; +import { FormControl, Grid, InputAdornment, TextField } from '@mui/material'; +import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone'; +import { I_Installation } from '../../../interfaces/InstallationTypes'; +import { useLocation } from 'react-router-dom'; +import routes from '../../../Resources/routes.json'; + +interface installationSearchProps { + installations: I_Installation[]; +} + +function InstallationSearch(props: installationSearchProps) { + const [searchTerm, setSearchTerm] = useState(''); + const currentLocation = useLocation(); + // const [filteredData, setFilteredData] = useState(props.installations); + + const indexedData = useMemo(() => { + return props.installations.map((item) => ({ + ...item, + nameLower: item.name.toLowerCase(), + locationLower: item.location.toLowerCase(), + regionLower: item.region.toLowerCase() + })); + }, [props.installations]); + + const filteredData = useMemo(() => { + return indexedData.filter( + (item) => + item.nameLower.includes(searchTerm.toLowerCase()) || + item.locationLower.includes(searchTerm.toLowerCase()) || + item.regionLower.includes(searchTerm.toLowerCase()) + ); + }, [searchTerm, indexedData]); + + return ( + <> + + +
+ + setSearchTerm(e.target.value)} + fullWidth + InputProps={{ + startAdornment: ( + + + + ) + }} + /> + +
+
+
+ + {/**/} + {/**/} + {/* {filteredData.map((installation) => {*/} + {/* return (*/} + {/* */} + {/* }*/} + {/* />*/} + {/* );*/} + {/* })}*/} + {/**/} + + ); +} + +export default InstallationSearch; diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx new file mode 100644 index 000000000..c9f4ac837 --- /dev/null +++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx @@ -0,0 +1,396 @@ +import React, { ChangeEvent, useContext, useEffect, useState } from 'react'; +import { Box, Card, Container, Grid, Tab, Tabs } from '@mui/material'; +import { TabsContainerWrapper } from 'src/layouts/TabsContainerWrapper'; +import { Link, Navigate, Route, Routes, useLocation } from 'react-router-dom'; +import routes from 'src/Resources/routes.json'; +import InstallationSearch from './InstallationSearch'; +import { FormattedMessage } from 'react-intl'; +import { UserContext } from '../../../contexts/userContext'; +import { InstallationsContext } from '../../../contexts/InstallationsContextProvider'; +import ListIcon from '@mui/icons-material/List'; +import AccountTreeIcon from '@mui/icons-material/AccountTree'; +import TreeView from '../Tree/treeView'; +import { ProductIdContext } from '../../../contexts/ProductIdContextProvider'; +import { UserType } from '../../../interfaces/UserTypes'; +import SodioHomeInstallation from './Installation'; + +function SodioHomeInstallationTabs() { + const location = useLocation(); + const context = useContext(UserContext); + const { currentUser } = context; + const tabList = [ + 'batteryview', + 'information', + 'manage', + 'overview', + 'log', + 'history' + ]; + + const [currentTab, setCurrentTab] = useState(undefined); + const [fetchedInstallations, setFetchedInstallations] = + useState(false); + const { + sodiohomeInstallations, + fetchAllSodiohomeInstallations, + currentProduct, + socket, + openSocket, + closeSocket + } = useContext(InstallationsContext); + const { product, setProduct } = useContext(ProductIdContext); + + // const webSocketsContext = useContext(WebSocketContext); + // const { socket, openSocket, closeSocket } = webSocketsContext; + + useEffect(() => { + let path = location.pathname.split('/'); + + if (path[path.length - 2] === 'list') { + setCurrentTab('list'); + } else if (path[path.length - 2] === 'tree') { + setCurrentTab('tree'); + } else { + //Even if we are located at path: /batteryview/mainstats, we want the BatteryView tab to be bold + setCurrentTab(path.find((pathElement) => tabList.includes(pathElement))); + } + }, [location]); + + useEffect(() => { + if (sodiohomeInstallations.length === 0 && fetchedInstallations === false) { + fetchAllSodiohomeInstallations(); + setFetchedInstallations(true); + } + }, [sodiohomeInstallations]); + + useEffect(() => { + if (sodiohomeInstallations && sodiohomeInstallations.length > 0) { + if (!socket) { + openSocket(1); + } else if (currentProduct == 0) { + closeSocket(); + openSocket(1); + } + } + }, [sodiohomeInstallations]); + + useEffect(() => { + setProduct(1); + }, []); + + const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => { + setCurrentTab(value); + }; + + const navigateToTabPath = (pathname: string, tab_value: string): string => { + let pathlist = pathname.split('/'); + let ret_path = ''; + for (let i = 1; i < pathlist.length; i++) { + if (Number.isNaN(Number(pathlist[i]))) { + ret_path += '/'; + ret_path += pathlist[i]; + } else { + ret_path += '/'; + ret_path += pathlist[i]; + ret_path += '/'; + break; + } + } + + ret_path += tab_value; + return ret_path; + }; + + const singleInstallationTabs = + currentUser.userType == UserType.admin + ? [ + { + value: 'batteryview', + label: ( + + ) + }, + { + value: 'overview', + label: + }, + { + value: 'log', + label: + }, + + { + value: 'manage', + label: ( + + ) + }, + + { + value: 'information', + label: ( + + ) + }, + { + value: 'history', + label: ( + + ) + } + ] + : [ + { + value: 'batteryview', + label: ( + + ) + }, + { + value: 'overview', + label: + }, + + { + value: 'information', + label: ( + + ) + } + ]; + + const tabs = + currentTab != 'list' && + currentTab != 'tree' && + !location.pathname.includes('folder') && + currentUser.userType == UserType.admin + ? [ + { + value: 'list', + icon: + }, + { + value: 'tree', + icon: + }, + { + value: 'batteryview', + label: ( + + ) + }, + { + value: 'overview', + label: + }, + { + value: 'log', + label: + }, + + { + value: 'manage', + label: ( + + ) + }, + + { + value: 'information', + label: ( + + ) + }, + { + value: 'history', + label: ( + + ) + } + ] + : currentTab != 'list' && + currentTab != 'tree' && + !location.pathname.includes('folder') && + currentUser.userType == UserType.client + ? [ + { + value: 'list', + icon: + }, + { + value: 'tree', + icon: + }, + { + value: 'batteryview', + label: ( + + ) + }, + { + value: 'overview', + label: + }, + + { + value: 'information', + label: ( + + ) + } + ] + : [ + { + value: 'list', + icon: + }, + { + value: 'tree', + icon: + } + ]; + + return sodiohomeInstallations.length > 1 ? ( + <> + + + + {tabs.map((tab) => ( + + ))} + + + + + + + + + + + } + /> + + } /> + + + } + > + + + + + + ) : sodiohomeInstallations.length === 1 ? ( + <> + {' '} + + + + {singleInstallationTabs.map((tab) => ( + + ))} + + + + + + + + + + + } + /> + + + + + + ) : null; +} + +export default SodioHomeInstallationTabs;