Inserted folder view in backend-frontend

Clients can have access to both products
Apply 20min chunk logic for Salidomo installations
This commit is contained in:
Noe 2024-08-12 09:48:16 +02:00
parent 2d3af976c1
commit 7db44785be
36 changed files with 894 additions and 359 deletions

View File

@ -41,10 +41,14 @@ public class Controller : ControllerBase
throw new Exceptions(401, "Wrong Password Exception", "Please try again.", Request.Path.Value!); throw new Exceptions(401, "Wrong Password Exception", "Please try again.", Request.Path.Value!);
} }
var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user)); var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user));
//TODO The Frontend should check for the MustResetPassword Flag //TODO The Frontend should check for the MustResetPassword Flag
return Db.Create(session) return Db.Create(session)
? session ? session
: throw new Exceptions(401,"Session Creation Exception", "Not allowed to log in.", Request.Path.Value!); : throw new Exceptions(401,"Session Creation Exception", "Not allowed to log in.", Request.Path.Value!);
@ -472,15 +476,16 @@ public class Controller : ControllerBase
[HttpGet(nameof(GetAllFoldersAndInstallations))] [HttpGet(nameof(GetAllFoldersAndInstallations))]
public ActionResult<IEnumerable<Object>> GetAllFoldersAndInstallations(Token authToken) public ActionResult<IEnumerable<Object>> GetAllFoldersAndInstallations(int productId, Token authToken)
{ {
var user = Db.GetSession(authToken)?.User; var user = Db.GetSession(authToken)?.User;
if (user is null) if (user is null)
return Unauthorized(); return Unauthorized();
var foldersAndInstallations = user var foldersAndInstallations = user
.AccessibleFoldersAndInstallations(product:0) .AccessibleFoldersAndInstallations(product:productId)
.Do(o => o.FillOrderNumbers()) .Do(o => o.FillOrderNumbers())
.Select(o => o.HideParentIfUserHasNoAccessToParent(user)) .Select(o => o.HideParentIfUserHasNoAccessToParent(user))
.OfType<Object>(); // Important! JSON serializer must see Objects otherwise .OfType<Object>(); // Important! JSON serializer must see Objects otherwise

View File

@ -1,3 +1,3 @@
namespace InnovEnergy.App.Backend.DataTypes; namespace InnovEnergy.App.Backend.DataTypes;
public class Folder : TreeNode {} public class Folder : TreeNode { }

View File

@ -143,7 +143,7 @@ public static class ExoCmd
{ {
const String url = "https://api-ch-dk-2.exoscale.com/v2/iam-role"; const String url = "https://api-ch-dk-2.exoscale.com/v2/iam-role";
const String method = "iam-role"; const String method = "iam-role";
String rolename = installation.Product==0?Db.Installations.Count(f => f.Product == 0) + installation.InstallationName:Db.Installations.Count(f => f.Product == 1) + installation.InstallationName; String rolename = installation.Product==0?Db.Installations.Count(f => f.Product == 0) + installation.Name:Db.Installations.Count(f => f.Product == 1) + installation.Name;
var contentString = $$""" var contentString = $$"""
@ -245,7 +245,7 @@ public static class ExoCmd
{ {
const String url = "https://api-ch-dk-2.exoscale.com/v2/iam-role"; const String url = "https://api-ch-dk-2.exoscale.com/v2/iam-role";
const String method = "iam-role"; const String method = "iam-role";
String rolename = installation.Product==0?Db.Installations.Count(f => f.Product == 0) + installation.InstallationName:Db.Installations.Count(f => f.Product == 1) + installation.InstallationName; String rolename = installation.Product==0?Db.Installations.Count(f => f.Product == 0) + installation.Name:Db.Installations.Count(f => f.Product == 1) + installation.Name;
var contentString = $$""" var contentString = $$"""
{ {

View File

@ -41,9 +41,9 @@ public static class FolderMethods
public static IEnumerable<Folder> UniqueChildFolders(this Folder parent) public static IEnumerable<Folder> UniqueChildFolders(this Folder parent)
{ {
var set = new HashSet<Folder>(Db.Folders, EqualityComparer<Folder>.Default); //var set = new HashSet<Folder>(Db.Folders, EqualityComparer<Folder>.Default);
return ChildFolders(parent).Where(set.Add); return ChildFolders(parent);
} }
public static IEnumerable<Installation> ChildInstallations(this Folder parent) public static IEnumerable<Installation> ChildInstallations(this Folder parent)
@ -56,6 +56,8 @@ public static class FolderMethods
public static IEnumerable<Folder> DescendantFolders(this Folder parent) public static IEnumerable<Folder> DescendantFolders(this Folder parent)
{ {
Console.WriteLine("Parent is "+parent.Id+" looking for descendant folders");
return parent return parent
.TraverseDepthFirstPreOrder(UniqueChildFolders) .TraverseDepthFirstPreOrder(UniqueChildFolders)
.Skip(1); // skip self .Skip(1); // skip self

View File

@ -56,6 +56,7 @@ public static class UserMethods
public static IEnumerable<Folder> DirectlyAccessibleFolders(this User user) public static IEnumerable<Folder> DirectlyAccessibleFolders(this User user)
{ {
return Db return Db
.FolderAccess .FolderAccess
.Where(r => r.UserId == user.Id) .Where(r => r.UserId == user.Id)

View File

@ -6,11 +6,11 @@ public class User : TreeNode
{ {
[Unique] [Unique]
public String Email { get; set; } = null!; public String Email { get; set; } = null!;
public int UserType { get; set; } = 0; public int UserType { get; set; } = 0;
public Boolean MustResetPassword { get; set; } = false; public Boolean MustResetPassword { get; set; } = false;
public String Language { get; set; } = null!; public String Language { get; set; } = null!;
public String? Password { get; set; } = null!; public String? Password { get; set; } = null!;
[Unique] [Unique]
public override String Name { get; set; } = null!; public override String Name { get; set; } = null!;

View File

@ -0,0 +1,10 @@
namespace InnovEnergy.App.Backend.DataTypes;
public class WebsocketMessage
{
public int id { get; set; }
public int status { get; set; }
public Boolean testingMode { get; set; }
}

View File

@ -21,7 +21,8 @@ public static class Program
RabbitMqManager.InitializeEnvironment(); RabbitMqManager.InitializeEnvironment();
RabbitMqManager.StartRabbitMqConsumer(); RabbitMqManager.StartRabbitMqConsumer();
WebsocketManager.MonitorInstallationTable(); WebsocketManager.MonitorSalimaxInstallationTable();
WebsocketManager.MonitorSalidomoInstallationTable();
builder.Services.AddControllers(); builder.Services.AddControllers();

View File

@ -1,5 +1,6 @@
using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.DataTypes; using InnovEnergy.App.Backend.DataTypes;
using InnovEnergy.App.Backend.DataTypes.Methods;
using SQLite; using SQLite;
namespace InnovEnergy.App.Backend.Relations; namespace InnovEnergy.App.Backend.Relations;
@ -11,7 +12,8 @@ public class Session : Relation<String, Int64>
[Unique ] public String Token { get => Left ; init => Left = value;} [Unique ] public String Token { get => Left ; init => Left = value;}
[Indexed] public Int64 UserId { get => Right; init => Right = value;} [Indexed] public Int64 UserId { get => Right; init => Right = value;}
[Indexed] public DateTime LastSeen { get; set; } [Indexed] public DateTime LastSeen { get; set; }
public Boolean AccessToSalimax { get; set; } = false;
public Boolean AccessToSalidomo { get; set; } = false;
[Ignore] public Boolean Valid => DateTime.Now - LastSeen < MaxAge [Ignore] public Boolean Valid => DateTime.Now - LastSeen < MaxAge
&& (User) is not null; && (User) is not null;
@ -30,6 +32,9 @@ public class Session : Relation<String, Int64>
Token = CreateToken(); Token = CreateToken();
UserId = user.Id; UserId = user.Id;
LastSeen = DateTime.Now; LastSeen = DateTime.Now;
AccessToSalimax = user.AccessibleInstallations(product: 0).ToList().Count > 0;
AccessToSalidomo = user.AccessibleInstallations(product: 1).ToList().Count > 0;
} }
private static String CreateToken() private static String CreateToken()

View File

@ -3,7 +3,8 @@ namespace InnovEnergy.App.Backend.Websockets;
public class InstallationInfo public class InstallationInfo
{ {
public int Status { get; set; } public int Status { get; set; }
public DateTime Timestamp { get; set; } public DateTime Timestamp { get; set; }
public int Product { get; set; }
public List<WebSocket> Connections { get; } = new List<WebSocket>(); public List<WebSocket> Connections { get; } = new List<WebSocket>();
} }

View File

@ -158,7 +158,8 @@ public static class RabbitMqManager
WebsocketManager.InstallationConnections[installationId] = new InstallationInfo WebsocketManager.InstallationConnections[installationId] = new InstallationInfo
{ {
Status = receivedStatusMessage.Status, Status = receivedStatusMessage.Status,
Timestamp = DateTime.Now Timestamp = DateTime.Now,
Product = installation.Product
}; };
} }
else else

View File

@ -4,6 +4,7 @@ using System.Net.WebSockets;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.DataTypes;
namespace InnovEnergy.App.Backend.Websockets; namespace InnovEnergy.App.Backend.Websockets;
@ -13,12 +14,12 @@ public static class WebsocketManager
//Every 2 minutes, check the timestamp of the latest received message for every installation. //Every 2 minutes, check the timestamp of the latest received message for every installation.
//If the difference between the two timestamps is more than two minutes, we consider this installation unavailable. //If the difference between the two timestamps is more than two minutes, we consider this installation unavailable.
public static async Task MonitorInstallationTable() public static async Task MonitorSalimaxInstallationTable()
{ {
while (true){ while (true){
lock (InstallationConnections){ lock (InstallationConnections){
foreach (var installationConnection in InstallationConnections){ foreach (var installationConnection in InstallationConnections){
if ((DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(1)){ if (installationConnection.Value.Product==0 && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(1)){
installationConnection.Value.Status = -1; installationConnection.Value.Status = -1;
if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);} if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);}
} }
@ -28,6 +29,21 @@ public static class WebsocketManager
} }
} }
public static async Task MonitorSalidomoInstallationTable()
{
while (true){
lock (InstallationConnections){
foreach (var installationConnection in InstallationConnections){
if (installationConnection.Value.Product==1 && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(20)){
installationConnection.Value.Status = -1;
if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);}
}
}
}
await Task.Delay(TimeSpan.FromMinutes(30));
}
}
//Inform all the connected websockets regarding installation "installationId" //Inform all the connected websockets regarding installation "installationId"
public static void InformWebsocketsForInstallation(Int64 installationId) public static void InformWebsocketsForInstallation(Int64 installationId)
{ {
@ -35,7 +51,6 @@ public static class WebsocketManager
var installationConnection = InstallationConnections[installationId]; var installationConnection = InstallationConnections[installationId];
Console.WriteLine("Update all the connected websockets for installation " + installationId); Console.WriteLine("Update all the connected websockets for installation " + installationId);
var jsonObject = new var jsonObject = new
{ {
id = installationId, id = installationId,
@ -96,6 +111,9 @@ public static class WebsocketManager
//Console.WriteLine("Received a new message from websocket"); //Console.WriteLine("Received a new message from websocket");
lock (InstallationConnections) lock (InstallationConnections)
{ {
List<WebsocketMessage> dataToSend = new List<WebsocketMessage>();
//Each front-end will send the list of the installations it wants to access //Each front-end will send the list of the installations it wants to access
//If this is a new key (installation id), initialize the list for this key and then add the websocket object for this client //If this is a new key (installation id), initialize the list for this key and then add the websocket object for this client
//Then, report the status of each requested installation to the front-end that created the websocket connection //Then, report the status of each requested installation to the front-end that created the websocket connection
@ -108,29 +126,42 @@ public static class WebsocketManager
//Console.WriteLine("Create new empty list for installation id " + installationId); //Console.WriteLine("Create new empty list for installation id " + installationId);
InstallationConnections[installationId] = new InstallationInfo InstallationConnections[installationId] = new InstallationInfo
{ {
Status = -1 Status = -1,
Product = installation.Product
}; };
} }
InstallationConnections[installationId].Connections.Add(currentWebSocket); InstallationConnections[installationId].Connections.Add(currentWebSocket);
var jsonObject = new var jsonObject = new WebsocketMessage
{ {
id = installationId, id = installationId,
status = InstallationConnections[installationId].Status, status = InstallationConnections[installationId].Status,
testingMode = installation.TestingMode testingMode = installation.TestingMode
}; };
var jsonString = JsonSerializer.Serialize(jsonObject); dataToSend.Add(jsonObject);
var dataToSend = Encoding.UTF8.GetBytes(jsonString);
//var jsonString = JsonSerializer.Serialize(jsonObject);
//var dataToSend = Encoding.UTF8.GetBytes(jsonString);
currentWebSocket.SendAsync(dataToSend, // currentWebSocket.SendAsync(dataToSend,
WebSocketMessageType.Text, // WebSocketMessageType.Text,
true, // Indicates that this is the end of the message // true, // Indicates that this is the end of the message
CancellationToken.None // CancellationToken.None
); // );
} }
var jsonString = JsonSerializer.Serialize(dataToSend);
var encodedDataToSend = Encoding.UTF8.GetBytes(jsonString);
currentWebSocket.SendAsync(encodedDataToSend,
WebSocketMessageType.Text,
true, // Indicates that this is the end of the message
CancellationToken.None
);
Console.WriteLine("Printing installation connection list"); Console.WriteLine("Printing installation connection list");
Console.WriteLine("----------------------------------------------"); Console.WriteLine("----------------------------------------------");

View File

@ -10,4 +10,5 @@ Salimax0006 ie-entwicklung@10.2.4.35 Steakhouse Mettmenstetten
Salimax0007 ie-entwicklung@10.2.4.154 LerchenhofHerr Twannberg Salimax0007 ie-entwicklung@10.2.4.154 LerchenhofHerr Twannberg
Salimax0008 ie-entwicklung@10.2.4.113 Wittmann Kottingbrunn Salimax0008 ie-entwicklung@10.2.4.113 Wittmann Kottingbrunn
Salimax0010 ie-entwicklung@10.2.4.211 Mohatech 1 (Beat Moser) Salimax0010 ie-entwicklung@10.2.4.211 Mohatech 1 (Beat Moser)
Salimax0011 ie-entwicklung@10.2.4.239 Thomas Tschirren (Enggistein)
SalidomoServer ig@134.209.238.170 SalidomoServer ig@134.209.238.170

View File

@ -54,6 +54,6 @@ INNOVENERGY_PROTOCOL_VERSION = '48TL200V3'
# S3 Credentials # S3 Credentials
S3BUCKET = "114-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e" S3BUCKET = "91-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
S3KEY = "EXO5b9198dc11544f42b44e1180" S3KEY = "EXOe6dce12288f11a676c2025a1"
S3SECRET = "ga-mD3SYZMJUfjksmXPKAHQhVkxPZYv57jC1oD_mkC0" S3SECRET = "xpqM4Eh0Gg1HaYVkzlR9X6PwYa-QNb-mVk0XUkwW3cc"

View File

@ -24,6 +24,7 @@ import { useNavigate } from 'react-router-dom';
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
import CheckBoxIcon from '@mui/icons-material/CheckBox'; import CheckBoxIcon from '@mui/icons-material/CheckBox';
import routes from 'src/Resources/routes.json'; import routes from 'src/Resources/routes.json';
import { ProductIdContext } from '../contexts/ProductIdContextProvider';
function Login() { function Login() {
const [username, setUsername] = useState(''); const [username, setUsername] = useState('');
@ -35,6 +36,8 @@ function Login() {
const theme = useTheme(); const theme = useTheme();
const context = useContext(UserContext); const context = useContext(UserContext);
const { setAccessToSalimax, setAccessToSalidomo } =
useContext(ProductIdContext);
const navigate = useNavigate(); const navigate = useNavigate();
if (!context) { if (!context) {
@ -73,11 +76,18 @@ function Login() {
setNewToken(response.data.token); setNewToken(response.data.token);
setUser(response.data.user); setUser(response.data.user);
setAccessToSalimax(response.data.accessToSalimax);
setAccessToSalidomo(response.data.accessToSalidomo);
if (rememberMe) { if (rememberMe) {
cookies.set('rememberedUsername', username, { path: '/' }); cookies.set('rememberedUsername', username, { path: '/' });
cookies.set('rememberedPassword', password, { path: '/' }); cookies.set('rememberedPassword', password, { path: '/' });
} }
navigate(routes.installations); if (response.data.accessToSalimax) {
navigate(routes.installations);
} else {
navigate(routes.salidomo_installations);
}
} }
}) })
.catch((error) => { .catch((error) => {

View File

@ -582,6 +582,7 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
<MenuItem disabled value=""> <MenuItem disabled value="">
Select Firmware Version Select Firmware Version
</MenuItem> </MenuItem>
<MenuItem value="AF09">AF09</MenuItem>
<MenuItem value="AF11">AF11</MenuItem> <MenuItem value="AF11">AF11</MenuItem>
<MenuItem value="AF0A">AF0A</MenuItem> <MenuItem value="AF0A">AF0A</MenuItem>
</Select> </Select>

View File

@ -200,7 +200,7 @@ function InformationSalidomo(props: InformationSalidomoProps) {
/> />
} }
name="installationName" name="installationName"
value={formValues.installationName} value={formValues.name}
onChange={handleChange} onChange={handleChange}
variant="outlined" variant="outlined"
fullWidth fullWidth

View File

@ -379,6 +379,7 @@ function Installation(props: singleInstallationProps) {
{loading && {loading &&
currentTab != 'information' && currentTab != 'information' &&
currentTab != 'history' && currentTab != 'history' &&
currentTab != 'manage' &&
currentTab != 'overview' && currentTab != 'overview' &&
currentTab != 'log' && ( currentTab != 'log' && (
<Container <Container

View File

@ -53,9 +53,12 @@ export const fetchAggregatedData = (
export const fetchData = ( export const fetchData = (
timestamp: UnixTime, timestamp: UnixTime,
s3Credentials?: I_S3Credentials s3Credentials?: I_S3Credentials,
cutdigits?: boolean
): Promise<FetchResult<Record<string, DataRecord>>> => { ): Promise<FetchResult<Record<string, DataRecord>>> => {
const s3Path = `${timestamp.ticks}.csv`; const s3Path = cutdigits
? `${timestamp.ticks.toString().slice(0, -2)}.csv`
: `${timestamp.ticks}.csv`;
if (s3Credentials && s3Credentials.s3Bucket) { if (s3Credentials && s3Credentials.s3Bucket) {
const s3Access = new S3Access( const s3Access = new S3Access(
s3Credentials.s3Bucket, s3Credentials.s3Bucket,
@ -71,10 +74,14 @@ export const fetchData = (
if (r.status === 404) { if (r.status === 404) {
return Promise.resolve(FetchResult.notAvailable); return Promise.resolve(FetchResult.notAvailable);
} else if (r.status === 200) { } else if (r.status === 200) {
console.log('FOUND ITTTTTTTTTTTT');
const csvtext = await r.text(); // Assuming the server returns the Base64 encoded ZIP file as text const csvtext = await r.text(); // Assuming the server returns the Base64 encoded ZIP file as text
const contentEncoding = r.headers.get('content-type'); const contentEncoding = r.headers.get('content-type');
console.log(contentEncoding);
if (contentEncoding != 'application/base64; charset=utf-8') { if (contentEncoding != 'application/base64; charset=utf-8') {
console.log('uncompressed');
return parseChunk(csvtext); return parseChunk(csvtext);
} }
@ -87,6 +94,8 @@ export const fetchData = (
// Assuming the CSV file is named "data.csv" inside the ZIP archive // Assuming the CSV file is named "data.csv" inside the ZIP archive
const csvContent = await zip.file('data.csv').async('text'); const csvContent = await zip.file('data.csv').async('text');
console.log(csvContent);
return parseChunk(csvContent); return parseChunk(csvContent);
} else { } else {
return Promise.resolve(FetchResult.notAvailable); return Promise.resolve(FetchResult.notAvailable);

View File

@ -14,6 +14,7 @@ import { InstallationsContext } from '../../../contexts/InstallationsContextProv
import Installation from './Installation'; import Installation from './Installation';
import { WebSocketContext } from '../../../contexts/WebSocketContextProvider'; import { WebSocketContext } from '../../../contexts/WebSocketContextProvider';
import { UserType } from '../../../interfaces/UserTypes'; import { UserType } from '../../../interfaces/UserTypes';
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
function InstallationTabs() { function InstallationTabs() {
const location = useLocation(); const location = useLocation();
@ -35,6 +36,8 @@ function InstallationTabs() {
const { salimaxInstallations, fetchAllInstallations } = const { salimaxInstallations, fetchAllInstallations } =
useContext(InstallationsContext); useContext(InstallationsContext);
const { product, setProduct } = useContext(ProductIdContext);
const webSocketsContext = useContext(WebSocketContext); const webSocketsContext = useContext(WebSocketContext);
const { socket, openSocket, closeSocket } = webSocketsContext; const { socket, openSocket, closeSocket } = webSocketsContext;
@ -67,6 +70,10 @@ function InstallationTabs() {
} }
}, [salimaxInstallations]); }, [salimaxInstallations]);
useEffect(() => {
setProduct(0);
}, []);
const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => { const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {
setCurrentTab(value); setCurrentTab(value);
}; };

View File

@ -1291,6 +1291,140 @@ function Overview(props: OverviewProps) {
</Card> </Card>
</Grid> </Grid>
</Grid> </Grid>
<Grid
container
direction="row"
justifyContent="center"
alignItems="stretch"
spacing={3}
>
<Grid item md={6} xs={12}>
<Card
sx={{
overflow: 'visible',
marginTop: '30px',
marginBottom: '30px'
}}
>
<Box
sx={{
marginLeft: '20px'
}}
>
<Box display="flex" alignItems="center">
<Box>
<Typography variant="subtitle1" noWrap>
<FormattedMessage
id="ac_load"
defaultMessage="AC Load"
/>
</Typography>
</Box>
</Box>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
pt: 3
}}
></Box>
</Box>
{dailyData && (
<ReactApexChart
options={{
...getChartOptions(
dailyDataArray[chartState].chartOverview.ACLoad,
'daily',
[],
true
),
chart: {
events: {
beforeZoom: (chartContext, options) => {
startZoom();
handleBeforeZoom(chartContext, options);
}
}
}
}}
series={[
{
...dailyDataArray[chartState].chartData.ACLoad,
color: '#ff9900'
}
]}
type="line"
height={400}
/>
)}
</Card>
</Grid>
<Grid item md={6} xs={12}>
<Card
sx={{
overflow: 'visible',
marginTop: '30px',
marginBottom: '30px'
}}
>
<Box
sx={{
marginLeft: '20px'
}}
>
<Box display="flex" alignItems="center">
<Box>
<Typography variant="subtitle1" noWrap>
<FormattedMessage
id="dc_load"
defaultMessage="DC Load"
/>
</Typography>
</Box>
</Box>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
pt: 3
}}
></Box>
</Box>
{dailyData && (
<ReactApexChart
options={{
...getChartOptions(
dailyDataArray[chartState].chartOverview.DCLoad,
'daily',
[],
true
),
chart: {
events: {
beforeZoom: (chartContext, options) => {
startZoom();
handleBeforeZoom(chartContext, options);
}
}
}
}}
series={[
{
...dailyDataArray[chartState].chartData.DCLoad,
color: '#ff3333'
}
]}
type="line"
height={400}
/>
)}
</Card>
</Grid>
</Grid>
</Grid> </Grid>
)} )}
</Grid> </Grid>

View File

@ -157,7 +157,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
noWrap noWrap
sx={{ marginTop: '10px', fontSize: 'small' }} sx={{ marginTop: '10px', fontSize: 'small' }}
> >
{installation.installationName} {installation.name}
</Typography> </Typography>
</TableCell> </TableCell>

View File

@ -27,13 +27,15 @@ import SalidomoOverview from '../Overview/salidomoOverview';
import { UserType } from '../../../interfaces/UserTypes'; import { UserType } from '../../../interfaces/UserTypes';
import HistoryOfActions from '../History/History'; import HistoryOfActions from '../History/History';
import BuildIcon from '@mui/icons-material/Build'; import BuildIcon from '@mui/icons-material/Build';
import AccessContextProvider from '../../../contexts/AccessContextProvider';
import Access from '../ManageAccess/Access';
interface singleInstallationProps { interface singleInstallationProps {
current_installation?: I_Installation; current_installation?: I_Installation;
type?: string; type?: string;
} }
function Installation(props: singleInstallationProps) { function SalidomoInstallation(props: singleInstallationProps) {
const context = useContext(UserContext); const context = useContext(UserContext);
const { currentUser } = context; const { currentUser } = context;
const location = useLocation().pathname; const location = useLocation().pathname;
@ -77,18 +79,19 @@ function Installation(props: singleInstallationProps) {
const continueFetching = useRef(false); const continueFetching = useRef(false);
const fetchDataPeriodically = async () => { const fetchDataPeriodically = async () => {
var timeperiodToSearch = 90; var timeperiodToSearch = 30;
let res; let res;
let timestampToFetch; let timestampToFetch;
for (var i = 0; i < timeperiodToSearch; i += 2) { for (var i = 0; i < timeperiodToSearch; i += 1) {
if (!continueFetching.current) { if (!continueFetching.current) {
return false; return false;
} }
timestampToFetch = UnixTime.now().earlier(TimeSpan.fromSeconds(i)); timestampToFetch = UnixTime.now().earlier(TimeSpan.fromMinutes(i));
console.log('timestamp to fetch is ' + timestampToFetch);
try { try {
res = await fetchData(timestampToFetch, s3Credentials); res = await fetchData(timestampToFetch, s3Credentials, true);
if (res !== FetchResult.notAvailable && res !== FetchResult.tryLater) { if (res !== FetchResult.notAvailable && res !== FetchResult.tryLater) {
break; break;
} }
@ -127,7 +130,7 @@ function Installation(props: singleInstallationProps) {
await timeout(2000); await timeout(2000);
} }
timestampToFetch = timestampToFetch.later(TimeSpan.fromSeconds(60)); timestampToFetch = timestampToFetch.later(TimeSpan.fromMinutes(20));
console.log('NEW TIMESTAMP TO FETCH IS ' + timestampToFetch); console.log('NEW TIMESTAMP TO FETCH IS ' + timestampToFetch);
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
@ -137,7 +140,7 @@ function Installation(props: singleInstallationProps) {
try { try {
console.log('Trying to fetch timestamp ' + timestampToFetch); console.log('Trying to fetch timestamp ' + timestampToFetch);
res = await fetchData(timestampToFetch, s3Credentials); res = await fetchData(timestampToFetch, s3Credentials, true);
if ( if (
res !== FetchResult.notAvailable && res !== FetchResult.notAvailable &&
res !== FetchResult.tryLater res !== FetchResult.tryLater
@ -148,7 +151,7 @@ function Installation(props: singleInstallationProps) {
console.error('Error fetching data:', err); console.error('Error fetching data:', err);
return false; return false;
} }
timestampToFetch = timestampToFetch.later(TimeSpan.fromSeconds(1)); timestampToFetch = timestampToFetch.later(TimeSpan.fromMinutes(1));
} }
} }
}; };
@ -213,7 +216,7 @@ function Installation(props: singleInstallationProps) {
fontSize: '14px' fontSize: '14px'
}} }}
> >
{props.current_installation.installationName} {props.current_installation.name}
</Typography> </Typography>
</div> </div>
<div style={{ display: 'flex', alignItems: 'center' }}> <div style={{ display: 'flex', alignItems: 'center' }}>
@ -296,6 +299,7 @@ function Installation(props: singleInstallationProps) {
{loading && {loading &&
currentTab != 'information' && currentTab != 'information' &&
currentTab != 'overview' && currentTab != 'overview' &&
currentTab != 'manage' &&
currentTab != 'history' && currentTab != 'history' &&
currentTab != 'log' && ( currentTab != 'log' && (
<Container <Container
@ -384,9 +388,23 @@ function Installation(props: singleInstallationProps) {
/> />
)} )}
{currentUser.userType == UserType.admin && (
<Route
path={routes.manage}
element={
<AccessContextProvider>
<Access
currentResource={props.current_installation}
resourceType={props.type}
></Access>
</AccessContextProvider>
}
/>
)}
<Route <Route
path={'*'} path={'*'}
element={<Navigate to={routes.information}></Navigate>} element={<Navigate to={routes.batteryview}></Navigate>}
/> />
</Routes> </Routes>
</Grid> </Grid>
@ -396,4 +414,4 @@ function Installation(props: singleInstallationProps) {
); );
} }
export default Installation; export default SalidomoInstallation;

View File

@ -5,10 +5,7 @@ import FlatInstallationView from './FlatInstallationView';
import { I_Installation } from '../../../interfaces/InstallationTypes'; import { I_Installation } from '../../../interfaces/InstallationTypes';
import { Route, Routes, useLocation } from 'react-router-dom'; import { Route, Routes, useLocation } from 'react-router-dom';
import routes from '../../../Resources/routes.json'; import routes from '../../../Resources/routes.json';
import Installation from './Installation'; import SalidomoInstallation from './Installation';
import { FormattedMessage } from 'react-intl';
import Button from '@mui/material/Button';
import SalidomonstallationForm from './SalidomoInstallationForm';
interface installationSearchProps { interface installationSearchProps {
installations: I_Installation[]; installations: I_Installation[];
@ -18,26 +15,11 @@ function InstallationSearch(props: installationSearchProps) {
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const currentLocation = useLocation(); const currentLocation = useLocation();
const [filteredData, setFilteredData] = useState(props.installations); const [filteredData, setFilteredData] = useState(props.installations);
const [openModalInstallation, setOpenModalInstallation] = useState(false);
const handleNewInstallationInsertion = () => {
setOpenModalInstallation(true);
};
const handleInstallationFormSubmit = () => {
setOpenModalInstallation(false);
};
const handleFormCancel = () => {
setOpenModalInstallation(false);
};
useEffect(() => { useEffect(() => {
const filtered = props.installations.filter( const filtered = props.installations.filter(
(item) => (item) =>
item.installationName item.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
.toLowerCase()
.includes(searchTerm.toLowerCase()) ||
item.location.toLowerCase().includes(searchTerm.toLowerCase()) || item.location.toLowerCase().includes(searchTerm.toLowerCase()) ||
item.region.toLowerCase().includes(searchTerm.toLowerCase()) item.region.toLowerCase().includes(searchTerm.toLowerCase())
); );
@ -46,12 +28,6 @@ function InstallationSearch(props: installationSearchProps) {
return ( return (
<> <>
{openModalInstallation && (
<SalidomonstallationForm
cancel={handleFormCancel}
submit={handleInstallationFormSubmit}
/>
)}
<Grid container> <Grid container>
<Grid <Grid
item item
@ -74,17 +50,6 @@ function InstallationSearch(props: installationSearchProps) {
alignItems: 'flex-start' alignItems: 'flex-start'
}} }}
> >
<Button
variant="contained"
onClick={handleNewInstallationInsertion}
sx={{ marginBottom: '8px' }}
>
<FormattedMessage
id="addNewInstallation"
defaultMessage="Add new installation"
/>
</Button>
<FormControl variant="outlined"> <FormControl variant="outlined">
<TextField <TextField
placeholder="Search" placeholder="Search"
@ -112,11 +77,11 @@ function InstallationSearch(props: installationSearchProps) {
key={installation.s3BucketId} key={installation.s3BucketId}
path={routes.installation + installation.s3BucketId + '*'} path={routes.installation + installation.s3BucketId + '*'}
element={ element={
<Installation <SalidomoInstallation
key={installation.s3BucketId} key={installation.s3BucketId}
current_installation={installation} current_installation={installation}
type="installation" type="installation"
></Installation> ></SalidomoInstallation>
} }
/> />
); );

View File

@ -21,26 +21,21 @@ import { FormattedMessage } from 'react-intl';
interface SalidomoInstallationFormProps { interface SalidomoInstallationFormProps {
cancel: () => void; cancel: () => void;
submit: () => void; submit: () => void;
parentid: number;
} }
function SalidomonstallationForm(props: SalidomoInstallationFormProps) { function SalidomonstallationForm(props: SalidomoInstallationFormProps) {
const theme = useTheme(); const theme = useTheme();
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
const [formValues, setFormValues] = useState<Partial<I_Installation>>({ const [formValues, setFormValues] = useState<Partial<I_Installation>>({
installationName: '', name: '',
region: '', region: '',
location: '', location: '',
country: '', country: '',
vpnIp: '', vpnIp: '',
vrmLink: '' vrmLink: ''
}); });
const requiredFields = [ const requiredFields = ['name', 'location', 'country', 'vpnIp', 'vrmLink'];
'installationName',
'location',
'country',
'vpnIp',
'vrmLink'
];
const DeviceTypes = ['Cerbo', 'Venus']; const DeviceTypes = ['Cerbo', 'Venus'];
const installationContext = useContext(InstallationsContext); const installationContext = useContext(InstallationsContext);
@ -57,7 +52,7 @@ function SalidomonstallationForm(props: SalidomoInstallationFormProps) {
}; };
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
setLoading(true); setLoading(true);
formValues.parentId = 1; formValues.parentId = props.parentid;
formValues.product = 1; formValues.product = 1;
const responseData = await createInstallation(formValues); const responseData = await createInstallation(formValues);
props.submit(); props.submit();
@ -120,11 +115,11 @@ function SalidomonstallationForm(props: SalidomoInstallationFormProps) {
defaultMessage="Installation Name" defaultMessage="Installation Name"
/> />
} }
name="installationName" name="name"
value={formValues.installationName} value={formValues.name}
onChange={handleChange} onChange={handleChange}
required required
error={formValues.installationName === ''} error={formValues.name === ''}
/> />
</div> </div>
<div> <div>

View File

@ -9,12 +9,24 @@ import { UserContext } from '../../../contexts/userContext';
import { InstallationsContext } from '../../../contexts/InstallationsContextProvider'; import { InstallationsContext } from '../../../contexts/InstallationsContextProvider';
import { WebSocketContext } from '../../../contexts/WebSocketContextProvider'; import { WebSocketContext } from '../../../contexts/WebSocketContextProvider';
import ListIcon from '@mui/icons-material/List'; 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 SalidomoInstallation from './Installation';
function SalidomoInstallationTabs() { function SalidomoInstallationTabs() {
const location = useLocation(); const location = useLocation();
const context = useContext(UserContext); const context = useContext(UserContext);
const { currentUser } = context; const { currentUser } = context;
const tabList = ['batteryview', 'information', 'overview', 'log', 'history']; const tabList = [
'batteryview',
'information',
'manage',
'overview',
'log',
'history'
];
const [currentTab, setCurrentTab] = useState<string>(undefined); const [currentTab, setCurrentTab] = useState<string>(undefined);
const [fetchedInstallations, setFetchedInstallations] = const [fetchedInstallations, setFetchedInstallations] =
@ -22,6 +34,8 @@ function SalidomoInstallationTabs() {
const { salidomoInstallations, fetchAllSalidomoInstallations } = const { salidomoInstallations, fetchAllSalidomoInstallations } =
useContext(InstallationsContext); useContext(InstallationsContext);
const { product, setProduct } = useContext(ProductIdContext);
const webSocketsContext = useContext(WebSocketContext); const webSocketsContext = useContext(WebSocketContext);
const { socket, openSocket, closeSocket } = webSocketsContext; const { socket, openSocket, closeSocket } = webSocketsContext;
@ -30,6 +44,8 @@ function SalidomoInstallationTabs() {
if (path[path.length - 2] === 'list') { if (path[path.length - 2] === 'list') {
setCurrentTab('list'); setCurrentTab('list');
} else if (path[path.length - 2] === 'tree') {
setCurrentTab('tree');
} else { } else {
//Even if we are located at path: /batteryview/mainstats, we want the BatteryView tab to be bold //Even if we are located at path: /batteryview/mainstats, we want the BatteryView tab to be bold
setCurrentTab(path.find((pathElement) => tabList.includes(pathElement))); setCurrentTab(path.find((pathElement) => tabList.includes(pathElement)));
@ -52,6 +68,10 @@ function SalidomoInstallationTabs() {
} }
}, [salidomoInstallations]); }, [salidomoInstallations]);
useEffect(() => {
setProduct(1);
}, []);
const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => { const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {
setCurrentTab(value); setCurrentTab(value);
}; };
@ -75,14 +95,9 @@ function SalidomoInstallationTabs() {
return ret_path; return ret_path;
}; };
const tabs = const singleInstallationTabs =
currentTab != 'list' && !location.pathname.includes('folder') currentUser.userType == UserType.admin
? [ ? [
{
value: 'list',
icon: <ListIcon id="mode-toggle-button-list-icon" />
},
{ {
value: 'batteryview', value: 'batteryview',
label: ( label: (
@ -101,6 +116,16 @@ function SalidomoInstallationTabs() {
label: <FormattedMessage id="log" defaultMessage="Log" /> label: <FormattedMessage id="log" defaultMessage="Log" />
}, },
{
value: 'manage',
label: (
<FormattedMessage
id="manage"
defaultMessage="Access Management"
/>
)
},
{ {
value: 'information', value: 'information',
label: ( label: (
@ -119,71 +144,247 @@ function SalidomoInstallationTabs() {
] ]
: [ : [
{ {
value: 'list', value: 'batteryview',
icon: <ListIcon id="mode-toggle-button-list-icon" /> label: (
<FormattedMessage
id="batteryview"
defaultMessage="Battery View"
/>
)
},
{
value: 'overview',
label: <FormattedMessage id="overview" defaultMessage="Overview" />
},
{
value: 'information',
label: (
<FormattedMessage id="information" defaultMessage="Information" />
)
} }
]; ];
return ( const tabs =
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe"> currentTab != 'list' &&
<TabsContainerWrapper> currentTab != 'tree' &&
<Tabs !location.pathname.includes('folder') &&
onChange={handleTabsChange} currentUser.userType == UserType.admin
value={currentTab} ? [
variant="scrollable" {
scrollButtons="auto" value: 'list',
textColor="primary" icon: <ListIcon id="mode-toggle-button-list-icon" />
indicatorColor="primary" },
> {
{tabs.map((tab) => ( value: 'tree',
<Tab icon: <AccountTreeIcon id="mode-toggle-button-tree-icon" />
key={tab.value} },
value={tab.value} {
icon={tab.icon} value: 'batteryview',
component={Link} label: (
label={tab.label} <FormattedMessage
to={ id="batteryview"
tab.value === 'list' defaultMessage="Battery View"
? routes[tab.value] />
: navigateToTabPath(location.pathname, routes[tab.value]) )
} },
/> {
))} value: 'overview',
</Tabs> label: <FormattedMessage id="overview" defaultMessage="Overview" />
</TabsContainerWrapper> },
<Card variant="outlined"> {
<Grid value: 'log',
container label: <FormattedMessage id="log" defaultMessage="Log" />
direction="row" },
justifyContent="center"
alignItems="stretch"
spacing={0}
>
<Routes>
<Route
path={routes.list + '*'}
element={
<Grid item xs={12}>
<Box p={4}>
<InstallationSearch installations={salidomoInstallations} />
</Box>
</Grid>
}
/>
<Route {
path={'*'} value: 'manage',
element={ label: (
<Navigate <FormattedMessage
to={routes.salidomo_installations + routes.list} id="manage"
></Navigate> defaultMessage="Access Management"
} />
></Route> )
</Routes> },
</Grid>
</Card> {
</Container> value: 'information',
); label: (
<FormattedMessage id="information" defaultMessage="Information" />
)
},
{
value: 'history',
label: (
<FormattedMessage
id="history"
defaultMessage="History Of Actions"
/>
)
}
]
: currentTab != 'list' &&
currentTab != 'tree' &&
!location.pathname.includes('folder') &&
currentUser.userType == UserType.client
? [
{
value: 'list',
icon: <ListIcon id="mode-toggle-button-list-icon" />
},
{
value: 'tree',
icon: <AccountTreeIcon id="mode-toggle-button-tree-icon" />
},
{
value: 'batteryview',
label: (
<FormattedMessage
id="batteryview"
defaultMessage="Battery View"
/>
)
},
{
value: 'overview',
label: <FormattedMessage id="overview" defaultMessage="Overview" />
},
{
value: 'information',
label: (
<FormattedMessage id="information" defaultMessage="Information" />
)
}
]
: [
{
value: 'list',
icon: <ListIcon id="mode-toggle-button-list-icon" />
},
{
value: 'tree',
icon: <AccountTreeIcon id="mode-toggle-button-tree-icon" />
}
];
return salidomoInstallations.length > 1 ? (
<>
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
<TabsContainerWrapper>
<Tabs
onChange={handleTabsChange}
value={currentTab}
variant="scrollable"
scrollButtons="auto"
textColor="primary"
indicatorColor="primary"
>
{tabs.map((tab) => (
<Tab
key={tab.value}
value={tab.value}
icon={tab.icon}
component={Link}
label={tab.label}
to={
tab.value === 'list' || tab.value === 'tree'
? routes[tab.value]
: navigateToTabPath(location.pathname, routes[tab.value])
}
/>
))}
</Tabs>
</TabsContainerWrapper>
<Card variant="outlined">
<Grid
container
direction="row"
justifyContent="center"
alignItems="stretch"
spacing={0}
>
<Routes>
<Route
path={routes.list + '*'}
element={
<Grid item xs={12}>
<Box p={4}>
<InstallationSearch
installations={salidomoInstallations}
/>
</Box>
</Grid>
}
/>
<Route path={routes.tree + '*'} element={<TreeView />} />
<Route
path={'*'}
element={
<Navigate
to={routes.salidomo_installations + routes.list}
></Navigate>
}
></Route>
</Routes>
</Grid>
</Card>
</Container>
</>
) : salidomoInstallations.length === 1 ? (
<>
{' '}
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
<TabsContainerWrapper>
<Tabs
onChange={handleTabsChange}
value={currentTab}
variant="scrollable"
scrollButtons="auto"
textColor="primary"
indicatorColor="primary"
>
{singleInstallationTabs.map((tab) => (
<Tab
key={tab.value}
value={tab.value}
component={Link}
label={tab.label}
to={routes[tab.value]}
/>
))}
</Tabs>
</TabsContainerWrapper>
<Card variant="outlined">
<Grid
container
direction="row"
justifyContent="center"
alignItems="stretch"
spacing={0}
>
<Routes>
<Route
path={'*'}
element={
<Grid item xs={12}>
<Box p={4}>
<SalidomoInstallation
current_installation={salidomoInstallations[0]}
type="installation"
></SalidomoInstallation>
</Box>
</Grid>
}
/>
</Routes>
</Grid>
</Card>
</Container>
</>
) : null;
} }
export default SalidomoInstallationTabs; export default SalidomoInstallationTabs;

View File

@ -10,6 +10,7 @@ import CancelIcon from '@mui/icons-material/Cancel';
import { WebSocketContext } from 'src/contexts/WebSocketContextProvider'; import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
import routes from 'src/Resources/routes.json'; import routes from 'src/Resources/routes.json';
import { useLocation, useNavigate } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
interface CustomTreeItemProps { interface CustomTreeItemProps {
node: I_Installation | I_Folder; node: I_Installation | I_Folder;
@ -44,12 +45,15 @@ function CustomTreeItem(props: CustomTreeItemProps) {
const navigate = useNavigate(); const navigate = useNavigate();
const [selected, setSelected] = useState(false); const [selected, setSelected] = useState(false);
const currentLocation = useLocation(); const currentLocation = useLocation();
const { product, setProduct } = useContext(ProductIdContext);
const handleSelectOneInstallation = (): void => { const handleSelectOneInstallation = (): void => {
let installation = props.node; let installation = props.node;
let path =
product == 0 ? routes.installations : routes.salidomo_installations;
if (installation.type != 'Folder') { if (installation.type != 'Folder') {
navigate( navigate(
routes.installations + path +
routes.tree + routes.tree +
routes.installation + routes.installation +
installation.s3BucketId + installation.s3BucketId +
@ -62,7 +66,7 @@ function CustomTreeItem(props: CustomTreeItemProps) {
setSelected(!selected); setSelected(!selected);
} else { } else {
navigate( navigate(
routes.installations + path +
routes.tree + routes.tree +
routes.folder + routes.folder +
installation.id + installation.id +
@ -156,7 +160,8 @@ function CustomTreeItem(props: CustomTreeItemProps) {
} }
sx={{ sx={{
display: display:
currentLocation.pathname === routes.installations + 'tree' || currentLocation.pathname ===
routes.salidomo_installations + routes.tree ||
currentLocation.pathname === routes.installations + routes.tree || currentLocation.pathname === routes.installations + routes.tree ||
currentLocation.pathname.includes('folder') currentLocation.pathname.includes('folder')
? 'block' ? 'block'

View File

@ -21,6 +21,8 @@ import FolderForm from './folderForm';
import InstallationForm from '../Installations/installationForm'; import InstallationForm from '../Installations/installationForm';
import { InstallationsContext } from '../../../contexts/InstallationsContextProvider'; import { InstallationsContext } from '../../../contexts/InstallationsContextProvider';
import { UserType } from '../../../interfaces/UserTypes'; import { UserType } from '../../../interfaces/UserTypes';
import SalidomoInstallationForm from '../SalidomoInstallations/SalidomoInstallationForm';
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
interface TreeInformationProps { interface TreeInformationProps {
folder: I_Folder; folder: I_Folder;
@ -50,6 +52,8 @@ function TreeInformation(props: TreeInformationProps) {
deleteFolder deleteFolder
} = installationContext; } = installationContext;
const { product, setProduct } = useContext(ProductIdContext);
const handleChange = (e) => { const handleChange = (e) => {
const { name, value } = e.target; const { name, value } = e.target;
setFormValues({ setFormValues({
@ -61,7 +65,7 @@ function TreeInformation(props: TreeInformationProps) {
const handleFolderInformationUpdate = () => { const handleFolderInformationUpdate = () => {
setLoading(true); setLoading(true);
setError(false); setError(false);
updateFolder(formValues); updateFolder(formValues, product);
}; };
const handleNewInstallationInsertion = () => { const handleNewInstallationInsertion = () => {
@ -80,7 +84,7 @@ function TreeInformation(props: TreeInformationProps) {
const deleteFolderModalHandle = () => { const deleteFolderModalHandle = () => {
setOpenModalDeleteFolder(false); setOpenModalDeleteFolder(false);
deleteFolder(formValues); deleteFolder(formValues, product);
setLoading(false); setLoading(false);
}; };
@ -195,13 +199,20 @@ function TreeInformation(props: TreeInformationProps) {
parentid={props.folder.id} parentid={props.folder.id}
/> />
)} )}
{openModalInstallation && ( {openModalInstallation && product == 0 && (
<InstallationForm <InstallationForm
cancel={handleFormCancel} cancel={handleFormCancel}
submit={handleInstallationFormSubmit} submit={handleInstallationFormSubmit}
parentid={props.folder.id} parentid={props.folder.id}
/> />
)} )}
{openModalInstallation && product == 1 && (
<SalidomoInstallationForm
cancel={handleFormCancel}
submit={handleInstallationFormSubmit}
parentid={props.folder.id}
/>
)}
<Container maxWidth="xl"> <Container maxWidth="xl">
<Grid <Grid
container container

View File

@ -8,19 +8,27 @@ import Installation from '../Installations/Installation';
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider'; import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
import { Route, Routes } from 'react-router-dom'; import { Route, Routes } from 'react-router-dom';
import routes from '../../../Resources/routes.json'; import routes from '../../../Resources/routes.json';
import Folder from './Folder';
import { WebSocketContext } from '../../../contexts/WebSocketContextProvider'; import { WebSocketContext } from '../../../contexts/WebSocketContextProvider';
import SalidomoInstallation from '../SalidomoInstallations/Installation';
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
import Folder from './Folder';
function InstallationTree() { function InstallationTree() {
const { foldersAndInstallations, fetchAllFoldersAndInstallations } = const { foldersAndInstallations, fetchAllFoldersAndInstallations } =
useContext(InstallationsContext); useContext(InstallationsContext);
const { product, setProduct } = useContext(ProductIdContext);
const webSocketContext = useContext(WebSocketContext); const webSocketContext = useContext(WebSocketContext);
const { getStatus } = webSocketContext; const { getStatus } = webSocketContext;
const sortedInstallations = [...foldersAndInstallations].sort((a, b) => { const sortedInstallations = [...foldersAndInstallations].sort((a, b) => {
// Compare the status field of each installation and sort them based on the status. // Compare the status field of each installation and sort them based on the status.
//Installations with alarms go first //Installations with alarms go first
if (a.type == 'Folder') {
return -1;
}
let a_status = getStatus(a.id); let a_status = getStatus(a.id);
let b_status = getStatus(b.id); let b_status = getStatus(b.id);
@ -34,7 +42,7 @@ function InstallationTree() {
}); });
useEffect(() => { useEffect(() => {
fetchAllFoldersAndInstallations(); fetchAllFoldersAndInstallations(product);
}, []); }, []);
const TreeNode = ({ node, parent_id }) => { const TreeNode = ({ node, parent_id }) => {
@ -61,17 +69,59 @@ function InstallationTree() {
</CustomTreeItem> </CustomTreeItem>
) )
); );
} else { } else if (node.type == 'Installation') {
return ( return (
node.parentId == parent_id && ( node.parentId == parent_id && (
<CustomTreeItem node={node} parent_id={parent_id} /> <CustomTreeItem node={node} parent_id={parent_id} />
) )
); );
} else {
return null;
} }
}; };
return ( return (
<Grid container spacing={1} sx={{ marginTop: 0.1 }}> <Grid container spacing={1} sx={{ marginTop: 0.1 }}>
<Routes>
{foldersAndInstallations.map((installation) => {
if (installation.type == 'Installation') {
return (
<Route
key={installation.s3BucketId}
path={routes.installation + installation.s3BucketId + '*'}
element={
product == 0 ? (
<Installation
key={installation.s3BucketId}
current_installation={installation}
type="installation"
></Installation>
) : (
<SalidomoInstallation
key={installation.s3BucketId}
current_installation={installation}
type="installation"
></SalidomoInstallation>
)
}
/>
);
} else {
return (
<Route
key={installation.id}
path={routes.folder + installation.id + '*'}
element={
<Folder
key={installation.id + installation.type}
current_folder={installation}
></Folder>
}
/>
);
}
})}
</Routes>
<Grid item xs={12} md={12}> <Grid item xs={12} md={12}>
<TreeView <TreeView
defaultCollapseIcon={<ExpandMoreIcon />} defaultCollapseIcon={<ExpandMoreIcon />}
@ -93,39 +143,6 @@ function InstallationTree() {
})} })}
</TreeView> </TreeView>
</Grid> </Grid>
<Routes>
{foldersAndInstallations.map((installation) => {
if (installation.type == 'Installation') {
return (
<Route
key={installation.s3BucketId}
path={routes.installation + installation.s3BucketId + '*'}
element={
<Installation
key={installation.s3BucketId}
current_installation={installation}
type="installation"
></Installation>
}
/>
);
} else {
return (
<Route
key={installation.id}
path={routes.folder + installation.id + '*'}
element={
<Folder
key={installation.id + installation.type}
current_folder={installation}
></Folder>
}
/>
);
}
})}
</Routes>
</Grid> </Grid>
); );
} }

View File

@ -14,6 +14,7 @@ import { I_Folder } from 'src/interfaces/InstallationTypes';
import { TokenContext } from 'src/contexts/tokenContext'; import { TokenContext } from 'src/contexts/tokenContext';
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider'; import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
interface folderFormProps { interface folderFormProps {
cancel: () => void; cancel: () => void;
@ -29,7 +30,7 @@ function folderForm(props: folderFormProps) {
information: '' information: ''
}); });
const requiredFields = ['name']; const requiredFields = ['name'];
const { product, setProduct } = useContext(ProductIdContext);
const tokencontext = useContext(TokenContext); const tokencontext = useContext(TokenContext);
const { removeToken } = tokencontext; const { removeToken } = tokencontext;
@ -47,7 +48,7 @@ function folderForm(props: folderFormProps) {
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
formValues.parentId = props.parentid; formValues.parentId = props.parentid;
setLoading(true); setLoading(true);
const responseData = await createFolder(formValues); const responseData = await createFolder(formValues, product);
props.submit(); props.submit();
}; };

View File

@ -18,7 +18,7 @@ interface I_InstallationContextProviderProps {
foldersAndInstallations: I_Installation[]; foldersAndInstallations: I_Installation[];
fetchAllInstallations: () => Promise<void>; fetchAllInstallations: () => Promise<void>;
fetchAllSalidomoInstallations: () => Promise<void>; fetchAllSalidomoInstallations: () => Promise<void>;
fetchAllFoldersAndInstallations: () => Promise<void>; fetchAllFoldersAndInstallations: (product: number) => Promise<void>;
createInstallation: (value: Partial<I_Installation>) => Promise<void>; createInstallation: (value: Partial<I_Installation>) => Promise<void>;
updateInstallation: (value: I_Installation, view: string) => Promise<void>; updateInstallation: (value: I_Installation, view: string) => Promise<void>;
loading: boolean; loading: boolean;
@ -28,9 +28,9 @@ interface I_InstallationContextProviderProps {
updated: boolean; updated: boolean;
setUpdated: (value: boolean) => void; setUpdated: (value: boolean) => void;
deleteInstallation: (value: I_Installation, view: string) => Promise<void>; deleteInstallation: (value: I_Installation, view: string) => Promise<void>;
createFolder: (value: Partial<I_Folder>) => Promise<void>; createFolder: (value: Partial<I_Folder>, product: number) => Promise<void>;
updateFolder: (value: I_Folder) => Promise<void>; updateFolder: (value: I_Folder, product: number) => Promise<void>;
deleteFolder: (value: I_Folder) => Promise<void>; deleteFolder: (value: I_Folder, product: number) => Promise<void>;
} }
export const InstallationsContext = export const InstallationsContext =
@ -40,7 +40,7 @@ export const InstallationsContext =
foldersAndInstallations: [], foldersAndInstallations: [],
fetchAllInstallations: () => Promise.resolve(), fetchAllInstallations: () => Promise.resolve(),
fetchAllSalidomoInstallations: () => Promise.resolve(), fetchAllSalidomoInstallations: () => Promise.resolve(),
fetchAllFoldersAndInstallations: () => Promise.resolve(), fetchAllFoldersAndInstallations: (product: number) => Promise.resolve(),
createInstallation: () => Promise.resolve(), createInstallation: () => Promise.resolve(),
updateInstallation: () => Promise.resolve(), updateInstallation: () => Promise.resolve(),
loading: false, loading: false,
@ -104,19 +104,22 @@ const InstallationsContextProvider = ({
}); });
}, []); }, []);
const fetchAllFoldersAndInstallations = useCallback(async () => { const fetchAllFoldersAndInstallations = useCallback(
return axiosConfig async (product: number) => {
.get('/GetAllFoldersAndInstallations') return axiosConfig
.then((res) => { .get(`/GetAllFoldersAndInstallations?productId=${product}`)
setFoldersAndInstallations(res.data); .then((res) => {
}) setFoldersAndInstallations(res.data);
.catch((err) => { })
if (err.response && err.response.status == 401) { .catch((err) => {
removeToken(); if (err.response && err.response.status == 401) {
navigate(routes.login); removeToken();
} navigate(routes.login);
}); }
}, []); });
},
[]
);
const createInstallation = useCallback( const createInstallation = useCallback(
async (formValues: Partial<I_Installation>) => { async (formValues: Partial<I_Installation>) => {
@ -124,11 +127,12 @@ const InstallationsContextProvider = ({
.post('/CreateInstallation', formValues) .post('/CreateInstallation', formValues)
.then((res) => { .then((res) => {
setLoading(false); setLoading(false);
if (formValues.product == 0) { fetchAllFoldersAndInstallations(formValues.product);
fetchAllFoldersAndInstallations(); // if (formValues.product == 0) {
} else { // fetchAllFoldersAndInstallations();
fetchAllSalidomoInstallations(); // } else {
} // fetchAllSalidomoInstallations();
// }
}) })
.catch((error) => { .catch((error) => {
@ -151,14 +155,12 @@ const InstallationsContextProvider = ({
if (response) { if (response) {
setLoading(false); setLoading(false);
setUpdated(true); setUpdated(true);
if (formValues.product == 0) { if (formValues.product == 0 && view == 'installation') {
if (view == 'installation') { fetchAllInstallations();
fetchAllInstallations(); } else if (formValues.product == 1 && view == 'installation') {
} else {
fetchAllFoldersAndInstallations();
}
} else {
fetchAllSalidomoInstallations(); fetchAllSalidomoInstallations();
} else {
fetchAllFoldersAndInstallations(formValues.product);
} }
setTimeout(() => { setTimeout(() => {
@ -186,14 +188,12 @@ const InstallationsContextProvider = ({
if (response) { if (response) {
setLoading(false); setLoading(false);
setUpdated(true); setUpdated(true);
if (formValues.product == 0) { if (formValues.product == 0 && view == 'installation') {
if (view == 'installation') { fetchAllInstallations();
fetchAllInstallations(); } else if (formValues.product == 1 && view == 'installation') {
} else {
fetchAllFoldersAndInstallations();
}
} else {
fetchAllSalidomoInstallations(); fetchAllSalidomoInstallations();
} else {
fetchAllFoldersAndInstallations(formValues.product);
} }
setTimeout(() => { setTimeout(() => {
@ -213,71 +213,80 @@ const InstallationsContextProvider = ({
[] []
); );
const createFolder = useCallback(async (formValues: Partial<I_Folder>) => { const createFolder = useCallback(
axiosConfig async (formValues: Partial<I_Folder>, product: number) => {
.post('/CreateFolder', formValues) axiosConfig
.then((res) => { .post('/CreateFolder', formValues)
setLoading(false); .then((res) => {
fetchAllFoldersAndInstallations();
})
.catch((error) => {
setLoading(false);
setError(true);
if (error.response && error.response.status == 401) {
removeToken();
navigate(routes.login);
}
});
}, []);
const updateFolder = useCallback(async (formValues: I_Folder) => {
axiosConfig
.put('/UpdateFolder', formValues)
.then((response) => {
if (response) {
setLoading(false); setLoading(false);
setUpdated(true); fetchAllFoldersAndInstallations(product);
fetchAllFoldersAndInstallations(); })
setTimeout(() => { .catch((error) => {
setUpdated(false);
}, 3000);
}
})
.catch((error) => {
setLoading(false);
setError(true);
if (error.response && error.response.status == 401) {
removeToken();
navigate(routes.login);
}
});
}, []);
const deleteFolder = useCallback(async (formValues: I_Folder) => {
axiosConfig
.delete(`/DeleteFolder?folderId=${formValues.id}`)
.then((response) => {
if (response) {
setLoading(false); setLoading(false);
setUpdated(true); setError(true);
fetchAllFoldersAndInstallations(); if (error.response && error.response.status == 401) {
removeToken();
navigate(routes.login);
}
});
},
[]
);
setTimeout(() => { const updateFolder = useCallback(
setUpdated(false); async (formValues: I_Folder, product: number) => {
}, 3000); axiosConfig
} .put('/UpdateFolder', formValues)
}) .then((response) => {
.catch((error) => { if (response) {
setLoading(false); setLoading(false);
setError(true); setUpdated(true);
if (error.response && error.response.status == 401) { fetchAllFoldersAndInstallations(product);
removeToken();
navigate(routes.login); setTimeout(() => {
} setUpdated(false);
}); }, 3000);
}, []); }
})
.catch((error) => {
setLoading(false);
setError(true);
if (error.response && error.response.status == 401) {
removeToken();
navigate(routes.login);
}
});
},
[]
);
const deleteFolder = useCallback(
async (formValues: I_Folder, product: number) => {
axiosConfig
.delete(`/DeleteFolder?folderId=${formValues.id}`)
.then((response) => {
if (response) {
setLoading(false);
setUpdated(true);
fetchAllFoldersAndInstallations(product);
setTimeout(() => {
setUpdated(false);
}, 3000);
}
})
.catch((error) => {
setLoading(false);
setError(true);
if (error.response && error.response.status == 401) {
removeToken();
navigate(routes.login);
}
});
},
[]
);
return ( return (
<InstallationsContext.Provider <InstallationsContext.Provider

View File

@ -0,0 +1,66 @@
import { createContext, ReactNode, useState } from 'react';
import { useLocation } from 'react-router-dom';
// Define the shape of the context
interface ProductIdContextType {
product?: number;
setProduct: (new_product: number) => void;
accessToSalimax: boolean;
accessToSalidomo: boolean;
setAccessToSalimax: (access: boolean) => void;
setAccessToSalidomo: (access: boolean) => void;
}
// Create the context.
export const ProductIdContext = createContext<ProductIdContextType | undefined>(
undefined
);
// Create a UserContextProvider component
export const ProductIdContextProvider = ({
children
}: {
children: ReactNode;
}) => {
const location = useLocation().pathname;
const [accessToSalimax, setAccessToSalimax] = useState(() => {
const storedValue = localStorage.getItem('accessToSalimax');
return storedValue === 'true';
});
const [accessToSalidomo, setAccessToSalidomo] = useState(() => {
const storedValue = localStorage.getItem('accessToSalidomo');
return storedValue === 'true';
});
const [product, setProduct] = useState(location.includes('salidomo') ? 1 : 0);
const changeProductId = (new_product: number) => {
setProduct(new_product);
};
const changeAccessSalimax = (access: boolean) => {
setAccessToSalimax(access);
localStorage.setItem('accessToSalimax', JSON.stringify(access));
};
const changeAccessSalidomo = (access: boolean) => {
setAccessToSalidomo(access);
localStorage.setItem('accessToSalidomo', JSON.stringify(access));
};
return (
<ProductIdContext.Provider
value={{
product,
setProduct: changeProductId,
accessToSalimax,
accessToSalidomo,
setAccessToSalimax: changeAccessSalimax,
setAccessToSalidomo: changeAccessSalidomo
}}
>
{children}
</ProductIdContext.Provider>
);
};
export default ProductIdContextProvider;

View File

@ -18,12 +18,8 @@ export const WebSocketContext = createContext<
const WebSocketContextProvider = ({ children }: { children: ReactNode }) => { const WebSocketContextProvider = ({ children }: { children: ReactNode }) => {
const [socket, setSocket] = useState<WebSocket>(null); const [socket, setSocket] = useState<WebSocket>(null);
const [installations, setInstallations] = useState<I_Installation[]>(null); const [installations, setInstallations] = useState<I_Installation[]>(null);
const [installationStatus, setInstallationStatus] = useState< const [installationStatus, setInstallationStatus] = useState(new Map());
Record<number, number> const [installationMode, setInstallationMode] = useState(new Map());
>({});
const [installationMode, setInstallatioMode] = useState<
Record<number, boolean>
>({});
const BUFFER_LENGTH = 5; const BUFFER_LENGTH = 5;
useEffect(() => { useEffect(() => {
@ -53,22 +49,33 @@ const WebSocketContextProvider = ({ children }: { children: ReactNode }) => {
// Listen for messages // Listen for messages
socket.addEventListener('message', (event) => { socket.addEventListener('message', (event) => {
const message = JSON.parse(event.data); // Parse the JSON data const message = JSON.parse(event.data); // Parse the JSON data
if (Array.isArray(message)) {
if (message.id != -1) { setInstallationMode((prevMode) => {
const installation_id = message.id; const newMode = new Map(prevMode);
message.forEach((item) => {
//console.log('Message from server ', installation_id, status); newMode.set(item.id, item.testingMode);
setInstallatioMode((prevMode) => { });
// Create a new object by spreading the previous state
const newMode = { ...prevMode };
newMode[installation_id] = message.testingMode;
return newMode; return newMode;
}); });
setInstallationStatus((prevStatus) => { setInstallationStatus((prevStatus) => {
// Create a new object by spreading the previous state const newStatus = new Map(prevStatus);
const newStatus = { ...prevStatus }; message.forEach((item) => {
newStatus[installation_id] = message.status; newStatus.set(item.id, item.status);
});
return newStatus;
});
} else if (message.id != -1) {
const installation_id = message.id;
setInstallationMode((prevMode) => {
const newMode = new Map(prevMode);
newMode.set(message.id, message.testingMode);
return newMode;
});
setInstallationStatus((prevStatus) => {
const newStatus = new Map(prevStatus);
newStatus.set(message.id, message.status);
return newStatus; return newStatus;
}); });
} }
@ -86,17 +93,16 @@ const WebSocketContextProvider = ({ children }: { children: ReactNode }) => {
}; };
const getStatus = (installationId: number) => { const getStatus = (installationId: number) => {
let status; return installationStatus.get(installationId);
if (!installationStatus.hasOwnProperty(installationId)) { // if (installationStatus.has(installationId)) {
status = -2; // installationStatus.get(installationId);
} else { // } else {
status = installationStatus[installationId]; // return -2;
} // }
return status;
}; };
const getTestingMode = (installationId: number) => { const getTestingMode = (installationId: number) => {
return installationMode[installationId]; return installationMode.get(installationId);
}; };
return ( return (

View File

@ -8,6 +8,7 @@ import { SidebarProvider } from 'src/contexts/SidebarContext';
import * as serviceWorker from 'src/serviceWorker'; import * as serviceWorker from 'src/serviceWorker';
import UserContextProvider from './contexts/userContext'; import UserContextProvider from './contexts/userContext';
import TokenContextProvider from './contexts/tokenContext'; import TokenContextProvider from './contexts/tokenContext';
import ProductIdContextProvider from './contexts/ProductIdContextProvider';
ReactDOM.render( ReactDOM.render(
<HelmetProvider> <HelmetProvider>
@ -15,7 +16,9 @@ ReactDOM.render(
<BrowserRouter> <BrowserRouter>
<UserContextProvider> <UserContextProvider>
<TokenContextProvider> <TokenContextProvider>
<App /> <ProductIdContextProvider>
<App />
</ProductIdContextProvider>
</TokenContextProvider> </TokenContextProvider>
</UserContextProvider> </UserContextProvider>
</BrowserRouter> </BrowserRouter>

View File

@ -26,6 +26,8 @@ export interface overviewInterface {
pvProduction: chartInfoInterface; pvProduction: chartInfoInterface;
dcBusVoltage: chartInfoInterface; dcBusVoltage: chartInfoInterface;
overview: chartInfoInterface; overview: chartInfoInterface;
ACLoad: chartInfoInterface;
DCLoad: chartInfoInterface;
} }
export interface chartAggregatedDataInterface { export interface chartAggregatedDataInterface {
@ -46,6 +48,8 @@ export interface chartDataInterface {
gridPower: { name: string; data: number[] }; gridPower: { name: string; data: number[] };
pvProduction: { name: string; data: number[] }; pvProduction: { name: string; data: number[] };
dcBusVoltage: { name: string; data: number[] }; dcBusVoltage: { name: string; data: number[] };
ACLoad: { name: string; data: number[] };
DCLoad: { name: string; data: number[] };
} }
export interface BatteryDataInterface { export interface BatteryDataInterface {
@ -339,7 +343,9 @@ export const transformInputToDailyData = async (
'/Battery/Dc/Power', '/Battery/Dc/Power',
'/GridMeter/Ac/Power/Active', '/GridMeter/Ac/Power/Active',
'/PvOnDc/Dc/Power', '/PvOnDc/Dc/Power',
'/DcDc/Dc/Link/Voltage' '/DcDc/Dc/Link/Voltage',
'/LoadOnAcGrid/Power/Active',
'/LoadOnDc/Power'
]; ];
const categories = [ const categories = [
'soc', 'soc',
@ -347,7 +353,9 @@ export const transformInputToDailyData = async (
'dcPower', 'dcPower',
'gridPower', 'gridPower',
'pvProduction', 'pvProduction',
'dcBusVoltage' 'dcBusVoltage',
'ACLoad',
'DCLoad'
]; ];
const chartData: chartDataInterface = { const chartData: chartDataInterface = {
@ -356,7 +364,9 @@ export const transformInputToDailyData = async (
dcPower: { name: 'Battery Power', data: [] }, dcPower: { name: 'Battery Power', data: [] },
gridPower: { name: 'Grid Power', data: [] }, gridPower: { name: 'Grid Power', data: [] },
pvProduction: { name: 'Pv Production', data: [] }, pvProduction: { name: 'Pv Production', data: [] },
dcBusVoltage: { name: 'DC Bus Voltage', data: [] } dcBusVoltage: { name: 'DC Bus Voltage', data: [] },
ACLoad: { name: 'AC Load', data: [] },
DCLoad: { name: 'DC Load', data: [] }
}; };
const chartOverview: overviewInterface = { const chartOverview: overviewInterface = {
@ -367,7 +377,9 @@ export const transformInputToDailyData = async (
gridPower: { magnitude: 0, unit: '', min: 0, max: 0 }, gridPower: { magnitude: 0, unit: '', min: 0, max: 0 },
pvProduction: { magnitude: 0, unit: '', min: 0, max: 0 }, pvProduction: { magnitude: 0, unit: '', min: 0, max: 0 },
dcBusVoltage: { magnitude: 0, unit: '', min: 0, max: 0 }, dcBusVoltage: { magnitude: 0, unit: '', min: 0, max: 0 },
overview: { magnitude: 0, unit: '', min: 0, max: 0 } overview: { magnitude: 0, unit: '', min: 0, max: 0 },
ACLoad: { magnitude: 0, unit: '', min: 0, max: 0 },
DCLoad: { magnitude: 0, unit: '', min: 0, max: 0 }
}; };
categories.forEach((category) => { categories.forEach((category) => {
@ -560,7 +572,9 @@ export const transformInputToAggregatedData = async (
gridPower: { magnitude: 0, unit: '', min: 0, max: 0 }, gridPower: { magnitude: 0, unit: '', min: 0, max: 0 },
pvProduction: { magnitude: 0, unit: '', min: 0, max: 0 }, pvProduction: { magnitude: 0, unit: '', min: 0, max: 0 },
dcBusVoltage: { magnitude: 0, unit: '', min: 0, max: 0 }, dcBusVoltage: { magnitude: 0, unit: '', min: 0, max: 0 },
overview: { magnitude: 0, unit: '', min: 0, max: 0 } overview: { magnitude: 0, unit: '', min: 0, max: 0 },
ACLoad: { magnitude: 0, unit: '', min: 0, max: 0 },
DCLoad: { magnitude: 0, unit: '', min: 0, max: 0 }
}; };
pathsToSearch.forEach((path) => { pathsToSearch.forEach((path) => {

View File

@ -16,6 +16,7 @@ import TableChartTwoToneIcon from '@mui/icons-material/TableChartTwoTone';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { UserContext } from '../../../../contexts/userContext'; import { UserContext } from '../../../../contexts/userContext';
import { UserType } from '../../../../interfaces/UserTypes'; import { UserType } from '../../../../interfaces/UserTypes';
import { ProductIdContext } from '../../../../contexts/ProductIdContextProvider';
const MenuWrapper = styled(Box)( const MenuWrapper = styled(Box)(
({ theme }) => ` ({ theme }) => `
@ -163,6 +164,7 @@ function SidebarMenu() {
const { closeSidebar } = useContext(SidebarContext); const { closeSidebar } = useContext(SidebarContext);
const context = useContext(UserContext); const context = useContext(UserContext);
const { currentUser, setUser } = context; const { currentUser, setUser } = context;
const { accessToSalimax, accessToSalidomo } = useContext(ProductIdContext);
return ( return (
<> <>
@ -176,23 +178,25 @@ function SidebarMenu() {
} }
> >
<SubMenuWrapper> <SubMenuWrapper>
<List component="div"> {accessToSalimax && (
<ListItem component="div"> <List component="div">
<Button <ListItem component="div">
disableRipple <Button
component={RouterLink} disableRipple
onClick={closeSidebar} component={RouterLink}
to="/installations" onClick={closeSidebar}
startIcon={<BrightnessLowTwoToneIcon />} to="/installations"
> startIcon={<BrightnessLowTwoToneIcon />}
<Box sx={{ marginTop: '3px' }}> >
<FormattedMessage id="salimax" defaultMessage="Salimax" /> <Box sx={{ marginTop: '3px' }}>
</Box> <FormattedMessage id="salimax" defaultMessage="Salimax" />
</Button> </Box>
</ListItem> </Button>
</List> </ListItem>
</List>
)}
{currentUser.userType == UserType.admin && ( {accessToSalidomo && (
<List component="div"> <List component="div">
<ListItem component="div"> <ListItem component="div">
<Button <Button