add sodistore pro as a new product
This commit is contained in:
parent
d59027a277
commit
3521da7a1d
|
|
@ -202,6 +202,8 @@ public class Controller : ControllerBase
|
||||||
bucketPath = "s3://" + installation.S3BucketId + "-e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa/" + startTimestamp;
|
bucketPath = "s3://" + installation.S3BucketId + "-e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa/" + startTimestamp;
|
||||||
else if (installation.Product == (int)ProductType.SodistoreGrid)
|
else if (installation.Product == (int)ProductType.SodistoreGrid)
|
||||||
bucketPath = "s3://" + installation.S3BucketId + "-5109c126-e141-43ab-8658-f3c44c838ae8/" + startTimestamp;
|
bucketPath = "s3://" + installation.S3BucketId + "-5109c126-e141-43ab-8658-f3c44c838ae8/" + startTimestamp;
|
||||||
|
else if (installation.Product == (int)ProductType.SodistorePro)
|
||||||
|
bucketPath = "s3://" + installation.S3BucketId + "-325c9373-9025-4a8d-bf5a-f9eedf1f155c/" + startTimestamp;
|
||||||
else
|
else
|
||||||
bucketPath = "s3://" + installation.S3BucketId + "-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e/" + startTimestamp;
|
bucketPath = "s3://" + installation.S3BucketId + "-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e/" + startTimestamp;
|
||||||
Console.WriteLine("Fetching data for "+startTimestamp);
|
Console.WriteLine("Fetching data for "+startTimestamp);
|
||||||
|
|
@ -815,9 +817,10 @@ public class Controller : ControllerBase
|
||||||
if (installation is null || !user.HasAccessTo(installation))
|
if (installation is null || !user.HasAccessTo(installation))
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
// AI diagnostics are scoped to SodistoreHome and SodiStoreMax only
|
// AI diagnostics are scoped to SodistoreHome, SodiStoreMax, and SodistorePro only
|
||||||
if (installation.Product != (int)ProductType.SodioHome &&
|
if (installation.Product != (int)ProductType.SodioHome &&
|
||||||
installation.Product != (int)ProductType.SodiStoreMax)
|
installation.Product != (int)ProductType.SodiStoreMax &&
|
||||||
|
installation.Product != (int)ProductType.SodistorePro)
|
||||||
return BadRequest("AI diagnostics not available for this product.");
|
return BadRequest("AI diagnostics not available for this product.");
|
||||||
|
|
||||||
var result = await DiagnosticService.DiagnoseAsync(installationId, errorDescription, user.Language ?? "en");
|
var result = await DiagnosticService.DiagnoseAsync(installationId, errorDescription, user.Language ?? "en");
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ public enum ProductType
|
||||||
Salidomo = 1,
|
Salidomo = 1,
|
||||||
SodioHome =2,
|
SodioHome =2,
|
||||||
SodiStoreMax=3,
|
SodiStoreMax=3,
|
||||||
SodistoreGrid=4
|
SodistoreGrid=4,
|
||||||
|
SodistorePro=5
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum StatusType
|
public enum StatusType
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,7 @@ public static class ExoCmd
|
||||||
String rolename = installation.Product==(int)ProductType.Salimax?Db.Installations.Count(f => f.Product == (int)ProductType.Salimax) + installation.Name:
|
String rolename = installation.Product==(int)ProductType.Salimax?Db.Installations.Count(f => f.Product == (int)ProductType.Salimax) + installation.Name:
|
||||||
installation.Product==(int)ProductType.SodiStoreMax?Db.Installations.Count(f => f.Product == (int)ProductType.SodiStoreMax) + installation.Name:
|
installation.Product==(int)ProductType.SodiStoreMax?Db.Installations.Count(f => f.Product == (int)ProductType.SodiStoreMax) + installation.Name:
|
||||||
installation.Product==(int)ProductType.SodistoreGrid?Db.Installations.Count(f => f.Product == (int)ProductType.SodistoreGrid) + installation.Name:
|
installation.Product==(int)ProductType.SodistoreGrid?Db.Installations.Count(f => f.Product == (int)ProductType.SodistoreGrid) + installation.Name:
|
||||||
|
installation.Product==(int)ProductType.SodistorePro?Db.Installations.Count(f => f.Product == (int)ProductType.SodistorePro) + installation.Name:
|
||||||
Db.Installations.Count(f => f.Product == (int)ProductType.Salidomo) + installation.Name;
|
Db.Installations.Count(f => f.Product == (int)ProductType.Salidomo) + installation.Name;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -350,6 +351,7 @@ public static class ExoCmd
|
||||||
String rolename = installation.Product==(int)ProductType.Salimax?Db.Installations.Count(f => f.Product == (int)ProductType.Salimax) + installation.Name:
|
String rolename = installation.Product==(int)ProductType.Salimax?Db.Installations.Count(f => f.Product == (int)ProductType.Salimax) + installation.Name:
|
||||||
installation.Product==(int)ProductType.SodiStoreMax?Db.Installations.Count(f => f.Product == (int)ProductType.SodiStoreMax) + installation.Name:
|
installation.Product==(int)ProductType.SodiStoreMax?Db.Installations.Count(f => f.Product == (int)ProductType.SodiStoreMax) + installation.Name:
|
||||||
installation.Product==(int)ProductType.SodistoreGrid?Db.Installations.Count(f => f.Product == (int)ProductType.SodistoreGrid) + installation.Name:
|
installation.Product==(int)ProductType.SodistoreGrid?Db.Installations.Count(f => f.Product == (int)ProductType.SodistoreGrid) + installation.Name:
|
||||||
|
installation.Product==(int)ProductType.SodistorePro?Db.Installations.Count(f => f.Product == (int)ProductType.SodistorePro) + installation.Name:
|
||||||
Db.Installations.Count(f => f.Product == (int)ProductType.Salidomo) + installation.Name;
|
Db.Installations.Count(f => f.Product == (int)ProductType.Salidomo) + installation.Name;
|
||||||
|
|
||||||
var contentString = $$"""
|
var contentString = $$"""
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ public static class InstallationMethods
|
||||||
private static readonly String SalidomoBucketNameSalt = "c0436b6a-d276-4cd8-9c44-1eae86cf5d0e";
|
private static readonly String SalidomoBucketNameSalt = "c0436b6a-d276-4cd8-9c44-1eae86cf5d0e";
|
||||||
private static readonly String SodioHomeBucketNameSalt = "e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa";
|
private static readonly String SodioHomeBucketNameSalt = "e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa";
|
||||||
private static readonly String SodistoreGridBucketNameSalt = "5109c126-e141-43ab-8658-f3c44c838ae8";
|
private static readonly String SodistoreGridBucketNameSalt = "5109c126-e141-43ab-8658-f3c44c838ae8";
|
||||||
|
private static readonly String SodistoreProBucketNameSalt = "325c9373-9025-4a8d-bf5a-f9eedf1f155c";
|
||||||
|
|
||||||
public static String BucketName(this Installation installation)
|
public static String BucketName(this Installation installation)
|
||||||
{
|
{
|
||||||
|
|
@ -29,6 +30,11 @@ public static class InstallationMethods
|
||||||
return $"{installation.S3BucketId}-{SodistoreGridBucketNameSalt}";
|
return $"{installation.S3BucketId}-{SodistoreGridBucketNameSalt}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (installation.Product == (int)ProductType.SodistorePro)
|
||||||
|
{
|
||||||
|
return $"{installation.S3BucketId}-{SodistoreProBucketNameSalt}";
|
||||||
|
}
|
||||||
|
|
||||||
return $"{installation.S3BucketId}-{SalidomoBucketNameSalt}";
|
return $"{installation.S3BucketId}-{SalidomoBucketNameSalt}";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ public static class SessionMethods
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (installation.Product == (int)ProductType.SodiStoreMax || installation.Product == (int)ProductType.SodioHome || installation.Product == (int)ProductType.SodistoreGrid)
|
if (installation.Product == (int)ProductType.SodiStoreMax || installation.Product == (int)ProductType.SodioHome || installation.Product == (int)ProductType.SodistoreGrid || installation.Product == (int)ProductType.SodistorePro)
|
||||||
{
|
{
|
||||||
return user is not null
|
return user is not null
|
||||||
&& user.UserType != 0
|
&& user.UserType != 0
|
||||||
|
|
@ -295,7 +295,7 @@ public static class SessionMethods
|
||||||
.Apply(Db.Update);
|
.Apply(Db.Update);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (installation.Product == (int)ProductType.SodiStoreMax || installation.Product == (int)ProductType.SodistoreGrid)
|
if (installation.Product == (int)ProductType.SodiStoreMax || installation.Product == (int)ProductType.SodistoreGrid || installation.Product == (int)ProductType.SodistorePro)
|
||||||
{
|
{
|
||||||
|
|
||||||
return user is not null
|
return user is not null
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ public class Session : Relation<String, Int64>
|
||||||
public Boolean AccessToSodistoreMax { get; set; } = false;
|
public Boolean AccessToSodistoreMax { get; set; } = false;
|
||||||
public Boolean AccessToSodioHome { get; set; } = false;
|
public Boolean AccessToSodioHome { get; set; } = false;
|
||||||
public Boolean AccessToSodistoreGrid { get; set; } = false;
|
public Boolean AccessToSodistoreGrid { get; set; } = false;
|
||||||
|
public Boolean AccessToSodistorePro { get; set; } = false;
|
||||||
[Ignore] public Boolean Valid => DateTime.Now - LastSeen <=MaxAge ;
|
[Ignore] public Boolean Valid => DateTime.Now - LastSeen <=MaxAge ;
|
||||||
|
|
||||||
// Private backing field
|
// Private backing field
|
||||||
|
|
@ -51,6 +52,7 @@ public class Session : Relation<String, Int64>
|
||||||
AccessToSodistoreMax = user.AccessibleInstallations(product: (int)ProductType.SodiStoreMax).ToList().Count > 0;
|
AccessToSodistoreMax = user.AccessibleInstallations(product: (int)ProductType.SodiStoreMax).ToList().Count > 0;
|
||||||
AccessToSodioHome = user.AccessibleInstallations(product: (int)ProductType.SodioHome).ToList().Count > 0;
|
AccessToSodioHome = user.AccessibleInstallations(product: (int)ProductType.SodioHome).ToList().Count > 0;
|
||||||
AccessToSodistoreGrid = user.AccessibleInstallations(product: (int)ProductType.SodistoreGrid).ToList().Count > 0;
|
AccessToSodistoreGrid = user.AccessibleInstallations(product: (int)ProductType.SodistoreGrid).ToList().Count > 0;
|
||||||
|
AccessToSodistorePro = user.AccessibleInstallations(product: (int)ProductType.SodistorePro).ToList().Count > 0;
|
||||||
|
|
||||||
Console.WriteLine("salimax" +user.AccessibleInstallations(product: (int)ProductType.Salimax).ToList().Count);
|
Console.WriteLine("salimax" +user.AccessibleInstallations(product: (int)ProductType.Salimax).ToList().Count);
|
||||||
Console.WriteLine("AccessToSodistoreMax" +user.AccessibleInstallations(product: (int)ProductType.SodiStoreMax).ToList().Count);
|
Console.WriteLine("AccessToSodistoreMax" +user.AccessibleInstallations(product: (int)ProductType.SodiStoreMax).ToList().Count);
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ public static class DailyIngestionService
|
||||||
Console.WriteLine($"[DailyIngestion] Starting ingestion for all SodioHome installations...");
|
Console.WriteLine($"[DailyIngestion] Starting ingestion for all SodioHome installations...");
|
||||||
|
|
||||||
var installations = Db.Installations
|
var installations = Db.Installations
|
||||||
.Where(i => i.Product == (Int32)ProductType.SodioHome && i.Device != 3) // Skip Growatt (device=3)
|
.Where(i => (i.Product == (Int32)ProductType.SodioHome || i.Product == (Int32)ProductType.SodistorePro) && i.Device != 3) // Skip Growatt (device=3)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
foreach (var installation in installations)
|
foreach (var installation in installations)
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ public static class ReportAggregationService
|
||||||
Console.WriteLine("[ReportAggregation] Running Monday weekly report generation...");
|
Console.WriteLine("[ReportAggregation] Running Monday weekly report generation...");
|
||||||
|
|
||||||
var installations = Db.Installations
|
var installations = Db.Installations
|
||||||
.Where(i => i.Product == (Int32)ProductType.SodioHome && i.Device != 3) // Skip Growatt (device=3)
|
.Where(i => (i.Product == (Int32)ProductType.SodioHome || i.Product == (Int32)ProductType.SodistorePro) && i.Device != 3) // Skip Growatt (device=3)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var generated = 0;
|
var generated = 0;
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,11 @@ public static class RabbitMqManager
|
||||||
monitorLink =
|
monitorLink =
|
||||||
$"https://monitor.inesco.energy/sodistoregrid_installations/list/installation/{installation.S3BucketId}/batteryview";
|
$"https://monitor.inesco.energy/sodistoregrid_installations/list/installation/{installation.S3BucketId}/batteryview";
|
||||||
}
|
}
|
||||||
|
else if (installation.Product == (int)ProductType.SodistorePro)
|
||||||
|
{
|
||||||
|
monitorLink =
|
||||||
|
$"https://monitor.inesco.energy/sodistorepro_installations/list/installation/{installation.S3BucketId}/batteryview";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
monitorLink =
|
monitorLink =
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,8 @@ public static class WebsocketManager
|
||||||
(installationConnection.Value.Product == (int)ProductType.Salidomo && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(60)) ||
|
(installationConnection.Value.Product == (int)ProductType.Salidomo && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(60)) ||
|
||||||
(installationConnection.Value.Product == (int)ProductType.SodioHome && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(4)) ||
|
(installationConnection.Value.Product == (int)ProductType.SodioHome && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(4)) ||
|
||||||
(installationConnection.Value.Product == (int)ProductType.SodiStoreMax && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2)) ||
|
(installationConnection.Value.Product == (int)ProductType.SodiStoreMax && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2)) ||
|
||||||
(installationConnection.Value.Product == (int)ProductType.SodistoreGrid && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2))
|
(installationConnection.Value.Product == (int)ProductType.SodistoreGrid && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2)) ||
|
||||||
|
(installationConnection.Value.Product == (int)ProductType.SodistorePro && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(4))
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Installation ID is " + installationConnection.Key);
|
Console.WriteLine("Installation ID is " + installationConnection.Key);
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@ function App() {
|
||||||
setAccessToSalidomo,
|
setAccessToSalidomo,
|
||||||
setAccessToSodiohome,
|
setAccessToSodiohome,
|
||||||
setAccessToSodistore,
|
setAccessToSodistore,
|
||||||
setAccessToSodistoreGrid
|
setAccessToSodistoreGrid,
|
||||||
|
setAccessToSodistorePro
|
||||||
} = useContext(ProductIdContext);
|
} = useContext(ProductIdContext);
|
||||||
|
|
||||||
const [language, setLanguage] = useState<string>(
|
const [language, setLanguage] = useState<string>(
|
||||||
|
|
@ -106,6 +107,7 @@ function App() {
|
||||||
setAccessToSodiohome(response.data.accessToSodioHome);
|
setAccessToSodiohome(response.data.accessToSodioHome);
|
||||||
setAccessToSodistore(response.data.accessToSodistoreMax);
|
setAccessToSodistore(response.data.accessToSodistoreMax);
|
||||||
setAccessToSodistoreGrid(response.data.accessToSodistoreGrid);
|
setAccessToSodistoreGrid(response.data.accessToSodistoreGrid);
|
||||||
|
setAccessToSodistorePro(response.data.accessToSodistorePro);
|
||||||
if (response.data.accessToSalimax) {
|
if (response.data.accessToSalimax) {
|
||||||
navigate(routes.installations);
|
navigate(routes.installations);
|
||||||
} else if (response.data.accessToSalidomo) {
|
} else if (response.data.accessToSalidomo) {
|
||||||
|
|
@ -114,6 +116,8 @@ function App() {
|
||||||
navigate(routes.sodistore_installations);
|
navigate(routes.sodistore_installations);
|
||||||
} else if (response.data.accessToSodistoreGrid) {
|
} else if (response.data.accessToSodistoreGrid) {
|
||||||
navigate(routes.sodistoregrid_installations);
|
navigate(routes.sodistoregrid_installations);
|
||||||
|
} else if (response.data.accessToSodistorePro) {
|
||||||
|
navigate(routes.sodistorepro_installations);
|
||||||
} else {
|
} else {
|
||||||
navigate(routes.sodiohome_installations);
|
navigate(routes.sodiohome_installations);
|
||||||
}
|
}
|
||||||
|
|
@ -228,6 +232,15 @@ function App() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path={routes.sodistorepro_installations + '*'}
|
||||||
|
element={
|
||||||
|
<AccessContextProvider>
|
||||||
|
<SodioHomeInstallationTabs product={5} />
|
||||||
|
</AccessContextProvider>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={routes.sodistoregrid_installations + '*'}
|
path={routes.sodistoregrid_installations + '*'}
|
||||||
element={
|
element={
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
"sodistore_installations": "/sodistore_installations/",
|
"sodistore_installations": "/sodistore_installations/",
|
||||||
"sodiohome_installations": "/sodiohome_installations/",
|
"sodiohome_installations": "/sodiohome_installations/",
|
||||||
"sodistoregrid_installations": "/sodistoregrid_installations/",
|
"sodistoregrid_installations": "/sodistoregrid_installations/",
|
||||||
|
"sodistorepro_installations": "/sodistorepro_installations/",
|
||||||
"installation": "installation/",
|
"installation": "installation/",
|
||||||
"login": "/login/",
|
"login": "/login/",
|
||||||
"forgotPassword": "/forgotPassword/",
|
"forgotPassword": "/forgotPassword/",
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@ function Login() {
|
||||||
setAccessToSalidomo,
|
setAccessToSalidomo,
|
||||||
setAccessToSodiohome,
|
setAccessToSodiohome,
|
||||||
setAccessToSodistore,
|
setAccessToSodistore,
|
||||||
setAccessToSodistoreGrid
|
setAccessToSodistoreGrid,
|
||||||
|
setAccessToSodistorePro
|
||||||
} = useContext(ProductIdContext);
|
} = useContext(ProductIdContext);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
|
@ -86,6 +87,7 @@ function Login() {
|
||||||
setAccessToSodiohome(response.data.accessToSodioHome);
|
setAccessToSodiohome(response.data.accessToSodioHome);
|
||||||
setAccessToSodistore(response.data.accessToSodistoreMax);
|
setAccessToSodistore(response.data.accessToSodistoreMax);
|
||||||
setAccessToSodistoreGrid(response.data.accessToSodistoreGrid);
|
setAccessToSodistoreGrid(response.data.accessToSodistoreGrid);
|
||||||
|
setAccessToSodistorePro(response.data.accessToSodistorePro);
|
||||||
if (response.data.accessToSalimax) {
|
if (response.data.accessToSalimax) {
|
||||||
navigate(routes.installations);
|
navigate(routes.installations);
|
||||||
} else if (response.data.accessToSalidomo) {
|
} else if (response.data.accessToSalidomo) {
|
||||||
|
|
@ -94,6 +96,8 @@ function Login() {
|
||||||
navigate(routes.sodistore_installations);
|
navigate(routes.sodistore_installations);
|
||||||
} else if (response.data.accessToSodistoreGrid) {
|
} else if (response.data.accessToSodistoreGrid) {
|
||||||
navigate(routes.sodistoregrid_installations);
|
navigate(routes.sodistoregrid_installations);
|
||||||
|
} else if (response.data.accessToSodistorePro) {
|
||||||
|
navigate(routes.sodistorepro_installations);
|
||||||
} else {
|
} else {
|
||||||
navigate(routes.sodiohome_installations);
|
navigate(routes.sodiohome_installations);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import {
|
||||||
computeFlatValues,
|
computeFlatValues,
|
||||||
wouldLoseData,
|
wouldLoseData,
|
||||||
SODIOHOME_DEVICE_TYPES,
|
SODIOHOME_DEVICE_TYPES,
|
||||||
|
buildSodistoreProPreset,
|
||||||
} from './installationSetupUtils';
|
} from './installationSetupUtils';
|
||||||
|
|
||||||
interface InformationSodistorehomeProps {
|
interface InformationSodistorehomeProps {
|
||||||
|
|
@ -94,13 +95,25 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
|
||||||
return [value.trim()];
|
return [value.trim()];
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeviceTypes = SODIOHOME_DEVICE_TYPES;
|
const isSodistorePro = props.values.product === 5;
|
||||||
|
const DeviceTypes = isSodistorePro
|
||||||
|
? [{ id: 4, name: 'inesco 12K - WR Hybrid' } as const]
|
||||||
|
: SODIOHOME_DEVICE_TYPES;
|
||||||
|
|
||||||
// Preset state — initializes from persisted installationModel, empty for legacy
|
// Preset state — initializes from persisted installationModel, empty for legacy
|
||||||
const [selectedPreset, setSelectedPreset] = useState<string>(
|
const [selectedPreset, setSelectedPreset] = useState<string>(
|
||||||
props.values.installationModel || ''
|
props.values.installationModel || ''
|
||||||
);
|
);
|
||||||
const presetConfig: PresetConfig | null = INSTALLATION_PRESETS[selectedPreset] || null;
|
const [inverterCount, setInverterCount] = useState<string>(
|
||||||
|
isSodistorePro && props.values.installationModel
|
||||||
|
? props.values.installationModel
|
||||||
|
: ''
|
||||||
|
);
|
||||||
|
const presetConfig: PresetConfig | null = isSodistorePro
|
||||||
|
? (inverterCount && parseInt(inverterCount, 10) > 0
|
||||||
|
? buildSodistoreProPreset(parseInt(inverterCount, 10))
|
||||||
|
: null)
|
||||||
|
: (INSTALLATION_PRESETS[selectedPreset] || null);
|
||||||
|
|
||||||
const [batterySnTree, setBatterySnTree] = useState<BatterySnTree>(() => {
|
const [batterySnTree, setBatterySnTree] = useState<BatterySnTree>(() => {
|
||||||
if (presetConfig) {
|
if (presetConfig) {
|
||||||
|
|
@ -200,6 +213,38 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleInverterCountChange = (value: string) => {
|
||||||
|
if (value !== '' && !/^\d+$/.test(value)) return;
|
||||||
|
if (value !== '' && parseInt(value, 10) > 20) return;
|
||||||
|
setInverterCount(value);
|
||||||
|
const count = parseInt(value, 10);
|
||||||
|
if (isNaN(count) || count < 1) {
|
||||||
|
setBatterySnTree([]);
|
||||||
|
setFormValues({ ...formValues, installationModel: value });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newConfig = buildSodistoreProPreset(count);
|
||||||
|
const newTree = batterySnTree.length > 0
|
||||||
|
? remapTree(batterySnTree, newConfig)
|
||||||
|
: buildEmptyTree(newConfig);
|
||||||
|
setBatterySnTree(newTree);
|
||||||
|
const newInvSNs = Array.from({ length: count }, (_, i) => inverterSerialNumbers[i] || '');
|
||||||
|
const newDlSNs = Array.from({ length: count }, (_, i) => dataloggerSerialNumbers[i] || '');
|
||||||
|
const newPvStrings = Array.from({ length: count }, (_, i) => pvStringsPerInverter[i] || '1');
|
||||||
|
setInverterSerialNumbers(newInvSNs);
|
||||||
|
setDataloggerSerialNumbers(newDlSNs);
|
||||||
|
setPvStringsPerInverter(newPvStrings);
|
||||||
|
const flat = computeFlatValues(newConfig, newTree);
|
||||||
|
setFormValues({
|
||||||
|
...formValues,
|
||||||
|
...flat,
|
||||||
|
installationModel: value,
|
||||||
|
inverterSN: newInvSNs.join('/'),
|
||||||
|
dataloggerSN: newDlSNs.join('/'),
|
||||||
|
pvStringsPerInverter: newPvStrings.join(','),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleInverterSnChange = (invIdx: number, value: string) => {
|
const handleInverterSnChange = (invIdx: number, value: string) => {
|
||||||
const updated = [...inverterSerialNumbers];
|
const updated = [...inverterSerialNumbers];
|
||||||
updated[invIdx] = value;
|
updated[invIdx] = value;
|
||||||
|
|
@ -608,6 +653,7 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{!isSodistorePro && (
|
||||||
<div>
|
<div>
|
||||||
<FormControl fullWidth sx={{ marginLeft: 1, marginTop: 1, marginBottom: 1, width: 440 }}>
|
<FormControl fullWidth sx={{ marginLeft: 1, marginTop: 1, marginBottom: 1, width: 440 }}>
|
||||||
<InputLabel sx={{ fontSize: 14, backgroundColor: 'white' }}>
|
<InputLabel sx={{ fontSize: 14, backgroundColor: 'white' }}>
|
||||||
|
|
@ -627,6 +673,7 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
|
|
@ -733,6 +780,19 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{isSodistorePro ? (
|
||||||
|
<div>
|
||||||
|
<TextField
|
||||||
|
label={<FormattedMessage id="numberOfInverters" defaultMessage="Number of Inverters" />}
|
||||||
|
type="text"
|
||||||
|
value={inverterCount}
|
||||||
|
onChange={(e) => handleInverterCountChange(e.target.value)}
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
inputProps={{ readOnly: !canEdit }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<FormControl sx={{ m: 1, width: '50ch' }}>
|
<FormControl sx={{ m: 1, width: '50ch' }}>
|
||||||
<InputLabel
|
<InputLabel
|
||||||
|
|
@ -759,6 +819,7 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
|
|
@ -919,7 +980,7 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="S3 Bucket Name"
|
label="S3 Bucket Name"
|
||||||
value={formValues.s3BucketId + '-e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa'}
|
value={formValues.s3BucketId + '-' + (isSodistorePro ? '325c9373-9025-4a8d-bf5a-f9eedf1f155c' : 'e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa')}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,14 @@ export const INSTALLATION_PRESETS: Record<string, PresetConfig> = {
|
||||||
'sodistore home 36': [[2, 2], [2, 2]],
|
'sodistore home 36': [[2, 2], [2, 2]],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const buildSodistoreProPreset = (inverterCount: number): PresetConfig =>
|
||||||
|
Array.from({ length: inverterCount }, () => [2, 2]);
|
||||||
|
|
||||||
|
export const parseSodistoreProInverterCount = (model: string): number => {
|
||||||
|
const n = parseInt(model, 10);
|
||||||
|
return isNaN(n) || n < 1 ? 1 : n;
|
||||||
|
};
|
||||||
|
|
||||||
export const buildEmptyTree = (preset: PresetConfig): BatterySnTree => {
|
export const buildEmptyTree = (preset: PresetConfig): BatterySnTree => {
|
||||||
return preset.map((inv) =>
|
return preset.map((inv) =>
|
||||||
inv.map((batteryCount) => Array.from({ length: batteryCount }, () => ''))
|
inv.map((batteryCount) => Array.from({ length: batteryCount }, () => ''))
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ export const fetchAggregatedDataJson = (
|
||||||
} else if (r.status === 200) {
|
} else if (r.status === 200) {
|
||||||
const jsontext = await r.text();
|
const jsontext = await r.text();
|
||||||
|
|
||||||
if (product === 2) {
|
if (product === 2 || product === 5) {
|
||||||
return parseSinexcelAggregatedData(jsontext);
|
return parseSinexcelAggregatedData(jsontext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,13 +107,15 @@ function UserAccess(props: UserAccessProps) {
|
||||||
|
|
||||||
const fetchAvailableInstallations = useCallback(async () => {
|
const fetchAvailableInstallations = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const [res0, res1, res2, res3] = await Promise.all([
|
const [res0, res1, res2, res3, res4, res5] = await Promise.all([
|
||||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=0`),
|
axiosConfig.get(`/GetAllInstallationsFromProduct?product=0`),
|
||||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=1`),
|
axiosConfig.get(`/GetAllInstallationsFromProduct?product=1`),
|
||||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=2`),
|
axiosConfig.get(`/GetAllInstallationsFromProduct?product=2`),
|
||||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=3`)
|
axiosConfig.get(`/GetAllInstallationsFromProduct?product=3`),
|
||||||
|
axiosConfig.get(`/GetAllInstallationsFromProduct?product=4`),
|
||||||
|
axiosConfig.get(`/GetAllInstallationsFromProduct?product=5`)
|
||||||
]);
|
]);
|
||||||
setAvailableInstallations([...res0.data, ...res1.data, ...res2.data, ...res3.data]);
|
setAvailableInstallations([...res0.data, ...res1.data, ...res2.data, ...res3.data, ...res4.data, ...res5.data]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.response && err.response.status === 401) removeToken();
|
if (err.response && err.response.status === 401) removeToken();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -817,7 +817,7 @@ function Overview(props: OverviewProps) {
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
color: '#ff9900'
|
color: '#ff9900'
|
||||||
},
|
},
|
||||||
...(product !== 2 ? [{
|
...((product !== 2 && product !== 5) ? [{
|
||||||
name: 'Net Energy',
|
name: 'Net Energy',
|
||||||
color: '#e65100',
|
color: '#e65100',
|
||||||
type: 'line',
|
type: 'line',
|
||||||
|
|
@ -840,7 +840,7 @@ function Overview(props: OverviewProps) {
|
||||||
alignItems="stretch"
|
alignItems="stretch"
|
||||||
spacing={3}
|
spacing={3}
|
||||||
>
|
>
|
||||||
{!(aggregatedData && product === 2) && (
|
{!(aggregatedData && (product === 2 || product === 5)) && (
|
||||||
<Grid item md={6} xs={12}>
|
<Grid item md={6} xs={12}>
|
||||||
<Card
|
<Card
|
||||||
sx={{
|
sx={{
|
||||||
|
|
@ -933,7 +933,7 @@ function Overview(props: OverviewProps) {
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
<Grid item md={(aggregatedData && product === 2) ? 12 : 6} xs={12}>
|
<Grid item md={(aggregatedData && (product === 2 || product === 5)) ? 12 : 6} xs={12}>
|
||||||
<Card
|
<Card
|
||||||
sx={{
|
sx={{
|
||||||
overflow: 'visible',
|
overflow: 'visible',
|
||||||
|
|
@ -1001,14 +1001,14 @@ function Overview(props: OverviewProps) {
|
||||||
<ReactApexChart
|
<ReactApexChart
|
||||||
options={{
|
options={{
|
||||||
...getChartOptions(
|
...getChartOptions(
|
||||||
product === 2
|
(product === 2 || product === 5)
|
||||||
? aggregatedDataArray[aggregatedChartState]
|
? aggregatedDataArray[aggregatedChartState]
|
||||||
.chartOverview.dcPowerWithoutHeating
|
.chartOverview.dcPowerWithoutHeating
|
||||||
: aggregatedDataArray[aggregatedChartState]
|
: aggregatedDataArray[aggregatedChartState]
|
||||||
.chartOverview.dcPower,
|
.chartOverview.dcPower,
|
||||||
'weekly',
|
'weekly',
|
||||||
aggregatedDataArray[aggregatedChartState].datelist,
|
aggregatedDataArray[aggregatedChartState].datelist,
|
||||||
product === 2
|
(product === 2 || product === 5)
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
series={[
|
series={[
|
||||||
|
|
@ -1017,7 +1017,7 @@ function Overview(props: OverviewProps) {
|
||||||
.chartData.dcChargingPower,
|
.chartData.dcChargingPower,
|
||||||
color: '#008FFB'
|
color: '#008FFB'
|
||||||
},
|
},
|
||||||
...(product !== 2 ? [{
|
...((product !== 2 && product !== 5) ? [{
|
||||||
...aggregatedDataArray[aggregatedChartState]
|
...aggregatedDataArray[aggregatedChartState]
|
||||||
.chartData.heatingPower,
|
.chartData.heatingPower,
|
||||||
color: '#ff9900'
|
color: '#ff9900'
|
||||||
|
|
@ -1073,7 +1073,7 @@ function Overview(props: OverviewProps) {
|
||||||
alignItems="stretch"
|
alignItems="stretch"
|
||||||
spacing={3}
|
spacing={3}
|
||||||
>
|
>
|
||||||
{product !== 2 && (
|
{(product !== 2 && product !== 5) && (
|
||||||
<Grid item md={6} xs={12}>
|
<Grid item md={6} xs={12}>
|
||||||
<Card
|
<Card
|
||||||
sx={{
|
sx={{
|
||||||
|
|
@ -1136,7 +1136,7 @@ function Overview(props: OverviewProps) {
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
{product !== 2 && (
|
{(product !== 2 && product !== 5) && (
|
||||||
<Grid item md={6} xs={12}>
|
<Grid item md={6} xs={12}>
|
||||||
<Card
|
<Card
|
||||||
sx={{
|
sx={{
|
||||||
|
|
@ -1392,7 +1392,7 @@ function Overview(props: OverviewProps) {
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{aggregatedData && product === 2 && (
|
{aggregatedData && (product === 2 || product === 5) && (
|
||||||
<Grid
|
<Grid
|
||||||
container
|
container
|
||||||
direction="row"
|
direction="row"
|
||||||
|
|
@ -1457,7 +1457,7 @@ function Overview(props: OverviewProps) {
|
||||||
alignItems="stretch"
|
alignItems="stretch"
|
||||||
spacing={3}
|
spacing={3}
|
||||||
>
|
>
|
||||||
<Grid item md={product === 2 ? 12 : 6} xs={12}>
|
<Grid item md={(product === 2 || product === 5) ? 12 : 6} xs={12}>
|
||||||
<Card
|
<Card
|
||||||
sx={{
|
sx={{
|
||||||
overflow: 'visible',
|
overflow: 'visible',
|
||||||
|
|
@ -1518,7 +1518,7 @@ function Overview(props: OverviewProps) {
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
{product !== 2 && (
|
{(product !== 2 && product !== 5) && (
|
||||||
<Grid item md={6} xs={12}>
|
<Grid item md={6} xs={12}>
|
||||||
<Card
|
<Card
|
||||||
sx={{
|
sx={{
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,14 @@ import { getDeviceTypeName } from '../Information/installationSetupUtils';
|
||||||
|
|
||||||
interface FlatInstallationViewProps {
|
interface FlatInstallationViewProps {
|
||||||
installations: I_Installation[];
|
installations: I_Installation[];
|
||||||
|
product?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
|
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
|
||||||
const currentLocation = useLocation();
|
const currentLocation = useLocation();
|
||||||
|
const baseRoute = props.product === 5 ? routes.sodistorepro_installations : routes.sodiohome_installations;
|
||||||
//
|
//
|
||||||
const sortedInstallations = [...props.installations].sort((a, b) => {
|
const sortedInstallations = [...props.installations].sort((a, b) => {
|
||||||
// Compare the status field of each installation and sort them based on the status.
|
// Compare the status field of each installation and sort them based on the status.
|
||||||
|
|
@ -51,7 +53,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
setSelectedInstallation(-1);
|
setSelectedInstallation(-1);
|
||||||
|
|
||||||
navigate(
|
navigate(
|
||||||
routes.sodiohome_installations +
|
baseRoute +
|
||||||
routes.list +
|
routes.list +
|
||||||
routes.installation +
|
routes.installation +
|
||||||
`${installationID}` +
|
`${installationID}` +
|
||||||
|
|
@ -82,9 +84,9 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
sx={{
|
sx={{
|
||||||
display:
|
display:
|
||||||
currentLocation.pathname ===
|
currentLocation.pathname ===
|
||||||
routes.sodiohome_installations + 'list' ||
|
baseRoute + 'list' ||
|
||||||
currentLocation.pathname ===
|
currentLocation.pathname ===
|
||||||
routes.sodiohome_installations + routes.list
|
baseRoute + routes.list
|
||||||
? 'block'
|
? 'block'
|
||||||
: 'none'
|
: 'none'
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,9 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
||||||
const s3Bucket =
|
const s3Bucket =
|
||||||
props.current_installation.s3BucketId.toString() +
|
props.current_installation.s3BucketId.toString() +
|
||||||
'-' +
|
'-' +
|
||||||
'e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa';
|
(props.current_installation.product === 5
|
||||||
|
? '325c9373-9025-4a8d-bf5a-f9eedf1f155c'
|
||||||
|
: 'e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa');
|
||||||
|
|
||||||
const context = useContext(UserContext);
|
const context = useContext(UserContext);
|
||||||
const { currentUser } = context;
|
const { currentUser } = context;
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,14 @@ import SodioHomeInstallation from './Installation';
|
||||||
|
|
||||||
interface installationSearchProps {
|
interface installationSearchProps {
|
||||||
installations: I_Installation[];
|
installations: I_Installation[];
|
||||||
|
product?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function InstallationSearch(props: installationSearchProps) {
|
function InstallationSearch(props: installationSearchProps) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const currentLocation = useLocation();
|
const currentLocation = useLocation();
|
||||||
|
const baseRoute = props.product === 5 ? routes.sodistorepro_installations : routes.sodiohome_installations;
|
||||||
// const [filteredData, setFilteredData] = useState(props.installations);
|
// const [filteredData, setFilteredData] = useState(props.installations);
|
||||||
|
|
||||||
const indexedData = useMemo(() => {
|
const indexedData = useMemo(() => {
|
||||||
|
|
@ -46,9 +48,9 @@ function InstallationSearch(props: installationSearchProps) {
|
||||||
sx={{
|
sx={{
|
||||||
display:
|
display:
|
||||||
currentLocation.pathname ===
|
currentLocation.pathname ===
|
||||||
routes.sodiohome_installations + 'list' ||
|
baseRoute + 'list' ||
|
||||||
currentLocation.pathname ===
|
currentLocation.pathname ===
|
||||||
routes.sodiohome_installations + routes.list
|
baseRoute + routes.list
|
||||||
? 'block'
|
? 'block'
|
||||||
: 'none'
|
: 'none'
|
||||||
}}
|
}}
|
||||||
|
|
@ -79,7 +81,7 @@ function InstallationSearch(props: installationSearchProps) {
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<FlatInstallationView installations={filteredData} />
|
<FlatInstallationView installations={filteredData} product={props.product} />
|
||||||
<Routes>
|
<Routes>
|
||||||
{filteredData.map((installation) => {
|
{filteredData.map((installation) => {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -23,20 +23,26 @@ interface SodistorehomeInstallationFormPros {
|
||||||
cancel: () => void;
|
cancel: () => void;
|
||||||
submit: () => void;
|
submit: () => void;
|
||||||
parentid: number;
|
parentid: number;
|
||||||
|
product?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros) {
|
function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [open, setOpen] = useState(true);
|
const [open, setOpen] = useState(true);
|
||||||
|
const isSodistorePro = props.product === 5;
|
||||||
const [formValues, setFormValues] = useState<Partial<I_Installation>>({
|
const [formValues, setFormValues] = useState<Partial<I_Installation>>({
|
||||||
name: '',
|
name: '',
|
||||||
vpnIp: '',
|
vpnIp: '',
|
||||||
installationModel: '',
|
installationModel: '',
|
||||||
externalEms: 'No',
|
externalEms: 'No',
|
||||||
|
...(isSodistorePro ? { device: 4 } : {}),
|
||||||
});
|
});
|
||||||
const requiredFields = ['name', 'vpnIp', 'installationModel'];
|
const [inverterCount, setInverterCount] = useState('');
|
||||||
|
const requiredFields = ['name', 'vpnIp', ...(isSodistorePro ? [] : ['installationModel'])];
|
||||||
|
|
||||||
const DeviceTypes = SODIOHOME_DEVICE_TYPES;
|
const DeviceTypes = isSodistorePro
|
||||||
|
? [{ id: 4, name: 'inesco 12K - WR Hybrid' }]
|
||||||
|
: SODIOHOME_DEVICE_TYPES;
|
||||||
const installationContext = useContext(InstallationsContext);
|
const installationContext = useContext(InstallationsContext);
|
||||||
const { createInstallation, loading, setLoading, error, setError } =
|
const { createInstallation, loading, setLoading, error, setError } =
|
||||||
installationContext;
|
installationContext;
|
||||||
|
|
@ -52,7 +58,10 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
formValues.parentId = props.parentid;
|
formValues.parentId = props.parentid;
|
||||||
formValues.product = 2;
|
formValues.product = props.product ?? 2;
|
||||||
|
if (isSodistorePro) {
|
||||||
|
formValues.installationModel = inverterCount;
|
||||||
|
}
|
||||||
const responseData = await createInstallation(formValues);
|
const responseData = await createInstallation(formValues);
|
||||||
props.submit();
|
props.submit();
|
||||||
};
|
};
|
||||||
|
|
@ -66,6 +75,9 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isSodistorePro && (!inverterCount || parseInt(inverterCount, 10) < 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -132,6 +144,29 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{isSodistorePro ? (
|
||||||
|
<div>
|
||||||
|
<TextField
|
||||||
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id="numberOfInverters"
|
||||||
|
defaultMessage="Number of Inverters"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
name="inverterCount"
|
||||||
|
type="text"
|
||||||
|
value={inverterCount}
|
||||||
|
onChange={(e) => {
|
||||||
|
const val = e.target.value;
|
||||||
|
if (val === '' || (/^\d+$/.test(val) && parseInt(val, 10) <= 20)) {
|
||||||
|
setInverterCount(val);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
required
|
||||||
|
error={!inverterCount || parseInt(inverterCount, 10) < 1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<FormControl
|
<FormControl
|
||||||
fullWidth
|
fullWidth
|
||||||
|
|
@ -167,7 +202,9 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isSodistorePro && (
|
||||||
<div>
|
<div>
|
||||||
<FormControl
|
<FormControl
|
||||||
fullWidth
|
fullWidth
|
||||||
|
|
@ -201,6 +238,7 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
||||||
closeSocket
|
closeSocket
|
||||||
} = useContext(InstallationsContext);
|
} = useContext(InstallationsContext);
|
||||||
const { product, setProduct } = useContext(ProductIdContext);
|
const { product, setProduct } = useContext(ProductIdContext);
|
||||||
|
const baseRoute = props.product === 5 ? routes.sodistorepro_installations : routes.sodiohome_installations;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let path = location.pathname.split('/');
|
let path = location.pathname.split('/');
|
||||||
|
|
@ -484,6 +485,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
||||||
<Box p={4}>
|
<Box p={4}>
|
||||||
<InstallationSearch
|
<InstallationSearch
|
||||||
installations={sodiohomeInstallations}
|
installations={sodiohomeInstallations}
|
||||||
|
product={props.product}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
@ -496,7 +498,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
||||||
path={'*'}
|
path={'*'}
|
||||||
element={
|
element={
|
||||||
<Navigate
|
<Navigate
|
||||||
to={routes.sodiohome_installations + routes.list}
|
to={baseRoute + routes.list}
|
||||||
></Navigate>
|
></Navigate>
|
||||||
}
|
}
|
||||||
></Route>
|
></Route>
|
||||||
|
|
|
||||||
|
|
@ -742,7 +742,8 @@ function TicketDetailPage() {
|
||||||
1: routes.salidomo_installations,
|
1: routes.salidomo_installations,
|
||||||
2: routes.sodiohome_installations,
|
2: routes.sodiohome_installations,
|
||||||
3: routes.sodistore_installations,
|
3: routes.sodistore_installations,
|
||||||
4: routes.sodistoregrid_installations
|
4: routes.sodistoregrid_installations,
|
||||||
|
5: routes.sodistorepro_installations
|
||||||
};
|
};
|
||||||
const prefix = productRoutes[detail.installationProduct] ?? routes.installations;
|
const prefix = productRoutes[detail.installationProduct] ?? routes.installations;
|
||||||
navigate(
|
navigate(
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
||||||
? routes.salidomo_installations
|
? routes.salidomo_installations
|
||||||
: installation.product == 2
|
: installation.product == 2
|
||||||
? routes.sodiohome_installations
|
? routes.sodiohome_installations
|
||||||
|
: installation.product == 5
|
||||||
|
? routes.sodistorepro_installations
|
||||||
: routes.sodistore_installations;
|
: routes.sodistore_installations;
|
||||||
|
|
||||||
let folder_path =
|
let folder_path =
|
||||||
|
|
@ -69,6 +71,8 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
||||||
? routes.salidomo_installations
|
? routes.salidomo_installations
|
||||||
: product == 2
|
: product == 2
|
||||||
? routes.sodiohome_installations
|
? routes.sodiohome_installations
|
||||||
|
: product == 5
|
||||||
|
? routes.sodistorepro_installations
|
||||||
: routes.sodistore_installations;
|
: routes.sodistore_installations;
|
||||||
|
|
||||||
if (installation.type != 'Folder') {
|
if (installation.type != 'Folder') {
|
||||||
|
|
|
||||||
|
|
@ -65,11 +65,12 @@ function TreeInformation(props: TreeInformationProps) {
|
||||||
setProduct(e.target.value); // Directly update the product state
|
setProduct(e.target.value); // Directly update the product state
|
||||||
};
|
};
|
||||||
|
|
||||||
const ProductTypes = ['Salimax', 'Salidomo', 'SodistoreHome', 'SodistoreMax', 'SodistoreGrid'];
|
const ProductTypes = ['Salimax', 'Salidomo', 'SodistoreHome', 'SodistoreMax', 'SodistoreGrid', 'SodistorePro'];
|
||||||
const ProductDisplayNames: Record<string, string> = {
|
const ProductDisplayNames: Record<string, string> = {
|
||||||
'SodistoreHome': 'Sodistore Home',
|
'SodistoreHome': 'Sodistore Home',
|
||||||
'SodistoreMax': 'Sodistore Max',
|
'SodistoreMax': 'Sodistore Max',
|
||||||
'SodistoreGrid': 'Sodistore Grid'
|
'SodistoreGrid': 'Sodistore Grid',
|
||||||
|
'SodistorePro': 'Sodistore Pro'
|
||||||
};
|
};
|
||||||
|
|
||||||
const isMobile = window.innerWidth <= 1490;
|
const isMobile = window.innerWidth <= 1490;
|
||||||
|
|
@ -345,11 +346,12 @@ function TreeInformation(props: TreeInformationProps) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{openModalInstallation && product == 'SodistoreHome' && (
|
{openModalInstallation && (product == 'SodistoreHome' || product == 'SodistorePro') && (
|
||||||
<SodiostorehomeInstallationForm
|
<SodiostorehomeInstallationForm
|
||||||
cancel={handleFormCancel}
|
cancel={handleFormCancel}
|
||||||
submit={handleInstallationFormSubmit}
|
submit={handleInstallationFormSubmit}
|
||||||
parentid={props.folder.id}
|
parentid={props.folder.id}
|
||||||
|
product={product == 'SodistorePro' ? 5 : undefined}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,7 @@ const InstallationsContextProvider = ({
|
||||||
`/GetAllInstallationsFromProduct?product=${product}`
|
`/GetAllInstallationsFromProduct?product=${product}`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (product === 2) {
|
if (product === 2 || product === 5) {
|
||||||
setSodiohomeInstallations(res.data);
|
setSodiohomeInstallations(res.data);
|
||||||
} else if (product === 1) {
|
} else if (product === 1) {
|
||||||
setSalidomoInstallations(res.data);
|
setSalidomoInstallations(res.data);
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,13 @@ interface ProductIdContextType {
|
||||||
accessToSodiohome: boolean;
|
accessToSodiohome: boolean;
|
||||||
accessToSodistore: boolean;
|
accessToSodistore: boolean;
|
||||||
accessToSodistoreGrid: boolean;
|
accessToSodistoreGrid: boolean;
|
||||||
|
accessToSodistorePro: boolean;
|
||||||
setAccessToSalimax: (access: boolean) => void;
|
setAccessToSalimax: (access: boolean) => void;
|
||||||
setAccessToSalidomo: (access: boolean) => void;
|
setAccessToSalidomo: (access: boolean) => void;
|
||||||
setAccessToSodiohome: (access: boolean) => void;
|
setAccessToSodiohome: (access: boolean) => void;
|
||||||
setAccessToSodistore: (access: boolean) => void;
|
setAccessToSodistore: (access: boolean) => void;
|
||||||
setAccessToSodistoreGrid: (access: boolean) => void;
|
setAccessToSodistoreGrid: (access: boolean) => void;
|
||||||
|
setAccessToSodistorePro: (access: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the context.
|
// Create the context.
|
||||||
|
|
@ -49,6 +51,10 @@ export const ProductIdContextProvider = ({
|
||||||
const storedValue = localStorage.getItem('accessToSodistoreGrid');
|
const storedValue = localStorage.getItem('accessToSodistoreGrid');
|
||||||
return storedValue === 'true';
|
return storedValue === 'true';
|
||||||
});
|
});
|
||||||
|
const [accessToSodistorePro, setAccessToSodistorePro] = useState(() => {
|
||||||
|
const storedValue = localStorage.getItem('accessToSodistorePro');
|
||||||
|
return storedValue === 'true';
|
||||||
|
});
|
||||||
// const [product, setProduct] = useState(location.includes('salidomo') ? 1 : 0);
|
// const [product, setProduct] = useState(location.includes('salidomo') ? 1 : 0);
|
||||||
// const [product, setProduct] = useState<number>(
|
// const [product, setProduct] = useState<number>(
|
||||||
// productMapping[Object.keys(productMapping).find((key) => location.includes(key)) || ''] || -1
|
// productMapping[Object.keys(productMapping).find((key) => location.includes(key)) || ''] || -1
|
||||||
|
|
@ -56,6 +62,8 @@ export const ProductIdContextProvider = ({
|
||||||
const [product, setProduct] = useState<number>(() => {
|
const [product, setProduct] = useState<number>(() => {
|
||||||
if (location.includes('salidomo')) {
|
if (location.includes('salidomo')) {
|
||||||
return 1;
|
return 1;
|
||||||
|
} else if (location.includes('sodistorepro')) {
|
||||||
|
return 5;
|
||||||
} else if (location.includes('sodiohome')) {
|
} else if (location.includes('sodiohome')) {
|
||||||
return 2;
|
return 2;
|
||||||
} else if (location.includes('sodistoregrid')) {
|
} else if (location.includes('sodistoregrid')) {
|
||||||
|
|
@ -92,6 +100,10 @@ export const ProductIdContextProvider = ({
|
||||||
setAccessToSodistoreGrid(access);
|
setAccessToSodistoreGrid(access);
|
||||||
localStorage.setItem('accessToSodistoreGrid', JSON.stringify(access));
|
localStorage.setItem('accessToSodistoreGrid', JSON.stringify(access));
|
||||||
};
|
};
|
||||||
|
const changeAccessSodistorePro = (access: boolean) => {
|
||||||
|
setAccessToSodistorePro(access);
|
||||||
|
localStorage.setItem('accessToSodistorePro', JSON.stringify(access));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProductIdContext.Provider
|
<ProductIdContext.Provider
|
||||||
|
|
@ -103,11 +115,13 @@ export const ProductIdContextProvider = ({
|
||||||
accessToSodiohome,
|
accessToSodiohome,
|
||||||
accessToSodistore,
|
accessToSodistore,
|
||||||
accessToSodistoreGrid,
|
accessToSodistoreGrid,
|
||||||
|
accessToSodistorePro,
|
||||||
setAccessToSalimax: changeAccessSalimax,
|
setAccessToSalimax: changeAccessSalimax,
|
||||||
setAccessToSalidomo: changeAccessSalidomo,
|
setAccessToSalidomo: changeAccessSalidomo,
|
||||||
setAccessToSodiohome: changeAccessSodiohome,
|
setAccessToSodiohome: changeAccessSodiohome,
|
||||||
setAccessToSodistore: changeAccessSodistore,
|
setAccessToSodistore: changeAccessSodistore,
|
||||||
setAccessToSodistoreGrid: changeAccessSodistoreGrid
|
setAccessToSodistoreGrid: changeAccessSodistoreGrid,
|
||||||
|
setAccessToSodistorePro: changeAccessSodistorePro
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ export const transformInputToBatteryViewDataJson = async (
|
||||||
}> => {
|
}> => {
|
||||||
const prefixes = ['', 'k', 'M', 'G', 'T'];
|
const prefixes = ['', 'k', 'M', 'G', 'T'];
|
||||||
const MAX_NUMBER = 9999999;
|
const MAX_NUMBER = 9999999;
|
||||||
const isSodioHome = product === 2;
|
const isSodioHome = product === 2 || product === 5;
|
||||||
const categories = isSodioHome
|
const categories = isSodioHome
|
||||||
? ['Soc', 'Power', 'Voltage', 'Current', 'Soh']
|
? ['Soc', 'Power', 'Voltage', 'Current', 'Soh']
|
||||||
: ['Soc', 'Temperature', 'Power', 'Voltage', 'Current'];
|
: ['Soc', 'Temperature', 'Power', 'Voltage', 'Current'];
|
||||||
|
|
@ -169,7 +169,7 @@ export const transformInputToBatteryViewDataJson = async (
|
||||||
);
|
);
|
||||||
|
|
||||||
const adjustedTimestamp =
|
const adjustedTimestamp =
|
||||||
product == 0 || product == 2 || product == 3 || product == 4
|
product == 0 || product == 2 || product == 3 || product == 4 || product == 5
|
||||||
? new Date(timestampArray[i] * 1000)
|
? new Date(timestampArray[i] * 1000)
|
||||||
: new Date(timestampArray[i] * 100000);
|
: new Date(timestampArray[i] * 100000);
|
||||||
//Timezone offset is negative, so we convert the timestamp to the current zone by subtracting the corresponding offset
|
//Timezone offset is negative, so we convert the timestamp to the current zone by subtracting the corresponding offset
|
||||||
|
|
@ -393,7 +393,7 @@ export const transformInputToDailyDataJson = async (
|
||||||
// custom fallback logic to handle differences between Growatt and Sinexcel.
|
// custom fallback logic to handle differences between Growatt and Sinexcel.
|
||||||
// Growatt has: Battery1AmbientTemperature, GridPower, PvPower
|
// Growatt has: Battery1AmbientTemperature, GridPower, PvPower
|
||||||
// Sinexcel has: Battery1Temperature, TotalGridPower (meter may be offline), PvPower1-4
|
// Sinexcel has: Battery1Temperature, TotalGridPower (meter may be offline), PvPower1-4
|
||||||
const pathsToSearch = product == 2
|
const pathsToSearch = (product == 2 || product == 5)
|
||||||
? [
|
? [
|
||||||
'SODIOHOME_SOC',
|
'SODIOHOME_SOC',
|
||||||
'SODIOHOME_TEMPERATURE',
|
'SODIOHOME_TEMPERATURE',
|
||||||
|
|
@ -516,8 +516,8 @@ export const transformInputToDailyDataJson = async (
|
||||||
|
|
||||||
let value: number | undefined = undefined;
|
let value: number | undefined = undefined;
|
||||||
|
|
||||||
if (product === 2) {
|
if (product === 2 || product === 5) {
|
||||||
// SodioHome: use top-level aggregated values (Sinexcel multi-inverter)
|
// SodioHome/SodistorePro: use top-level aggregated values (Sinexcel multi-inverter)
|
||||||
const inv = result?.InverterRecord;
|
const inv = result?.InverterRecord;
|
||||||
if (inv) {
|
if (inv) {
|
||||||
switch (category_index) {
|
switch (category_index) {
|
||||||
|
|
@ -735,7 +735,7 @@ export const transformInputToAggregatedDataJson = async (
|
||||||
const timestampPromises = [];
|
const timestampPromises = [];
|
||||||
|
|
||||||
while (currentDay.isBefore(end_date)) {
|
while (currentDay.isBefore(end_date)) {
|
||||||
const dateFormat = product === 2
|
const dateFormat = (product === 2 || product === 5)
|
||||||
? currentDay.format('DDMMYYYY')
|
? currentDay.format('DDMMYYYY')
|
||||||
: currentDay.format('YYYY-MM-DD');
|
: currentDay.format('YYYY-MM-DD');
|
||||||
timestampPromises.push(
|
timestampPromises.push(
|
||||||
|
|
|
||||||
|
|
@ -648,5 +648,7 @@
|
||||||
"terms_cookies_body": "Browser-Speicher wird für Anmeldesitzungen und Benutzereinstellungen verwendet. Dies ist für die korrekte Funktion der Plattform erforderlich.",
|
"terms_cookies_body": "Browser-Speicher wird für Anmeldesitzungen und Benutzereinstellungen verwendet. Dies ist für die korrekte Funktion der Plattform erforderlich.",
|
||||||
"terms_usage_heading": "Nutzungsbedingungen",
|
"terms_usage_heading": "Nutzungsbedingungen",
|
||||||
"terms_usage_body": "Durch die Nutzung dieser Plattform erkennen Sie die allgemeinen Nutzungsbedingungen von inesco Energy an. Bei Fragen wenden Sie sich bitte an Ihren Systemadministrator.",
|
"terms_usage_body": "Durch die Nutzung dieser Plattform erkennen Sie die allgemeinen Nutzungsbedingungen von inesco Energy an. Bei Fragen wenden Sie sich bitte an Ihren Systemadministrator.",
|
||||||
"terms_acknowledge_button": "Ich verstehe"
|
"terms_acknowledge_button": "Ich verstehe",
|
||||||
|
"sodistorepro": "Sodistore Pro",
|
||||||
|
"numberOfInverters": "Anzahl der Wechselrichter"
|
||||||
}
|
}
|
||||||
|
|
@ -396,5 +396,7 @@
|
||||||
"terms_cookies_body": "Browser storage is used for login sessions and user preferences. This is required for the platform to function correctly.",
|
"terms_cookies_body": "Browser storage is used for login sessions and user preferences. This is required for the platform to function correctly.",
|
||||||
"terms_usage_heading": "Terms of Use",
|
"terms_usage_heading": "Terms of Use",
|
||||||
"terms_usage_body": "By using this platform, you acknowledge the general terms of use of inesco Energy. For questions, please contact your system administrator.",
|
"terms_usage_body": "By using this platform, you acknowledge the general terms of use of inesco Energy. For questions, please contact your system administrator.",
|
||||||
"terms_acknowledge_button": "I understand"
|
"terms_acknowledge_button": "I understand",
|
||||||
|
"sodistorepro": "Sodistore Pro",
|
||||||
|
"numberOfInverters": "Number of Inverters"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -648,5 +648,7 @@
|
||||||
"terms_cookies_body": "Le stockage du navigateur est utilisé pour les sessions de connexion et les préférences utilisateur. Ceci est nécessaire au bon fonctionnement de la plateforme.",
|
"terms_cookies_body": "Le stockage du navigateur est utilisé pour les sessions de connexion et les préférences utilisateur. Ceci est nécessaire au bon fonctionnement de la plateforme.",
|
||||||
"terms_usage_heading": "Conditions d'utilisation",
|
"terms_usage_heading": "Conditions d'utilisation",
|
||||||
"terms_usage_body": "En utilisant cette plateforme, vous reconnaissez les conditions générales d'utilisation d'inesco Energy. Pour toute question, veuillez contacter votre administrateur système.",
|
"terms_usage_body": "En utilisant cette plateforme, vous reconnaissez les conditions générales d'utilisation d'inesco Energy. Pour toute question, veuillez contacter votre administrateur système.",
|
||||||
"terms_acknowledge_button": "Je comprends"
|
"terms_acknowledge_button": "Je comprends",
|
||||||
|
"sodistorepro": "Sodistore Pro",
|
||||||
|
"numberOfInverters": "Nombre d'onduleurs"
|
||||||
}
|
}
|
||||||
|
|
@ -648,5 +648,7 @@
|
||||||
"terms_cookies_body": "L'archiviazione del browser viene utilizzata per le sessioni di accesso e le preferenze utente. Questo è necessario per il corretto funzionamento della piattaforma.",
|
"terms_cookies_body": "L'archiviazione del browser viene utilizzata per le sessioni di accesso e le preferenze utente. Questo è necessario per il corretto funzionamento della piattaforma.",
|
||||||
"terms_usage_heading": "Condizioni d'uso",
|
"terms_usage_heading": "Condizioni d'uso",
|
||||||
"terms_usage_body": "Utilizzando questa piattaforma, si riconoscono le condizioni generali d'uso di inesco Energy. Per domande, contattare l'amministratore di sistema.",
|
"terms_usage_body": "Utilizzando questa piattaforma, si riconoscono le condizioni generali d'uso di inesco Energy. Per domande, contattare l'amministratore di sistema.",
|
||||||
"terms_acknowledge_button": "Ho capito"
|
"terms_acknowledge_button": "Ho capito",
|
||||||
|
"sodistorepro": "Sodistore Pro",
|
||||||
|
"numberOfInverters": "Numero di inverter"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,8 @@ function SidebarMenu() {
|
||||||
accessToSodistore,
|
accessToSodistore,
|
||||||
accessToSalidomo,
|
accessToSalidomo,
|
||||||
accessToSodiohome,
|
accessToSodiohome,
|
||||||
accessToSodistoreGrid
|
accessToSodistoreGrid,
|
||||||
|
accessToSodistorePro
|
||||||
} = useContext(ProductIdContext);
|
} = useContext(ProductIdContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -285,6 +286,27 @@ function SidebarMenu() {
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{accessToSodistorePro && (
|
||||||
|
<List component="div">
|
||||||
|
<ListItem component="div">
|
||||||
|
<Button
|
||||||
|
disableRipple
|
||||||
|
component={RouterLink}
|
||||||
|
onClick={closeSidebar}
|
||||||
|
to="/sodistorepro_installations"
|
||||||
|
startIcon={<BrightnessLowTwoToneIcon />}
|
||||||
|
>
|
||||||
|
<Box sx={{ marginTop: '3px' }}>
|
||||||
|
<FormattedMessage
|
||||||
|
id="sodistorepro"
|
||||||
|
defaultMessage="Sodistore Pro"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Button>
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
)}
|
||||||
</SubMenuWrapper>
|
</SubMenuWrapper>
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue