Merge remote-tracking branch 'origin/main'
# Conflicts: # typescript/Frontend/src/components/Context/S3CredentialsContextProvider.tsx
This commit is contained in:
commit
1ed3a64ade
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
Prototype ie-entwicklung@10.2.3.115
|
||||||
|
Salimax001 ie-entwicklung@10.2.3.104
|
||||||
|
Salimax002 ie-entwicklung@10.2.4.29
|
||||||
|
Salimax003 ie-entwicklung@10.2.4.33
|
||||||
|
Salimax004 ie-entwicklung@10.2.4.32
|
14
csharp/App/SaliMax/run (Salimax0002).sh → csharp/App/SaliMax/deploy.sh
Normal file → Executable file
14
csharp/App/SaliMax/run (Salimax0002).sh → csharp/App/SaliMax/deploy.sh
Normal file → Executable file
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
dotnet_version='net6.0'
|
dotnet_version='net6.0'
|
||||||
salimax_ip='10.2.4.29'
|
salimax_ip="$1"
|
||||||
username='ie-entwicklung'
|
username='ie-entwicklung'
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
@ -20,15 +20,3 @@ rsync -v \
|
||||||
./bin/Release/$dotnet_version/linux-x64/publish/* \
|
./bin/Release/$dotnet_version/linux-x64/publish/* \
|
||||||
$username@$salimax_ip:~/salimax
|
$username@$salimax_ip:~/salimax
|
||||||
|
|
||||||
echo -e "\n============================ Restart Salimax sevice ============================\n"
|
|
||||||
|
|
||||||
ssh -tt \
|
|
||||||
$username@$salimax_ip \
|
|
||||||
sudo systemctl restart salimax.service
|
|
||||||
|
|
||||||
|
|
||||||
echo -e "\n============================ Print service output ============================\n"
|
|
||||||
|
|
||||||
ssh -tt \
|
|
||||||
$username@$salimax_ip \
|
|
||||||
journalctl -f -u salimax.service
|
|
|
@ -1,34 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
dotnet_version='net6.0'
|
|
||||||
salimax_ip='10.2.3.104'
|
|
||||||
username='ie-entwicklung'
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo -e "\n============================ Build ============================\n"
|
|
||||||
|
|
||||||
dotnet publish \
|
|
||||||
./SaliMax.csproj \
|
|
||||||
-p:PublishTrimmed=false \
|
|
||||||
-c Release \
|
|
||||||
-r linux-x64
|
|
||||||
|
|
||||||
echo -e "\n============================ Deploy ============================\n"
|
|
||||||
|
|
||||||
rsync -v \
|
|
||||||
./bin/Release/$dotnet_version/linux-x64/publish/* \
|
|
||||||
$username@$salimax_ip:~/salimax
|
|
||||||
|
|
||||||
echo -e "\n============================ Restart Salimax sevice ============================\n"
|
|
||||||
|
|
||||||
ssh -tt \
|
|
||||||
$username@$salimax_ip \
|
|
||||||
sudo systemctl restart salimax.service
|
|
||||||
|
|
||||||
|
|
||||||
echo -e "\n============================ Print service output ============================\n"
|
|
||||||
|
|
||||||
ssh -tt \
|
|
||||||
$username@$salimax_ip \
|
|
||||||
journalctl -f -u salimax.service
|
|
|
@ -1,34 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
dotnet_version='net6.0'
|
|
||||||
salimax_ip='10.2.4.33'
|
|
||||||
username='ie-entwicklung'
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo -e "\n============================ Build ============================\n"
|
|
||||||
|
|
||||||
dotnet publish \
|
|
||||||
./SaliMax.csproj \
|
|
||||||
-p:PublishTrimmed=false \
|
|
||||||
-c Release \
|
|
||||||
-r linux-x64
|
|
||||||
|
|
||||||
echo -e "\n============================ Deploy ============================\n"
|
|
||||||
|
|
||||||
rsync -v \
|
|
||||||
./bin/Release/$dotnet_version/linux-x64/publish/* \
|
|
||||||
$username@$salimax_ip:~/salimax
|
|
||||||
|
|
||||||
echo -e "\n============================ Restart Salimax sevice ============================\n"
|
|
||||||
|
|
||||||
ssh -tt \
|
|
||||||
$username@$salimax_ip \
|
|
||||||
sudo systemctl restart salimax.service
|
|
||||||
|
|
||||||
|
|
||||||
echo -e "\n============================ Print service output ============================\n"
|
|
||||||
|
|
||||||
ssh -tt \
|
|
||||||
$username@$salimax_ip \
|
|
||||||
journalctl -f -u salimax.service
|
|
|
@ -1,34 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
dotnet_version='net6.0'
|
|
||||||
salimax_ip='10.2.3.115'
|
|
||||||
username='ie-entwicklung'
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo -e "\n============================ Build ============================\n"
|
|
||||||
|
|
||||||
dotnet publish \
|
|
||||||
./SaliMax.csproj \
|
|
||||||
-p:PublishTrimmed=false \
|
|
||||||
-c Release \
|
|
||||||
-r linux-x64
|
|
||||||
|
|
||||||
echo -e "\n============================ Deploy ============================\n"
|
|
||||||
|
|
||||||
rsync -v \
|
|
||||||
./bin/Release/$dotnet_version/linux-x64/publish/* \
|
|
||||||
$username@$salimax_ip:~/salimax
|
|
||||||
|
|
||||||
echo -e "\n============================ Restart Salimax sevice ============================\n"
|
|
||||||
|
|
||||||
ssh -tt \
|
|
||||||
$username@$salimax_ip \
|
|
||||||
sudo systemctl restart salimax.service
|
|
||||||
|
|
||||||
|
|
||||||
echo -e "\n============================ Print service output ============================\n"
|
|
||||||
|
|
||||||
ssh -tt \
|
|
||||||
$username@$salimax_ip \
|
|
||||||
journalctl -f -u salimax.service
|
|
|
@ -21,9 +21,19 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
|
||||||
public required Double BatterySelfDischargePower { get; set; }
|
public required Double BatterySelfDischargePower { get; set; }
|
||||||
public required Double HoldSocZone { get; set; }
|
public required Double HoldSocZone { get; set; }
|
||||||
|
|
||||||
public required Double MaxDcBusVoltage { get; set; }
|
public required Double MaxDcLinkVoltageFromAcDc { get; set; }
|
||||||
public required Double MinDcBusVoltage { get; set; }
|
public required Double MinDcLinkVoltageFromAcDc { get; set; }
|
||||||
public required Double ReferenceDcBusVoltage { get; set; }
|
public required Double ReferenceDcLinkVoltageFromAcDc { get; set; }
|
||||||
|
|
||||||
|
public required Double LowerDcLinkVoltageFromDc { get; set; }
|
||||||
|
public required Double ReferenceDcLinkVoltageFromDc { get; set; }
|
||||||
|
public required Double UpperDcLinkVoltageFromDc { get; set; }
|
||||||
|
|
||||||
|
public required Double MaxBatteryChargingCurrent { get; set; }
|
||||||
|
public required Double MaxBatteryDischargingCurrent { get; set; }
|
||||||
|
|
||||||
|
public required Double MaxChargeBatteryVoltage { get; set; }
|
||||||
|
public required Double MinDischargeBatteryVoltage { get; set; }
|
||||||
|
|
||||||
public required DeviceConfig Devices { get; set; }
|
public required DeviceConfig Devices { get; set; }
|
||||||
public required S3Config? S3 { get; set; }
|
public required S3Config? S3 { get; set; }
|
||||||
|
@ -42,6 +52,9 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
|
||||||
MinDcBusVoltage = 690,
|
MinDcBusVoltage = 690,
|
||||||
ReferenceDcBusVoltage = 750,
|
ReferenceDcBusVoltage = 750,
|
||||||
MaxDcBusVoltage = 810,
|
MaxDcBusVoltage = 810,
|
||||||
|
LowerDcBusVoltageWindow = 50,
|
||||||
|
ReferenceDcBusVoltageWindow = 750,
|
||||||
|
UpperDcBusVoltageWindow = 50,
|
||||||
Devices = new ()
|
Devices = new ()
|
||||||
{
|
{
|
||||||
TruConvertAcIp = new() { Host = "localhost", Port = 5001},
|
TruConvertAcIp = new() { Host = "localhost", Port = 5001},
|
||||||
|
@ -64,9 +77,16 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
|
||||||
GridSetPoint = 0,
|
GridSetPoint = 0,
|
||||||
BatterySelfDischargePower = 200,
|
BatterySelfDischargePower = 200,
|
||||||
HoldSocZone = 1, // TODO: find better name,
|
HoldSocZone = 1, // TODO: find better name,
|
||||||
MinDcBusVoltage = 690,
|
MinDcLinkVoltageFromAcDc = 690,
|
||||||
ReferenceDcBusVoltage = 750,
|
ReferenceDcLinkVoltageFromAcDc = 750,
|
||||||
MaxDcBusVoltage = 810,
|
MaxDcLinkVoltageFromAcDc = 810,
|
||||||
|
LowerDcLinkVoltageFromDc = 50,
|
||||||
|
ReferenceDcLinkVoltageFromDc = 750,
|
||||||
|
UpperDcLinkVoltageFromDc = 50,
|
||||||
|
MaxBatteryChargingCurrent = 210,
|
||||||
|
MaxBatteryDischargingCurrent = 210,
|
||||||
|
MaxChargeBatteryVoltage = 57,
|
||||||
|
MinDischargeBatteryVoltage = 0,
|
||||||
S3 = new()
|
S3 = new()
|
||||||
{
|
{
|
||||||
Bucket = "saliomameiringen",
|
Bucket = "saliomameiringen",
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Tunnels</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div style="font-family: sans-serif; margin: 20px">
|
|
||||||
<a target="_blank" href="http://localhost:8001">Inverter</a><br>
|
|
||||||
<a target="_blank" href="http://localhost:8002">DCDC</a><br>
|
|
||||||
<a target="_blank" href="http://localhost:8003">Emu Meter</a><br>
|
|
||||||
<a target="_blank" href="http://localhost:8004">ADAM</a><br>
|
|
||||||
<a target="_blank" href="http://localhost:8005">AMPT</a><br>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,44 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
host=ie-entwicklung@10.2.3.115
|
|
||||||
|
|
||||||
tunnel() {
|
|
||||||
name=$1
|
|
||||||
ip=$2
|
|
||||||
rPort=$3
|
|
||||||
lPort=$4
|
|
||||||
|
|
||||||
echo -n "localhost:$lPort $name "
|
|
||||||
ssh -nNTL "$lPort:$ip:$rPort" "$host" 2> /dev/null &
|
|
||||||
|
|
||||||
until nc -vz 127.0.0.1 $lPort 2> /dev/null
|
|
||||||
do
|
|
||||||
echo -n .
|
|
||||||
sleep 0.3
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "ok"
|
|
||||||
}
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
tunnel "Trumpf Inverter (http) " 10.0.2.1 80 8001
|
|
||||||
tunnel "Trumpf DCDC (http) " 10.0.3.1 80 8002
|
|
||||||
tunnel "Ext Emu Meter (http) " 10.0.4.1 80 8003
|
|
||||||
tunnel "Int Emu Meter (http) " 10.0.4.2 80 8004
|
|
||||||
tunnel "AMPT (http) " 10.0.5.1 8080 8005
|
|
||||||
|
|
||||||
tunnel "Trumpf Inverter (modbus)" 10.0.2.1 502 5001
|
|
||||||
tunnel "Trumpf DCDC (modbus) " 10.0.3.1 502 5002
|
|
||||||
tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 5003
|
|
||||||
tunnel "Int Emu Meter " 10.0.4.2 502 5004
|
|
||||||
tunnel "AMPT (modbus) " 10.0.5.1 502 5005
|
|
||||||
tunnel "Adam " 10.0.1.1 502 5006
|
|
||||||
tunnel "Batteries " 127.0.0.1 6855 5007
|
|
||||||
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "press any key to close the tunnels ..."
|
|
||||||
read -r -n 1 -s
|
|
||||||
kill $(jobs -p)
|
|
||||||
echo "done"
|
|
|
@ -1,43 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
host=ie-entwicklung@10.2.4.29
|
|
||||||
|
|
||||||
tunnel() {
|
|
||||||
name=$1
|
|
||||||
ip=$2
|
|
||||||
rPort=$3
|
|
||||||
lPort=$4
|
|
||||||
|
|
||||||
echo -n "localhost:$lPort $name "
|
|
||||||
ssh -nNTL "$lPort:$ip:$rPort" "$host" 2> /dev/null &
|
|
||||||
|
|
||||||
until nc -vz 127.0.0.1 $lPort 2> /dev/null
|
|
||||||
do
|
|
||||||
echo -n .
|
|
||||||
sleep 0.3
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "ok"
|
|
||||||
}
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
tunnel "Trumpf Inverter (http) " 10.0.2.1 80 9001
|
|
||||||
tunnel "Trumpf DCDC (http) " 10.0.3.1 80 9002
|
|
||||||
tunnel "Ext Emu Meter (http) " 10.0.4.1 80 9003
|
|
||||||
tunnel "Int Emu Meter (http) " 10.0.4.2 80 9004
|
|
||||||
tunnel "AMPT (http) " 10.0.5.1 8080 9005
|
|
||||||
|
|
||||||
#tunnel "Trumpf Inverter (modbus)" 10.0.2.1 502 3001
|
|
||||||
#tunnel "Trumpf DCDC (modbus) " 10.0.3.1 502 3002
|
|
||||||
#tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 3003
|
|
||||||
#tunnel "Int Emu Meter " 10.0.4.2 502 3004
|
|
||||||
#tunnel "AMPT (modbus) " 10.0.5.1 502 3005
|
|
||||||
#tunnel "Batteries " 127.0.0.1 6855 3007
|
|
||||||
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "press any key to close the tunnels ..."
|
|
||||||
read -r -n 1 -s
|
|
||||||
kill $(jobs -p)
|
|
||||||
echo "done"
|
|
|
@ -1,43 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
host=ie-entwicklung@10.2.4.33
|
|
||||||
|
|
||||||
tunnel() {
|
|
||||||
name=$1
|
|
||||||
ip=$2
|
|
||||||
rPort=$3
|
|
||||||
lPort=$4
|
|
||||||
|
|
||||||
echo -n "localhost:$lPort $name "
|
|
||||||
ssh -nNTL "$lPort:$ip:$rPort" "$host" 2> /dev/null &
|
|
||||||
|
|
||||||
until nc -vz 127.0.0.1 $lPort 2> /dev/null
|
|
||||||
do
|
|
||||||
echo -n .
|
|
||||||
sleep 0.3
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "ok"
|
|
||||||
}
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
tunnel "Trumpf Inverter (http) " 10.0.2.1 80 6001
|
|
||||||
tunnel "Trumpf DCDC (http) " 10.0.3.1 80 6002
|
|
||||||
tunnel "Ext Emu Meter (http) " 10.0.4.1 80 6003
|
|
||||||
tunnel "Int Emu Meter (http) " 10.0.4.2 80 6004
|
|
||||||
tunnel "AMPT (http) " 10.0.5.1 8080 6005
|
|
||||||
|
|
||||||
tunnel "Trumpf Inverter (modbus)" 10.0.2.1 502 3001
|
|
||||||
tunnel "Trumpf DCDC (modbus) " 10.0.3.1 502 3002
|
|
||||||
tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 3003
|
|
||||||
tunnel "Int Emu Meter " 10.0.4.2 502 3004
|
|
||||||
tunnel "AMPT (modbus) " 10.0.5.1 502 3005
|
|
||||||
tunnel "Batteries " 127.0.0.1 6855 3007
|
|
||||||
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "press any key to close the tunnels ..."
|
|
||||||
read -r -n 1 -s
|
|
||||||
kill $(jobs -p)
|
|
||||||
echo "done"
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
host=ie-entwicklung@10.2.3.104
|
host="ie-entwicklung@$1"
|
||||||
|
|
||||||
tunnel() {
|
tunnel() {
|
||||||
name=$1
|
name=$1
|
||||||
|
@ -27,19 +27,16 @@ tunnel "Trumpf DCDC (http) " 10.0.3.1 80 7002
|
||||||
tunnel "Ext Emu Meter (http) " 10.0.4.1 80 7003
|
tunnel "Ext Emu Meter (http) " 10.0.4.1 80 7003
|
||||||
tunnel "Int Emu Meter (http) " 10.0.4.2 80 7004
|
tunnel "Int Emu Meter (http) " 10.0.4.2 80 7004
|
||||||
tunnel "AMPT (http) " 10.0.5.1 8080 7005
|
tunnel "AMPT (http) " 10.0.5.1 8080 7005
|
||||||
|
tunnel "Doepke (http) " 10.0.6.1 80 7006
|
||||||
|
|
||||||
|
tunnel "Trumpf Inverter (modbus)" 10.0.2.1 502 5001
|
||||||
tunnel "Trumpf Inverter (modbus)" 10.0.2.1 502 4001
|
tunnel "Trumpf DCDC (modbus) " 10.0.3.1 502 5002
|
||||||
tunnel "Trumpf DCDC (modbus) " 10.0.3.1 502 4002
|
tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 5003
|
||||||
tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 4003
|
tunnel "Int Emu Meter " 10.0.4.2 502 5004
|
||||||
tunnel "Int Emu Meter " 10.0.4.2 502 4004
|
tunnel "AMPT (modbus) " 10.0.5.1 502 5005
|
||||||
tunnel "AMPT (modbus) " 10.0.5.1 502 4005
|
|
||||||
tunnel "Adam " 10.0.1.1 502 4006
|
|
||||||
tunnel "Batteries " 127.0.0.1 6855 5007
|
tunnel "Batteries " 127.0.0.1 6855 5007
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "press any key to close the tunnels ..."
|
echo "press any key to close the tunnels ..."
|
||||||
read -r -n 1 -s
|
read -r -n 1 -s
|
|
@ -13,7 +13,8 @@ public class Battery48TlRecords
|
||||||
Eoc = !empty && records.All(r => r.Eoc);
|
Eoc = !empty && records.All(r => r.Eoc);
|
||||||
Warnings = records.SelectMany(r => r.Warnings).Distinct().ToList();
|
Warnings = records.SelectMany(r => r.Warnings).Distinct().ToList();
|
||||||
Alarms = records.SelectMany(r => r.Alarms).Distinct().ToList();
|
Alarms = records.SelectMany(r => r.Alarms).Distinct().ToList();
|
||||||
Soc = empty ? 0 : records.Min(r => r.Soc.Value);
|
Soc = empty ? 0 : records.Average(r => r.Soc.Value);
|
||||||
|
CurrentMinSoc = empty ? 0 : records.Min(r => r.Soc.Value);
|
||||||
Temperature = records.Any() ? records.Average(b => b.Temperatures.Cells.Average.Value) : 0;
|
Temperature = records.Any() ? records.Average(b => b.Temperatures.Cells.Average.Value) : 0;
|
||||||
HeatingCurrent = records.Any() ? records.Sum(b => b.HeatingCurrent) : 0;
|
HeatingCurrent = records.Any() ? records.Sum(b => b.HeatingCurrent) : 0;
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ public class Battery48TlRecords
|
||||||
public IReadOnlyList<String> Warnings { get; init; }
|
public IReadOnlyList<String> Warnings { get; init; }
|
||||||
public IReadOnlyList<String> Alarms { get; init; }
|
public IReadOnlyList<String> Alarms { get; init; }
|
||||||
public Percent Soc { get; init; }
|
public Percent Soc { get; init; }
|
||||||
|
public Percent CurrentMinSoc { get; init; }
|
||||||
public Temperature Temperature { get; init; }
|
public Temperature Temperature { get; init; }
|
||||||
public Current HeatingCurrent { get; init; }
|
public Current HeatingCurrent { get; init; }
|
||||||
|
|
||||||
|
|
|
@ -9,4 +9,6 @@ public readonly partial struct UnixTime
|
||||||
// IMPORTANT: init is necessary for JSON deserializer
|
// IMPORTANT: init is necessary for JSON deserializer
|
||||||
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
|
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
|
||||||
public UInt32 Ticks { get; init; }
|
public UInt32 Ticks { get; init; }
|
||||||
|
|
||||||
|
public UnixTime RoundTo(UnixTimeSpan span) => Epoch + this / span * span;
|
||||||
}
|
}
|
|
@ -1,136 +0,0 @@
|
||||||
{
|
|
||||||
"Information": {
|
|
||||||
"defaultMessage": "Information"
|
|
||||||
},
|
|
||||||
"addNewChild": {
|
|
||||||
"defaultMessage": "Neues Kind hinzufügen"
|
|
||||||
},
|
|
||||||
"addNewDialogButton": {
|
|
||||||
"defaultMessage": "Neue Dialogschaltfläche hinzufügen"
|
|
||||||
},
|
|
||||||
"addUser": {
|
|
||||||
"defaultMessage": "Nutzer erstellen"
|
|
||||||
},
|
|
||||||
"alarms": {
|
|
||||||
"defaultMessage": "Alarme"
|
|
||||||
},
|
|
||||||
"applyChanges": {
|
|
||||||
"defaultMessage": "Änderungen speichern"
|
|
||||||
},
|
|
||||||
"country": {
|
|
||||||
"defaultMessage": "Land"
|
|
||||||
},
|
|
||||||
"createNewFolder": {
|
|
||||||
"defaultMessage": "Neuen Ordner erstellen"
|
|
||||||
},
|
|
||||||
"createNewUser": {
|
|
||||||
"defaultMessage": "Neuen Nutzer erstellen"
|
|
||||||
},
|
|
||||||
"customerName": {
|
|
||||||
"defaultMessage": "Kundenname"
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"defaultMessage": "Email"
|
|
||||||
},
|
|
||||||
"english": {
|
|
||||||
"defaultMessage": "Englisch"
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
},
|
|
||||||
"folder": {
|
|
||||||
"defaultMessage": "Ordner"
|
|
||||||
},
|
|
||||||
"german": {
|
|
||||||
"defaultMessage": "Deutsch"
|
|
||||||
},
|
|
||||||
"groupTabs": {
|
|
||||||
"defaultMessage": "Gruppen"
|
|
||||||
},
|
|
||||||
"groupTree": {
|
|
||||||
"defaultMessage": "Gruppenbaum"
|
|
||||||
},
|
|
||||||
"information": {
|
|
||||||
"defaultMessage": "Information"
|
|
||||||
},
|
|
||||||
"inheritedAccess": {
|
|
||||||
"defaultMessage": "Vererbter Zugriff von"
|
|
||||||
},
|
|
||||||
"installation": {
|
|
||||||
"defaultMessage": "Installation"
|
|
||||||
},
|
|
||||||
"installationTabs": {
|
|
||||||
"defaultMessage": "Installationen"
|
|
||||||
},
|
|
||||||
"installations": {
|
|
||||||
"defaultMessage": "Installationen"
|
|
||||||
},
|
|
||||||
"lastWeek": {
|
|
||||||
"defaultMessage": "Letzte Woche"
|
|
||||||
},
|
|
||||||
"location": {
|
|
||||||
"defaultMessage": "Standort"
|
|
||||||
},
|
|
||||||
"log": {
|
|
||||||
"defaultMessage": "Logbuch"
|
|
||||||
},
|
|
||||||
"logout": {
|
|
||||||
"defaultMessage": "Abmelden"
|
|
||||||
},
|
|
||||||
"makeASelection": {
|
|
||||||
"defaultMessage": "Bitte wählen Sie links eine Auswahl"
|
|
||||||
},
|
|
||||||
"manageAccess": {
|
|
||||||
"defaultMessage": "Zugriff verwalten"
|
|
||||||
},
|
|
||||||
"move": {
|
|
||||||
"defaultMessage": "Verschieben"
|
|
||||||
},
|
|
||||||
"moveTo": {
|
|
||||||
"defaultMessage": "Verschieben zu"
|
|
||||||
},
|
|
||||||
"moveTree": {
|
|
||||||
"defaultMessage": "Baum verschieben"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"defaultMessage": "Name"
|
|
||||||
},
|
|
||||||
"navigationTabs": {
|
|
||||||
"defaultMessage": "Navigation"
|
|
||||||
},
|
|
||||||
"orderNumbers": {
|
|
||||||
"defaultMessage": "Bestellnummer"
|
|
||||||
},
|
|
||||||
"region": {
|
|
||||||
"defaultMessage": "Region"
|
|
||||||
},
|
|
||||||
"requiredLocation": {
|
|
||||||
"defaultMessage": "Standort ist erforderlich"
|
|
||||||
},
|
|
||||||
"requiredName": {
|
|
||||||
"defaultMessage": "Name ist erforderlich"
|
|
||||||
},
|
|
||||||
"requiredRegion": {
|
|
||||||
"defaultMessage": "Region ist erforderlich"
|
|
||||||
},
|
|
||||||
"search": {
|
|
||||||
"defaultMessage": "Suche"
|
|
||||||
},
|
|
||||||
"submit": {
|
|
||||||
"defaultMessage": "Senden"
|
|
||||||
},
|
|
||||||
"updateFolderErrorMessage": {
|
|
||||||
"defaultMessage": "Fehler, Ordner kann nicht aktualisiert werden"
|
|
||||||
},
|
|
||||||
"updatedSuccessfully": {
|
|
||||||
"defaultMessage": "Erfolgreich aktualisiert"
|
|
||||||
},
|
|
||||||
"user": {
|
|
||||||
"defaultMessage": "Nutzer"
|
|
||||||
},
|
|
||||||
"userTabs": {
|
|
||||||
"defaultMessage": "Nutzer"
|
|
||||||
},
|
|
||||||
"users": {
|
|
||||||
"defaultMessage": "Nutzer"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
{
|
|
||||||
"Information": "Information",
|
|
||||||
"addNewChild": "Add new child",
|
|
||||||
"addNewDialogButton": "Add new dialog button",
|
|
||||||
"addUser": "Create user",
|
|
||||||
"alarms": "Alarms",
|
|
||||||
"applyChanges": "Apply changes",
|
|
||||||
"country": "Country",
|
|
||||||
"createNewFolder": "Create new folder",
|
|
||||||
"createNewUser": "Create new user",
|
|
||||||
"customerName": "Customer",
|
|
||||||
"email": "Email",
|
|
||||||
"english": "English",
|
|
||||||
"error": {},
|
|
||||||
"folder": "Folder",
|
|
||||||
"german": "German",
|
|
||||||
"groupTabs": "Group tabs",
|
|
||||||
"groupTree": "Group tree",
|
|
||||||
"information": "Information",
|
|
||||||
"inheritedAccess": "Inherited access from",
|
|
||||||
"installation": "Installation",
|
|
||||||
"installationTabs": "Installation tabs",
|
|
||||||
"installations": "Installations",
|
|
||||||
"lastWeek": "Last week",
|
|
||||||
"location": "Location",
|
|
||||||
"log": "Log",
|
|
||||||
"logout": "Logout",
|
|
||||||
"makeASelection": "Please make a selection on the left",
|
|
||||||
"manageAccess": "Manage access",
|
|
||||||
"move": "Move",
|
|
||||||
"moveTo": "Move to",
|
|
||||||
"moveTree": "Move tree",
|
|
||||||
"name": "Name",
|
|
||||||
"navigationTabs": "Navigation tabs",
|
|
||||||
"orderNumbers": "Order number",
|
|
||||||
"region": "Region",
|
|
||||||
"requiredLocation": "Location is required",
|
|
||||||
"requiredName": "Name is required",
|
|
||||||
"requiredRegion": "Region is required",
|
|
||||||
"search": "Search",
|
|
||||||
"submit": "Submit",
|
|
||||||
"updateFolderErrorMessage": "Couldn't update folder, an error occured",
|
|
||||||
"updatedSuccessfully": "Updated successfully",
|
|
||||||
"user": "User",
|
|
||||||
"userTabs": "user tabs",
|
|
||||||
"users": "Users"
|
|
||||||
}
|
|
|
@ -1,136 +0,0 @@
|
||||||
{
|
|
||||||
"Information": {
|
|
||||||
"defaultMessage": "Informations"
|
|
||||||
},
|
|
||||||
"addNewChild": {
|
|
||||||
"defaultMessage": "Ajouter un nouvel enfant"
|
|
||||||
},
|
|
||||||
"addNewDialogButton": {
|
|
||||||
"defaultMessage": "Ajouter un nouveau bouton de dialogue"
|
|
||||||
},
|
|
||||||
"addUser": {
|
|
||||||
"defaultMessage": "Créer un utilisateur"
|
|
||||||
},
|
|
||||||
"alarms": {
|
|
||||||
"defaultMessage": "Alarmes"
|
|
||||||
},
|
|
||||||
"applyChanges": {
|
|
||||||
"defaultMessage": "Appliquer les modifications"
|
|
||||||
},
|
|
||||||
"country": {
|
|
||||||
"defaultMessage": "Pays"
|
|
||||||
},
|
|
||||||
"createNewFolder": {
|
|
||||||
"defaultMessage": "Créer un nouveau dossier"
|
|
||||||
},
|
|
||||||
"createNewUser": {
|
|
||||||
"defaultMessage": "Créer un nouvel utilisateur"
|
|
||||||
},
|
|
||||||
"customerName": {
|
|
||||||
"defaultMessage": "Nom du client"
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"defaultMessage": "E-mail"
|
|
||||||
},
|
|
||||||
"english": {
|
|
||||||
"defaultMessage": "Anglais"
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
},
|
|
||||||
"folder": {
|
|
||||||
"defaultMessage": "Dossier"
|
|
||||||
},
|
|
||||||
"german": {
|
|
||||||
"defaultMessage": "Allemand"
|
|
||||||
},
|
|
||||||
"groupTabs": {
|
|
||||||
"defaultMessage": "Onglets de groupe"
|
|
||||||
},
|
|
||||||
"groupTree": {
|
|
||||||
"defaultMessage": "Arbre de groupe"
|
|
||||||
},
|
|
||||||
"information": {
|
|
||||||
"defaultMessage": "Informations"
|
|
||||||
},
|
|
||||||
"inheritedAccess": {
|
|
||||||
"defaultMessage": "Accès hérité de"
|
|
||||||
},
|
|
||||||
"installation": {
|
|
||||||
"defaultMessage": "Installation"
|
|
||||||
},
|
|
||||||
"installationTabs": {
|
|
||||||
"defaultMessage": "Onglets d'installation"
|
|
||||||
},
|
|
||||||
"installations": {
|
|
||||||
"defaultMessage": "Installations"
|
|
||||||
},
|
|
||||||
"lastWeek": {
|
|
||||||
"defaultMessage": "La semaine dernière"
|
|
||||||
},
|
|
||||||
"location": {
|
|
||||||
"defaultMessage": "Localisation"
|
|
||||||
},
|
|
||||||
"log": {
|
|
||||||
"defaultMessage": "Journal"
|
|
||||||
},
|
|
||||||
"logout": {
|
|
||||||
"defaultMessage": "Déconnexion"
|
|
||||||
},
|
|
||||||
"makeASelection": {
|
|
||||||
"defaultMessage": "Veuillez faire une sélection à gauche"
|
|
||||||
},
|
|
||||||
"manageAccess": {
|
|
||||||
"defaultMessage": "Gérer l'accès"
|
|
||||||
},
|
|
||||||
"move": {
|
|
||||||
"defaultMessage": "Déplacer"
|
|
||||||
},
|
|
||||||
"moveTo": {
|
|
||||||
"defaultMessage": "Déplacer à"
|
|
||||||
},
|
|
||||||
"moveTree": {
|
|
||||||
"defaultMessage": "Déplacer l'arbre"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"defaultMessage": "Nom"
|
|
||||||
},
|
|
||||||
"navigationTabs": {
|
|
||||||
"defaultMessage": "Onglets de navigation"
|
|
||||||
},
|
|
||||||
"orderNumbers": {
|
|
||||||
"defaultMessage": "Numéro de commande"
|
|
||||||
},
|
|
||||||
"region": {
|
|
||||||
"defaultMessage": "Région"
|
|
||||||
},
|
|
||||||
"requiredLocation": {
|
|
||||||
"defaultMessage": "L'emplacement est requis"
|
|
||||||
},
|
|
||||||
"requiredName": {
|
|
||||||
"defaultMessage": "Le nom est obligatoire"
|
|
||||||
},
|
|
||||||
"requiredRegion": {
|
|
||||||
"defaultMessage": "La région est obligatoire"
|
|
||||||
},
|
|
||||||
"search": {
|
|
||||||
"defaultMessage": "Recherche"
|
|
||||||
},
|
|
||||||
"submit": {
|
|
||||||
"defaultMessage": "Soumettre"
|
|
||||||
},
|
|
||||||
"updateFolderErrorMessage": {
|
|
||||||
"defaultMessage": "Une erreur s'est produite, impossible de mettre à jour le dossier."
|
|
||||||
},
|
|
||||||
"updatedSuccessfully": {
|
|
||||||
"defaultMessage": "Mise à jour réussie"
|
|
||||||
},
|
|
||||||
"user": {
|
|
||||||
"defaultMessage": "Utilisateur"
|
|
||||||
},
|
|
||||||
"userTabs": {
|
|
||||||
"defaultMessage": "Onglets utilisateurs"
|
|
||||||
},
|
|
||||||
"users": {
|
|
||||||
"defaultMessage": "Utilisateurs"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
.App {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-logo {
|
|
||||||
height: 40vmin;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
.App-logo {
|
|
||||||
animation: App-logo-spin infinite 20s linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-header {
|
|
||||||
background-color: #282c34;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: calc(10px + 2vmin);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-link {
|
|
||||||
color: #61dafb;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@keyframes App-logo-spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { render, screen } from '@testing-library/react';
|
|
||||||
import App from './App';
|
|
||||||
|
|
||||||
test('renders learn react link', () => {
|
|
||||||
render(<App />);
|
|
||||||
const linkElement = screen.getByText(/learn react/i);
|
|
||||||
expect(linkElement).toBeInTheDocument();
|
|
||||||
});
|
|
|
@ -4,7 +4,7 @@ import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
|
||||||
|
|
||||||
import { Container, Grid, Box } from "@mui/material";
|
import { Container, Grid, Box } from "@mui/material";
|
||||||
import routes from "./routes.json";
|
import routes from "./routes.json";
|
||||||
import { IntlProvider, MessageFormatElement } from "react-intl";
|
import { IntlProvider } from "react-intl";
|
||||||
import { useContext, useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
import en from "./lang/en.json";
|
import en from "./lang/en.json";
|
||||||
import de from "./lang/de.json";
|
import de from "./lang/de.json";
|
||||||
|
@ -28,9 +28,9 @@ const App = () => {
|
||||||
const getTranslations = () => {
|
const getTranslations = () => {
|
||||||
switch (language) {
|
switch (language) {
|
||||||
case "en":
|
case "en":
|
||||||
return de;
|
|
||||||
case "de":
|
|
||||||
return en;
|
return en;
|
||||||
|
case "de":
|
||||||
|
return de;
|
||||||
case "fr":
|
case "fr":
|
||||||
return fr;
|
return fr;
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,6 @@ const App = () => {
|
||||||
if (token && getCurrentUser()?.mustResetPassword) {
|
if (token && getCurrentUser()?.mustResetPassword) {
|
||||||
return <ResetPassword />;
|
return <ResetPassword />;
|
||||||
}
|
}
|
||||||
console.log("lang", language);
|
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<IntlProvider
|
<IntlProvider
|
||||||
|
@ -78,7 +77,7 @@ const App = () => {
|
||||||
bgcolor={colors.greyLight}
|
bgcolor={colors.greyLight}
|
||||||
pt={3}
|
pt={3}
|
||||||
borderTop="2px solid"
|
borderTop="2px solid"
|
||||||
borderColor=" #a8b0be"
|
borderColor={colors.borderColor}
|
||||||
flex="1 0 auto"
|
flex="1 0 auto"
|
||||||
>
|
>
|
||||||
<Container maxWidth="xl">
|
<Container maxWidth="xl">
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
import React, { useContext, useState } from "react";
|
import React, { useContext, useState } from "react";
|
||||||
import {
|
import { Alert, CircularProgress, Grid, useTheme } from "@mui/material";
|
||||||
Alert,
|
|
||||||
CircularProgress,
|
|
||||||
Grid,
|
|
||||||
Typography,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import { axiosConfigWithoutToken } from "./config/axiosConfig";
|
import { axiosConfigWithoutToken } from "./config/axiosConfig";
|
||||||
import InnovenergyTextfield from "./components/Layout/InnovenergyTextfield";
|
import InnovenergyTextfield from "./components/Layout/InnovenergyTextfield";
|
||||||
|
|
|
@ -2,8 +2,9 @@ import { createContext, ReactNode, useCallback, useState } from "react";
|
||||||
import axiosConfig from "../../config/axiosConfig";
|
import axiosConfig from "../../config/axiosConfig";
|
||||||
import { transformArrayToTree } from "../../util/group.util";
|
import { transformArrayToTree } from "../../util/group.util";
|
||||||
import { I_Folder, I_Installation } from "../../util/types";
|
import { I_Folder, I_Installation } from "../../util/types";
|
||||||
|
import { AxiosError } from "axios";
|
||||||
|
|
||||||
interface GroupContextProviderProps {
|
interface I_GroupContextProviderProps {
|
||||||
currentType: string;
|
currentType: string;
|
||||||
setCurrentType: (value: string) => void;
|
setCurrentType: (value: string) => void;
|
||||||
data: (I_Folder | I_Installation)[];
|
data: (I_Folder | I_Installation)[];
|
||||||
|
@ -11,10 +12,10 @@ interface GroupContextProviderProps {
|
||||||
fetchData: () => Promise<void>;
|
fetchData: () => Promise<void>;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
setLoading: (value: boolean) => void;
|
setLoading: (value: boolean) => void;
|
||||||
getError: boolean;
|
getError?: AxiosError;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GroupContext = createContext<GroupContextProviderProps>({
|
export const GroupContext = createContext<I_GroupContextProviderProps>({
|
||||||
currentType: "",
|
currentType: "",
|
||||||
setCurrentType: () => {},
|
setCurrentType: () => {},
|
||||||
data: [],
|
data: [],
|
||||||
|
@ -22,14 +23,14 @@ export const GroupContext = createContext<GroupContextProviderProps>({
|
||||||
fetchData: () => Promise.resolve(),
|
fetchData: () => Promise.resolve(),
|
||||||
loading: false,
|
loading: false,
|
||||||
setLoading: () => {},
|
setLoading: () => {},
|
||||||
getError: false,
|
getError: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const GroupContextProvider = ({ children }: { children: ReactNode }) => {
|
const GroupContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const [currentType, setCurrentType] = useState("");
|
const [currentType, setCurrentType] = useState("");
|
||||||
const [data, setData] = useState<(I_Folder | I_Installation)[]>([]);
|
const [data, setData] = useState<(I_Folder | I_Installation)[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [getError, setGetError] = useState(false);
|
const [getError, setGetError] = useState<AxiosError>();
|
||||||
|
|
||||||
const fetchData = useCallback(async () => {
|
const fetchData = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { createContext, ReactNode, useCallback, useState } from "react";
|
||||||
import axiosConfig from "../../config/axiosConfig";
|
import axiosConfig from "../../config/axiosConfig";
|
||||||
import { I_Installation } from "../../util/types";
|
import { I_Installation } from "../../util/types";
|
||||||
|
|
||||||
interface InstallationContextProviderProps {
|
interface I_InstallationContextProviderProps {
|
||||||
data: I_Installation[];
|
data: I_Installation[];
|
||||||
setData: (value: I_Installation[]) => void;
|
setData: (value: I_Installation[]) => void;
|
||||||
fetchData: () => Promise<void>;
|
fetchData: () => Promise<void>;
|
||||||
|
@ -14,7 +14,7 @@ interface InstallationContextProviderProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InstallationsContext =
|
export const InstallationsContext =
|
||||||
createContext<InstallationContextProviderProps>({
|
createContext<I_InstallationContextProviderProps>({
|
||||||
data: [],
|
data: [],
|
||||||
setData: () => {},
|
setData: () => {},
|
||||||
fetchData: () => Promise.resolve(),
|
fetchData: () => Promise.resolve(),
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import { createContext, ReactNode, useState } from "react";
|
import { createContext, ReactNode, useState } from "react";
|
||||||
import { TreeElement } from "../Installations/Log/CheckboxTree";
|
import { I_TreeElement } from "../Installations/Log/CheckboxTree";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
interface LogContextProviderProps {
|
interface I_LogContextProviderProps {
|
||||||
toggles: TreeElement[] | null;
|
toggles: I_TreeElement[] | null;
|
||||||
setToggles: (value: TreeElement[]) => void;
|
setToggles: (value: I_TreeElement[]) => void;
|
||||||
checkedToggles: string[];
|
checkedToggles: string[];
|
||||||
setChecked: (newValue: string) => void;
|
setChecked: (newValue: string) => void;
|
||||||
removeChecked: (value: string) => void;
|
removeChecked: (value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LogContext = createContext<LogContextProviderProps>({
|
export const LogContext = createContext<I_LogContextProviderProps>({
|
||||||
toggles: [],
|
toggles: [],
|
||||||
setToggles: () => {},
|
setToggles: () => {},
|
||||||
checkedToggles: [],
|
checkedToggles: [],
|
||||||
|
@ -19,7 +19,7 @@ export const LogContext = createContext<LogContextProviderProps>({
|
||||||
});
|
});
|
||||||
|
|
||||||
const LogContextProvider = ({ children }: { children: ReactNode }) => {
|
const LogContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const [toggles, setToggles] = useState<TreeElement[] | null>(null);
|
const [toggles, setToggles] = useState<I_TreeElement[] | null>(null);
|
||||||
const [checkedToggles, setCheckedToggles] = useState<string[]>([]);
|
const [checkedToggles, setCheckedToggles] = useState<string[]>([]);
|
||||||
|
|
||||||
const setChecked = (newValue: string) => {
|
const setChecked = (newValue: string) => {
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
import { createContext, ReactNode, useState } from "react";
|
import { createContext, ReactNode, useState } from "react";
|
||||||
import { I_User } from "../../util/user.util";
|
import { I_S3Credentials } from "../../util/types";
|
||||||
import { I_Installation, S3Credentials } from "../../util/types";
|
|
||||||
import { UnixTime } from "../../dataCache/time";
|
import { UnixTime } from "../../dataCache/time";
|
||||||
import { FetchResult } from "../../dataCache/dataCache";
|
import { FetchResult } from "../../dataCache/dataCache";
|
||||||
import { DataRecord } from "../../dataCache/data";
|
import { DataRecord } from "../../dataCache/data";
|
||||||
import { S3Access } from "../../dataCache/S3/S3Access";
|
import { S3Access } from "../../dataCache/S3/S3Access";
|
||||||
import { parseCsv } from "../../util/graph.util";
|
import { parseCsv } from "../../util/graph.util";
|
||||||
|
|
||||||
interface S3CredentialsContextProviderProps {
|
interface I_S3CredentialsContextProviderProps {
|
||||||
s3Credentials?: S3Credentials;
|
s3Credentials?: I_S3Credentials;
|
||||||
saveS3Credentials: (credentials: S3Credentials, id: string) => void;
|
saveS3Credentials: (credentials: I_S3Credentials, id: string) => void;
|
||||||
fetchData: (timestamp: UnixTime) => Promise<FetchResult<DataRecord>>;
|
fetchData: (timestamp: UnixTime) => Promise<FetchResult<DataRecord>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const S3CredentialsContext =
|
export const S3CredentialsContext =
|
||||||
createContext<S3CredentialsContextProviderProps>({
|
createContext<I_S3CredentialsContextProviderProps>({
|
||||||
s3Credentials: {} as S3Credentials,
|
s3Credentials: {} as I_S3Credentials,
|
||||||
saveS3Credentials: () => {},
|
saveS3Credentials: () => {},
|
||||||
fetchData: () => ({} as Promise<FetchResult<DataRecord>>),
|
fetchData: () => ({} as Promise<FetchResult<DataRecord>>),
|
||||||
});
|
});
|
||||||
|
@ -25,18 +24,11 @@ const S3CredentialsContextProvider = ({
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}) => {
|
}) => {
|
||||||
const [s3Credentials, setS3Credentials] = useState<S3Credentials>();
|
const [s3Credentials, setS3Credentials] = useState<I_S3Credentials>();
|
||||||
|
|
||||||
const saveS3Credentials = (credentials: S3Credentials, id: string) => {
|
const saveS3Credentials = (credentials: I_S3Credentials, id: string) => {
|
||||||
const s3Bucket = id + "-3e5b3069-214a-43ee-8d85-57d72000c10d";
|
const s3Bucket = id + "-3e5b3069-214a-43ee-8d85-57d72000c10d";
|
||||||
setS3Credentials({ s3Bucket, ...credentials });
|
setS3Credentials({ s3Bucket, ...credentials });
|
||||||
/* setS3Credentials({
|
|
||||||
s3Region: "sos-ch-dk-2",
|
|
||||||
s3Provider: "exo.io",
|
|
||||||
s3Key: "EXO15c0bf710e158e9b83270f0a",
|
|
||||||
s3Secret: "Dd5jYSiZtt_Zt5Ba5mDmaiLCdASUaKLfduSKY-SU-lg",
|
|
||||||
s3Bucket: "saliomameiringen",
|
|
||||||
});*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchData = (timestamp: UnixTime): Promise<FetchResult<DataRecord>> => {
|
const fetchData = (timestamp: UnixTime): Promise<FetchResult<DataRecord>> => {
|
||||||
|
@ -63,7 +55,6 @@ const S3CredentialsContextProvider = ({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.log("catch", e);
|
|
||||||
return Promise.resolve(FetchResult.tryLater);
|
return Promise.resolve(FetchResult.tryLater);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { createContext, ReactNode, useState } from "react";
|
import { createContext, ReactNode, useState } from "react";
|
||||||
import { I_User } from "../../util/user.util";
|
import { I_User } from "../../util/user.util";
|
||||||
|
|
||||||
interface InstallationContextProviderProps {
|
interface I_InstallationContextProviderProps {
|
||||||
currentUser?: I_User;
|
currentUser?: I_User;
|
||||||
setCurrentUser: (value: I_User) => void;
|
setCurrentUser: (value: I_User) => void;
|
||||||
saveCurrentUser: (user: I_User) => void;
|
saveCurrentUser: (user: I_User) => void;
|
||||||
getCurrentUser: () => I_User;
|
getCurrentUser: () => I_User;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UserContext = createContext<InstallationContextProviderProps>({
|
export const UserContext = createContext<I_InstallationContextProviderProps>({
|
||||||
currentUser: {} as I_User,
|
currentUser: {} as I_User,
|
||||||
setCurrentUser: () => {},
|
setCurrentUser: () => {},
|
||||||
saveCurrentUser: () => {},
|
saveCurrentUser: () => {},
|
||||||
|
|
|
@ -8,14 +8,14 @@ import {
|
||||||
} from "react";
|
} from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import axiosConfig from "../../config/axiosConfig";
|
import axiosConfig from "../../config/axiosConfig";
|
||||||
import { I_User, UserWithInheritedAccess } from "../../util/user.util";
|
import { I_User, I_UserWithInheritedAccess } from "../../util/user.util";
|
||||||
import { GroupContext } from "./GroupContextProvider";
|
import { GroupContext } from "./GroupContextProvider";
|
||||||
|
|
||||||
interface UsersContextProviderProps {
|
interface I_UsersContextProviderProps {
|
||||||
directAccessUsers: I_User[];
|
directAccessUsers: I_User[];
|
||||||
setDirectAccessUsers: (value: I_User[]) => void;
|
setDirectAccessUsers: (value: I_User[]) => void;
|
||||||
inheritedAccessUsers: UserWithInheritedAccess[];
|
inheritedAccessUsers: I_UserWithInheritedAccess[];
|
||||||
setInheritedAccessUsers: (value: UserWithInheritedAccess[]) => void;
|
setInheritedAccessUsers: (value: I_UserWithInheritedAccess[]) => void;
|
||||||
availableUsers: I_User[];
|
availableUsers: I_User[];
|
||||||
setAvailableUsers: (value: I_User[]) => void;
|
setAvailableUsers: (value: I_User[]) => void;
|
||||||
getAvailableUsersForResource: () => I_User[];
|
getAvailableUsersForResource: () => I_User[];
|
||||||
|
@ -24,7 +24,7 @@ interface UsersContextProviderProps {
|
||||||
fetchAvailableUsers: () => Promise<void>;
|
fetchAvailableUsers: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UsersContext = createContext<UsersContextProviderProps>({
|
export const UsersContext = createContext<I_UsersContextProviderProps>({
|
||||||
directAccessUsers: [],
|
directAccessUsers: [],
|
||||||
setDirectAccessUsers: () => {},
|
setDirectAccessUsers: () => {},
|
||||||
inheritedAccessUsers: [],
|
inheritedAccessUsers: [],
|
||||||
|
@ -48,7 +48,7 @@ export const UsersContext = createContext<UsersContextProviderProps>({
|
||||||
const UsersContextProvider = ({ children }: { children: ReactNode }) => {
|
const UsersContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const [directAccessUsers, setDirectAccessUsers] = useState<I_User[]>([]);
|
const [directAccessUsers, setDirectAccessUsers] = useState<I_User[]>([]);
|
||||||
const [inheritedAccessUsers, setInheritedAccessUsers] = useState<
|
const [inheritedAccessUsers, setInheritedAccessUsers] = useState<
|
||||||
UserWithInheritedAccess[]
|
I_UserWithInheritedAccess[]
|
||||||
>([]);
|
>([]);
|
||||||
const [availableUsers, setAvailableUsers] = useState<I_User[]>([]);
|
const [availableUsers, setAvailableUsers] = useState<I_User[]>([]);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Grid } from "@mui/material";
|
import { Grid } from "@mui/material";
|
||||||
import UsersContextProvider from "../../Context/UsersContextProvider";
|
import UsersContextProvider from "../../Context/UsersContextProvider";
|
||||||
import AvailableUserDialog from "./AvailableUserDialog";
|
import AvailableUserDialog from "./AvailableUserDialog";
|
||||||
import InnovenergyList from "./InnovenergyList";
|
import InnovenergyList from "../../Layout/InnovenergyList";
|
||||||
import UsersWithDirectAccess from "./UsersWithDirectAccess";
|
import UsersWithDirectAccess from "./UsersWithDirectAccess";
|
||||||
import UsersWithInheritedAccess from "./UsersWithInheritedAccess";
|
import UsersWithInheritedAccess from "./UsersWithInheritedAccess";
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
|
|
|
@ -4,7 +4,6 @@ import {
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
TextField,
|
TextField,
|
||||||
colors,
|
|
||||||
Alert,
|
Alert,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import DialogActions from "@mui/material/DialogActions";
|
import DialogActions from "@mui/material/DialogActions";
|
||||||
|
|
|
@ -18,11 +18,11 @@ import { UserContext } from "../../Context/UserContextProvider";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
const UsersWithDirectAccess = () => {
|
const UsersWithDirectAccess = () => {
|
||||||
|
const { id } = useParams();
|
||||||
|
const [error, setError] = useState<any>();
|
||||||
const { fetchUsersWithDirectAccessForResource, directAccessUsers } =
|
const { fetchUsersWithDirectAccessForResource, directAccessUsers } =
|
||||||
useContext(UsersContext);
|
useContext(UsersContext);
|
||||||
const [error, setError] = useState<any>();
|
|
||||||
const { currentType } = useContext(GroupContext);
|
const { currentType } = useContext(GroupContext);
|
||||||
const { id } = useParams();
|
|
||||||
const { getCurrentUser } = useContext(UserContext);
|
const { getCurrentUser } = useContext(UserContext);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { Fragment, useContext, useEffect } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
filterDuplicateUsers,
|
filterDuplicateUsers,
|
||||||
UserWithInheritedAccess,
|
I_UserWithInheritedAccess,
|
||||||
} from "../../../util/user.util";
|
} from "../../../util/user.util";
|
||||||
import routes from "../../../routes.json";
|
import routes from "../../../routes.json";
|
||||||
import PersonIcon from "@mui/icons-material/Person";
|
import PersonIcon from "@mui/icons-material/Person";
|
||||||
|
@ -29,7 +29,7 @@ const UsersWithInheritedAccess = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{filterDuplicateUsers(inheritedAccessUsers).map(
|
{filterDuplicateUsers(inheritedAccessUsers).map(
|
||||||
({ user, folderName }: UserWithInheritedAccess) => {
|
({ user, folderName }: I_UserWithInheritedAccess) => {
|
||||||
return (
|
return (
|
||||||
<Fragment key={user.id}>
|
<Fragment key={user.id}>
|
||||||
<ListItem id={"inherited-access-user-list-item" + user.id}>
|
<ListItem id={"inherited-access-user-list-item" + user.id}>
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
import { Dialog, DialogContent, DialogTitle, IconButton } from "@mui/material";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useState } from "react";
|
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
|
||||||
import axiosConfig from "../../config/axiosConfig";
|
import axiosConfig from "../../config/axiosConfig";
|
||||||
import { I_Folder, I_Installation } from "../../util/types";
|
import { I_Folder, I_Installation } from "../../util/types";
|
||||||
import FolderForm from "./FolderForm";
|
import FolderForm from "./Detail/FolderForm";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import InstallationForm from "../Installations/Detail/InstallationForm";
|
||||||
import InnovenergyButton from "../Layout/InnovenergyButton";
|
import AddNewDialog from "./AddNewDialog";
|
||||||
import InstallationForm from "../Installations/InstallationForm";
|
|
||||||
import AddNewDialog from "./AddNew";
|
|
||||||
|
|
||||||
interface AddNewDialogProps {
|
interface AddNewDialogProps {
|
||||||
values: I_Folder | I_Installation;
|
values: I_Folder | I_Installation;
|
||||||
|
@ -15,8 +11,6 @@ interface AddNewDialogProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddNewButtons = (props: AddNewDialogProps) => {
|
const AddNewButtons = (props: AddNewDialogProps) => {
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
|
|
||||||
const handleFolderSubmit = (data: I_Folder, childData: Partial<I_Folder>) => {
|
const handleFolderSubmit = (data: I_Folder, childData: Partial<I_Folder>) => {
|
||||||
return axiosConfig
|
return axiosConfig
|
||||||
.post("/CreateFolder", {
|
.post("/CreateFolder", {
|
||||||
|
@ -24,7 +18,6 @@ const AddNewButtons = (props: AddNewDialogProps) => {
|
||||||
parentId: data.id,
|
parentId: data.id,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setOpen(false);
|
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -39,7 +32,6 @@ const AddNewButtons = (props: AddNewDialogProps) => {
|
||||||
parentId: data.id,
|
parentId: data.id,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setOpen(false);
|
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,15 +5,15 @@ import CloseIcon from "@mui/icons-material/Close";
|
||||||
import { ReactNode, useState } from "react";
|
import { ReactNode, useState } from "react";
|
||||||
import InnovenergyButton from "../Layout/InnovenergyButton";
|
import InnovenergyButton from "../Layout/InnovenergyButton";
|
||||||
|
|
||||||
interface AddNewDialogProps {
|
interface I_AddNewDialogProps {
|
||||||
form: ReactNode;
|
form: ReactNode;
|
||||||
message: ReactNode;
|
message: ReactNode;
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddNewDialog = (props: AddNewDialogProps) => {
|
const AddNewDialog = (props: I_AddNewDialogProps) => {
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const { form, id, message } = props;
|
const { form, id, message } = props;
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
|
@ -2,12 +2,11 @@ import { Box, CircularProgress, Alert, useTheme } from "@mui/material";
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import axiosConfig from "../../config/axiosConfig";
|
import axiosConfig from "../../../config/axiosConfig";
|
||||||
import { I_Folder } from "../../util/types";
|
import { I_Folder } from "../../../util/types";
|
||||||
import AddNewButtons from "./AddNewButtons";
|
import AddNewButtons from "../AddNewButtons";
|
||||||
import FolderForm from "./FolderForm";
|
import FolderForm from "./FolderForm";
|
||||||
import MoveDialog from "./Tree/MoveDialog";
|
import MoveDialog from "./MoveDialog";
|
||||||
import { colors } from "index";
|
|
||||||
|
|
||||||
const Folder = () => {
|
const Folder = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
@ -43,7 +42,7 @@ const Folder = () => {
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
py: 3,
|
py: 3,
|
||||||
bgcolor: colors.greyDark,
|
bgcolor: theme.palette.primary.dark,
|
||||||
px: 1,
|
px: 1,
|
||||||
borderBottom: 2,
|
borderBottom: 2,
|
||||||
borderRight: 2,
|
borderRight: 2,
|
||||||
|
@ -54,7 +53,6 @@ const Folder = () => {
|
||||||
borderBottomLeftRadius: 4,
|
borderBottomLeftRadius: 4,
|
||||||
borderBottomRightRadius: 4,
|
borderBottomRightRadius: 4,
|
||||||
borderColor: theme.palette.text.disabled,
|
borderColor: theme.palette.text.disabled,
|
||||||
marginTop: 0.05,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FolderForm
|
<FolderForm
|
|
@ -3,15 +3,13 @@ import { AxiosResponse } from "axios";
|
||||||
import { useFormik } from "formik";
|
import { useFormik } from "formik";
|
||||||
import { ReactNode, useContext, useState } from "react";
|
import { ReactNode, useContext, useState } from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import { I_Folder } from "../../util/types";
|
import { I_Folder } from "../../../util/types";
|
||||||
import { GroupContext } from "../Context/GroupContextProvider";
|
import { GroupContext } from "../../Context/GroupContextProvider";
|
||||||
import InnovenergySnackbar from "../InnovenergySnackbar";
|
import InnovenergySnackbar from "../../Layout/InnovenergySnackbar";
|
||||||
import InnovenergyButton from "../Layout/InnovenergyButton";
|
import InnovenergyButton from "../../Layout/InnovenergyButton";
|
||||||
import {
|
import { I_InnovenergyTextfieldProps } from "../../Layout/InnovenergyTextfield";
|
||||||
I_InnovenergyTextfieldProps,
|
import { UserContext } from "../../Context/UserContextProvider";
|
||||||
IePropertyGrid,
|
import InnovenergyPropertyGrid from "../../Layout/InnovenergyPropertyGrid";
|
||||||
} from "../Layout/InnovenergyTextfield";
|
|
||||||
import { UserContext } from "../Context/UserContextProvider";
|
|
||||||
|
|
||||||
interface I_CustomerFormProps {
|
interface I_CustomerFormProps {
|
||||||
values: I_Folder;
|
values: I_Folder;
|
||||||
|
@ -24,14 +22,16 @@ interface I_CustomerFormProps {
|
||||||
|
|
||||||
const FolderForm = (props: I_CustomerFormProps) => {
|
const FolderForm = (props: I_CustomerFormProps) => {
|
||||||
const { values, additionalButtons, handleSubmit } = props;
|
const { values, additionalButtons, handleSubmit } = props;
|
||||||
const intl = useIntl();
|
|
||||||
const { fetchData } = useContext(GroupContext);
|
|
||||||
const { getCurrentUser } = useContext(UserContext);
|
|
||||||
|
|
||||||
const [snackbarOpen, setSnackbarOpen] = useState(false);
|
const [snackbarOpen, setSnackbarOpen] = useState(false);
|
||||||
const [error, setError] = useState();
|
const [error, setError] = useState();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
const theme = useTheme();
|
||||||
|
const { fetchData } = useContext(GroupContext);
|
||||||
|
const { getCurrentUser } = useContext(UserContext);
|
||||||
|
|
||||||
const readOnly = !getCurrentUser().hasWriteAccess;
|
const readOnly = !getCurrentUser().hasWriteAccess;
|
||||||
|
|
||||||
const formik = useFormik({
|
const formik = useFormik({
|
||||||
|
@ -55,7 +55,6 @@ const FolderForm = (props: I_CustomerFormProps) => {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const rows: I_InnovenergyTextfieldProps[] = [
|
const rows: I_InnovenergyTextfieldProps[] = [
|
||||||
{
|
{
|
||||||
|
@ -83,9 +82,8 @@ const FolderForm = (props: I_CustomerFormProps) => {
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
<IePropertyGrid rows={rows} />
|
<InnovenergyPropertyGrid rows={rows} />
|
||||||
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
|
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
|
||||||
{loading && (
|
{loading && (
|
||||||
<CircularProgress sx={{ color: theme.palette.secondary.main }} />
|
<CircularProgress sx={{ color: theme.palette.secondary.main }} />
|
||||||
|
@ -109,7 +107,6 @@ const FolderForm = (props: I_CustomerFormProps) => {
|
||||||
open={snackbarOpen}
|
open={snackbarOpen}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,21 +9,21 @@ import { GroupContext } from "../../Context/GroupContextProvider";
|
||||||
import InnovenergyButton from "../../Layout/InnovenergyButton";
|
import InnovenergyButton from "../../Layout/InnovenergyButton";
|
||||||
import MoveTree from "./MoveTree";
|
import MoveTree from "./MoveTree";
|
||||||
|
|
||||||
interface MoveDialogProps {
|
interface I_MoveDialogProps {
|
||||||
values: I_Folder;
|
values: I_Folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MoveDialog = (props: MoveDialogProps) => {
|
const MoveDialog = (props: I_MoveDialogProps) => {
|
||||||
const { values } = props;
|
const { values } = props;
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const [selectedParentId, setSelectedParentId] = useState<number>(
|
const [selectedParentId, setSelectedParentId] = useState<number>(
|
||||||
values.parentId
|
values.parentId
|
||||||
);
|
);
|
||||||
|
const [error, setError] = useState();
|
||||||
|
|
||||||
const { fetchData, currentType } = useContext(GroupContext);
|
const { fetchData, currentType } = useContext(GroupContext);
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const [error, setError] = useState();
|
|
||||||
|
|
||||||
const handleMove = () => {
|
const handleMove = () => {
|
||||||
const route =
|
const route =
|
|
@ -6,14 +6,14 @@ import { I_Folder, I_Installation } from "../../../util/types";
|
||||||
import { GroupContext } from "../../Context/GroupContextProvider";
|
import { GroupContext } from "../../Context/GroupContextProvider";
|
||||||
import { instanceOfFolder } from "../../../util/group.util";
|
import { instanceOfFolder } from "../../../util/group.util";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
import InnovEnergyTreeItem from "../../Layout/InnovEnergyTreeItem";
|
import InnovenergyTreeItem from "../../Layout/InnovenergyTreeItem";
|
||||||
|
|
||||||
interface MoveTreeProps {
|
interface I_MoveTreeProps {
|
||||||
setSelectedParentId: (value: number) => void;
|
setSelectedParentId: (value: number) => void;
|
||||||
parentId: number;
|
parentId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MoveTree = (props: MoveTreeProps) => {
|
const MoveTree = (props: I_MoveTreeProps) => {
|
||||||
const { data } = useContext(GroupContext);
|
const { data } = useContext(GroupContext);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ const MoveTree = (props: MoveTreeProps) => {
|
||||||
.filter((element) => element.type === "Folder")
|
.filter((element) => element.type === "Folder")
|
||||||
.map((element) => {
|
.map((element) => {
|
||||||
return (
|
return (
|
||||||
<InnovEnergyTreeItem
|
<InnovenergyTreeItem
|
||||||
id={"move-tree-item-" + element.id}
|
id={"move-tree-item-" + element.id}
|
||||||
key={"move-tree-item-" + element.id}
|
key={"move-tree-item-" + element.id}
|
||||||
nodeId={element.id.toString()}
|
nodeId={element.id.toString()}
|
||||||
|
@ -37,7 +37,7 @@ const MoveTree = (props: MoveTreeProps) => {
|
||||||
label={element.name}
|
label={element.name}
|
||||||
>
|
>
|
||||||
{getNodes(element)}
|
{getNodes(element)}
|
||||||
</InnovEnergyTreeItem>
|
</InnovenergyTreeItem>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -12,6 +12,8 @@ import { useTheme } from "@mui/material";
|
||||||
|
|
||||||
const GroupTabs = () => {
|
const GroupTabs = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const intl = useIntl();
|
||||||
|
const { currentType } = useContext(GroupContext);
|
||||||
const routeMatch = useRouteMatch([
|
const routeMatch = useRouteMatch([
|
||||||
routes.installations + routes.tree + routes.folder + ":id",
|
routes.installations + routes.tree + routes.folder + ":id",
|
||||||
routes.installations + routes.tree + routes.manageAccess + ":id",
|
routes.installations + routes.tree + routes.manageAccess + ":id",
|
||||||
|
@ -19,8 +21,6 @@ const GroupTabs = () => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const id = routeMatch?.params?.id;
|
const id = routeMatch?.params?.id;
|
||||||
const intl = useIntl();
|
|
||||||
const { currentType } = useContext(GroupContext);
|
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
return (
|
return (
|
||||||
|
@ -36,7 +36,8 @@ const GroupTabs = () => {
|
||||||
borderColor: theme.palette.text.disabled,
|
borderColor: theme.palette.text.disabled,
|
||||||
borderBottom: 0,
|
borderBottom: 0,
|
||||||
mb: -1 / 8,
|
mb: -1 / 8,
|
||||||
}}}
|
},
|
||||||
|
}}
|
||||||
id="styled-tab-folder"
|
id="styled-tab-folder"
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: "folder",
|
id: "folder",
|
||||||
|
@ -49,7 +50,6 @@ const GroupTabs = () => {
|
||||||
to={routes.folder + id}
|
to={routes.folder + id}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
||||||
<InnovenergyTab
|
<InnovenergyTab
|
||||||
sx={{
|
sx={{
|
||||||
"&.Mui-selected": {
|
"&.Mui-selected": {
|
||||||
|
@ -58,7 +58,8 @@ const GroupTabs = () => {
|
||||||
borderColor: theme.palette.text.disabled,
|
borderColor: theme.palette.text.disabled,
|
||||||
borderBottom: 0,
|
borderBottom: 0,
|
||||||
mb: -1 / 8,
|
mb: -1 / 8,
|
||||||
}}}
|
},
|
||||||
|
}}
|
||||||
id="styled-tab-installation"
|
id="styled-tab-installation"
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: "installation",
|
id: "installation",
|
||||||
|
@ -83,7 +84,8 @@ const GroupTabs = () => {
|
||||||
borderColor: theme.palette.text.disabled,
|
borderColor: theme.palette.text.disabled,
|
||||||
borderBottom: 0,
|
borderBottom: 0,
|
||||||
mb: -1 / 8,
|
mb: -1 / 8,
|
||||||
}}}
|
},
|
||||||
|
}}
|
||||||
id="styled-tab-manage-access"
|
id="styled-tab-manage-access"
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: "manageAccess",
|
id: "manageAccess",
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import TreeView from "@mui/lab/TreeView";
|
import TreeView from "@mui/lab/TreeView";
|
||||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||||
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
||||||
import { ReactNode, useContext, useEffect, useState } from "react";
|
import React, { ReactNode, useContext, useEffect, useState } from "react";
|
||||||
import { TreeItem } from "@mui/lab";
|
import { I_Folder, I_Installation } from "../../util/types";
|
||||||
import { I_Folder, I_Installation } from "../../../util/types";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import routes from "../../../routes.json";
|
import routes from "../../routes.json";
|
||||||
import { GroupContext } from "../../Context/GroupContextProvider";
|
import { GroupContext } from "../Context/GroupContextProvider";
|
||||||
import { instanceOfFolder } from "../../../util/group.util";
|
import { instanceOfFolder } from "../../util/group.util";
|
||||||
import { Grid, CircularProgress, useTheme } from "@mui/material";
|
import { Grid, CircularProgress, useTheme, Alert } from "@mui/material";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
import { colors } from "../../..";
|
import InnovenergyTreeItem from "../Layout/InnovenergyTreeItem";
|
||||||
import InnovEnergyTreeItem from "../../Layout/InnovEnergyTreeItem";
|
import TypeIcon from "./TypeIcon";
|
||||||
|
|
||||||
const GroupTree = () => {
|
const GroupTree = () => {
|
||||||
const { setCurrentType, fetchData, data, loading } = useContext(GroupContext);
|
const { setCurrentType, fetchData, data, loading, getError } =
|
||||||
|
useContext(GroupContext);
|
||||||
const [openNodes, setOpenNodes] = useState<string[]>([]);
|
const [openNodes, setOpenNodes] = useState<string[]>([]);
|
||||||
const [selected, setSelected] = useState<string>("");
|
const [selected, setSelected] = useState<string>("");
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
@ -48,19 +48,27 @@ const GroupTree = () => {
|
||||||
}}
|
}}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
>
|
>
|
||||||
<InnovEnergyTreeItem
|
<InnovenergyTreeItem
|
||||||
id={"group-tree-item-" + element.id}
|
id={"group-tree-item-" + element.id}
|
||||||
key={element.type + element.id}
|
key={element.type + element.id}
|
||||||
nodeId={element.type + element.id}
|
nodeId={element.type + element.id}
|
||||||
label={element.name}
|
label={
|
||||||
onClick={() => setCurrentType(element.type)}
|
<>
|
||||||
|
<TypeIcon type={element.type} /> {element.name}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentType(element.type);
|
||||||
|
}}
|
||||||
|
sx={{ ".MuiTreeItem-label": { display: "flex" } }}
|
||||||
>
|
>
|
||||||
{getNodes(element)}
|
{getNodes(element)}
|
||||||
</InnovEnergyTreeItem>
|
</InnovenergyTreeItem>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<Grid container justifyContent="center" width="100%">
|
<Grid container justifyContent="center" width="100%">
|
||||||
|
@ -68,6 +76,9 @@ const GroupTree = () => {
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (getError) {
|
||||||
|
return <Alert severity="error">{getError.message} </Alert>;
|
||||||
|
}
|
||||||
if (data) {
|
if (data) {
|
||||||
return (
|
return (
|
||||||
<TreeView
|
<TreeView
|
|
@ -1,12 +1,12 @@
|
||||||
import { Grid } from "@mui/material";
|
import { Grid } from "@mui/material";
|
||||||
import { Routes, Route } from "react-router";
|
import { Routes, Route } from "react-router";
|
||||||
import routes from "../../routes.json";
|
import routes from "../../routes.json";
|
||||||
import Folder from "./Folder";
|
import Folder from "./Detail/Folder";
|
||||||
import GroupTabs from "./GroupTabs";
|
import GroupTabs from "./GroupTabs";
|
||||||
import GroupContextProvider from "../Context/GroupContextProvider";
|
import GroupContextProvider from "../Context/GroupContextProvider";
|
||||||
import GroupTree from "./Tree/GroupTree";
|
import GroupTree from "./GroupTree";
|
||||||
import AccessManagement from "./AccessManagement/AccessManagement";
|
import AccessManagement from "./AccessManagement/AccessManagement";
|
||||||
import Installation from "../Installations/Installation";
|
import Installation from "../Installations/Detail/Installation";
|
||||||
import useRouteMatch from "../../hooks/useRouteMatch";
|
import useRouteMatch from "../../hooks/useRouteMatch";
|
||||||
import useInstallation from "../../hooks/useInstallation";
|
import useInstallation from "../../hooks/useInstallation";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
@ -25,7 +25,6 @@ const Groups = () => {
|
||||||
const id = routeMatch?.params?.id;
|
const id = routeMatch?.params?.id;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// TODO remove if
|
|
||||||
getInstallation(id);
|
getInstallation(id);
|
||||||
}, [id]);
|
}, [id]);
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
.groupTreeLink {
|
|
||||||
text-decoration: "none";
|
|
||||||
color: "red";
|
|
||||||
}
|
|
|
@ -1,14 +1,19 @@
|
||||||
import FolderIcon from "@mui/icons-material/Folder";
|
import FolderIcon from "@mui/icons-material/Folder";
|
||||||
import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile";
|
import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile";
|
||||||
|
|
||||||
interface TypeIconProps {
|
interface I_TypeIconProps {
|
||||||
type: string | undefined;
|
type: string | undefined;
|
||||||
}
|
}
|
||||||
const TypeIcon = (props: TypeIconProps) => {
|
|
||||||
|
const TypeIcon = (props: I_TypeIconProps) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
{props.type === "Folder" ? <FolderIcon /> : <InsertDriveFileIcon />}
|
{props.type === "Folder" ? (
|
||||||
</div>
|
<FolderIcon sx={{ height: "20px", mt: "1px", mr: 1 }} />
|
||||||
|
) : (
|
||||||
|
<InsertDriveFileIcon sx={{ height: "20px", mt: "1px", mr: 1 }} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
import { Alert, Box, CircularProgress, useTheme } from "@mui/material";
|
import { Alert, Box, CircularProgress, useTheme } from "@mui/material";
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import { useContext, useEffect, useState } from "react";
|
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import axiosConfig from "../../config/axiosConfig";
|
import axiosConfig from "../../../config/axiosConfig";
|
||||||
import { I_Folder, I_Installation } from "../../util/types";
|
import { I_Folder, I_Installation } from "../../../util/types";
|
||||||
import InstallationForm from "./InstallationForm";
|
import InstallationForm from "./InstallationForm";
|
||||||
import { colors } from "../..";
|
import MoveDialog from "../../Groups/Detail/MoveDialog";
|
||||||
import { S3CredentialsContext } from "../Context/S3CredentialsContextProvider";
|
|
||||||
import MoveDialog from "../Groups/Tree/MoveDialog";
|
|
||||||
|
|
||||||
interface I_InstallationProps {
|
interface I_InstallationProps {
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
|
@ -31,7 +28,7 @@ const Installation = (props: I_InstallationProps) => {
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
py: 3,
|
py: 3,
|
||||||
bgcolor: colors.greyDark,
|
bgcolor: theme.palette.primary.dark,
|
||||||
px: 1,
|
px: 1,
|
||||||
borderLeft: 2,
|
borderLeft: 2,
|
||||||
borderRight: 2,
|
borderRight: 2,
|
||||||
|
@ -46,7 +43,7 @@ const Installation = (props: I_InstallationProps) => {
|
||||||
<InstallationForm
|
<InstallationForm
|
||||||
values={values}
|
values={values}
|
||||||
handleSubmit={handleSubmit}
|
handleSubmit={handleSubmit}
|
||||||
additionalButtons={props.hasMoveButton ? [moveDialog] : []}
|
additionalButtons={props.hasMoveButton ? [moveDialog] : undefined}
|
||||||
/>
|
/>
|
||||||
<Box>
|
<Box>
|
||||||
{values.s3WriteKey && <div>{`Write key: ${values.s3WriteKey}`}</div>}
|
{values.s3WriteKey && <div>{`Write key: ${values.s3WriteKey}`}</div>}
|
|
@ -2,18 +2,15 @@ import { Alert, Grid, Snackbar } from "@mui/material";
|
||||||
import { useFormik } from "formik";
|
import { useFormik } from "formik";
|
||||||
import { ReactNode, useContext, useState } from "react";
|
import { ReactNode, useContext, useState } from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import axiosConfig from "../../config/axiosConfig";
|
import { I_Installation } from "../../../util/types";
|
||||||
import { I_Folder, I_Installation } from "../../util/types";
|
import { InstallationsContext } from "../../Context/InstallationsContextProvider";
|
||||||
import { InstallationsContext } from "../Context/InstallationsContextProvider";
|
import InnovenergyButton from "../../Layout/InnovenergyButton";
|
||||||
import MoveDialog from "../Groups/Tree/MoveDialog";
|
import { I_InnovenergyTextfieldProps } from "../../Layout/InnovenergyTextfield";
|
||||||
import InnovenergyButton from "../Layout/InnovenergyButton";
|
import { UserContext } from "../../Context/UserContextProvider";
|
||||||
import InnovenergyTextfield, {
|
|
||||||
I_InnovenergyTextfieldProps,
|
|
||||||
IePropertyGrid,
|
|
||||||
} from "../Layout/InnovenergyTextfield";
|
|
||||||
import { UserContext } from "../Context/UserContextProvider";
|
|
||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import { AxiosResponse } from "axios";
|
import { AxiosError, AxiosResponse } from "axios";
|
||||||
|
import InnovenergyPropertyGrid from "../../Layout/InnovenergyPropertyGrid";
|
||||||
|
import { GroupContext } from "../../Context/GroupContextProvider";
|
||||||
|
|
||||||
interface I_InstallationFormProps {
|
interface I_InstallationFormProps {
|
||||||
values: I_Installation;
|
values: I_Installation;
|
||||||
|
@ -27,8 +24,11 @@ interface I_InstallationFormProps {
|
||||||
const InstallationForm = (props: I_InstallationFormProps) => {
|
const InstallationForm = (props: I_InstallationFormProps) => {
|
||||||
const { values, additionalButtons, handleSubmit } = props;
|
const { values, additionalButtons, handleSubmit } = props;
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const [error, setError] = useState<AxiosError>();
|
||||||
|
|
||||||
|
const { fetchData: fetchInstallations } = useContext(InstallationsContext);
|
||||||
|
const { fetchData: fetchGroups } = useContext(GroupContext);
|
||||||
|
|
||||||
const { fetchData } = useContext(InstallationsContext);
|
|
||||||
const { getCurrentUser } = useContext(UserContext);
|
const { getCurrentUser } = useContext(UserContext);
|
||||||
|
|
||||||
const readOnly = !getCurrentUser().hasWriteAccess;
|
const readOnly = !getCurrentUser().hasWriteAccess;
|
||||||
|
@ -66,9 +66,15 @@ const InstallationForm = (props: I_InstallationFormProps) => {
|
||||||
onSubmit: (formikValues) => {
|
onSubmit: (formikValues) => {
|
||||||
handleSubmit(values, formikValues)
|
handleSubmit(values, formikValues)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
fetchData();
|
setOpen(true);
|
||||||
|
additionalButtons && additionalButtons.length > 0
|
||||||
|
? fetchGroups()
|
||||||
|
: fetchInstallations();
|
||||||
})
|
})
|
||||||
.catch((err) => {});
|
.catch((err) => {
|
||||||
|
setError(err);
|
||||||
|
setOpen(true);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
validationSchema,
|
validationSchema,
|
||||||
});
|
});
|
||||||
|
@ -150,7 +156,7 @@ const InstallationForm = (props: I_InstallationFormProps) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
<IePropertyGrid rows={rows} />
|
<InnovenergyPropertyGrid rows={rows} />
|
||||||
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
|
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
|
||||||
{/*{hasMoveButton && !readOnly && <MoveDialog values={values} />}*/}
|
{/*{hasMoveButton && !readOnly && <MoveDialog values={values} />}*/}
|
||||||
{!readOnly &&
|
{!readOnly &&
|
||||||
|
@ -175,10 +181,14 @@ const InstallationForm = (props: I_InstallationFormProps) => {
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
>
|
>
|
||||||
<Alert onClose={handleClose} severity="success" sx={{ width: "100%" }}>
|
<Alert onClose={handleClose} severity="success" sx={{ width: "100%" }}>
|
||||||
|
{error ? (
|
||||||
|
error.message
|
||||||
|
) : (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="updatedSuccessfully"
|
id="updatedSuccessfully"
|
||||||
defaultMessage="Updated successfully"
|
defaultMessage="Updated successfully"
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</Alert>
|
</Alert>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
</form>
|
</form>
|
|
@ -1,492 +0,0 @@
|
||||||
import { I_GraphData } from "../../util/types";
|
|
||||||
|
|
||||||
export type Datum = string | number | boolean | Array<string>;
|
|
||||||
type LogData = Record<string, Datum>;
|
|
||||||
|
|
||||||
export type TimeSeries = Record<number, LogData>;
|
|
||||||
|
|
||||||
export const timeSeries: TimeSeries = {
|
|
||||||
1673427378: {
|
|
||||||
"TruConvertAc/Ac/Current": 124,
|
|
||||||
"TruConvertAc/Ac/Voltage": 200,
|
|
||||||
"TruConvertAc/Ac/Phi": 2.89,
|
|
||||||
"TruConvertAc/Frequency": 2.89,
|
|
||||||
},
|
|
||||||
1674427380: {
|
|
||||||
"TruConvertAc/Ac/Current": 150,
|
|
||||||
"TruConvertAc/Ac/Voltage": 120,
|
|
||||||
"TruConvertAc/Ac/Phi": 3.21,
|
|
||||||
"TruConvertAc/Frequency": 2.41,
|
|
||||||
},
|
|
||||||
1675427382: {
|
|
||||||
"TruConvertAc/Ac/Current": 153,
|
|
||||||
"TruConvertAc/Ac/Voltage": 140,
|
|
||||||
"TruConvertAc/Ac/Phi": 3.9,
|
|
||||||
"TruConvertAc/Frequency": 4.41,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const exampleLogData: I_GraphData[] = [
|
|
||||||
{
|
|
||||||
TimeStamp: "1673427378",
|
|
||||||
Devices: [
|
|
||||||
{
|
|
||||||
Name: "TruConvertAc",
|
|
||||||
Type: "Inverter",
|
|
||||||
Ac: [
|
|
||||||
{
|
|
||||||
Current: 2.85,
|
|
||||||
Voltage: 236.2,
|
|
||||||
Phi: 2.896,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 2.87,
|
|
||||||
Voltage: 234.5,
|
|
||||||
Phi: 2.793,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 2.65,
|
|
||||||
Voltage: 240.6,
|
|
||||||
Phi: 2.941,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
Frequency: 50.02,
|
|
||||||
Dc: {
|
|
||||||
Current: -2.254,
|
|
||||||
Voltage: 869,
|
|
||||||
},
|
|
||||||
Alarms: [],
|
|
||||||
MainState: "Operation",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "TruConvertDc",
|
|
||||||
Type: "DcDc",
|
|
||||||
Dc: {
|
|
||||||
Current: -2.017,
|
|
||||||
Voltage: 868,
|
|
||||||
},
|
|
||||||
Dc48: {
|
|
||||||
Current: -31,
|
|
||||||
Voltage: 55.9,
|
|
||||||
},
|
|
||||||
Warnings: [],
|
|
||||||
Alarms: [],
|
|
||||||
"DC Power": -1751,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "EmuMeter",
|
|
||||||
Type: "Grid",
|
|
||||||
Ac: [
|
|
||||||
{
|
|
||||||
Current: 15.658,
|
|
||||||
Voltage: 236.3,
|
|
||||||
Phi: 0.318,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 14.052,
|
|
||||||
Voltage: 234.8,
|
|
||||||
Phi: 0.2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 9.046,
|
|
||||||
Voltage: 240.4,
|
|
||||||
Phi: 0.142,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
Frequency: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "EmuMeter",
|
|
||||||
Type: "AcInToAcOut",
|
|
||||||
Ac: [
|
|
||||||
{
|
|
||||||
Current: 15.658,
|
|
||||||
Voltage: 236.3,
|
|
||||||
Phi: 0.318,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 14.052,
|
|
||||||
Voltage: 234.8,
|
|
||||||
Phi: 0.2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 9.046,
|
|
||||||
Voltage: 240.4,
|
|
||||||
Phi: 0.142,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
Frequency: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "AMPT",
|
|
||||||
Type: "PvOnDc",
|
|
||||||
Dc: {
|
|
||||||
Current: 0.229,
|
|
||||||
Voltage: 921.855,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "48TL Battery",
|
|
||||||
Type: "Battery",
|
|
||||||
Dc48: {
|
|
||||||
Current: 38.56,
|
|
||||||
Voltage: 53.29,
|
|
||||||
},
|
|
||||||
Alarms: [],
|
|
||||||
Warnings: [],
|
|
||||||
Soc: 21.6,
|
|
||||||
HeaterOn: true,
|
|
||||||
EocReached: false,
|
|
||||||
BatteryCold: false,
|
|
||||||
Temperature: 265.3,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TimeStamp: "1673427380",
|
|
||||||
Devices: [
|
|
||||||
{
|
|
||||||
Name: "TruConvertAc",
|
|
||||||
Type: "Inverter",
|
|
||||||
Ac: [
|
|
||||||
{
|
|
||||||
Current: 3.01,
|
|
||||||
Voltage: 235.6,
|
|
||||||
Phi: 2.941,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 3.04,
|
|
||||||
Voltage: 234.4,
|
|
||||||
Phi: 2.739,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 2.48,
|
|
||||||
Voltage: 241.1,
|
|
||||||
Phi: 2.765,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
Frequency: 50.02,
|
|
||||||
Dc: {
|
|
||||||
Current: -2.215,
|
|
||||||
Voltage: 868,
|
|
||||||
},
|
|
||||||
Alarms: [],
|
|
||||||
MainState: "Operation",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "TruConvertDc",
|
|
||||||
Type: "DcDc",
|
|
||||||
Dc: {
|
|
||||||
Current: -1.953,
|
|
||||||
Voltage: 868,
|
|
||||||
},
|
|
||||||
Dc48: {
|
|
||||||
Current: -30,
|
|
||||||
Voltage: 55.9,
|
|
||||||
},
|
|
||||||
Warnings: [],
|
|
||||||
Alarms: [],
|
|
||||||
"DC Power": -1695,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "EmuMeter",
|
|
||||||
Type: "Grid",
|
|
||||||
Ac: [
|
|
||||||
{
|
|
||||||
Current: 15.658,
|
|
||||||
Voltage: 236.3,
|
|
||||||
Phi: 0.318,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 14.052,
|
|
||||||
Voltage: 234.8,
|
|
||||||
Phi: 0.2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 9.046,
|
|
||||||
Voltage: 240.4,
|
|
||||||
Phi: 0.142,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
Frequency: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "EmuMeter",
|
|
||||||
Type: "AcInToAcOut",
|
|
||||||
Ac: [
|
|
||||||
{
|
|
||||||
Current: 15.658,
|
|
||||||
Voltage: 236.3,
|
|
||||||
Phi: 0.318,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 14.052,
|
|
||||||
Voltage: 234.8,
|
|
||||||
Phi: 0.2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 9.046,
|
|
||||||
Voltage: 240.4,
|
|
||||||
Phi: 0.142,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
Frequency: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "AMPT",
|
|
||||||
Type: "PvOnDc",
|
|
||||||
Dc: {
|
|
||||||
Current: 0.229,
|
|
||||||
Voltage: 921.855,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "48TL Battery",
|
|
||||||
Type: "Battery",
|
|
||||||
Dc48: {
|
|
||||||
Current: 38.51,
|
|
||||||
Voltage: 53.29,
|
|
||||||
},
|
|
||||||
Alarms: [],
|
|
||||||
Warnings: [],
|
|
||||||
Soc: 21.6,
|
|
||||||
HeaterOn: true,
|
|
||||||
EocReached: false,
|
|
||||||
BatteryCold: false,
|
|
||||||
Temperature: 265.3,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TimeStamp: "1673427381",
|
|
||||||
Devices: [
|
|
||||||
{
|
|
||||||
Name: "TruConvertAc",
|
|
||||||
Type: "Inverter",
|
|
||||||
Ac: [
|
|
||||||
{
|
|
||||||
Current: 0.4,
|
|
||||||
Voltage: 237.2,
|
|
||||||
Phi: 1.4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 0.55,
|
|
||||||
Voltage: 235.6,
|
|
||||||
Phi: 1.551,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 0.44,
|
|
||||||
Voltage: 241.7,
|
|
||||||
Phi: 1.501,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
Frequency: 50.02,
|
|
||||||
Dc: {
|
|
||||||
Current: 0.007,
|
|
||||||
Voltage: 849,
|
|
||||||
},
|
|
||||||
Alarms: [],
|
|
||||||
MainState: "Operation",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "TruConvertDc",
|
|
||||||
Type: "DcDc",
|
|
||||||
Dc: {
|
|
||||||
Current: 0.153,
|
|
||||||
Voltage: 849,
|
|
||||||
},
|
|
||||||
Dc48: {
|
|
||||||
Current: 3,
|
|
||||||
Voltage: 52.0,
|
|
||||||
},
|
|
||||||
Warnings: [],
|
|
||||||
Alarms: [],
|
|
||||||
"DC Power": 130,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "EmuMeter",
|
|
||||||
Type: "Grid",
|
|
||||||
Ac: [
|
|
||||||
{
|
|
||||||
Current: 15.658,
|
|
||||||
Voltage: 236.3,
|
|
||||||
Phi: 0.318,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 14.052,
|
|
||||||
Voltage: 234.8,
|
|
||||||
Phi: 0.2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 9.046,
|
|
||||||
Voltage: 240.4,
|
|
||||||
Phi: 0.142,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
Frequency: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "EmuMeter",
|
|
||||||
Type: "AcInToAcOut",
|
|
||||||
Ac: [
|
|
||||||
{
|
|
||||||
Current: 15.658,
|
|
||||||
Voltage: 236.3,
|
|
||||||
Phi: 0.318,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 14.052,
|
|
||||||
Voltage: 234.8,
|
|
||||||
Phi: 0.2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 9.046,
|
|
||||||
Voltage: 240.4,
|
|
||||||
Phi: 0.142,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
Frequency: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "AMPT",
|
|
||||||
Type: "PvOnDc",
|
|
||||||
Dc: {
|
|
||||||
Current: 0.229,
|
|
||||||
Voltage: 921.855,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "48TL Battery",
|
|
||||||
Type: "Battery",
|
|
||||||
Dc48: {
|
|
||||||
Current: 6.14,
|
|
||||||
Voltage: 51.96,
|
|
||||||
},
|
|
||||||
Alarms: [],
|
|
||||||
Warnings: [],
|
|
||||||
Soc: 21.6,
|
|
||||||
HeaterOn: true,
|
|
||||||
EocReached: false,
|
|
||||||
BatteryCold: false,
|
|
||||||
Temperature: 265.3,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TimeStamp: "1673427383",
|
|
||||||
Devices: [
|
|
||||||
{
|
|
||||||
Name: "TruConvertAc",
|
|
||||||
Type: "Inverter",
|
|
||||||
Ac: [
|
|
||||||
{
|
|
||||||
Current: 1.16,
|
|
||||||
Voltage: 237.7,
|
|
||||||
Phi: 0.376,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 1.25,
|
|
||||||
Voltage: 236.0,
|
|
||||||
Phi: 0.707,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 0.98,
|
|
||||||
Voltage: 242.1,
|
|
||||||
Phi: 0.246,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
Frequency: 50.02,
|
|
||||||
Dc: {
|
|
||||||
Current: 0.847,
|
|
||||||
Voltage: 847,
|
|
||||||
},
|
|
||||||
Alarms: [],
|
|
||||||
MainState: "Operation",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "TruConvertDc",
|
|
||||||
Type: "DcDc",
|
|
||||||
Dc: {
|
|
||||||
Current: 0.979,
|
|
||||||
Voltage: 846,
|
|
||||||
},
|
|
||||||
Dc48: {
|
|
||||||
Current: 18,
|
|
||||||
Voltage: 51.2,
|
|
||||||
},
|
|
||||||
Warnings: [],
|
|
||||||
Alarms: [],
|
|
||||||
"DC Power": 828,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "EmuMeter",
|
|
||||||
Type: "Grid",
|
|
||||||
Ac: [
|
|
||||||
{
|
|
||||||
Current: 12.563,
|
|
||||||
Voltage: 237.2,
|
|
||||||
Phi: 0.376,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 10.913,
|
|
||||||
Voltage: 235.6,
|
|
||||||
Phi: 0.246,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 5.983,
|
|
||||||
Voltage: 241.2,
|
|
||||||
Phi: 0.142,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
Frequency: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "EmuMeter",
|
|
||||||
Type: "AcInToAcOut",
|
|
||||||
Ac: [
|
|
||||||
{
|
|
||||||
Current: 12.563,
|
|
||||||
Voltage: 237.2,
|
|
||||||
Phi: 0.376,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 10.913,
|
|
||||||
Voltage: 235.6,
|
|
||||||
Phi: 0.246,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Current: 5.983,
|
|
||||||
Voltage: 241.2,
|
|
||||||
Phi: 0.142,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
Frequency: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "AMPT",
|
|
||||||
Type: "PvOnDc",
|
|
||||||
Dc: {
|
|
||||||
Current: 0.229,
|
|
||||||
Voltage: 921.855,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "48TL Battery",
|
|
||||||
Type: "Battery",
|
|
||||||
Dc48: {
|
|
||||||
Current: -7.99,
|
|
||||||
Voltage: 51.31,
|
|
||||||
},
|
|
||||||
Alarms: [],
|
|
||||||
Warnings: [],
|
|
||||||
Soc: 21.6,
|
|
||||||
HeaterOn: true,
|
|
||||||
EocReached: false,
|
|
||||||
BatteryCold: false,
|
|
||||||
Temperature: 265.3,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default exampleLogData;
|
|
|
@ -1,13 +1,7 @@
|
||||||
import List from "@mui/material/List";
|
import List from "@mui/material/List";
|
||||||
import ListItemButton from "@mui/material/ListItemButton";
|
import ListItemButton from "@mui/material/ListItemButton";
|
||||||
import ListItemText from "@mui/material/ListItemText";
|
import ListItemText from "@mui/material/ListItemText";
|
||||||
import {
|
import { Alert, CircularProgress, Grid, useTheme } from "@mui/material";
|
||||||
Alert,
|
|
||||||
CircularProgress,
|
|
||||||
Divider,
|
|
||||||
Grid,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import useRouteMatch from "../../hooks/useRouteMatch";
|
import useRouteMatch from "../../hooks/useRouteMatch";
|
||||||
import routes from "../../routes.json";
|
import routes from "../../routes.json";
|
||||||
|
@ -21,14 +15,6 @@ interface InstallationListProps {
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPathWithoutId = (path?: string) => {
|
|
||||||
if (path) {
|
|
||||||
const splitString = path.split(":");
|
|
||||||
return splitString[0];
|
|
||||||
}
|
|
||||||
return routes.installation;
|
|
||||||
};
|
|
||||||
|
|
||||||
const filterData = (
|
const filterData = (
|
||||||
searchQuery: string,
|
searchQuery: string,
|
||||||
data: I_Installation[] | undefined
|
data: I_Installation[] | undefined
|
||||||
|
@ -45,15 +31,12 @@ const filterData = (
|
||||||
|
|
||||||
const InstallationList = (props: InstallationListProps) => {
|
const InstallationList = (props: InstallationListProps) => {
|
||||||
const { fetchData, data, loading, error } = useContext(InstallationsContext);
|
const { fetchData, data, loading, error } = useContext(InstallationsContext);
|
||||||
|
|
||||||
const filteredData = filterData(props.searchQuery, data);
|
const filteredData = filterData(props.searchQuery, data);
|
||||||
|
|
||||||
const routeMatch = useRouteMatch([
|
const routeMatch = useRouteMatch([
|
||||||
routes.installations + routes.list + routes.installation + ":id",
|
routes.installations + routes.list + routes.installation + ":id",
|
||||||
routes.installations + routes.list + routes.liveView + ":id",
|
routes.installations + routes.list + routes.liveView + ":id",
|
||||||
routes.installations + routes.list + routes.log + ":id",
|
routes.installations + routes.list + routes.log + ":id",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -108,13 +91,13 @@ const InstallationList = (props: InstallationListProps) => {
|
||||||
borderStyle: "solid",
|
borderStyle: "solid",
|
||||||
backgroundColor: theme.palette.primary.dark,
|
backgroundColor: theme.palette.primary.dark,
|
||||||
"&.Mui-selected": {
|
"&.Mui-selected": {
|
||||||
backgroundColor: colors.orangeSelected,
|
backgroundColor: theme.palette.secondary.main,
|
||||||
},
|
},
|
||||||
":hover": {
|
":hover": {
|
||||||
backgroundColor: colors.orangeHover,
|
backgroundColor: theme.palette.secondary.light,
|
||||||
},
|
},
|
||||||
"&.Mui-selected:hover": {
|
"&.Mui-selected:hover": {
|
||||||
backgroundColor: colors.orangeSelected,
|
backgroundColor: theme.palette.secondary.main,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -5,14 +5,13 @@ import routes from "../../routes.json";
|
||||||
import useRouteMatch from "../../hooks/useRouteMatch";
|
import useRouteMatch from "../../hooks/useRouteMatch";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
import InnovenergyTab from "../Layout/InnovenergyTab";
|
import InnovenergyTab from "../Layout/InnovenergyTab";
|
||||||
import InnovenergyTabBorder from "../Layout/InnovenergyTab";
|
|
||||||
import InnovenergyTabs from "components/Layout/InnovenergyTabs";
|
import InnovenergyTabs from "components/Layout/InnovenergyTabs";
|
||||||
import { useTheme } from "@mui/material";
|
import { useTheme } from "@mui/material";
|
||||||
import { red } from "@mui/material/colors";
|
|
||||||
import { colors } from "index";
|
import { colors } from "index";
|
||||||
|
|
||||||
const InstallationTabs = () => {
|
const InstallationTabs = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const intl = useIntl();
|
||||||
const routeMatch = useRouteMatch([
|
const routeMatch = useRouteMatch([
|
||||||
routes.installations + routes.list + routes.installation + ":id",
|
routes.installations + routes.list + routes.installation + ":id",
|
||||||
routes.installations + routes.list + routes.liveView + ":id",
|
routes.installations + routes.list + routes.liveView + ":id",
|
||||||
|
@ -20,7 +19,6 @@ const InstallationTabs = () => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const id = routeMatch?.params?.id;
|
const id = routeMatch?.params?.id;
|
||||||
const intl = useIntl();
|
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { Grid, colors } from "@mui/material";
|
import { Grid } from "@mui/material";
|
||||||
import { Routes, Route } from "react-router";
|
import { Routes, Route } from "react-router";
|
||||||
import LiveView from "./LiveView";
|
import LiveView from "./LiveView/LiveView";
|
||||||
import InstallationTabs from "./InstallationTabs";
|
import InstallationTabs from "./InstallationTabs";
|
||||||
import Log from "./Log/Log";
|
import Log from "./Log/Log";
|
||||||
import routes from "../../routes.json";
|
import routes from "../../routes.json";
|
||||||
import InstallationsContextProvider from "../Context/InstallationsContextProvider";
|
import InstallationsContextProvider from "../Context/InstallationsContextProvider";
|
||||||
import SearchSidebar from "../Layout/Search";
|
import SearchSidebar from "../Layout/Search";
|
||||||
import InstallationList from "./InstallationList";
|
import InstallationList from "./InstallationList";
|
||||||
import Installation from "./Installation";
|
import Installation from "./Detail/Installation";
|
||||||
import CheckboxTree from "./Log/CheckboxTree";
|
import CheckboxTree from "./Log/CheckboxTree";
|
||||||
import LogContextProvider from "../Context/LogContextProvider";
|
import LogContextProvider from "../Context/LogContextProvider";
|
||||||
import useRouteMatch from "../../hooks/useRouteMatch";
|
import useRouteMatch from "../../hooks/useRouteMatch";
|
||||||
|
@ -27,7 +27,6 @@ const Installations = () => {
|
||||||
const id = routeMatch?.params?.id;
|
const id = routeMatch?.params?.id;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// TODO remove if
|
|
||||||
getInstallation(id);
|
getInstallation(id);
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import TopologyView from "./Log/TopologyView";
|
import TopologyView from "./TopologyView";
|
||||||
import { Box, useTheme } from "@mui/material";
|
import { Box, useTheme } from "@mui/material";
|
||||||
import { colors } from "../../index";
|
|
||||||
|
|
||||||
const LiveView = () => {
|
const LiveView = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
@ -8,7 +7,7 @@ const LiveView = () => {
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
py: 3,
|
py: 3,
|
||||||
bgcolor: colors.greyDark,
|
bgcolor: theme.palette.primary.dark,
|
||||||
px: 1,
|
px: 1,
|
||||||
borderLeft: 2,
|
borderLeft: 2,
|
||||||
borderRight: 2,
|
borderRight: 2,
|
|
@ -1,14 +1,14 @@
|
||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material";
|
||||||
import { getBoxColor } from "../../../util/graph.util";
|
import { getBoxColor } from "../../../util/graph.util";
|
||||||
|
|
||||||
export interface BoxDataValue {
|
export interface I_BoxDataValue {
|
||||||
unit: string;
|
unit: string;
|
||||||
value: string | number;
|
value: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BoxData = {
|
export type BoxData = {
|
||||||
label: string;
|
label: string;
|
||||||
values: BoxDataValue[];
|
values: I_BoxDataValue[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TopologyBoxProps = {
|
export type TopologyBoxProps = {
|
|
@ -1,4 +1,4 @@
|
||||||
import { Alert, Box, useTheme } from "@mui/material";
|
import { Alert, Box } from "@mui/material";
|
||||||
import TopologyColumn from "./TopologyColumn";
|
import TopologyColumn from "./TopologyColumn";
|
||||||
import { TimeSpan, UnixTime } from "../../../dataCache/time";
|
import { TimeSpan, UnixTime } from "../../../dataCache/time";
|
||||||
import {
|
import {
|
||||||
|
@ -15,7 +15,6 @@ import { S3CredentialsContext } from "../../Context/S3CredentialsContextProvider
|
||||||
const TopologyView = () => {
|
const TopologyView = () => {
|
||||||
const [values, setValues] = useState<TopologyValues | null>(null);
|
const [values, setValues] = useState<TopologyValues | null>(null);
|
||||||
const { fetchData } = useContext(S3CredentialsContext);
|
const { fetchData } = useContext(S3CredentialsContext);
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
|
@ -225,7 +224,6 @@ const TopologyView = () => {
|
||||||
/>
|
/>
|
||||||
</Alert>
|
</Alert>
|
||||||
);
|
);
|
||||||
return <div>TopologyView</div>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TopologyView;
|
export default TopologyView;
|
|
@ -1,5 +1,5 @@
|
||||||
import { TreeItem, TreeView } from "@mui/lab";
|
import { TreeView } from "@mui/lab";
|
||||||
import { Checkbox, Divider, useTheme } from "@mui/material";
|
import { Checkbox } from "@mui/material";
|
||||||
import { useContext, ReactNode } from "react";
|
import { useContext, ReactNode } from "react";
|
||||||
import { LogContext } from "../../Context/LogContextProvider";
|
import { LogContext } from "../../Context/LogContextProvider";
|
||||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||||
|
@ -7,17 +7,12 @@ import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
||||||
import useRouteMatch from "../../../hooks/useRouteMatch";
|
import useRouteMatch from "../../../hooks/useRouteMatch";
|
||||||
import routes from "../../../routes.json";
|
import routes from "../../../routes.json";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { colors } from "index";
|
import InnovenergyTreeItem from "../../Layout/InnovenergyTreeItem";
|
||||||
import InnovEnergyTreeItem from "../../Layout/InnovEnergyTreeItem";
|
|
||||||
|
|
||||||
export interface ToggleElement {
|
export interface I_TreeElement {
|
||||||
[key: string]: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TreeElement {
|
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
children: TreeElement[];
|
children: I_TreeElement[];
|
||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,13 +23,13 @@ const CheckboxTree = () => {
|
||||||
routes.installations + routes.list + routes.log + ":id",
|
routes.installations + routes.list + routes.log + ":id",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const getNodes = (element: TreeElement): null | ReactNode => {
|
const getNodes = (element: I_TreeElement): null | ReactNode => {
|
||||||
return element.children.length > 0 ? renderTree(element.children) : null;
|
return element.children.length > 0 ? renderTree(element.children) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (
|
const handleClick = (
|
||||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
|
event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
|
||||||
element: TreeElement,
|
element: I_TreeElement,
|
||||||
checked?: string
|
checked?: string
|
||||||
) => {
|
) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
|
@ -50,12 +45,13 @@ const CheckboxTree = () => {
|
||||||
) => {
|
) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
};
|
};
|
||||||
const renderTree = (data: TreeElement[]): ReactNode => {
|
|
||||||
|
const renderTree = (data: I_TreeElement[]): ReactNode => {
|
||||||
return data.map((element) => {
|
return data.map((element) => {
|
||||||
const checked = checkedToggles.find((toggle) => element.id === toggle);
|
const checked = checkedToggles.find((toggle) => element.id === toggle);
|
||||||
const splitName = element.name.split("/");
|
const splitName = element.name.split("/");
|
||||||
return (
|
return (
|
||||||
<InnovEnergyTreeItem
|
<InnovenergyTreeItem
|
||||||
id={"checkbox-tree-" + element.name}
|
id={"checkbox-tree-" + element.name}
|
||||||
key={element.name}
|
key={element.name}
|
||||||
nodeId={element.name}
|
nodeId={element.name}
|
||||||
|
@ -77,7 +73,7 @@ const CheckboxTree = () => {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{getNodes(element)}
|
{getNodes(element)}
|
||||||
</InnovEnergyTreeItem>
|
</InnovenergyTreeItem>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,21 +3,21 @@ import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
|
||||||
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
import { createTimes } from "../../../../util/graph.util";
|
||||||
|
import { TimeRange, UnixTime } from "../../../../dataCache/time";
|
||||||
|
import { useTheme } from "@mui/material";
|
||||||
|
import { colors } from "../../../../index";
|
||||||
import ShortcutButton from "./ShortcutButton";
|
import ShortcutButton from "./ShortcutButton";
|
||||||
import { createTimes } from "../../../util/graph.util";
|
|
||||||
import { TimeRange, UnixTime } from "../../../dataCache/time";
|
|
||||||
import { TextField, useTheme } from "@mui/material";
|
|
||||||
import { colors } from "../../..";
|
|
||||||
|
|
||||||
interface DateRangePickerProps {
|
interface I_DateRangePickerProps {
|
||||||
setRange: (value: Date[]) => void;
|
setRange: (value: Date[]) => void;
|
||||||
range: Date[];
|
range: Date[];
|
||||||
getCacheSeries: (xaxisRange0: Date, xaxisRange1: Date) => void;
|
getCacheSeries: (xaxisRange0: Date, xaxisRange1: Date) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DateRangePicker = (props: DateRangePickerProps) => {
|
const DateRangePicker = (props: I_DateRangePickerProps) => {
|
||||||
const theme = useTheme();
|
|
||||||
const { setRange, range, getCacheSeries } = props;
|
const { setRange, range, getCacheSeries } = props;
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
const handleChange = (fromDate: Date, toDate: Date) => {
|
const handleChange = (fromDate: Date, toDate: Date) => {
|
||||||
const timeRange = createTimes(
|
const timeRange = createTimes(
|
|
@ -1,17 +1,15 @@
|
||||||
import { colors } from "@mui/material";
|
import { UnixTime, TimeSpan } from "../../../../dataCache/time";
|
||||||
import { UnixTime, TimeSpan } from "../../../dataCache/time";
|
import { createTimes } from "../../../../util/graph.util";
|
||||||
import { createTimes } from "../../../util/graph.util";
|
import InnovenergyButton from "../../../Layout/InnovenergyButton";
|
||||||
import InnovenergyButton from "../../Layout/InnovenergyButton";
|
|
||||||
import { red } from "@mui/material/colors";
|
|
||||||
|
|
||||||
interface ShortcutButtonProps {
|
interface I_ShortcutButtonProps {
|
||||||
setRange: (value: Date[]) => void;
|
setRange: (value: Date[]) => void;
|
||||||
getCacheSeries: (xaxisRange0: Date, xaxisRange1: Date) => void;
|
getCacheSeries: (xaxisRange0: Date, xaxisRange1: Date) => void;
|
||||||
dayRange: number;
|
dayRange: number;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ShortcutButton = (props: ShortcutButtonProps) => {
|
const ShortcutButton = (props: I_ShortcutButtonProps) => {
|
||||||
return (
|
return (
|
||||||
<InnovenergyButton
|
<InnovenergyButton
|
||||||
onClick={() => {
|
onClick={() => {
|
|
@ -19,6 +19,7 @@ const Log = () => {
|
||||||
borderBottomLeftRadius: 4,
|
borderBottomLeftRadius: 4,
|
||||||
borderBottomRightRadius: 4,
|
borderBottomRightRadius: 4,
|
||||||
borderColor: theme.palette.text.disabled,
|
borderColor: theme.palette.text.disabled,
|
||||||
|
overflowX: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ScalarGraph />
|
<ScalarGraph />
|
||||||
|
|
|
@ -1,50 +1,51 @@
|
||||||
import Plot from "react-plotly.js";
|
import Plot from "react-plotly.js";
|
||||||
import { DataRecord, RecordSeries } from "../../../dataCache/data";
|
import { RecordSeries } from "../../../dataCache/data";
|
||||||
import {
|
import {
|
||||||
GraphData,
|
I_GraphData,
|
||||||
createTimes,
|
createTimes,
|
||||||
getTreeElements,
|
getTreeElements,
|
||||||
isNumeric,
|
isNumeric,
|
||||||
parseCsv,
|
|
||||||
stringToColor,
|
stringToColor,
|
||||||
transformToBarGraphData,
|
transformToBarGraphData,
|
||||||
} from "../../../util/graph.util";
|
} from "../../../util/graph.util";
|
||||||
import { TimeRange, TimeSpan, UnixTime } from "../../../dataCache/time";
|
import { TimeRange, TimeSpan, UnixTime } from "../../../dataCache/time";
|
||||||
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
||||||
import { BehaviorSubject, startWith, throttleTime, withLatestFrom } from "rxjs";
|
import { BehaviorSubject, startWith, throttleTime, withLatestFrom } from "rxjs";
|
||||||
import { S3Access } from "../../../dataCache/S3/S3Access";
|
import DataCache from "../../../dataCache/dataCache";
|
||||||
import DataCache, { FetchResult } from "../../../dataCache/dataCache";
|
|
||||||
import { LogContext } from "../../Context/LogContextProvider";
|
import { LogContext } from "../../Context/LogContextProvider";
|
||||||
import { isDefined } from "../../../dataCache/utils/maybe";
|
import { isDefined } from "../../../dataCache/utils/maybe";
|
||||||
import { Data, Icons, Layout, PlotRelayoutEvent, relayout } from "plotly.js";
|
import { Data, Layout, PlotRelayoutEvent } from "plotly.js";
|
||||||
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import DateRangePicker from "./DateRangePicker";
|
import DateRangePicker from "./DatePicker/DateRangePicker";
|
||||||
import { LocalizationProvider } from "@mui/x-date-pickers";
|
import { LocalizationProvider } from "@mui/x-date-pickers";
|
||||||
import { Alert, useTheme } from "@mui/material";
|
import { Alert } from "@mui/material";
|
||||||
import { S3CredentialsContext } from "../../Context/S3CredentialsContextProvider";
|
import { S3CredentialsContext } from "../../Context/S3CredentialsContextProvider";
|
||||||
|
|
||||||
const NUMBER_OF_NODES = 100;
|
const NUMBER_OF_NODES = 100;
|
||||||
|
|
||||||
const ScalarGraph = () => {
|
const ScalarGraph = () => {
|
||||||
const timeRange = createTimes(
|
const timeRange = createTimes(
|
||||||
UnixTime.now()
|
UnixTime.now().rangeBefore(TimeSpan.fromDays(1)),
|
||||||
/* .fromTicks(1682085650) */
|
|
||||||
.rangeBefore(TimeSpan.fromDays(1)),
|
|
||||||
NUMBER_OF_NODES
|
NUMBER_OF_NODES
|
||||||
);
|
);
|
||||||
|
|
||||||
const [timeSeries, setTimeSeries] = useState<RecordSeries>([]);
|
const [timeSeries, setTimeSeries] = useState<RecordSeries>([]);
|
||||||
|
const [plotTitles, setPlotTitles] = useState<string[]>([]);
|
||||||
const [range, setRange] = useState([
|
const [range, setRange] = useState([
|
||||||
timeRange[0].toDate(),
|
timeRange[0].toDate(),
|
||||||
timeRange[timeRange.length - 1].toDate(),
|
timeRange[timeRange.length - 1].toDate(),
|
||||||
]);
|
]);
|
||||||
const [plotTitles, setPlotTitles] = useState<string[]>([]);
|
|
||||||
|
|
||||||
const { toggles, setToggles, checkedToggles } = useContext(LogContext);
|
const { toggles, setToggles, checkedToggles } = useContext(LogContext);
|
||||||
const { fetchData } = useContext(S3CredentialsContext);
|
const { fetchData } = useContext(S3CredentialsContext);
|
||||||
|
|
||||||
const times$ = useMemo(() => new BehaviorSubject(timeRange), []);
|
const times$ = useMemo(() => new BehaviorSubject(timeRange), []);
|
||||||
|
|
||||||
|
const cache = useMemo(() => {
|
||||||
|
return new DataCache(fetchData, TimeSpan.fromSeconds(2));
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subscription = cache.gotData
|
const subscription = cache.gotData
|
||||||
.pipe(
|
.pipe(
|
||||||
|
@ -57,8 +58,6 @@ const ScalarGraph = () => {
|
||||||
setTimeSeries(timeSeries);
|
setTimeSeries(timeSeries);
|
||||||
|
|
||||||
const toggleValues = timeSeries.find((timeStamp) => timeStamp.value);
|
const toggleValues = timeSeries.find((timeStamp) => timeStamp.value);
|
||||||
console.log("toggles", timeSeries, toggleValues);
|
|
||||||
|
|
||||||
if (toggles === null && toggleValues && toggleValues.value) {
|
if (toggles === null && toggleValues && toggleValues.value) {
|
||||||
const treeElements = getTreeElements(toggleValues.value);
|
const treeElements = getTreeElements(toggleValues.value);
|
||||||
setToggles(treeElements);
|
setToggles(treeElements);
|
||||||
|
@ -67,11 +66,7 @@ const ScalarGraph = () => {
|
||||||
return () => subscription.unsubscribe();
|
return () => subscription.unsubscribe();
|
||||||
}, [toggles]);
|
}, [toggles]);
|
||||||
|
|
||||||
const cache = useMemo(() => {
|
const transformToGraphData = (input: RecordSeries): I_GraphData => {
|
||||||
return new DataCache(fetchData, TimeSpan.fromSeconds(2));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const transformToGraphData = (input: RecordSeries): GraphData => {
|
|
||||||
const transformedObject: any = {};
|
const transformedObject: any = {};
|
||||||
|
|
||||||
input.forEach((item) => {
|
input.forEach((item) => {
|
||||||
|
@ -95,6 +90,7 @@ const ScalarGraph = () => {
|
||||||
setPlotTitles(Object.keys(transformedObject));
|
setPlotTitles(Object.keys(transformedObject));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return Object.keys(transformedObject).length > 0
|
return Object.keys(transformedObject).length > 0
|
||||||
? transformedObject
|
? transformedObject
|
||||||
: plotTitles.reduce(
|
: plotTitles.reduce(
|
||||||
|
@ -105,7 +101,7 @@ const ScalarGraph = () => {
|
||||||
y: [],
|
y: [],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
{} as GraphData
|
{} as I_GraphData
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -138,9 +134,7 @@ const ScalarGraph = () => {
|
||||||
[getCacheSeries]
|
[getCacheSeries]
|
||||||
);
|
);
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
const renderGraphs = () => {
|
const renderGraphs = () => {
|
||||||
console.log("toggles", toggles);
|
|
||||||
if (checkedToggles.length > 0) {
|
if (checkedToggles.length > 0) {
|
||||||
const coordinateTimeSeries = transformToGraphData(timeSeries);
|
const coordinateTimeSeries = transformToGraphData(timeSeries);
|
||||||
const visibleGraphs = Object.keys(coordinateTimeSeries).filter((path) => {
|
const visibleGraphs = Object.keys(coordinateTimeSeries).filter((path) => {
|
||||||
|
@ -158,7 +152,6 @@ const ScalarGraph = () => {
|
||||||
</LocalizationProvider>
|
</LocalizationProvider>
|
||||||
{visibleGraphs.map((path) => {
|
{visibleGraphs.map((path) => {
|
||||||
const isScalar = isNumeric(coordinateTimeSeries[path].y[0]);
|
const isScalar = isNumeric(coordinateTimeSeries[path].y[0]);
|
||||||
console.log("graphdata", timeSeries, coordinateTimeSeries);
|
|
||||||
|
|
||||||
const data = isScalar
|
const data = isScalar
|
||||||
? [
|
? [
|
||||||
|
@ -212,6 +205,15 @@ const ScalarGraph = () => {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if (checkedToggles.length === 0) {
|
||||||
|
return (
|
||||||
|
<Alert sx={{ mt: 2 }} severity="info">
|
||||||
|
<FormattedMessage
|
||||||
|
id="makeASelection"
|
||||||
|
defaultMessage="Please make a selection on the left"
|
||||||
|
/>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
} else if (toggles === null) {
|
} else if (toggles === null) {
|
||||||
return (
|
return (
|
||||||
<Alert sx={{ mt: 2 }} severity="error">
|
<Alert sx={{ mt: 2 }} severity="error">
|
||||||
|
@ -222,14 +224,7 @@ const ScalarGraph = () => {
|
||||||
</Alert>
|
</Alert>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return null;
|
||||||
<Alert sx={{ mt: 2 }} severity="info">
|
|
||||||
<FormattedMessage
|
|
||||||
id="makeASelection"
|
|
||||||
defaultMessage="Please make a selection on the left"
|
|
||||||
/>
|
|
||||||
</Alert>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return <>{renderGraphs()}</>;
|
return <>{renderGraphs()}</>;
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
.topologyBoxTitle{
|
|
||||||
margin-block-start: "0";
|
|
||||||
margin-block-end: "0";
|
|
||||||
background-color: titleColor;
|
|
||||||
padding: "5px";
|
|
||||||
border-top-left-radius: "4px";
|
|
||||||
border-top-right-radius: "4px";
|
|
||||||
display: "flex";
|
|
||||||
justify-content: "center";
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
import { Box, CircularProgress, Alert, useTheme } from "@mui/material";
|
|
||||||
import { AxiosError } from "axios";
|
|
||||||
import { useState, useEffect, FC } from "react";
|
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import axiosConfig from "../../config/axiosConfig";
|
|
||||||
export interface I_FormProps<T> {
|
|
||||||
values: T;
|
|
||||||
id: string;
|
|
||||||
hasMoveButton?: boolean;
|
|
||||||
}
|
|
||||||
interface I_DetailProps<T> {
|
|
||||||
formComponent: FC<I_FormProps<T>>;
|
|
||||||
route: string;
|
|
||||||
hasMoveButton?: boolean;
|
|
||||||
}
|
|
||||||
const Detail = <T extends { id: number }>(props: I_DetailProps<T>) => {
|
|
||||||
const { id } = useParams();
|
|
||||||
const { formComponent: FormComponent, route, hasMoveButton } = props;
|
|
||||||
const [values, setValues] = useState<T>();
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [error, setError] = useState<AxiosError>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setLoading(true);
|
|
||||||
axiosConfig
|
|
||||||
.get(route + id)
|
|
||||||
.then((res) => {
|
|
||||||
setValues(res.data);
|
|
||||||
setLoading(false);
|
|
||||||
})
|
|
||||||
.catch((err: AxiosError) => {
|
|
||||||
setError(err);
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
}, [id, route]);
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
if (values && values.id && values.id.toString() === id) {
|
|
||||||
return (
|
|
||||||
<FormComponent values={values} id={id} hasMoveButton={hasMoveButton} />
|
|
||||||
);
|
|
||||||
} else if (loading) {
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
sx={{ width: 1 / 2, justifyContent: "center", display: "flex", mt: 10 }}
|
|
||||||
>
|
|
||||||
<CircularProgress sx={{ m: 2, color: theme.palette.secondary.main }} />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
} else if (error) {
|
|
||||||
return (
|
|
||||||
<Alert severity="error" sx={{ mt: 1 }}>
|
|
||||||
{error.message}
|
|
||||||
</Alert>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Detail;
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Button, SxProps, Theme, colors, useTheme } from "@mui/material";
|
import { Button, SxProps, Theme, useTheme } from "@mui/material";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
interface I_InnovenergyButtonProps {
|
interface I_InnovenergyButtonProps {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { List, useTheme } from "@mui/material";
|
import { List, useTheme } from "@mui/material";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
interface InnovenergyListProps {
|
interface I_InnovenergyListProps {
|
||||||
id: string;
|
id: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const InnovenergyList = (props: InnovenergyListProps) => {
|
const InnovenergyList = (props: I_InnovenergyListProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<List
|
<List
|
||||||
|
@ -18,7 +18,6 @@ const InnovenergyList = (props: InnovenergyListProps) => {
|
||||||
pb: 0,
|
pb: 0,
|
||||||
}}
|
}}
|
||||||
component="nav"
|
component="nav"
|
||||||
aria-labelledby="nested-list-subheader"
|
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</List>
|
</List>
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { InputLabel, TextField } from "@mui/material";
|
||||||
|
import { colors } from "../../index";
|
||||||
|
import { I_InnovenergyTextfieldProps } from "./InnovenergyTextfield";
|
||||||
|
|
||||||
|
interface I_InnovenergyPropertyGridProps {
|
||||||
|
rows: I_InnovenergyTextfieldProps[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InnovenergyPropertyGrid = (
|
||||||
|
props: I_InnovenergyPropertyGridProps
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "space-around",
|
||||||
|
marginRight: "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.rows.map((prop) => (
|
||||||
|
<InputLabel key={prop.label}>{prop.label}</InputLabel>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{props.rows.map((element) => {
|
||||||
|
return (
|
||||||
|
<TextField
|
||||||
|
key={element.label}
|
||||||
|
color="secondary"
|
||||||
|
id={element.id}
|
||||||
|
variant="outlined"
|
||||||
|
name={element.name}
|
||||||
|
type={element.type}
|
||||||
|
fullWidth
|
||||||
|
sx={{
|
||||||
|
input: {
|
||||||
|
bgcolor: "white",
|
||||||
|
border: 0.5,
|
||||||
|
borderRadius: 1,
|
||||||
|
height: 15,
|
||||||
|
},
|
||||||
|
my: 0.5,
|
||||||
|
".Mui-focused": {
|
||||||
|
borderRadius: 1,
|
||||||
|
},
|
||||||
|
"MuiInputLabel-root": {
|
||||||
|
color: colors.black,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
value={element.value || ""}
|
||||||
|
onChange={element.handleChange}
|
||||||
|
InputProps={{
|
||||||
|
readOnly: element.readOnly,
|
||||||
|
}}
|
||||||
|
disabled={element.disabled}
|
||||||
|
helperText={element.helperText}
|
||||||
|
error={element.error}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InnovenergyPropertyGrid;
|
|
@ -1,13 +1,13 @@
|
||||||
import { Alert, Snackbar } from "@mui/material";
|
import { Alert, Snackbar } from "@mui/material";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
interface InnovenergySnackbarProps {
|
interface I_InnovenergySnackbarProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
setOpen: (value: boolean) => void;
|
setOpen: (value: boolean) => void;
|
||||||
error?: any;
|
error?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const InnovenergySnackbar = (props: InnovenergySnackbarProps) => {
|
const InnovenergySnackbar = (props: I_InnovenergySnackbarProps) => {
|
||||||
const { open, setOpen, error } = props;
|
const { open, setOpen, error } = props;
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
|
@ -1,15 +1,15 @@
|
||||||
import { SxProps, Tabs, Theme, useTheme } from "@mui/material";
|
import { Tabs, useTheme } from "@mui/material";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { colors } from "index";
|
import { colors } from "index";
|
||||||
|
|
||||||
interface AntTabsProps {
|
interface I_AntTabsProps {
|
||||||
id: string;
|
id: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
sx?: any;
|
sx?: any;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const InnovenergyTabs = (props: AntTabsProps) => {
|
const InnovenergyTabs = (props: I_AntTabsProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs
|
||||||
|
@ -25,7 +25,6 @@ const InnovenergyTabs = (props: AntTabsProps) => {
|
||||||
"&.Mui-selected": {
|
"&.Mui-selected": {
|
||||||
color: colors.black,
|
color: colors.black,
|
||||||
backgroundColor: theme.palette.primary.light,
|
backgroundColor: theme.palette.primary.light,
|
||||||
borderColor: `#90A7c5 #90A7c5 #fff`,
|
|
||||||
},
|
},
|
||||||
"& .MuiTabs-indicator": {
|
"& .MuiTabs-indicator": {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
|
|
@ -1,17 +1,4 @@
|
||||||
import {
|
import { Grid, InputLabel, TextField } from "@mui/material";
|
||||||
Grid,
|
|
||||||
InputLabel,
|
|
||||||
Paper,
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableContainer,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
TextField,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { colors } from "../../index";
|
|
||||||
|
|
||||||
export interface I_InnovenergyTextfieldProps {
|
export interface I_InnovenergyTextfieldProps {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -26,67 +13,7 @@ export interface I_InnovenergyTextfieldProps {
|
||||||
error?: boolean;
|
error?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IePropertyGrid = (props: {
|
|
||||||
rows: I_InnovenergyTextfieldProps[];
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
justifyContent: "space-around",
|
|
||||||
marginRight: "20px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.rows.map((prop) => (
|
|
||||||
<InputLabel key={prop.label}>{prop.label}</InputLabel>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{props.rows.map((element) => {
|
|
||||||
return (
|
|
||||||
<TextField
|
|
||||||
key={element.label}
|
|
||||||
color="secondary"
|
|
||||||
id={element.id}
|
|
||||||
variant="outlined"
|
|
||||||
name={element.name}
|
|
||||||
type={element.type}
|
|
||||||
fullWidth
|
|
||||||
sx={{
|
|
||||||
input: {
|
|
||||||
bgcolor: "white",
|
|
||||||
border: 0.5,
|
|
||||||
borderRadius: 1,
|
|
||||||
height: 15,
|
|
||||||
},
|
|
||||||
my: 0.5,
|
|
||||||
".Mui-focused": {
|
|
||||||
borderRadius: 1,
|
|
||||||
},
|
|
||||||
"MuiInputLabel-root": {
|
|
||||||
color: colors.black,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
value={element.value || ""}
|
|
||||||
onChange={element.handleChange}
|
|
||||||
InputProps={{
|
|
||||||
readOnly: element.readOnly,
|
|
||||||
}}
|
|
||||||
disabled={element.disabled}
|
|
||||||
helperText={element.helperText}
|
|
||||||
error={element.error}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const InnovenergyTextfield = (props: I_InnovenergyTextfieldProps) => {
|
const InnovenergyTextfield = (props: I_InnovenergyTextfieldProps) => {
|
||||||
const theme = useTheme();
|
|
||||||
return (
|
return (
|
||||||
<Grid container direction="row" alignItems="center" spacing={2}>
|
<Grid container direction="row" alignItems="center" spacing={2}>
|
||||||
<Grid item xs={3}>
|
<Grid item xs={3}>
|
||||||
|
@ -107,7 +34,6 @@ const InnovenergyTextfield = (props: I_InnovenergyTextfieldProps) => {
|
||||||
borderRadius: 1,
|
borderRadius: 1,
|
||||||
},
|
},
|
||||||
my: 0.5,
|
my: 0.5,
|
||||||
borderColor: "red",
|
|
||||||
}}
|
}}
|
||||||
value={props.value || ""}
|
value={props.value || ""}
|
||||||
onChange={props.handleChange}
|
onChange={props.handleChange}
|
||||||
|
|
|
@ -2,14 +2,14 @@ import { TreeItem, TreeItemProps } from "@mui/lab";
|
||||||
import { colors } from "../../index";
|
import { colors } from "../../index";
|
||||||
import { useTheme } from "@mui/material";
|
import { useTheme } from "@mui/material";
|
||||||
|
|
||||||
const InnovEnergyTreeItem = (props: TreeItemProps) => {
|
const InnovenergyTreeItem = (props: TreeItemProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<TreeItem
|
<TreeItem
|
||||||
id={props.id}
|
id={props.id}
|
||||||
nodeId={props.nodeId}
|
nodeId={props.nodeId}
|
||||||
label={props.label}
|
label={props.label}
|
||||||
onClick={() => props.onClick}
|
onClick={props.onClick}
|
||||||
sx={{
|
sx={{
|
||||||
".MuiTreeItem-content": {
|
".MuiTreeItem-content": {
|
||||||
py: "10px",
|
py: "10px",
|
||||||
|
@ -30,6 +30,7 @@ const InnovEnergyTreeItem = (props: TreeItemProps) => {
|
||||||
},
|
},
|
||||||
borderRadius: "4px",
|
borderRadius: "4px",
|
||||||
bgcolor: colors.greyDark,
|
bgcolor: colors.greyDark,
|
||||||
|
...props.sx,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
|
@ -37,4 +38,4 @@ const InnovEnergyTreeItem = (props: TreeItemProps) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default InnovEnergyTreeItem;
|
export default InnovenergyTreeItem;
|
|
@ -1,12 +1,12 @@
|
||||||
import { MenuItem, Select } from "@mui/material";
|
import { MenuItem, Select } from "@mui/material";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
interface LanguageSelectProps {
|
interface I_LanguageSelectProps {
|
||||||
language: string;
|
language: string;
|
||||||
setLanguage: (language: string) => void;
|
setLanguage: (language: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LanguageSelect = (props: LanguageSelectProps) => {
|
const LanguageSelect = (props: I_LanguageSelectProps) => {
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
color="secondary"
|
color="secondary"
|
||||||
|
|
|
@ -2,14 +2,15 @@ import { FormattedMessage } from "react-intl";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import axiosConfig from "../../config/axiosConfig";
|
import axiosConfig from "../../config/axiosConfig";
|
||||||
import InnovenergyButton from "./InnovenergyButton";
|
import InnovenergyButton from "./InnovenergyButton";
|
||||||
import { colors } from "index";
|
import { useTheme } from "@mui/material";
|
||||||
|
|
||||||
interface LogoutButtonProps {
|
interface I_LogoutButtonProps {
|
||||||
removeToken: () => void;
|
removeToken: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LogoutButton = (props: LogoutButtonProps) => {
|
const LogoutButton = (props: I_LogoutButtonProps) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InnovenergyButton
|
<InnovenergyButton
|
||||||
|
@ -20,7 +21,11 @@ const LogoutButton = (props: LogoutButtonProps) => {
|
||||||
props.removeToken();
|
props.removeToken();
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
sx={{ mx: 1, bgcolor: colors.orangeSelected, textTransform: "none" }}
|
sx={{
|
||||||
|
mx: 1,
|
||||||
|
bgcolor: theme.palette.secondary.main,
|
||||||
|
textTransform: "none",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<FormattedMessage id="logout" defaultMessage="Logout" />
|
<FormattedMessage id="logout" defaultMessage="Logout" />
|
||||||
</InnovenergyButton>
|
</InnovenergyButton>
|
||||||
|
|
|
@ -27,10 +27,14 @@ const ModeButtons = () => {
|
||||||
>
|
>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
sx={{
|
sx={{
|
||||||
color: "#707071",
|
color: theme.palette.text.secondary,
|
||||||
"&.Mui-selected": {
|
"&.Mui-selected": {
|
||||||
bgcolor: theme.palette.primary.dark,
|
bgcolor: theme.palette.secondary.dark,
|
||||||
color: "#707071",
|
color: theme.palette.text.primary,
|
||||||
|
},
|
||||||
|
"&.Mui-selected:hover": {
|
||||||
|
bgcolor: theme.palette.secondary.dark,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
id="mode-toggle-button-list"
|
id="mode-toggle-button-list"
|
||||||
|
@ -42,10 +46,14 @@ const ModeButtons = () => {
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
sx={{
|
sx={{
|
||||||
color: "#707071",
|
color: theme.palette.text.secondary,
|
||||||
"&.Mui-selected": {
|
"&.Mui-selected": {
|
||||||
bgcolor: theme.palette.primary.dark,
|
bgcolor: theme.palette.secondary.dark,
|
||||||
color: "#707071",
|
color: theme.palette.text.primary,
|
||||||
|
},
|
||||||
|
"&.Mui-selected:hover": {
|
||||||
|
bgcolor: theme.palette.secondary.dark,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
id="mode-toggle-button-tree"
|
id="mode-toggle-button-tree"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import useRouteMatch from "../../hooks/useRouteMatch";
|
import useRouteMatch from "../../hooks/useRouteMatch";
|
||||||
import routes from "../../routes.json";
|
import routes from "../../routes.json";
|
||||||
|
|
|
@ -2,7 +2,6 @@ import {
|
||||||
OutlinedInputProps,
|
OutlinedInputProps,
|
||||||
TextField,
|
TextField,
|
||||||
TextFieldProps,
|
TextFieldProps,
|
||||||
alpha,
|
|
||||||
styled,
|
styled,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
@ -10,7 +9,7 @@ import { colors } from "index";
|
||||||
import { FC, useState } from "react";
|
import { FC, useState } from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
interface SearchSidebarProps {
|
interface I_SearchSidebarProps {
|
||||||
listComponent: FC<{ searchQuery: string }>;
|
listComponent: FC<{ searchQuery: string }>;
|
||||||
id: string;
|
id: string;
|
||||||
height?: string;
|
height?: string;
|
||||||
|
@ -45,11 +44,10 @@ const SearchInputTextfield = styled((props: TextFieldProps) => (
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const SearchSidebar = (props: SearchSidebarProps) => {
|
const SearchSidebar = (props: I_SearchSidebarProps) => {
|
||||||
const { listComponent: ListComponent, id, height } = props;
|
const { listComponent: ListComponent, id, height } = props;
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ height: height ?? "750px", overflow: "hidden" }}>
|
<div style={{ height: height ?? "750px", overflow: "hidden" }}>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Dialog, DialogTitle, IconButton, DialogContent, colors } from "@mui/material";
|
import { Dialog, DialogTitle, IconButton, DialogContent } from "@mui/material";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import axiosConfig from "../../config/axiosConfig";
|
import axiosConfig from "../../config/axiosConfig";
|
||||||
|
@ -24,7 +24,7 @@ const AddUser = () => {
|
||||||
<>
|
<>
|
||||||
<InnovenergyButton
|
<InnovenergyButton
|
||||||
id="add-user-button"
|
id="add-user-button"
|
||||||
sx={{ my: 1, }}
|
sx={{ my: 1 }}
|
||||||
onClick={() => setOpen(true)}
|
onClick={() => setOpen(true)}
|
||||||
>
|
>
|
||||||
<FormattedMessage id="addUser" defaultMessage="Create user" />
|
<FormattedMessage id="addUser" defaultMessage="Create user" />
|
||||||
|
@ -37,7 +37,6 @@ const AddUser = () => {
|
||||||
".MuiDialogContent-root": { overflowX: "hidden" },
|
".MuiDialogContent-root": { overflowX: "hidden" },
|
||||||
maxHeight: 500,
|
maxHeight: 500,
|
||||||
}}
|
}}
|
||||||
scroll="paper"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
maxWidth="sm"
|
maxWidth="sm"
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Box, CircularProgress, Alert, collapseClasses, useTheme } from "@mui/material";
|
import { Box, CircularProgress, Alert, useTheme } from "@mui/material";
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
@ -8,10 +8,11 @@ import UserForm from "./UserForm";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
import { colors } from "../..";
|
import { colors } from "../..";
|
||||||
|
|
||||||
interface I_DetailProps {
|
interface I_UserProps {
|
||||||
hasMoveButton?: boolean;
|
hasMoveButton?: boolean;
|
||||||
}
|
}
|
||||||
const Detail = (props: I_DetailProps) => {
|
|
||||||
|
const User = (props: I_UserProps) => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const { locale } = useIntl();
|
const { locale } = useIntl();
|
||||||
const [values, setValues] = useState<I_User>();
|
const [values, setValues] = useState<I_User>();
|
||||||
|
@ -81,4 +82,4 @@ const Detail = (props: I_DetailProps) => {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Detail;
|
export default User;
|
||||||
|
|
|
@ -11,17 +11,16 @@ import { useContext, useState } from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import { I_User } from "../../util/user.util";
|
import { I_User } from "../../util/user.util";
|
||||||
import InnovenergyButton from "../Layout/InnovenergyButton";
|
import InnovenergyButton from "../Layout/InnovenergyButton";
|
||||||
import InnovenergyTextfield, {
|
import { I_InnovenergyTextfieldProps } from "../Layout/InnovenergyTextfield";
|
||||||
I_InnovenergyTextfieldProps,
|
|
||||||
IePropertyGrid,
|
|
||||||
} from "../Layout/InnovenergyTextfield";
|
|
||||||
import { UserContext } from "../Context/UserContextProvider";
|
import { UserContext } from "../Context/UserContextProvider";
|
||||||
import { UsersContext } from "../Context/UsersContextProvider";
|
import { UsersContext } from "../Context/UsersContextProvider";
|
||||||
|
import InnovenergyPropertyGrid from "../Layout/InnovenergyPropertyGrid";
|
||||||
|
|
||||||
interface I_UserFormProps {
|
interface I_UserFormProps {
|
||||||
handleSubmit: (formikValues: Partial<I_User>) => Promise<AxiosResponse>;
|
handleSubmit: (formikValues: Partial<I_User>) => Promise<AxiosResponse>;
|
||||||
values?: I_User;
|
values?: I_User;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserForm = (props: I_UserFormProps) => {
|
const UserForm = (props: I_UserFormProps) => {
|
||||||
const { values, handleSubmit } = props;
|
const { values, handleSubmit } = props;
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
@ -100,7 +99,7 @@ const UserForm = (props: I_UserFormProps) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
<IePropertyGrid rows={rows} />
|
<InnovenergyPropertyGrid rows={rows} />
|
||||||
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
|
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
|
||||||
{loading && (
|
{loading && (
|
||||||
<CircularProgress sx={{ color: theme.palette.secondary.main }} />
|
<CircularProgress sx={{ color: theme.palette.secondary.main }} />
|
||||||
|
@ -125,7 +124,6 @@ const UserForm = (props: I_UserFormProps) => {
|
||||||
severity={error ? "error" : "success"}
|
severity={error ? "error" : "success"}
|
||||||
sx={{ width: "100%" }}
|
sx={{ width: "100%" }}
|
||||||
>
|
>
|
||||||
{/* TODO how to handle err translation? */}
|
|
||||||
{error ? (
|
{error ? (
|
||||||
<FormattedMessage id="error" defaultMessage={error.message} />
|
<FormattedMessage id="error" defaultMessage={error.message} />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -28,11 +28,11 @@ const filterData = (searchQuery: string, data: I_User[] | undefined) => {
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface UserListProps {
|
interface I_UserListProps {
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserList = (props: UserListProps) => {
|
const UserList = (props: I_UserListProps) => {
|
||||||
const { availableUsers } = useContext(UsersContext);
|
const { availableUsers } = useContext(UsersContext);
|
||||||
const filteredData = filterData(props.searchQuery, availableUsers);
|
const filteredData = filterData(props.searchQuery, availableUsers);
|
||||||
|
|
||||||
|
@ -44,7 +44,6 @@ const UserList = (props: UserListProps) => {
|
||||||
<List
|
<List
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
bgcolor: "background.paper",
|
|
||||||
position: "relative",
|
position: "relative",
|
||||||
overflow: "auto",
|
overflow: "auto",
|
||||||
py: 0,
|
py: 0,
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { Maybe } from "yup";
|
import { Maybe } from "yup";
|
||||||
import { Timestamped } from "./types";
|
import { Timestamped } from "./types";
|
||||||
import { isDefined } from "./utils/maybe";
|
import { isDefined } from "./utils/maybe";
|
||||||
import { CsvEntry } from "../util/graph.util";
|
import { I_CsvEntry } from "../util/graph.util";
|
||||||
|
|
||||||
export type DataRecord = Record<string, CsvEntry>;
|
export type DataRecord = Record<string, I_CsvEntry>;
|
||||||
|
|
||||||
export type DataPoint = Timestamped<Maybe<DataRecord>>;
|
export type DataPoint = Timestamped<Maybe<DataRecord>>;
|
||||||
export type RecordSeries = Array<DataPoint>;
|
export type RecordSeries = Array<DataPoint>;
|
||||||
export type PointSeries = Array<Timestamped<Maybe<CsvEntry>>>;
|
export type PointSeries = Array<Timestamped<Maybe<I_CsvEntry>>>;
|
||||||
export type DataSeries = Array<Maybe<CsvEntry>>;
|
export type DataSeries = Array<Maybe<I_CsvEntry>>;
|
||||||
|
|
||||||
export function getPoints(
|
export function getPoints(
|
||||||
recordSeries: RecordSeries,
|
recordSeries: RecordSeries,
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { SkipListNode } from "./skipList/skipListNode";
|
||||||
import { RecordSeries } from "./data";
|
import { RecordSeries } from "./data";
|
||||||
import { isUndefined, Maybe } from "./utils/maybe";
|
import { isUndefined, Maybe } from "./utils/maybe";
|
||||||
import { isNumber } from "./utils/runtimeTypeChecking";
|
import { isNumber } from "./utils/runtimeTypeChecking";
|
||||||
import { CsvEntry } from "../util/graph.util";
|
import { I_CsvEntry } from "../util/graph.util";
|
||||||
|
|
||||||
export const FetchResult = {
|
export const FetchResult = {
|
||||||
notAvailable: "N/A",
|
notAvailable: "N/A",
|
||||||
|
@ -31,7 +31,7 @@ function reverseBits(x: number): number {
|
||||||
return x >>> 0;
|
return x >>> 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class DataCache<T extends Record<string, CsvEntry>> {
|
export default class DataCache<T extends Record<string, I_CsvEntry>> {
|
||||||
private readonly cache: SkipList<Maybe<T>> = new SkipList<Maybe<T>>();
|
private readonly cache: SkipList<Maybe<T>> = new SkipList<Maybe<T>>();
|
||||||
private readonly resolution: TimeSpan;
|
private readonly resolution: TimeSpan;
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ export default class DataCache<T extends Record<string, CsvEntry>> {
|
||||||
const n = after.index - t;
|
const n = after.index - t;
|
||||||
const pn = p + n;
|
const pn = p + n;
|
||||||
|
|
||||||
let interpolated: Record<string, CsvEntry> = {};
|
let interpolated: Record<string, I_CsvEntry> = {};
|
||||||
|
|
||||||
//What about string nodes? like Alarms
|
//What about string nodes? like Alarms
|
||||||
for (const k of Object.keys(dataBefore)) {
|
for (const k of Object.keys(dataBefore)) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ code {
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||||
monospace;
|
monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: #f3f4f7;
|
background-color: #f3f4f7;
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import "./index.css";
|
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
import "./index.css";
|
||||||
import reportWebVitals from "./reportWebVitals";
|
import reportWebVitals from "./reportWebVitals";
|
||||||
import { createTheme, ThemeProvider } from "@mui/material";
|
import { createTheme, ThemeProvider } from "@mui/material";
|
||||||
import UserContextProvider from "./components/Context/UserContextProvider";
|
import UserContextProvider from "./components/Context/UserContextProvider";
|
||||||
|
@ -12,6 +12,7 @@ const root = ReactDOM.createRoot(
|
||||||
|
|
||||||
export const colors = {
|
export const colors = {
|
||||||
black: "#000000",
|
black: "#000000",
|
||||||
|
darkGrey: "#707071",
|
||||||
blueBlack: "#2b3e54",
|
blueBlack: "#2b3e54",
|
||||||
borderColor: "#a8b0be",
|
borderColor: "#a8b0be",
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ const theme = createTheme({
|
||||||
palette: {
|
palette: {
|
||||||
text: {
|
text: {
|
||||||
primary: colors.black,
|
primary: colors.black,
|
||||||
|
secondary: colors.darkGrey,
|
||||||
disabled: colors.borderColor,
|
disabled: colors.borderColor,
|
||||||
},
|
},
|
||||||
primary: {
|
primary: {
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
$innovenergy-orange: #F59100;
|
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
|
Before Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
Before Width: | Height: | Size: 50 KiB |
|
@ -1,14 +1,12 @@
|
||||||
|
import { t } from "testcafe";
|
||||||
import {Selector, t} from 'testcafe';
|
|
||||||
|
|
||||||
export async function login(name, pw) {
|
export async function login(name, pw) {
|
||||||
await t
|
await t
|
||||||
.typeText('#username-textfield', name)
|
.typeText("#username-textfield", name)
|
||||||
.typeText('#password-textfield', pw)
|
.typeText("#password-textfield", pw)
|
||||||
.click('.MuiButtonBase-root') //todo id
|
.click(".MuiButtonBase-root"); //todo id
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function logout() {
|
export async function logout() {
|
||||||
await t
|
await t.click(".MuiButtonBase-root");
|
||||||
.click('.MuiButtonBase-root');
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { Datum, TypedArray } from "plotly.js";
|
import { Datum, TypedArray } from "plotly.js";
|
||||||
import { TreeElement } from "../components/Installations/Log/CheckboxTree";
|
import { I_TreeElement } from "../components/Installations/Log/CheckboxTree";
|
||||||
import { TimeRange, UnixTime } from "../dataCache/time";
|
import { TimeRange, UnixTime } from "../dataCache/time";
|
||||||
import { DataPoint, DataRecord } from "../dataCache/data";
|
import { DataPoint, DataRecord } from "../dataCache/data";
|
||||||
import { isDefined } from "../dataCache/utils/maybe";
|
import { isDefined } from "../dataCache/utils/maybe";
|
||||||
import {
|
import {
|
||||||
BoxData,
|
BoxData,
|
||||||
BoxDataValue,
|
I_BoxDataValue,
|
||||||
} from "../components/Installations/Log/TopologyBox";
|
} from "../components/Installations/LiveView/TopologyBox";
|
||||||
|
|
||||||
export interface GraphCoordinates {
|
export interface I_GraphCoordinates {
|
||||||
x: Datum[] | Datum[][] | TypedArray;
|
x: Datum[] | Datum[][] | TypedArray;
|
||||||
y: Datum[] | Datum[][] | TypedArray;
|
y: Datum[] | Datum[][] | TypedArray;
|
||||||
xaxis?: string;
|
xaxis?: string;
|
||||||
|
@ -32,8 +32,8 @@ export const createTimes = (
|
||||||
return roundedRange.sample(oneSpan);
|
return roundedRange.sample(oneSpan);
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface GraphData {
|
export interface I_GraphData {
|
||||||
[path: string]: GraphCoordinates;
|
[path: string]: I_GraphCoordinates;
|
||||||
}
|
}
|
||||||
|
|
||||||
// connections must have the word Connection in the prop name, so the topology works correctly
|
// connections must have the word Connection in the prop name, so the topology works correctly
|
||||||
|
@ -141,11 +141,12 @@ export const extractTopologyValues = (
|
||||||
const timeSeriesValue = timeSeriesData.value;
|
const timeSeriesValue = timeSeriesData.value;
|
||||||
if (isDefined(timeSeriesValue)) {
|
if (isDefined(timeSeriesValue)) {
|
||||||
return Object.keys(topologyPaths).reduce((acc, topologyKey) => {
|
return Object.keys(topologyPaths).reduce((acc, topologyKey) => {
|
||||||
let topologyValues: BoxDataValue[];
|
let topologyValues: I_BoxDataValue[];
|
||||||
const values = topologyPaths[topologyKey as keyof TopologyValues].map(
|
const values = topologyPaths[topologyKey as keyof TopologyValues].map(
|
||||||
(topologyPath) => timeSeriesValue[topologyPath]
|
(topologyPath) => timeSeriesValue[topologyPath]
|
||||||
);
|
);
|
||||||
switch (topologyKey as keyof TopologyValues) {
|
switch (topologyKey as keyof TopologyValues) {
|
||||||
|
// special cases for certain connections should be extracted accordingly in this switch
|
||||||
case "gridToAcInConnection":
|
case "gridToAcInConnection":
|
||||||
topologyValues = [
|
topologyValues = [
|
||||||
values.reduce(
|
values.reduce(
|
||||||
|
@ -195,18 +196,17 @@ export const getHighestConnectionValue = (values: TopologyValues) =>
|
||||||
|
|
||||||
export const getAmount = (
|
export const getAmount = (
|
||||||
highestConnectionValue: number,
|
highestConnectionValue: number,
|
||||||
values: BoxDataValue[]
|
values: I_BoxDataValue[]
|
||||||
) => {
|
) => {
|
||||||
return Math.abs(values[0].value as number) / highestConnectionValue;
|
return Math.abs(values[0].value as number) / highestConnectionValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface CsvEntry {
|
export interface I_CsvEntry {
|
||||||
value: string | number;
|
value: string | number;
|
||||||
unit: string;
|
unit: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parseCsv = (text: string): DataRecord => {
|
export const parseCsv = (text: string): DataRecord => {
|
||||||
console.log("parseText", text);
|
|
||||||
const y = text
|
const y = text
|
||||||
.split(/\r?\n/)
|
.split(/\r?\n/)
|
||||||
.filter((split) => split.length > 0)
|
.filter((split) => split.length > 0)
|
||||||
|
@ -215,11 +215,6 @@ export const parseCsv = (text: string): DataRecord => {
|
||||||
});
|
});
|
||||||
return y
|
return y
|
||||||
.map((fields) => {
|
.map((fields) => {
|
||||||
if (fields[0].includes("LoadOnAcIsland")) {
|
|
||||||
console.log("fields", fields, {
|
|
||||||
[fields[0]]: { value: parseFloat(fields[1]), unit: fields[2] },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (isNaN(Number(fields[1])) || fields[1] === "") {
|
if (isNaN(Number(fields[1])) || fields[1] === "") {
|
||||||
return { [fields[0]]: { value: fields[1], unit: fields[2] } };
|
return { [fields[0]]: { value: fields[1], unit: fields[2] } };
|
||||||
}
|
}
|
||||||
|
@ -229,9 +224,9 @@ export const parseCsv = (text: string): DataRecord => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const insertTreeElements = (
|
export const insertTreeElements = (
|
||||||
children: TreeElement[] = [],
|
children: I_TreeElement[] = [],
|
||||||
[head, ...tail]: string[]
|
[head, ...tail]: string[]
|
||||||
): TreeElement[] => {
|
): I_TreeElement[] => {
|
||||||
let child = children.find((child) => child.name === head);
|
let child = children.find((child) => child.name === head);
|
||||||
|
|
||||||
if (!child) {
|
if (!child) {
|
||||||
|
@ -253,7 +248,7 @@ export const isText = (data: any): data is string => {
|
||||||
return typeof data === "string";
|
return typeof data === "string";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const flattenBarGraphData = (arr: any): GraphCoordinates[] => {
|
export const flattenBarGraphData = (arr: any): I_GraphCoordinates[] => {
|
||||||
return arr.reduce((flat: any, toFlatten: any) => {
|
return arr.reduce((flat: any, toFlatten: any) => {
|
||||||
return flat.concat(
|
return flat.concat(
|
||||||
Array.isArray(toFlatten) ? flattenBarGraphData(toFlatten) : toFlatten
|
Array.isArray(toFlatten) ? flattenBarGraphData(toFlatten) : toFlatten
|
||||||
|
@ -281,7 +276,7 @@ export const isNumeric = (value: any) => {
|
||||||
return !isNaN(value) && !isNaN(parseFloat(value));
|
return !isNaN(value) && !isNaN(parseFloat(value));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const transformToBarGraphData = (data: GraphCoordinates) => {
|
export const transformToBarGraphData = (data: I_GraphCoordinates) => {
|
||||||
let names: string[] = [];
|
let names: string[] = [];
|
||||||
const barGraphData = data.y.map((text, i) => {
|
const barGraphData = data.y.map((text, i) => {
|
||||||
if (isText(text)) {
|
if (isText(text)) {
|
||||||
|
@ -317,6 +312,6 @@ export const getTreeElements = (toggleValues: DataRecord) => {
|
||||||
)
|
)
|
||||||
.reduce(
|
.reduce(
|
||||||
(children, path) => insertTreeElements(children, path),
|
(children, path) => insertTreeElements(children, path),
|
||||||
[] as TreeElement[]
|
[] as I_TreeElement[]
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// TODO add if required or not
|
export interface I_S3Credentials {
|
||||||
export interface S3Credentials {
|
|
||||||
s3Region: string;
|
s3Region: string;
|
||||||
s3Provider: string;
|
s3Provider: string;
|
||||||
s3Key: string;
|
s3Key: string;
|
||||||
|
@ -7,7 +6,7 @@ export interface S3Credentials {
|
||||||
s3Bucket?: string;
|
s3Bucket?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface I_Installation extends S3Credentials {
|
export interface I_Installation extends I_S3Credentials {
|
||||||
type: string;
|
type: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
status?: number;
|
status?: number;
|
||||||
|
@ -35,37 +34,3 @@ export interface I_Folder {
|
||||||
type: string;
|
type: string;
|
||||||
children?: (I_Installation | I_Folder)[];
|
children?: (I_Installation | I_Folder)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface I_Ac {
|
|
||||||
Current: number;
|
|
||||||
Voltage: number;
|
|
||||||
Phi?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface I_Dc {
|
|
||||||
Current: number;
|
|
||||||
Voltage: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface I_Device {
|
|
||||||
Name: string;
|
|
||||||
Type: string;
|
|
||||||
Ac?: I_Ac[];
|
|
||||||
Dc?: I_Dc;
|
|
||||||
Dc48?: I_Dc;
|
|
||||||
Frequency?: number;
|
|
||||||
Alarms?: any[];
|
|
||||||
Warnings?: any[];
|
|
||||||
MainState?: string;
|
|
||||||
"DC Power"?: number;
|
|
||||||
Soc?: number;
|
|
||||||
HeaterOn?: boolean;
|
|
||||||
EocReached?: boolean;
|
|
||||||
BatteryCold?: boolean;
|
|
||||||
Temperature?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface I_GraphData {
|
|
||||||
TimeStamp: string;
|
|
||||||
Devices: I_Device[];
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ export interface I_User {
|
||||||
mustResetPassword: boolean;
|
mustResetPassword: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserWithInheritedAccess {
|
export interface I_UserWithInheritedAccess {
|
||||||
folderId: number;
|
folderId: number;
|
||||||
folderName: string;
|
folderName: string;
|
||||||
user: I_User;
|
user: I_User;
|
||||||
|
@ -20,7 +20,7 @@ export interface UserWithInheritedAccess {
|
||||||
|
|
||||||
export const filterDuplicateUsers = (data: any[]) => {
|
export const filterDuplicateUsers = (data: any[]) => {
|
||||||
return data.reduce(
|
return data.reduce(
|
||||||
(prev: UserWithInheritedAccess[], curr: UserWithInheritedAccess) => {
|
(prev: I_UserWithInheritedAccess[], curr: I_UserWithInheritedAccess) => {
|
||||||
const foundUser = prev.find(({ user }) => user.id === curr.user.id);
|
const foundUser = prev.find(({ user }) => user.id === curr.user.id);
|
||||||
if (foundUser) {
|
if (foundUser) {
|
||||||
const prevIds = foundUser.user.folderIds
|
const prevIds = foundUser.user.folderIds
|
||||||
|
|
Loading…
Reference in New Issue