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:
parent
2d3af976c1
commit
7db44785be
|
@ -40,10 +40,14 @@ public class Controller : ControllerBase
|
|||
//return Unauthorized("No Password set");
|
||||
throw new Exceptions(401, "Wrong Password Exception", "Please try again.", Request.Path.Value!);
|
||||
}
|
||||
|
||||
|
||||
|
||||
var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user));
|
||||
|
||||
//TODO The Frontend should check for the MustResetPassword Flag
|
||||
|
||||
|
||||
|
||||
return Db.Create(session)
|
||||
? session
|
||||
|
@ -472,15 +476,16 @@ public class Controller : ControllerBase
|
|||
|
||||
|
||||
[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;
|
||||
|
||||
if (user is null)
|
||||
return Unauthorized();
|
||||
|
||||
|
||||
var foldersAndInstallations = user
|
||||
.AccessibleFoldersAndInstallations(product:0)
|
||||
.AccessibleFoldersAndInstallations(product:productId)
|
||||
.Do(o => o.FillOrderNumbers())
|
||||
.Select(o => o.HideParentIfUserHasNoAccessToParent(user))
|
||||
.OfType<Object>(); // Important! JSON serializer must see Objects otherwise
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
namespace InnovEnergy.App.Backend.DataTypes;
|
||||
|
||||
public class Folder : TreeNode {}
|
||||
public class Folder : TreeNode { }
|
|
@ -143,7 +143,7 @@ public static class ExoCmd
|
|||
{
|
||||
const String url = "https://api-ch-dk-2.exoscale.com/v2/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 = $$"""
|
||||
|
@ -245,7 +245,7 @@ public static class ExoCmd
|
|||
{
|
||||
const String url = "https://api-ch-dk-2.exoscale.com/v2/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 = $$"""
|
||||
{
|
||||
|
|
|
@ -41,9 +41,9 @@ public static class FolderMethods
|
|||
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)
|
||||
|
@ -56,6 +56,8 @@ public static class FolderMethods
|
|||
|
||||
public static IEnumerable<Folder> DescendantFolders(this Folder parent)
|
||||
{
|
||||
|
||||
Console.WriteLine("Parent is "+parent.Id+" looking for descendant folders");
|
||||
return parent
|
||||
.TraverseDepthFirstPreOrder(UniqueChildFolders)
|
||||
.Skip(1); // skip self
|
||||
|
|
|
@ -18,7 +18,7 @@ public static class UserMethods
|
|||
var fromFolders = user
|
||||
.AccessibleFolders()
|
||||
.SelectMany(u => u.ChildInstallations()).ToList().Where(f=>f.Product==product);
|
||||
|
||||
|
||||
return direct
|
||||
.Concat(fromFolders)
|
||||
.Distinct();
|
||||
|
@ -56,6 +56,7 @@ public static class UserMethods
|
|||
|
||||
public static IEnumerable<Folder> DirectlyAccessibleFolders(this User user)
|
||||
{
|
||||
|
||||
return Db
|
||||
.FolderAccess
|
||||
.Where(r => r.UserId == user.Id)
|
||||
|
|
|
@ -6,11 +6,11 @@ public class User : TreeNode
|
|||
{
|
||||
|
||||
[Unique]
|
||||
public String Email { get; set; } = null!;
|
||||
public int UserType { get; set; } = 0;
|
||||
public Boolean MustResetPassword { get; set; } = false;
|
||||
public String Language { get; set; } = null!;
|
||||
public String? Password { get; set; } = null!;
|
||||
public String Email { get; set; } = null!;
|
||||
public int UserType { get; set; } = 0;
|
||||
public Boolean MustResetPassword { get; set; } = false;
|
||||
public String Language { get; set; } = null!;
|
||||
public String? Password { get; set; } = null!;
|
||||
|
||||
[Unique]
|
||||
public override String Name { get; set; } = null!;
|
||||
|
|
|
@ -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; }
|
||||
|
||||
}
|
|
@ -21,7 +21,8 @@ public static class Program
|
|||
|
||||
RabbitMqManager.InitializeEnvironment();
|
||||
RabbitMqManager.StartRabbitMqConsumer();
|
||||
WebsocketManager.MonitorInstallationTable();
|
||||
WebsocketManager.MonitorSalimaxInstallationTable();
|
||||
WebsocketManager.MonitorSalidomoInstallationTable();
|
||||
|
||||
|
||||
builder.Services.AddControllers();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using InnovEnergy.App.Backend.Database;
|
||||
using InnovEnergy.App.Backend.DataTypes;
|
||||
using InnovEnergy.App.Backend.DataTypes.Methods;
|
||||
using SQLite;
|
||||
|
||||
namespace InnovEnergy.App.Backend.Relations;
|
||||
|
@ -11,7 +12,8 @@ public class Session : Relation<String, Int64>
|
|||
[Unique ] public String Token { get => Left ; init => Left = value;}
|
||||
[Indexed] public Int64 UserId { get => Right; init => Right = value;}
|
||||
[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
|
||||
&& (User) is not null;
|
||||
|
||||
|
@ -30,6 +32,9 @@ public class Session : Relation<String, Int64>
|
|||
Token = CreateToken();
|
||||
UserId = user.Id;
|
||||
LastSeen = DateTime.Now;
|
||||
AccessToSalimax = user.AccessibleInstallations(product: 0).ToList().Count > 0;
|
||||
AccessToSalidomo = user.AccessibleInstallations(product: 1).ToList().Count > 0;
|
||||
|
||||
}
|
||||
|
||||
private static String CreateToken()
|
||||
|
|
|
@ -3,7 +3,8 @@ namespace InnovEnergy.App.Backend.Websockets;
|
|||
|
||||
public class InstallationInfo
|
||||
{
|
||||
public int Status { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
public int Status { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
public int Product { get; set; }
|
||||
public List<WebSocket> Connections { get; } = new List<WebSocket>();
|
||||
}
|
|
@ -158,7 +158,8 @@ public static class RabbitMqManager
|
|||
WebsocketManager.InstallationConnections[installationId] = new InstallationInfo
|
||||
{
|
||||
Status = receivedStatusMessage.Status,
|
||||
Timestamp = DateTime.Now
|
||||
Timestamp = DateTime.Now,
|
||||
Product = installation.Product
|
||||
};
|
||||
}
|
||||
else
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Net.WebSockets;
|
|||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using InnovEnergy.App.Backend.Database;
|
||||
using InnovEnergy.App.Backend.DataTypes;
|
||||
|
||||
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.
|
||||
//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){
|
||||
lock (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;
|
||||
if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);}
|
||||
}
|
||||
|
@ -27,6 +28,21 @@ public static class WebsocketManager
|
|||
await Task.Delay(TimeSpan.FromMinutes(2));
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
public static void InformWebsocketsForInstallation(Int64 installationId)
|
||||
|
@ -35,7 +51,6 @@ public static class WebsocketManager
|
|||
var installationConnection = InstallationConnections[installationId];
|
||||
Console.WriteLine("Update all the connected websockets for installation " + installationId);
|
||||
|
||||
|
||||
var jsonObject = new
|
||||
{
|
||||
id = installationId,
|
||||
|
@ -96,6 +111,9 @@ public static class WebsocketManager
|
|||
//Console.WriteLine("Received a new message from websocket");
|
||||
lock (InstallationConnections)
|
||||
{
|
||||
|
||||
List<WebsocketMessage> dataToSend = new List<WebsocketMessage>();
|
||||
|
||||
//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
|
||||
//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);
|
||||
InstallationConnections[installationId] = new InstallationInfo
|
||||
{
|
||||
Status = -1
|
||||
Status = -1,
|
||||
Product = installation.Product
|
||||
};
|
||||
}
|
||||
|
||||
InstallationConnections[installationId].Connections.Add(currentWebSocket);
|
||||
|
||||
var jsonObject = new
|
||||
var jsonObject = new WebsocketMessage
|
||||
{
|
||||
id = installationId,
|
||||
status = InstallationConnections[installationId].Status,
|
||||
testingMode = installation.TestingMode
|
||||
};
|
||||
|
||||
dataToSend.Add(jsonObject);
|
||||
|
||||
var jsonString = JsonSerializer.Serialize(jsonObject);
|
||||
var dataToSend = Encoding.UTF8.GetBytes(jsonString);
|
||||
//var jsonString = JsonSerializer.Serialize(jsonObject);
|
||||
//var dataToSend = Encoding.UTF8.GetBytes(jsonString);
|
||||
|
||||
|
||||
currentWebSocket.SendAsync(dataToSend,
|
||||
WebSocketMessageType.Text,
|
||||
true, // Indicates that this is the end of the message
|
||||
CancellationToken.None
|
||||
);
|
||||
// currentWebSocket.SendAsync(dataToSend,
|
||||
// WebSocketMessageType.Text,
|
||||
// true, // Indicates that this is the end of the message
|
||||
// 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("----------------------------------------------");
|
||||
|
|
|
@ -10,4 +10,5 @@ Salimax0006 ie-entwicklung@10.2.4.35 Steakhouse Mettmenstetten
|
|||
Salimax0007 ie-entwicklung@10.2.4.154 LerchenhofHerr Twannberg
|
||||
Salimax0008 ie-entwicklung@10.2.4.113 Wittmann Kottingbrunn
|
||||
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
|
|
@ -54,6 +54,6 @@ INNOVENERGY_PROTOCOL_VERSION = '48TL200V3'
|
|||
|
||||
|
||||
# S3 Credentials
|
||||
S3BUCKET = "114-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
|
||||
S3KEY = "EXO5b9198dc11544f42b44e1180"
|
||||
S3SECRET = "ga-mD3SYZMJUfjksmXPKAHQhVkxPZYv57jC1oD_mkC0"
|
||||
S3BUCKET = "91-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
|
||||
S3KEY = "EXOe6dce12288f11a676c2025a1"
|
||||
S3SECRET = "xpqM4Eh0Gg1HaYVkzlR9X6PwYa-QNb-mVk0XUkwW3cc"
|
||||
|
|
|
@ -24,6 +24,7 @@ import { useNavigate } from 'react-router-dom';
|
|||
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
|
||||
import CheckBoxIcon from '@mui/icons-material/CheckBox';
|
||||
import routes from 'src/Resources/routes.json';
|
||||
import { ProductIdContext } from '../contexts/ProductIdContextProvider';
|
||||
|
||||
function Login() {
|
||||
const [username, setUsername] = useState('');
|
||||
|
@ -35,6 +36,8 @@ function Login() {
|
|||
|
||||
const theme = useTheme();
|
||||
const context = useContext(UserContext);
|
||||
const { setAccessToSalimax, setAccessToSalidomo } =
|
||||
useContext(ProductIdContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
if (!context) {
|
||||
|
@ -73,11 +76,18 @@ function Login() {
|
|||
setNewToken(response.data.token);
|
||||
setUser(response.data.user);
|
||||
|
||||
setAccessToSalimax(response.data.accessToSalimax);
|
||||
setAccessToSalidomo(response.data.accessToSalidomo);
|
||||
|
||||
if (rememberMe) {
|
||||
cookies.set('rememberedUsername', username, { path: '/' });
|
||||
cookies.set('rememberedPassword', password, { path: '/' });
|
||||
}
|
||||
navigate(routes.installations);
|
||||
if (response.data.accessToSalimax) {
|
||||
navigate(routes.installations);
|
||||
} else {
|
||||
navigate(routes.salidomo_installations);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
|
@ -582,6 +582,7 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
|
|||
<MenuItem disabled value="">
|
||||
Select Firmware Version
|
||||
</MenuItem>
|
||||
<MenuItem value="AF09">AF09</MenuItem>
|
||||
<MenuItem value="AF11">AF11</MenuItem>
|
||||
<MenuItem value="AF0A">AF0A</MenuItem>
|
||||
</Select>
|
||||
|
|
|
@ -200,7 +200,7 @@ function InformationSalidomo(props: InformationSalidomoProps) {
|
|||
/>
|
||||
}
|
||||
name="installationName"
|
||||
value={formValues.installationName}
|
||||
value={formValues.name}
|
||||
onChange={handleChange}
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
|
|
|
@ -379,6 +379,7 @@ function Installation(props: singleInstallationProps) {
|
|||
{loading &&
|
||||
currentTab != 'information' &&
|
||||
currentTab != 'history' &&
|
||||
currentTab != 'manage' &&
|
||||
currentTab != 'overview' &&
|
||||
currentTab != 'log' && (
|
||||
<Container
|
||||
|
|
|
@ -53,9 +53,12 @@ export const fetchAggregatedData = (
|
|||
|
||||
export const fetchData = (
|
||||
timestamp: UnixTime,
|
||||
s3Credentials?: I_S3Credentials
|
||||
s3Credentials?: I_S3Credentials,
|
||||
cutdigits?: boolean
|
||||
): 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) {
|
||||
const s3Access = new S3Access(
|
||||
s3Credentials.s3Bucket,
|
||||
|
@ -71,10 +74,14 @@ export const fetchData = (
|
|||
if (r.status === 404) {
|
||||
return Promise.resolve(FetchResult.notAvailable);
|
||||
} 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 contentEncoding = r.headers.get('content-type');
|
||||
|
||||
console.log(contentEncoding);
|
||||
|
||||
if (contentEncoding != 'application/base64; charset=utf-8') {
|
||||
console.log('uncompressed');
|
||||
return parseChunk(csvtext);
|
||||
}
|
||||
|
||||
|
@ -87,6 +94,8 @@ export const fetchData = (
|
|||
// Assuming the CSV file is named "data.csv" inside the ZIP archive
|
||||
const csvContent = await zip.file('data.csv').async('text');
|
||||
|
||||
console.log(csvContent);
|
||||
|
||||
return parseChunk(csvContent);
|
||||
} else {
|
||||
return Promise.resolve(FetchResult.notAvailable);
|
||||
|
|
|
@ -14,6 +14,7 @@ import { InstallationsContext } from '../../../contexts/InstallationsContextProv
|
|||
import Installation from './Installation';
|
||||
import { WebSocketContext } from '../../../contexts/WebSocketContextProvider';
|
||||
import { UserType } from '../../../interfaces/UserTypes';
|
||||
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
|
||||
|
||||
function InstallationTabs() {
|
||||
const location = useLocation();
|
||||
|
@ -35,6 +36,8 @@ function InstallationTabs() {
|
|||
const { salimaxInstallations, fetchAllInstallations } =
|
||||
useContext(InstallationsContext);
|
||||
|
||||
const { product, setProduct } = useContext(ProductIdContext);
|
||||
|
||||
const webSocketsContext = useContext(WebSocketContext);
|
||||
const { socket, openSocket, closeSocket } = webSocketsContext;
|
||||
|
||||
|
@ -67,6 +70,10 @@ function InstallationTabs() {
|
|||
}
|
||||
}, [salimaxInstallations]);
|
||||
|
||||
useEffect(() => {
|
||||
setProduct(0);
|
||||
}, []);
|
||||
|
||||
const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {
|
||||
setCurrentTab(value);
|
||||
};
|
||||
|
|
|
@ -1291,6 +1291,140 @@ function Overview(props: OverviewProps) {
|
|||
</Card>
|
||||
</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>
|
||||
|
|
|
@ -157,7 +157,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
noWrap
|
||||
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||
>
|
||||
{installation.installationName}
|
||||
{installation.name}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
|
||||
|
|
|
@ -27,13 +27,15 @@ import SalidomoOverview from '../Overview/salidomoOverview';
|
|||
import { UserType } from '../../../interfaces/UserTypes';
|
||||
import HistoryOfActions from '../History/History';
|
||||
import BuildIcon from '@mui/icons-material/Build';
|
||||
import AccessContextProvider from '../../../contexts/AccessContextProvider';
|
||||
import Access from '../ManageAccess/Access';
|
||||
|
||||
interface singleInstallationProps {
|
||||
current_installation?: I_Installation;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
function Installation(props: singleInstallationProps) {
|
||||
function SalidomoInstallation(props: singleInstallationProps) {
|
||||
const context = useContext(UserContext);
|
||||
const { currentUser } = context;
|
||||
const location = useLocation().pathname;
|
||||
|
@ -77,18 +79,19 @@ function Installation(props: singleInstallationProps) {
|
|||
const continueFetching = useRef(false);
|
||||
|
||||
const fetchDataPeriodically = async () => {
|
||||
var timeperiodToSearch = 90;
|
||||
var timeperiodToSearch = 30;
|
||||
let res;
|
||||
let timestampToFetch;
|
||||
|
||||
for (var i = 0; i < timeperiodToSearch; i += 2) {
|
||||
for (var i = 0; i < timeperiodToSearch; i += 1) {
|
||||
if (!continueFetching.current) {
|
||||
return false;
|
||||
}
|
||||
timestampToFetch = UnixTime.now().earlier(TimeSpan.fromSeconds(i));
|
||||
timestampToFetch = UnixTime.now().earlier(TimeSpan.fromMinutes(i));
|
||||
console.log('timestamp to fetch is ' + timestampToFetch);
|
||||
|
||||
try {
|
||||
res = await fetchData(timestampToFetch, s3Credentials);
|
||||
res = await fetchData(timestampToFetch, s3Credentials, true);
|
||||
if (res !== FetchResult.notAvailable && res !== FetchResult.tryLater) {
|
||||
break;
|
||||
}
|
||||
|
@ -127,7 +130,7 @@ function Installation(props: singleInstallationProps) {
|
|||
await timeout(2000);
|
||||
}
|
||||
|
||||
timestampToFetch = timestampToFetch.later(TimeSpan.fromSeconds(60));
|
||||
timestampToFetch = timestampToFetch.later(TimeSpan.fromMinutes(20));
|
||||
console.log('NEW TIMESTAMP TO FETCH IS ' + timestampToFetch);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
|
@ -137,7 +140,7 @@ function Installation(props: singleInstallationProps) {
|
|||
|
||||
try {
|
||||
console.log('Trying to fetch timestamp ' + timestampToFetch);
|
||||
res = await fetchData(timestampToFetch, s3Credentials);
|
||||
res = await fetchData(timestampToFetch, s3Credentials, true);
|
||||
if (
|
||||
res !== FetchResult.notAvailable &&
|
||||
res !== FetchResult.tryLater
|
||||
|
@ -148,7 +151,7 @@ function Installation(props: singleInstallationProps) {
|
|||
console.error('Error fetching data:', err);
|
||||
return false;
|
||||
}
|
||||
timestampToFetch = timestampToFetch.later(TimeSpan.fromSeconds(1));
|
||||
timestampToFetch = timestampToFetch.later(TimeSpan.fromMinutes(1));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -213,7 +216,7 @@ function Installation(props: singleInstallationProps) {
|
|||
fontSize: '14px'
|
||||
}}
|
||||
>
|
||||
{props.current_installation.installationName}
|
||||
{props.current_installation.name}
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
|
@ -296,6 +299,7 @@ function Installation(props: singleInstallationProps) {
|
|||
{loading &&
|
||||
currentTab != 'information' &&
|
||||
currentTab != 'overview' &&
|
||||
currentTab != 'manage' &&
|
||||
currentTab != 'history' &&
|
||||
currentTab != 'log' && (
|
||||
<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
|
||||
path={'*'}
|
||||
element={<Navigate to={routes.information}></Navigate>}
|
||||
element={<Navigate to={routes.batteryview}></Navigate>}
|
||||
/>
|
||||
</Routes>
|
||||
</Grid>
|
||||
|
@ -396,4 +414,4 @@ function Installation(props: singleInstallationProps) {
|
|||
);
|
||||
}
|
||||
|
||||
export default Installation;
|
||||
export default SalidomoInstallation;
|
||||
|
|
|
@ -5,10 +5,7 @@ import FlatInstallationView from './FlatInstallationView';
|
|||
import { I_Installation } from '../../../interfaces/InstallationTypes';
|
||||
import { Route, Routes, useLocation } from 'react-router-dom';
|
||||
import routes from '../../../Resources/routes.json';
|
||||
import Installation from './Installation';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import Button from '@mui/material/Button';
|
||||
import SalidomonstallationForm from './SalidomoInstallationForm';
|
||||
import SalidomoInstallation from './Installation';
|
||||
|
||||
interface installationSearchProps {
|
||||
installations: I_Installation[];
|
||||
|
@ -18,26 +15,11 @@ function InstallationSearch(props: installationSearchProps) {
|
|||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const currentLocation = useLocation();
|
||||
const [filteredData, setFilteredData] = useState(props.installations);
|
||||
const [openModalInstallation, setOpenModalInstallation] = useState(false);
|
||||
|
||||
const handleNewInstallationInsertion = () => {
|
||||
setOpenModalInstallation(true);
|
||||
};
|
||||
|
||||
const handleInstallationFormSubmit = () => {
|
||||
setOpenModalInstallation(false);
|
||||
};
|
||||
|
||||
const handleFormCancel = () => {
|
||||
setOpenModalInstallation(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const filtered = props.installations.filter(
|
||||
(item) =>
|
||||
item.installationName
|
||||
.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase()) ||
|
||||
item.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
item.location.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
item.region.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
@ -46,12 +28,6 @@ function InstallationSearch(props: installationSearchProps) {
|
|||
|
||||
return (
|
||||
<>
|
||||
{openModalInstallation && (
|
||||
<SalidomonstallationForm
|
||||
cancel={handleFormCancel}
|
||||
submit={handleInstallationFormSubmit}
|
||||
/>
|
||||
)}
|
||||
<Grid container>
|
||||
<Grid
|
||||
item
|
||||
|
@ -74,17 +50,6 @@ function InstallationSearch(props: installationSearchProps) {
|
|||
alignItems: 'flex-start'
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleNewInstallationInsertion}
|
||||
sx={{ marginBottom: '8px' }}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="addNewInstallation"
|
||||
defaultMessage="Add new installation"
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<FormControl variant="outlined">
|
||||
<TextField
|
||||
placeholder="Search"
|
||||
|
@ -112,11 +77,11 @@ function InstallationSearch(props: installationSearchProps) {
|
|||
key={installation.s3BucketId}
|
||||
path={routes.installation + installation.s3BucketId + '*'}
|
||||
element={
|
||||
<Installation
|
||||
<SalidomoInstallation
|
||||
key={installation.s3BucketId}
|
||||
current_installation={installation}
|
||||
type="installation"
|
||||
></Installation>
|
||||
></SalidomoInstallation>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -21,26 +21,21 @@ import { FormattedMessage } from 'react-intl';
|
|||
interface SalidomoInstallationFormProps {
|
||||
cancel: () => void;
|
||||
submit: () => void;
|
||||
parentid: number;
|
||||
}
|
||||
|
||||
function SalidomonstallationForm(props: SalidomoInstallationFormProps) {
|
||||
const theme = useTheme();
|
||||
const [open, setOpen] = useState(true);
|
||||
const [formValues, setFormValues] = useState<Partial<I_Installation>>({
|
||||
installationName: '',
|
||||
name: '',
|
||||
region: '',
|
||||
location: '',
|
||||
country: '',
|
||||
vpnIp: '',
|
||||
vrmLink: ''
|
||||
});
|
||||
const requiredFields = [
|
||||
'installationName',
|
||||
'location',
|
||||
'country',
|
||||
'vpnIp',
|
||||
'vrmLink'
|
||||
];
|
||||
const requiredFields = ['name', 'location', 'country', 'vpnIp', 'vrmLink'];
|
||||
|
||||
const DeviceTypes = ['Cerbo', 'Venus'];
|
||||
const installationContext = useContext(InstallationsContext);
|
||||
|
@ -57,7 +52,7 @@ function SalidomonstallationForm(props: SalidomoInstallationFormProps) {
|
|||
};
|
||||
const handleSubmit = async (e) => {
|
||||
setLoading(true);
|
||||
formValues.parentId = 1;
|
||||
formValues.parentId = props.parentid;
|
||||
formValues.product = 1;
|
||||
const responseData = await createInstallation(formValues);
|
||||
props.submit();
|
||||
|
@ -120,11 +115,11 @@ function SalidomonstallationForm(props: SalidomoInstallationFormProps) {
|
|||
defaultMessage="Installation Name"
|
||||
/>
|
||||
}
|
||||
name="installationName"
|
||||
value={formValues.installationName}
|
||||
name="name"
|
||||
value={formValues.name}
|
||||
onChange={handleChange}
|
||||
required
|
||||
error={formValues.installationName === ''}
|
||||
error={formValues.name === ''}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
@ -9,12 +9,24 @@ import { UserContext } from '../../../contexts/userContext';
|
|||
import { InstallationsContext } from '../../../contexts/InstallationsContextProvider';
|
||||
import { WebSocketContext } from '../../../contexts/WebSocketContextProvider';
|
||||
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() {
|
||||
const location = useLocation();
|
||||
const context = useContext(UserContext);
|
||||
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 [fetchedInstallations, setFetchedInstallations] =
|
||||
|
@ -22,6 +34,8 @@ function SalidomoInstallationTabs() {
|
|||
const { salidomoInstallations, fetchAllSalidomoInstallations } =
|
||||
useContext(InstallationsContext);
|
||||
|
||||
const { product, setProduct } = useContext(ProductIdContext);
|
||||
|
||||
const webSocketsContext = useContext(WebSocketContext);
|
||||
const { socket, openSocket, closeSocket } = webSocketsContext;
|
||||
|
||||
|
@ -30,6 +44,8 @@ function SalidomoInstallationTabs() {
|
|||
|
||||
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)));
|
||||
|
@ -52,6 +68,10 @@ function SalidomoInstallationTabs() {
|
|||
}
|
||||
}, [salidomoInstallations]);
|
||||
|
||||
useEffect(() => {
|
||||
setProduct(1);
|
||||
}, []);
|
||||
|
||||
const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {
|
||||
setCurrentTab(value);
|
||||
};
|
||||
|
@ -75,14 +95,9 @@ function SalidomoInstallationTabs() {
|
|||
return ret_path;
|
||||
};
|
||||
|
||||
const tabs =
|
||||
currentTab != 'list' && !location.pathname.includes('folder')
|
||||
const singleInstallationTabs =
|
||||
currentUser.userType == UserType.admin
|
||||
? [
|
||||
{
|
||||
value: 'list',
|
||||
icon: <ListIcon id="mode-toggle-button-list-icon" />
|
||||
},
|
||||
|
||||
{
|
||||
value: 'batteryview',
|
||||
label: (
|
||||
|
@ -101,6 +116,16 @@ function SalidomoInstallationTabs() {
|
|||
label: <FormattedMessage id="log" defaultMessage="Log" />
|
||||
},
|
||||
|
||||
{
|
||||
value: 'manage',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="manage"
|
||||
defaultMessage="Access Management"
|
||||
/>
|
||||
)
|
||||
},
|
||||
|
||||
{
|
||||
value: 'information',
|
||||
label: (
|
||||
|
@ -119,71 +144,247 @@ function SalidomoInstallationTabs() {
|
|||
]
|
||||
: [
|
||||
{
|
||||
value: 'list',
|
||||
icon: <ListIcon id="mode-toggle-button-list-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" />
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<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'
|
||||
? 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>
|
||||
}
|
||||
/>
|
||||
const tabs =
|
||||
currentTab != 'list' &&
|
||||
currentTab != 'tree' &&
|
||||
!location.pathname.includes('folder') &&
|
||||
currentUser.userType == UserType.admin
|
||||
? [
|
||||
{
|
||||
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: 'log',
|
||||
label: <FormattedMessage id="log" defaultMessage="Log" />
|
||||
},
|
||||
|
||||
<Route
|
||||
path={'*'}
|
||||
element={
|
||||
<Navigate
|
||||
to={routes.salidomo_installations + routes.list}
|
||||
></Navigate>
|
||||
}
|
||||
></Route>
|
||||
</Routes>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
{
|
||||
value: 'manage',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="manage"
|
||||
defaultMessage="Access Management"
|
||||
/>
|
||||
)
|
||||
},
|
||||
|
||||
{
|
||||
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;
|
||||
|
|
|
@ -10,6 +10,7 @@ import CancelIcon from '@mui/icons-material/Cancel';
|
|||
import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
|
||||
import routes from 'src/Resources/routes.json';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
|
||||
|
||||
interface CustomTreeItemProps {
|
||||
node: I_Installation | I_Folder;
|
||||
|
@ -44,12 +45,15 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
|||
const navigate = useNavigate();
|
||||
const [selected, setSelected] = useState(false);
|
||||
const currentLocation = useLocation();
|
||||
const { product, setProduct } = useContext(ProductIdContext);
|
||||
|
||||
const handleSelectOneInstallation = (): void => {
|
||||
let installation = props.node;
|
||||
let path =
|
||||
product == 0 ? routes.installations : routes.salidomo_installations;
|
||||
if (installation.type != 'Folder') {
|
||||
navigate(
|
||||
routes.installations +
|
||||
path +
|
||||
routes.tree +
|
||||
routes.installation +
|
||||
installation.s3BucketId +
|
||||
|
@ -62,7 +66,7 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
|||
setSelected(!selected);
|
||||
} else {
|
||||
navigate(
|
||||
routes.installations +
|
||||
path +
|
||||
routes.tree +
|
||||
routes.folder +
|
||||
installation.id +
|
||||
|
@ -156,7 +160,8 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
|||
}
|
||||
sx={{
|
||||
display:
|
||||
currentLocation.pathname === routes.installations + 'tree' ||
|
||||
currentLocation.pathname ===
|
||||
routes.salidomo_installations + routes.tree ||
|
||||
currentLocation.pathname === routes.installations + routes.tree ||
|
||||
currentLocation.pathname.includes('folder')
|
||||
? 'block'
|
||||
|
|
|
@ -21,6 +21,8 @@ import FolderForm from './folderForm';
|
|||
import InstallationForm from '../Installations/installationForm';
|
||||
import { InstallationsContext } from '../../../contexts/InstallationsContextProvider';
|
||||
import { UserType } from '../../../interfaces/UserTypes';
|
||||
import SalidomoInstallationForm from '../SalidomoInstallations/SalidomoInstallationForm';
|
||||
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
|
||||
|
||||
interface TreeInformationProps {
|
||||
folder: I_Folder;
|
||||
|
@ -50,6 +52,8 @@ function TreeInformation(props: TreeInformationProps) {
|
|||
deleteFolder
|
||||
} = installationContext;
|
||||
|
||||
const { product, setProduct } = useContext(ProductIdContext);
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormValues({
|
||||
|
@ -61,7 +65,7 @@ function TreeInformation(props: TreeInformationProps) {
|
|||
const handleFolderInformationUpdate = () => {
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
updateFolder(formValues);
|
||||
updateFolder(formValues, product);
|
||||
};
|
||||
|
||||
const handleNewInstallationInsertion = () => {
|
||||
|
@ -80,7 +84,7 @@ function TreeInformation(props: TreeInformationProps) {
|
|||
|
||||
const deleteFolderModalHandle = () => {
|
||||
setOpenModalDeleteFolder(false);
|
||||
deleteFolder(formValues);
|
||||
deleteFolder(formValues, product);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
@ -195,13 +199,20 @@ function TreeInformation(props: TreeInformationProps) {
|
|||
parentid={props.folder.id}
|
||||
/>
|
||||
)}
|
||||
{openModalInstallation && (
|
||||
{openModalInstallation && product == 0 && (
|
||||
<InstallationForm
|
||||
cancel={handleFormCancel}
|
||||
submit={handleInstallationFormSubmit}
|
||||
parentid={props.folder.id}
|
||||
/>
|
||||
)}
|
||||
{openModalInstallation && product == 1 && (
|
||||
<SalidomoInstallationForm
|
||||
cancel={handleFormCancel}
|
||||
submit={handleInstallationFormSubmit}
|
||||
parentid={props.folder.id}
|
||||
/>
|
||||
)}
|
||||
<Container maxWidth="xl">
|
||||
<Grid
|
||||
container
|
||||
|
|
|
@ -8,19 +8,27 @@ import Installation from '../Installations/Installation';
|
|||
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import routes from '../../../Resources/routes.json';
|
||||
import Folder from './Folder';
|
||||
import { WebSocketContext } from '../../../contexts/WebSocketContextProvider';
|
||||
import SalidomoInstallation from '../SalidomoInstallations/Installation';
|
||||
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
|
||||
import Folder from './Folder';
|
||||
|
||||
function InstallationTree() {
|
||||
const { foldersAndInstallations, fetchAllFoldersAndInstallations } =
|
||||
useContext(InstallationsContext);
|
||||
|
||||
const { product, setProduct } = useContext(ProductIdContext);
|
||||
|
||||
const webSocketContext = useContext(WebSocketContext);
|
||||
const { getStatus } = webSocketContext;
|
||||
|
||||
const sortedInstallations = [...foldersAndInstallations].sort((a, b) => {
|
||||
// Compare the status field of each installation and sort them based on the status.
|
||||
//Installations with alarms go first
|
||||
|
||||
if (a.type == 'Folder') {
|
||||
return -1;
|
||||
}
|
||||
let a_status = getStatus(a.id);
|
||||
let b_status = getStatus(b.id);
|
||||
|
||||
|
@ -34,7 +42,7 @@ function InstallationTree() {
|
|||
});
|
||||
|
||||
useEffect(() => {
|
||||
fetchAllFoldersAndInstallations();
|
||||
fetchAllFoldersAndInstallations(product);
|
||||
}, []);
|
||||
|
||||
const TreeNode = ({ node, parent_id }) => {
|
||||
|
@ -61,17 +69,59 @@ function InstallationTree() {
|
|||
</CustomTreeItem>
|
||||
)
|
||||
);
|
||||
} else {
|
||||
} else if (node.type == 'Installation') {
|
||||
return (
|
||||
node.parentId == parent_id && (
|
||||
<CustomTreeItem node={node} parent_id={parent_id} />
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<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}>
|
||||
<TreeView
|
||||
defaultCollapseIcon={<ExpandMoreIcon />}
|
||||
|
@ -93,39 +143,6 @@ function InstallationTree() {
|
|||
})}
|
||||
</TreeView>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import { I_Folder } from 'src/interfaces/InstallationTypes';
|
|||
import { TokenContext } from 'src/contexts/tokenContext';
|
||||
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
|
||||
|
||||
interface folderFormProps {
|
||||
cancel: () => void;
|
||||
|
@ -29,7 +30,7 @@ function folderForm(props: folderFormProps) {
|
|||
information: ''
|
||||
});
|
||||
const requiredFields = ['name'];
|
||||
|
||||
const { product, setProduct } = useContext(ProductIdContext);
|
||||
const tokencontext = useContext(TokenContext);
|
||||
const { removeToken } = tokencontext;
|
||||
|
||||
|
@ -47,7 +48,7 @@ function folderForm(props: folderFormProps) {
|
|||
const handleSubmit = async (e) => {
|
||||
formValues.parentId = props.parentid;
|
||||
setLoading(true);
|
||||
const responseData = await createFolder(formValues);
|
||||
const responseData = await createFolder(formValues, product);
|
||||
|
||||
props.submit();
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@ interface I_InstallationContextProviderProps {
|
|||
foldersAndInstallations: I_Installation[];
|
||||
fetchAllInstallations: () => Promise<void>;
|
||||
fetchAllSalidomoInstallations: () => Promise<void>;
|
||||
fetchAllFoldersAndInstallations: () => Promise<void>;
|
||||
fetchAllFoldersAndInstallations: (product: number) => Promise<void>;
|
||||
createInstallation: (value: Partial<I_Installation>) => Promise<void>;
|
||||
updateInstallation: (value: I_Installation, view: string) => Promise<void>;
|
||||
loading: boolean;
|
||||
|
@ -28,9 +28,9 @@ interface I_InstallationContextProviderProps {
|
|||
updated: boolean;
|
||||
setUpdated: (value: boolean) => void;
|
||||
deleteInstallation: (value: I_Installation, view: string) => Promise<void>;
|
||||
createFolder: (value: Partial<I_Folder>) => Promise<void>;
|
||||
updateFolder: (value: I_Folder) => Promise<void>;
|
||||
deleteFolder: (value: I_Folder) => Promise<void>;
|
||||
createFolder: (value: Partial<I_Folder>, product: number) => Promise<void>;
|
||||
updateFolder: (value: I_Folder, product: number) => Promise<void>;
|
||||
deleteFolder: (value: I_Folder, product: number) => Promise<void>;
|
||||
}
|
||||
|
||||
export const InstallationsContext =
|
||||
|
@ -40,7 +40,7 @@ export const InstallationsContext =
|
|||
foldersAndInstallations: [],
|
||||
fetchAllInstallations: () => Promise.resolve(),
|
||||
fetchAllSalidomoInstallations: () => Promise.resolve(),
|
||||
fetchAllFoldersAndInstallations: () => Promise.resolve(),
|
||||
fetchAllFoldersAndInstallations: (product: number) => Promise.resolve(),
|
||||
createInstallation: () => Promise.resolve(),
|
||||
updateInstallation: () => Promise.resolve(),
|
||||
loading: false,
|
||||
|
@ -104,19 +104,22 @@ const InstallationsContextProvider = ({
|
|||
});
|
||||
}, []);
|
||||
|
||||
const fetchAllFoldersAndInstallations = useCallback(async () => {
|
||||
return axiosConfig
|
||||
.get('/GetAllFoldersAndInstallations')
|
||||
.then((res) => {
|
||||
setFoldersAndInstallations(res.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.response && err.response.status == 401) {
|
||||
removeToken();
|
||||
navigate(routes.login);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
const fetchAllFoldersAndInstallations = useCallback(
|
||||
async (product: number) => {
|
||||
return axiosConfig
|
||||
.get(`/GetAllFoldersAndInstallations?productId=${product}`)
|
||||
.then((res) => {
|
||||
setFoldersAndInstallations(res.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.response && err.response.status == 401) {
|
||||
removeToken();
|
||||
navigate(routes.login);
|
||||
}
|
||||
});
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const createInstallation = useCallback(
|
||||
async (formValues: Partial<I_Installation>) => {
|
||||
|
@ -124,11 +127,12 @@ const InstallationsContextProvider = ({
|
|||
.post('/CreateInstallation', formValues)
|
||||
.then((res) => {
|
||||
setLoading(false);
|
||||
if (formValues.product == 0) {
|
||||
fetchAllFoldersAndInstallations();
|
||||
} else {
|
||||
fetchAllSalidomoInstallations();
|
||||
}
|
||||
fetchAllFoldersAndInstallations(formValues.product);
|
||||
// if (formValues.product == 0) {
|
||||
// fetchAllFoldersAndInstallations();
|
||||
// } else {
|
||||
// fetchAllSalidomoInstallations();
|
||||
// }
|
||||
})
|
||||
|
||||
.catch((error) => {
|
||||
|
@ -151,14 +155,12 @@ const InstallationsContextProvider = ({
|
|||
if (response) {
|
||||
setLoading(false);
|
||||
setUpdated(true);
|
||||
if (formValues.product == 0) {
|
||||
if (view == 'installation') {
|
||||
fetchAllInstallations();
|
||||
} else {
|
||||
fetchAllFoldersAndInstallations();
|
||||
}
|
||||
} else {
|
||||
if (formValues.product == 0 && view == 'installation') {
|
||||
fetchAllInstallations();
|
||||
} else if (formValues.product == 1 && view == 'installation') {
|
||||
fetchAllSalidomoInstallations();
|
||||
} else {
|
||||
fetchAllFoldersAndInstallations(formValues.product);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
|
@ -186,14 +188,12 @@ const InstallationsContextProvider = ({
|
|||
if (response) {
|
||||
setLoading(false);
|
||||
setUpdated(true);
|
||||
if (formValues.product == 0) {
|
||||
if (view == 'installation') {
|
||||
fetchAllInstallations();
|
||||
} else {
|
||||
fetchAllFoldersAndInstallations();
|
||||
}
|
||||
} else {
|
||||
if (formValues.product == 0 && view == 'installation') {
|
||||
fetchAllInstallations();
|
||||
} else if (formValues.product == 1 && view == 'installation') {
|
||||
fetchAllSalidomoInstallations();
|
||||
} else {
|
||||
fetchAllFoldersAndInstallations(formValues.product);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
|
@ -213,71 +213,80 @@ const InstallationsContextProvider = ({
|
|||
[]
|
||||
);
|
||||
|
||||
const createFolder = useCallback(async (formValues: Partial<I_Folder>) => {
|
||||
axiosConfig
|
||||
.post('/CreateFolder', formValues)
|
||||
.then((res) => {
|
||||
setLoading(false);
|
||||
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) {
|
||||
const createFolder = useCallback(
|
||||
async (formValues: Partial<I_Folder>, product: number) => {
|
||||
axiosConfig
|
||||
.post('/CreateFolder', formValues)
|
||||
.then((res) => {
|
||||
setLoading(false);
|
||||
setUpdated(true);
|
||||
fetchAllFoldersAndInstallations();
|
||||
fetchAllFoldersAndInstallations(product);
|
||||
})
|
||||
|
||||
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) => {
|
||||
axiosConfig
|
||||
.delete(`/DeleteFolder?folderId=${formValues.id}`)
|
||||
.then((response) => {
|
||||
if (response) {
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
setUpdated(true);
|
||||
fetchAllFoldersAndInstallations();
|
||||
setError(true);
|
||||
if (error.response && error.response.status == 401) {
|
||||
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 updateFolder = useCallback(
|
||||
async (formValues: I_Folder, product: number) => {
|
||||
axiosConfig
|
||||
.put('/UpdateFolder', formValues)
|
||||
.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);
|
||||
}
|
||||
});
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
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 (
|
||||
<InstallationsContext.Provider
|
||||
|
|
|
@ -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;
|
|
@ -18,12 +18,8 @@ export const WebSocketContext = createContext<
|
|||
const WebSocketContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [socket, setSocket] = useState<WebSocket>(null);
|
||||
const [installations, setInstallations] = useState<I_Installation[]>(null);
|
||||
const [installationStatus, setInstallationStatus] = useState<
|
||||
Record<number, number>
|
||||
>({});
|
||||
const [installationMode, setInstallatioMode] = useState<
|
||||
Record<number, boolean>
|
||||
>({});
|
||||
const [installationStatus, setInstallationStatus] = useState(new Map());
|
||||
const [installationMode, setInstallationMode] = useState(new Map());
|
||||
const BUFFER_LENGTH = 5;
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -53,22 +49,33 @@ const WebSocketContextProvider = ({ children }: { children: ReactNode }) => {
|
|||
// Listen for messages
|
||||
socket.addEventListener('message', (event) => {
|
||||
const message = JSON.parse(event.data); // Parse the JSON data
|
||||
|
||||
if (message.id != -1) {
|
||||
const installation_id = message.id;
|
||||
|
||||
//console.log('Message from server ', installation_id, status);
|
||||
setInstallatioMode((prevMode) => {
|
||||
// Create a new object by spreading the previous state
|
||||
const newMode = { ...prevMode };
|
||||
newMode[installation_id] = message.testingMode;
|
||||
if (Array.isArray(message)) {
|
||||
setInstallationMode((prevMode) => {
|
||||
const newMode = new Map(prevMode);
|
||||
message.forEach((item) => {
|
||||
newMode.set(item.id, item.testingMode);
|
||||
});
|
||||
return newMode;
|
||||
});
|
||||
|
||||
setInstallationStatus((prevStatus) => {
|
||||
// Create a new object by spreading the previous state
|
||||
const newStatus = { ...prevStatus };
|
||||
newStatus[installation_id] = message.status;
|
||||
const newStatus = new Map(prevStatus);
|
||||
message.forEach((item) => {
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
@ -86,17 +93,16 @@ const WebSocketContextProvider = ({ children }: { children: ReactNode }) => {
|
|||
};
|
||||
|
||||
const getStatus = (installationId: number) => {
|
||||
let status;
|
||||
if (!installationStatus.hasOwnProperty(installationId)) {
|
||||
status = -2;
|
||||
} else {
|
||||
status = installationStatus[installationId];
|
||||
}
|
||||
return status;
|
||||
return installationStatus.get(installationId);
|
||||
// if (installationStatus.has(installationId)) {
|
||||
// installationStatus.get(installationId);
|
||||
// } else {
|
||||
// return -2;
|
||||
// }
|
||||
};
|
||||
|
||||
const getTestingMode = (installationId: number) => {
|
||||
return installationMode[installationId];
|
||||
return installationMode.get(installationId);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -8,6 +8,7 @@ import { SidebarProvider } from 'src/contexts/SidebarContext';
|
|||
import * as serviceWorker from 'src/serviceWorker';
|
||||
import UserContextProvider from './contexts/userContext';
|
||||
import TokenContextProvider from './contexts/tokenContext';
|
||||
import ProductIdContextProvider from './contexts/ProductIdContextProvider';
|
||||
|
||||
ReactDOM.render(
|
||||
<HelmetProvider>
|
||||
|
@ -15,7 +16,9 @@ ReactDOM.render(
|
|||
<BrowserRouter>
|
||||
<UserContextProvider>
|
||||
<TokenContextProvider>
|
||||
<App />
|
||||
<ProductIdContextProvider>
|
||||
<App />
|
||||
</ProductIdContextProvider>
|
||||
</TokenContextProvider>
|
||||
</UserContextProvider>
|
||||
</BrowserRouter>
|
||||
|
|
|
@ -26,6 +26,8 @@ export interface overviewInterface {
|
|||
pvProduction: chartInfoInterface;
|
||||
dcBusVoltage: chartInfoInterface;
|
||||
overview: chartInfoInterface;
|
||||
ACLoad: chartInfoInterface;
|
||||
DCLoad: chartInfoInterface;
|
||||
}
|
||||
|
||||
export interface chartAggregatedDataInterface {
|
||||
|
@ -46,6 +48,8 @@ export interface chartDataInterface {
|
|||
gridPower: { name: string; data: number[] };
|
||||
pvProduction: { name: string; data: number[] };
|
||||
dcBusVoltage: { name: string; data: number[] };
|
||||
ACLoad: { name: string; data: number[] };
|
||||
DCLoad: { name: string; data: number[] };
|
||||
}
|
||||
|
||||
export interface BatteryDataInterface {
|
||||
|
@ -339,7 +343,9 @@ export const transformInputToDailyData = async (
|
|||
'/Battery/Dc/Power',
|
||||
'/GridMeter/Ac/Power/Active',
|
||||
'/PvOnDc/Dc/Power',
|
||||
'/DcDc/Dc/Link/Voltage'
|
||||
'/DcDc/Dc/Link/Voltage',
|
||||
'/LoadOnAcGrid/Power/Active',
|
||||
'/LoadOnDc/Power'
|
||||
];
|
||||
const categories = [
|
||||
'soc',
|
||||
|
@ -347,7 +353,9 @@ export const transformInputToDailyData = async (
|
|||
'dcPower',
|
||||
'gridPower',
|
||||
'pvProduction',
|
||||
'dcBusVoltage'
|
||||
'dcBusVoltage',
|
||||
'ACLoad',
|
||||
'DCLoad'
|
||||
];
|
||||
|
||||
const chartData: chartDataInterface = {
|
||||
|
@ -356,7 +364,9 @@ export const transformInputToDailyData = async (
|
|||
dcPower: { name: 'Battery Power', data: [] },
|
||||
gridPower: { name: 'Grid Power', 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 = {
|
||||
|
@ -367,7 +377,9 @@ export const transformInputToDailyData = async (
|
|||
gridPower: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
pvProduction: { 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) => {
|
||||
|
@ -560,7 +572,9 @@ export const transformInputToAggregatedData = async (
|
|||
gridPower: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
pvProduction: { 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) => {
|
||||
|
|
|
@ -16,6 +16,7 @@ import TableChartTwoToneIcon from '@mui/icons-material/TableChartTwoTone';
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
import { UserContext } from '../../../../contexts/userContext';
|
||||
import { UserType } from '../../../../interfaces/UserTypes';
|
||||
import { ProductIdContext } from '../../../../contexts/ProductIdContextProvider';
|
||||
|
||||
const MenuWrapper = styled(Box)(
|
||||
({ theme }) => `
|
||||
|
@ -163,6 +164,7 @@ function SidebarMenu() {
|
|||
const { closeSidebar } = useContext(SidebarContext);
|
||||
const context = useContext(UserContext);
|
||||
const { currentUser, setUser } = context;
|
||||
const { accessToSalimax, accessToSalidomo } = useContext(ProductIdContext);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -176,23 +178,25 @@ function SidebarMenu() {
|
|||
}
|
||||
>
|
||||
<SubMenuWrapper>
|
||||
<List component="div">
|
||||
<ListItem component="div">
|
||||
<Button
|
||||
disableRipple
|
||||
component={RouterLink}
|
||||
onClick={closeSidebar}
|
||||
to="/installations"
|
||||
startIcon={<BrightnessLowTwoToneIcon />}
|
||||
>
|
||||
<Box sx={{ marginTop: '3px' }}>
|
||||
<FormattedMessage id="salimax" defaultMessage="Salimax" />
|
||||
</Box>
|
||||
</Button>
|
||||
</ListItem>
|
||||
</List>
|
||||
{accessToSalimax && (
|
||||
<List component="div">
|
||||
<ListItem component="div">
|
||||
<Button
|
||||
disableRipple
|
||||
component={RouterLink}
|
||||
onClick={closeSidebar}
|
||||
to="/installations"
|
||||
startIcon={<BrightnessLowTwoToneIcon />}
|
||||
>
|
||||
<Box sx={{ marginTop: '3px' }}>
|
||||
<FormattedMessage id="salimax" defaultMessage="Salimax" />
|
||||
</Box>
|
||||
</Button>
|
||||
</ListItem>
|
||||
</List>
|
||||
)}
|
||||
|
||||
{currentUser.userType == UserType.admin && (
|
||||
{accessToSalidomo && (
|
||||
<List component="div">
|
||||
<ListItem component="div">
|
||||
<Button
|
||||
|
|
Loading…
Reference in New Issue