Compare commits
No commits in common. "812962ace00d4c3c19698233c1c01db01dbc4a3e" and "897f3137f5db87d7cfe0d557415f9b02d7c08dce" have entirely different histories.
812962ace0
...
897f3137f5
|
|
@ -202,8 +202,6 @@ public class Controller : ControllerBase
|
|||
bucketPath = "s3://" + installation.S3BucketId + "-e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa/" + startTimestamp;
|
||||
else if (installation.Product == (int)ProductType.SodistoreGrid)
|
||||
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
|
||||
bucketPath = "s3://" + installation.S3BucketId + "-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e/" + startTimestamp;
|
||||
Console.WriteLine("Fetching data for "+startTimestamp);
|
||||
|
|
@ -817,10 +815,9 @@ public class Controller : ControllerBase
|
|||
if (installation is null || !user.HasAccessTo(installation))
|
||||
return Unauthorized();
|
||||
|
||||
// AI diagnostics are scoped to SodistoreHome, SodiStoreMax, and SodistorePro only
|
||||
// AI diagnostics are scoped to SodistoreHome and SodiStoreMax only
|
||||
if (installation.Product != (int)ProductType.SodioHome &&
|
||||
installation.Product != (int)ProductType.SodiStoreMax &&
|
||||
installation.Product != (int)ProductType.SodistorePro)
|
||||
installation.Product != (int)ProductType.SodiStoreMax)
|
||||
return BadRequest("AI diagnostics not available for this product.");
|
||||
|
||||
var result = await DiagnosticService.DiagnoseAsync(installationId, errorDescription, user.Language ?? "en");
|
||||
|
|
@ -2137,31 +2134,6 @@ public class Controller : ControllerBase
|
|||
});
|
||||
}
|
||||
|
||||
var assigneeChanged = ticket.AssigneeId != existing.AssigneeId
|
||||
&& ticket.AssigneeId.HasValue;
|
||||
|
||||
if (assigneeChanged)
|
||||
{
|
||||
var assignee = Db.GetUserById(ticket.AssigneeId);
|
||||
|
||||
Db.Create(new TicketTimelineEvent
|
||||
{
|
||||
TicketId = ticket.Id,
|
||||
EventType = (Int32)TimelineEventType.Assigned,
|
||||
Description = $"Ticket assigned to {assignee?.Name ?? "unknown"}.",
|
||||
ActorType = (Int32)TimelineActorType.Human,
|
||||
ActorId = user.Id,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
if (assignee is not null)
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try { await assignee.SendTicketAssignedEmail(ticket); }
|
||||
catch (Exception ex) { Console.WriteLine($"Failed to send ticket assignment email: {ex}"); }
|
||||
});
|
||||
}
|
||||
|
||||
return Db.Update(ticket) ? ticket : StatusCode(500, "Update failed.");
|
||||
}
|
||||
|
||||
|
|
@ -2337,17 +2309,4 @@ public class Controller : ControllerBase
|
|||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPut(nameof(AcknowledgeTerms))]
|
||||
public ActionResult AcknowledgeTerms(Int32 version, Token authToken)
|
||||
{
|
||||
var session = Db.GetSession(authToken);
|
||||
if (session is null) return Unauthorized();
|
||||
|
||||
var user = Db.GetUserById(session.User.Id);
|
||||
if (user is null) return Unauthorized();
|
||||
|
||||
user.AcknowledgedTermsVersion = version;
|
||||
return Db.Update(user) ? Ok() : StatusCode(500);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ public enum ProductType
|
|||
Salidomo = 1,
|
||||
SodioHome =2,
|
||||
SodiStoreMax=3,
|
||||
SodistoreGrid=4,
|
||||
SodistorePro=5
|
||||
SodistoreGrid=4
|
||||
}
|
||||
|
||||
public enum StatusType
|
||||
|
|
@ -28,13 +27,6 @@ public class Installation : TreeNode
|
|||
public String Location { get; set; } = "";
|
||||
public String Region { get; set; } = "";
|
||||
public String Country { get; set; } = "";
|
||||
public String Street { get; set; } = "";
|
||||
public String PostCode { get; set; } = "";
|
||||
public String City { get; set; } = "";
|
||||
public String Canton { get; set; } = "";
|
||||
public String DistributionPartner { get; set; } = "";
|
||||
public String InverterFirmwareVersion { get; set; } = "";
|
||||
public String BatteryFirmwareVersion { get; set; } = "";
|
||||
public String VpnIp { get; set; } = "";
|
||||
public String InstallationName { get; set; } = "";
|
||||
|
||||
|
|
@ -57,11 +49,7 @@ public class Installation : TreeNode
|
|||
public int BatteryClusterNumber { get; set; } = 0;
|
||||
public int BatteryNumber { get; set; } = 0;
|
||||
public string BatterySerialNumbers { get; set; } = "";
|
||||
public string PvStringsPerInverter { get; set; } = "";
|
||||
public string InstallationModel { get; set; } = "";
|
||||
public string ExternalEms { get; set; } = "No";
|
||||
public string CouplingType { get; set; } = "DC";
|
||||
|
||||
|
||||
[Ignore]
|
||||
public String OrderNumbers { get; set; }
|
||||
public String VrmLink { get; set; } = "";
|
||||
|
|
|
|||
|
|
@ -146,7 +146,6 @@ public static class ExoCmd
|
|||
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.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;
|
||||
|
||||
|
||||
|
|
@ -351,7 +350,6 @@ public static class ExoCmd
|
|||
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.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;
|
||||
|
||||
var contentString = $$"""
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ public static class InstallationMethods
|
|||
private static readonly String SalidomoBucketNameSalt = "c0436b6a-d276-4cd8-9c44-1eae86cf5d0e";
|
||||
private static readonly String SodioHomeBucketNameSalt = "e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa";
|
||||
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)
|
||||
{
|
||||
|
|
@ -30,11 +29,6 @@ public static class InstallationMethods
|
|||
return $"{installation.S3BucketId}-{SodistoreGridBucketNameSalt}";
|
||||
}
|
||||
|
||||
if (installation.Product == (int)ProductType.SodistorePro)
|
||||
{
|
||||
return $"{installation.S3BucketId}-{SodistoreProBucketNameSalt}";
|
||||
}
|
||||
|
||||
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 || installation.Product == (int)ProductType.SodistorePro)
|
||||
if (installation.Product == (int)ProductType.SodiStoreMax || installation.Product == (int)ProductType.SodioHome || installation.Product == (int)ProductType.SodistoreGrid)
|
||||
{
|
||||
return user is not null
|
||||
&& user.UserType != 0
|
||||
|
|
@ -295,7 +295,7 @@ public static class SessionMethods
|
|||
.Apply(Db.Update);
|
||||
}
|
||||
|
||||
if (installation.Product == (int)ProductType.SodiStoreMax || installation.Product == (int)ProductType.SodistoreGrid || installation.Product == (int)ProductType.SodistorePro)
|
||||
if (installation.Product == (int)ProductType.SodiStoreMax || installation.Product == (int)ProductType.SodistoreGrid)
|
||||
{
|
||||
|
||||
return user is not null
|
||||
|
|
|
|||
|
|
@ -243,22 +243,22 @@ public static class UserMethods
|
|||
var (subject, body) = (user.Language ?? "en") switch
|
||||
{
|
||||
"de" => (
|
||||
"Passwort Ihres inesco energy Kontos zurücksetzen",
|
||||
"Passwort Ihres Inesco Energy Kontos zurücksetzen",
|
||||
$"Sehr geehrte/r {user.Name}\n" +
|
||||
$"Um Ihr Passwort zurückzusetzen, öffnen Sie bitte diesen Link: {resetLink}?token={encodedToken}"
|
||||
),
|
||||
"fr" => (
|
||||
"Réinitialisation du mot de passe de votre compte inesco energy",
|
||||
"Réinitialisation du mot de passe de votre compte Inesco Energy",
|
||||
$"Cher/Chère {user.Name}\n" +
|
||||
$"Pour réinitialiser votre mot de passe, veuillez ouvrir ce lien : {resetLink}?token={encodedToken}"
|
||||
),
|
||||
"it" => (
|
||||
"Reimposta la password del tuo account inesco energy",
|
||||
"Reimposta la password del tuo account Inesco Energy",
|
||||
$"Gentile {user.Name}\n" +
|
||||
$"Per reimpostare la password, apra questo link: {resetLink}?token={encodedToken}"
|
||||
),
|
||||
_ => (
|
||||
"Reset the password of your inesco energy Account",
|
||||
"Reset the password of your Inesco Energy Account",
|
||||
$"Dear {user.Name}\n" +
|
||||
$"To reset your password please open this link: {resetLink}?token={encodedToken}"
|
||||
)
|
||||
|
|
@ -274,89 +274,28 @@ public static class UserMethods
|
|||
var (subject, body) = (user.Language ?? "en") switch
|
||||
{
|
||||
"de" => (
|
||||
"Ihr neues inesco energy Konto",
|
||||
"Ihr neues Inesco Energy Konto",
|
||||
$"Sehr geehrte/r {user.Name}\n" +
|
||||
$"Um Ihr Passwort festzulegen und sich bei Ihrem inesco energy Konto anzumelden, öffnen Sie bitte diesen Link: {resetLink}"
|
||||
$"Um Ihr Passwort festzulegen und sich bei Ihrem Inesco Energy Konto anzumelden, öffnen Sie bitte diesen Link: {resetLink}"
|
||||
),
|
||||
"fr" => (
|
||||
"Votre nouveau compte inesco energy",
|
||||
"Votre nouveau compte Inesco Energy",
|
||||
$"Cher/Chère {user.Name}\n" +
|
||||
$"Pour définir votre mot de passe et vous connecter à votre compte inesco energy, veuillez ouvrir ce lien : {resetLink}"
|
||||
$"Pour définir votre mot de passe et vous connecter à votre compte Inesco Energy, veuillez ouvrir ce lien : {resetLink}"
|
||||
),
|
||||
"it" => (
|
||||
"Il tuo nuovo account inesco energy",
|
||||
"Il tuo nuovo account Inesco Energy",
|
||||
$"Gentile {user.Name}\n" +
|
||||
$"Per impostare la password e accedere al suo account inesco energy, apra questo link: {resetLink}"
|
||||
$"Per impostare la password e accedere al suo account Inesco Energy, apra questo link: {resetLink}"
|
||||
),
|
||||
_ => (
|
||||
"Your new inesco energy Account",
|
||||
"Your new Inesco Energy Account",
|
||||
$"Dear {user.Name}\n" +
|
||||
$"To set your password and log in to your inesco energy Account open this link: {resetLink}"
|
||||
$"To set your password and log in to your Inesco Energy Account open this link: {resetLink}"
|
||||
)
|
||||
};
|
||||
|
||||
return user.SendEmail(subject, body);
|
||||
}
|
||||
|
||||
public static Task SendTicketAssignedEmail(this User user, Ticket ticket)
|
||||
{
|
||||
var ticketLink = $"https://monitor.inesco.energy/tickets/{ticket.Id}";
|
||||
var priority = (TicketPriority)ticket.Priority;
|
||||
var category = (TicketCategory)ticket.Category;
|
||||
|
||||
var (subject, body) = (user.Language ?? "en") switch
|
||||
{
|
||||
"de" => (
|
||||
$"inesco energy – Ticket #{ticket.Id} wurde Ihnen zugewiesen",
|
||||
$"Sehr geehrte/r {user.Name},\n\n" +
|
||||
$"Ein Ticket wurde Ihnen zugewiesen:\n\n" +
|
||||
$"Ticket: #{ticket.Id}\n" +
|
||||
$"Betreff: {ticket.Subject}\n" +
|
||||
$"Priorität: {priority}\n" +
|
||||
$"Kategorie: {category}\n\n" +
|
||||
$"Beschreibung:\n{ticket.Description}\n\n" +
|
||||
$"Öffnen Sie das Ticket hier: {ticketLink}\n\n" +
|
||||
"Mit freundlichen Grüssen\ninesco energy Monitor"
|
||||
),
|
||||
"fr" => (
|
||||
$"inesco energy – Le ticket #{ticket.Id} vous a été attribué",
|
||||
$"Cher/Chère {user.Name},\n\n" +
|
||||
$"Un ticket vous a été attribué :\n\n" +
|
||||
$"Ticket : #{ticket.Id}\n" +
|
||||
$"Objet : {ticket.Subject}\n" +
|
||||
$"Priorité : {priority}\n" +
|
||||
$"Catégorie : {category}\n\n" +
|
||||
$"Description :\n{ticket.Description}\n\n" +
|
||||
$"Ouvrir le ticket : {ticketLink}\n\n" +
|
||||
"Cordialement,\ninesco energy Monitor"
|
||||
),
|
||||
"it" => (
|
||||
$"inesco energy – Il ticket #{ticket.Id} le è stato assegnato",
|
||||
$"Gentile {user.Name},\n\n" +
|
||||
$"Le è stato assegnato un ticket:\n\n" +
|
||||
$"Ticket: #{ticket.Id}\n" +
|
||||
$"Oggetto: {ticket.Subject}\n" +
|
||||
$"Priorità: {priority}\n" +
|
||||
$"Categoria: {category}\n\n" +
|
||||
$"Descrizione:\n{ticket.Description}\n\n" +
|
||||
$"Aprire il ticket: {ticketLink}\n\n" +
|
||||
"Cordiali saluti,\ninesco energy Monitor"
|
||||
),
|
||||
_ => (
|
||||
$"inesco energy – Ticket #{ticket.Id} has been assigned to you",
|
||||
$"Dear {user.Name},\n\n" +
|
||||
$"A ticket has been assigned to you:\n\n" +
|
||||
$"Ticket: #{ticket.Id}\n" +
|
||||
$"Subject: {ticket.Subject}\n" +
|
||||
$"Priority: {priority}\n" +
|
||||
$"Category: {category}\n\n" +
|
||||
$"Description:\n{ticket.Description}\n\n" +
|
||||
$"Open the ticket: {ticketLink}\n\n" +
|
||||
"Best regards,\ninesco energy Monitor"
|
||||
)
|
||||
};
|
||||
|
||||
return user.SendEmail(subject, body);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ public class User : TreeNode
|
|||
public Boolean MustResetPassword { get; set; } = false;
|
||||
public String? Password { get; set; } = null!;
|
||||
public String Language { get; set; } = "en";
|
||||
public Int32? AcknowledgedTermsVersion { get; set; }
|
||||
|
||||
[Unique]
|
||||
public override String Name { get; set; } = null!;
|
||||
|
|
|
|||
|
|
@ -83,12 +83,10 @@ public static partial class Db
|
|||
Connection.Execute("UPDATE User SET Language = 'fr' WHERE Language = 'french'");
|
||||
Connection.Execute("UPDATE User SET Language = 'it' WHERE Language = 'italian'");
|
||||
|
||||
// One-time migration: rebrand to inesco energy
|
||||
Connection.Execute("UPDATE Folder SET Name = 'inesco energy' WHERE Name = 'InnovEnergy'");
|
||||
Connection.Execute("UPDATE Folder SET Name = 'inesco energy' WHERE Name = 'inesco Energy'");
|
||||
// One-time migration: rebrand to inesco Energy
|
||||
Connection.Execute("UPDATE Folder SET Name = 'inesco Energy' WHERE Name = 'InnovEnergy'");
|
||||
Connection.Execute("UPDATE Folder SET Name = 'Sodistore Max Installations' WHERE Name = 'SodistoreMax Installations'");
|
||||
Connection.Execute("UPDATE User SET Name = 'inesco energy Master Admin' WHERE Name = 'InnovEnergy Master Admin'");
|
||||
Connection.Execute("UPDATE User SET Name = 'inesco energy Master Admin' WHERE Name = 'inesco Energy Master Admin'");
|
||||
Connection.Execute("UPDATE User SET Name = 'inesco Energy Master Admin' WHERE Name = 'InnovEnergy Master Admin'");
|
||||
|
||||
//UpdateKeys();
|
||||
CleanupSessions().SupressAwaitWarning();
|
||||
|
|
@ -132,11 +130,6 @@ public static partial class Db
|
|||
fileConnection.CreateTable<TicketAiDiagnosis>();
|
||||
fileConnection.CreateTable<TicketTimelineEvent>();
|
||||
|
||||
// Migrate new columns: set defaults for existing rows where NULL or empty
|
||||
fileConnection.Execute("UPDATE Installation SET ExternalEms = 'No' WHERE ExternalEms IS NULL OR ExternalEms = ''");
|
||||
fileConnection.Execute("UPDATE Installation SET InstallationModel = '' WHERE InstallationModel IS NULL");
|
||||
fileConnection.Execute("UPDATE Installation SET PvStringsPerInverter = '' WHERE PvStringsPerInverter IS NULL");
|
||||
|
||||
return fileConnection;
|
||||
//return CopyDbToMemory(fileConnection);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
"SmtpUsername" : "no-reply@inesco.ch",
|
||||
"SmtpPassword" : "1ci4vi%+bfccIp",
|
||||
"SmtpPort" : 587,
|
||||
"SenderName" : "inesco energy",
|
||||
"SenderName" : "Inesco Energy",
|
||||
"SenderAddress" : "no-reply@inesco.ch"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ public class Session : Relation<String, Int64>
|
|||
public Boolean AccessToSodistoreMax { get; set; } = false;
|
||||
public Boolean AccessToSodioHome { get; set; } = false;
|
||||
public Boolean AccessToSodistoreGrid { get; set; } = false;
|
||||
public Boolean AccessToSodistorePro { get; set; } = false;
|
||||
[Ignore] public Boolean Valid => DateTime.Now - LastSeen <=MaxAge ;
|
||||
|
||||
// Private backing field
|
||||
|
|
@ -52,7 +51,6 @@ public class Session : Relation<String, Int64>
|
|||
AccessToSodistoreMax = user.AccessibleInstallations(product: (int)ProductType.SodiStoreMax).ToList().Count > 0;
|
||||
AccessToSodioHome = user.AccessibleInstallations(product: (int)ProductType.SodioHome).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("AccessToSodistoreMax" +user.AccessibleInstallations(product: (int)ProductType.SodiStoreMax).ToList().Count);
|
||||
|
|
|
|||
|
|
@ -1223,7 +1223,7 @@ textarea:focus{outline:none;border-color:#3498db}
|
|||
<div id="app"></div>
|
||||
<div class="nav" id="nav"></div>
|
||||
<div class="thankyou">Vielen Dank für Ihre Zeit — Ihr Beitrag macht einen echten Unterschied für unsere Kunden. 🙏</div>
|
||||
<div style="text-align:center;padding-bottom:24px;font-size:11px;color:#bbb">inesco energy Monitor</div>
|
||||
<div style="text-align:center;padding-bottom:24px;font-size:11px;color:#bbb">inesco Energy Monitor</div>
|
||||
<script>
|
||||
var ALARMS = %%ALARMS_JSON%%;
|
||||
var SUBMIT_URL = "%%SUBMIT_URL%%";
|
||||
|
|
@ -1473,7 +1473,7 @@ render();
|
|||
<p style="margin-bottom:0;font-size:13px;color:#555">Vielen Dank für Ihre Zeit — Ihr Beitrag macht einen echten Unterschied für unsere Kunden. 🙏</p>
|
||||
</td></tr>
|
||||
<tr><td style="background:#ecf0f1;padding:12px 28px;text-align:center;font-size:11px;color:#888;border-top:1px solid #ddd">
|
||||
inesco energy Monitor
|
||||
inesco Energy Monitor
|
||||
</td></tr>
|
||||
</table></td></tr></table></body></html>
|
||||
""";
|
||||
|
|
@ -1545,7 +1545,7 @@ render();
|
|||
<p>Hallo <strong>{name}</strong>,</p>
|
||||
<p style="margin-top:12px">Kurze Erinnerung — die heutige Alarmprüfung <strong>(Stapel {batch.BatchNumber})</strong> schließt um <strong>8:00 Uhr morgen früh</strong>. Es dauert nur 10 Minuten!</p>
|
||||
<p style="margin:20px 0"><a href="{reviewUrl}" style="background:#3498db;color:#fff;text-decoration:none;padding:10px 24px;border-radius:6px;font-size:13px;display:inline-block">Überprüfung abschließen →</a></p>
|
||||
<p style="font-size:11px;color:#bbb">inesco energy Monitor</p>
|
||||
<p style="font-size:11px;color:#bbb">inesco Energy Monitor</p>
|
||||
</body></html>
|
||||
""";
|
||||
await SendEmailAsync(email, subject, html);
|
||||
|
|
@ -1645,7 +1645,7 @@ render();
|
|||
<table style="border-collapse:collapse;width:100%">
|
||||
{beforeAfterRows}
|
||||
</table>
|
||||
<p style="margin-top:18px;font-size:11px;color:#bbb">inesco energy Monitor</p>
|
||||
<p style="margin-top:18px;font-size:11px;color:#bbb">inesco Energy Monitor</p>
|
||||
</body></html>
|
||||
""";
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public static class DailyIngestionService
|
|||
Console.WriteLine($"[DailyIngestion] Starting ingestion for all SodioHome installations...");
|
||||
|
||||
var installations = Db.Installations
|
||||
.Where(i => (i.Product == (Int32)ProductType.SodioHome || i.Product == (Int32)ProductType.SodistorePro) && i.Device != 3) // Skip Growatt (device=3)
|
||||
.Where(i => i.Product == (Int32)ProductType.SodioHome && i.Device != 3) // Skip Growatt (device=3)
|
||||
.ToList();
|
||||
|
||||
foreach (var installation in installations)
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ public static class ReportAggregationService
|
|||
Console.WriteLine("[ReportAggregation] Running Monday weekly report generation...");
|
||||
|
||||
var installations = Db.Installations
|
||||
.Where(i => (i.Product == (Int32)ProductType.SodioHome || i.Product == (Int32)ProductType.SodistorePro) && i.Device != 3) // Skip Growatt (device=3)
|
||||
.Where(i => i.Product == (Int32)ProductType.SodioHome && i.Device != 3) // Skip Growatt (device=3)
|
||||
.ToList();
|
||||
|
||||
var generated = 0;
|
||||
|
|
@ -364,15 +364,12 @@ public static class ReportAggregationService
|
|||
var installationName = installation?.Name ?? $"Installation {installationId}";
|
||||
var monthName = new DateTime(year, month, 1).ToString("MMMM yyyy");
|
||||
|
||||
var weatherCity = !string.IsNullOrWhiteSpace(installation?.City) ? installation.City : installation?.Location;
|
||||
var weatherRegion = !string.IsNullOrWhiteSpace(installation?.Canton) ? installation.Canton : installation?.Region;
|
||||
|
||||
var aiInsight = await GenerateMonthlyAiInsightAsync(
|
||||
installationName, monthName, days.Count,
|
||||
totalPv, totalConsump, totalGridIn, totalGridOut,
|
||||
totalBattChg, totalBattDis, energySaved, savingsCHF,
|
||||
selfSufficiency, batteryEff, language,
|
||||
weatherCity, installation?.Country, weatherRegion);
|
||||
installation?.Location, installation?.Country, installation?.Region);
|
||||
|
||||
var monthlySummary = new MonthlyReportSummary
|
||||
{
|
||||
|
|
@ -594,8 +591,6 @@ public static class ReportAggregationService
|
|||
var installationName = installation?.Name
|
||||
?? $"Installation {report.InstallationId}";
|
||||
var monthName = new DateTime(report.Year, report.Month, 1).ToString("MMMM yyyy");
|
||||
var weatherCity = !string.IsNullOrWhiteSpace(installation?.City) ? installation.City : installation?.Location;
|
||||
var weatherRegion = !string.IsNullOrWhiteSpace(installation?.Canton) ? installation.Canton : installation?.Region;
|
||||
return GetOrGenerateInsightAsync("monthly", report.Id, language,
|
||||
() => GenerateMonthlyAiInsightAsync(
|
||||
installationName, monthName, report.WeekCount,
|
||||
|
|
@ -604,7 +599,7 @@ public static class ReportAggregationService
|
|||
report.TotalBatteryCharged, report.TotalBatteryDischarged,
|
||||
report.TotalEnergySaved, report.TotalSavingsCHF,
|
||||
report.SelfSufficiencyPercent, report.BatteryEfficiencyPercent, language,
|
||||
weatherCity, installation?.Country, weatherRegion));
|
||||
installation?.Location, installation?.Country, installation?.Region));
|
||||
}
|
||||
|
||||
/// <summary>Cached-or-generated AI insight for a stored YearlyReportSummary.</summary>
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ public static class ReportEmailService
|
|||
GridIn: "Netz Ein",
|
||||
GridOut: "Netz Aus",
|
||||
BattInOut: "Batt. Laden/Entl.",
|
||||
Footer: "Erstellt von <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||||
Footer: "Erstellt von <strong style=\"color:#666\">inesco Energy Monitor</strong>",
|
||||
FooterLink: "Detaillierte Berichte ansehen auf monitor.inesco.energy"
|
||||
),
|
||||
"fr" => new EmailStrings(
|
||||
|
|
@ -151,7 +151,7 @@ public static class ReportEmailService
|
|||
GridIn: "Réseau Ent.",
|
||||
GridOut: "Réseau Sor.",
|
||||
BattInOut: "Batt. Ch./Déch.",
|
||||
Footer: "Généré par <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||||
Footer: "Généré par <strong style=\"color:#666\">inesco Energy Monitor</strong>",
|
||||
FooterLink: "Consultez vos rapports détaillés sur monitor.inesco.energy"
|
||||
),
|
||||
"it" => new EmailStrings(
|
||||
|
|
@ -182,7 +182,7 @@ public static class ReportEmailService
|
|||
GridIn: "Rete Ent.",
|
||||
GridOut: "Rete Usc.",
|
||||
BattInOut: "Batt. Car./Sc.",
|
||||
Footer: "Generato da <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||||
Footer: "Generato da <strong style=\"color:#666\">inesco Energy Monitor</strong>",
|
||||
FooterLink: "Visualizza i tuoi report dettagliati su monitor.inesco.energy"
|
||||
),
|
||||
_ => new EmailStrings(
|
||||
|
|
@ -213,7 +213,7 @@ public static class ReportEmailService
|
|||
GridIn: "Grid In",
|
||||
GridOut: "Grid Out",
|
||||
BattInOut: "Batt. Ch./Dis.",
|
||||
Footer: "Generated by <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||||
Footer: "Generated by <strong style=\"color:#666\">inesco Energy Monitor</strong>",
|
||||
FooterLink: "View your detailed reports at monitor.inesco.energy"
|
||||
)
|
||||
};
|
||||
|
|
@ -340,7 +340,7 @@ public static class ReportEmailService
|
|||
<!-- Header -->
|
||||
<tr>
|
||||
<td style=""background:#2c3e50;padding:24px 30px;color:#ffffff"">
|
||||
<img src=""{LogoBase64}"" alt=""inesco energy"" style=""height:36px;margin-bottom:12px"" />
|
||||
<img src=""{LogoBase64}"" alt=""inesco Energy"" style=""height:36px;margin-bottom:12px"" />
|
||||
<div style=""font-size:20px;font-weight:bold"">{s.Title}</div>
|
||||
<div style=""font-size:14px;margin-top:6px;opacity:0.9"">{r.InstallationName}</div>
|
||||
<div style=""font-size:13px;margin-top:2px;opacity:0.7"">{r.PeriodStart} — {r.PeriodEnd}</div>
|
||||
|
|
@ -546,56 +546,56 @@ public static class ReportEmailService
|
|||
"Kennzahl", "Gesamt", "PV-Produktion", "Verbrauch", "Netzbezug", "Netzeinspeisung", "Batterie Laden / Entladen",
|
||||
"Energie gespart", "Solar + Batterie, nicht vom Netz", "Geschätzte Ersparnis", "bei 0.39 CHF/kWh",
|
||||
"Energieunabhängigkeit", "aus eigenem Solar + Batterie System", "Batterie-Eff.", "Entladung vs. Ladung",
|
||||
"Tage aggregiert", "Erstellt von <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||||
"Tage aggregiert", "Erstellt von <strong style=\"color:#666\">inesco Energy Monitor</strong>",
|
||||
"Detaillierte Berichte ansehen auf monitor.inesco.energy"),
|
||||
("de", "yearly") => new AggregatedEmailStrings(
|
||||
"Jährlicher Leistungsbericht", "Jährliche Erkenntnisse", "Jährliche Zusammenfassung", "Ihre Ersparnisse dieses Jahr",
|
||||
"Kennzahl", "Gesamt", "PV-Produktion", "Verbrauch", "Netzbezug", "Netzeinspeisung", "Batterie Laden / Entladen",
|
||||
"Energie gespart", "Solar + Batterie, nicht vom Netz", "Geschätzte Ersparnis", "bei 0.39 CHF/kWh",
|
||||
"Energieunabhängigkeit", "aus eigenem Solar + Batterie System", "Batterie-Eff.", "Entladung vs. Ladung",
|
||||
"Monate aggregiert", "Erstellt von <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||||
"Monate aggregiert", "Erstellt von <strong style=\"color:#666\">inesco Energy Monitor</strong>",
|
||||
"Detaillierte Berichte ansehen auf monitor.inesco.energy"),
|
||||
("fr", "monthly") => new AggregatedEmailStrings(
|
||||
"Rapport de performance mensuel", "Aperçus du mois", "Résumé du mois", "Vos économies ce mois",
|
||||
"Indicateur", "Total", "Production PV", "Consommation", "Import réseau", "Export réseau", "Batterie Charge / Décharge",
|
||||
"Énergie économisée", "solaire + batterie, non achetée au réseau", "Économies estimées", "à 0.39 CHF/kWh",
|
||||
"Indépendance énergétique", "de votre système solaire + batterie", "Eff. batterie", "décharge vs charge",
|
||||
"jours agrégés", "Généré par <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||||
"jours agrégés", "Généré par <strong style=\"color:#666\">inesco Energy Monitor</strong>",
|
||||
"Consultez vos rapports détaillés sur monitor.inesco.energy"),
|
||||
("fr", "yearly") => new AggregatedEmailStrings(
|
||||
"Rapport de performance annuel", "Aperçus de l'année", "Résumé de l'année", "Vos économies cette année",
|
||||
"Indicateur", "Total", "Production PV", "Consommation", "Import réseau", "Export réseau", "Batterie Charge / Décharge",
|
||||
"Énergie économisée", "solaire + batterie, non achetée au réseau", "Économies estimées", "à 0.39 CHF/kWh",
|
||||
"Indépendance énergétique", "de votre système solaire + batterie", "Eff. batterie", "décharge vs charge",
|
||||
"mois agrégés", "Généré par <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||||
"mois agrégés", "Généré par <strong style=\"color:#666\">inesco Energy Monitor</strong>",
|
||||
"Consultez vos rapports détaillés sur monitor.inesco.energy"),
|
||||
("it", "monthly") => new AggregatedEmailStrings(
|
||||
"Rapporto mensile delle prestazioni", "Approfondimenti mensili", "Riepilogo mensile", "I tuoi risparmi questo mese",
|
||||
"Metrica", "Totale", "Produzione PV", "Consumo", "Import dalla rete", "Export nella rete", "Batteria Carica / Scarica",
|
||||
"Energia risparmiata", "solare + batteria, non acquistata dalla rete", "Risparmio stimato", "a 0.39 CHF/kWh",
|
||||
"Indipendenza energetica", "dal proprio impianto solare + batteria", "Eff. batteria", "scarica vs carica",
|
||||
"giorni aggregati", "Generato da <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||||
"giorni aggregati", "Generato da <strong style=\"color:#666\">inesco Energy Monitor</strong>",
|
||||
"Visualizza i tuoi report dettagliati su monitor.inesco.energy"),
|
||||
("it", "yearly") => new AggregatedEmailStrings(
|
||||
"Rapporto annuale delle prestazioni", "Approfondimenti annuali", "Riepilogo annuale", "I tuoi risparmi quest'anno",
|
||||
"Metrica", "Totale", "Produzione PV", "Consumo", "Import dalla rete", "Export nella rete", "Batteria Carica / Scarica",
|
||||
"Energia risparmiata", "solare + batteria, non acquistata dalla rete", "Risparmio stimato", "a 0.39 CHF/kWh",
|
||||
"Indipendenza energetica", "dal proprio impianto solare + batteria", "Eff. batteria", "scarica vs carica",
|
||||
"mesi aggregati", "Generato da <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||||
"mesi aggregati", "Generato da <strong style=\"color:#666\">inesco Energy Monitor</strong>",
|
||||
"Visualizza i tuoi report dettagliati su monitor.inesco.energy"),
|
||||
(_, "monthly") => new AggregatedEmailStrings(
|
||||
"Monthly Performance Report", "Monthly Insights", "Monthly Summary", "Your Savings This Month",
|
||||
"Metric", "Total", "PV Production", "Consumption", "Grid Import", "Grid Export", "Battery Charge / Discharge",
|
||||
"Energy Saved", "solar + battery, not bought from grid", "Est. Money Saved", "at 0.39 CHF/kWh",
|
||||
"Energy Independence", "from your own solar + battery system", "Battery Eff.", "discharge vs charge",
|
||||
"days aggregated", "Generated by <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||||
"days aggregated", "Generated by <strong style=\"color:#666\">inesco Energy Monitor</strong>",
|
||||
"View your detailed reports at monitor.inesco.energy"),
|
||||
_ => new AggregatedEmailStrings(
|
||||
"Annual Performance Report", "Annual Insights", "Annual Summary", "Your Savings This Year",
|
||||
"Metric", "Total", "PV Production", "Consumption", "Grid Import", "Grid Export", "Battery Charge / Discharge",
|
||||
"Energy Saved", "solar + battery, not bought from grid", "Est. Money Saved", "at 0.39 CHF/kWh",
|
||||
"Energy Independence", "from your own solar + battery system", "Battery Eff.", "discharge vs charge",
|
||||
"months aggregated", "Generated by <strong style=\"color:#666\">inesco energy Monitor</strong>",
|
||||
"months aggregated", "Generated by <strong style=\"color:#666\">inesco Energy Monitor</strong>",
|
||||
"View your detailed reports at monitor.inesco.energy")
|
||||
};
|
||||
|
||||
|
|
@ -637,7 +637,7 @@ public static class ReportEmailService
|
|||
<!-- Header -->
|
||||
<tr>
|
||||
<td style=""background:#2c3e50;padding:24px 30px;color:#ffffff"">
|
||||
<img src=""{LogoBase64}"" alt=""inesco energy"" style=""height:36px;margin-bottom:12px"" />
|
||||
<img src=""{LogoBase64}"" alt=""inesco Energy"" style=""height:36px;margin-bottom:12px"" />
|
||||
<div style=""font-size:20px;font-weight:bold"">{dailyTitle}</div>
|
||||
<div style=""font-size:14px;margin-top:6px;opacity:0.9"">{installationName}</div>
|
||||
<div style=""font-size:13px;margin-top:2px;opacity:0.7"">{record.Date}</div>
|
||||
|
|
@ -723,7 +723,7 @@ public static class ReportEmailService
|
|||
<!-- Header -->
|
||||
<tr>
|
||||
<td style=""background:#2c3e50;padding:24px 30px;color:#ffffff"">
|
||||
<img src=""{LogoBase64}"" alt=""inesco energy"" style=""height:36px;margin-bottom:12px"" />
|
||||
<img src=""{LogoBase64}"" alt=""inesco Energy"" style=""height:36px;margin-bottom:12px"" />
|
||||
<div style=""font-size:20px;font-weight:bold"">{s.Title}</div>
|
||||
<div style=""font-size:14px;margin-top:6px;opacity:0.9"">{installationName}</div>
|
||||
<div style=""font-size:13px;margin-top:2px;opacity:0.7"">{periodStart} — {periodEnd}</div>
|
||||
|
|
|
|||
|
|
@ -179,9 +179,9 @@ public static class WeeklyReportService
|
|||
|
||||
// 4. Get installation location for weather forecast
|
||||
var installation = Db.GetInstallationById(installationId);
|
||||
var location = !string.IsNullOrWhiteSpace(installation?.City) ? installation.City : installation?.Location;
|
||||
var location = installation?.Location;
|
||||
var country = installation?.Country;
|
||||
var region = !string.IsNullOrWhiteSpace(installation?.Canton) ? installation.Canton : installation?.Region;
|
||||
var region = installation?.Region;
|
||||
Console.WriteLine($"[WeeklyReportService] Installation {installationId}: Location='{location}', Region='{region}', Country='{country}', HourlyRecords={currentHourlyData.Count}");
|
||||
|
||||
return await GenerateReportFromDataAsync(
|
||||
|
|
|
|||
|
|
@ -105,11 +105,6 @@ public static class RabbitMqManager
|
|||
monitorLink =
|
||||
$"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
|
||||
{
|
||||
monitorLink =
|
||||
|
|
@ -136,7 +131,7 @@ public static class RabbitMqManager
|
|||
Console.WriteLine("Send replace battery email to the support team for installation "+installationId);
|
||||
string recipient = "support@innov.energy";
|
||||
string subject = $"Battery Alarm from {installation.Name}: 2 or more strings broken";
|
||||
string text = $"Dear inesco energy Support Team,\n" +
|
||||
string text = $"Dear inesco Energy Support Team,\n" +
|
||||
$"\n"+
|
||||
$"Installation Name: {installation.Name}\n"+
|
||||
$"\n"+
|
||||
|
|
@ -148,7 +143,7 @@ public static class RabbitMqManager
|
|||
$"\n"+
|
||||
$"Thank you for your great support:)";
|
||||
//Disable this function now
|
||||
//Mailer.Send("inesco energy Support Team", recipient, subject, text);
|
||||
//Mailer.Send("inesco Energy Support Team", recipient, subject, text);
|
||||
}
|
||||
//Create a new error and add it to the database
|
||||
Db.HandleError(newError, installationId);
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@ public static class WebsocketManager
|
|||
(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.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.SodistorePro && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(4))
|
||||
(installationConnection.Value.Product == (int)ProductType.SodistoreGrid && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2))
|
||||
)
|
||||
{
|
||||
Console.WriteLine("Installation ID is " + installationConnection.Key);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<title>inesco energy</title>
|
||||
<title>Inesco Energy</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -34,41 +34,15 @@ function App() {
|
|||
const searchParams = new URLSearchParams(location.search);
|
||||
const username = searchParams.get('username');
|
||||
const {
|
||||
accessToSalimax,
|
||||
accessToSodiohome,
|
||||
accessToSodistore,
|
||||
accessToSodistoreGrid,
|
||||
accessToSodistorePro,
|
||||
setAccessToSalimax,
|
||||
setAccessToSalidomo,
|
||||
setAccessToSodiohome,
|
||||
setAccessToSodistore,
|
||||
setAccessToSodistoreGrid,
|
||||
setAccessToSodistorePro
|
||||
setAccessToSodistoreGrid
|
||||
} = useContext(ProductIdContext);
|
||||
|
||||
const defaultRoute = accessToSodiohome
|
||||
? routes.sodiohome_installations
|
||||
: accessToSodistorePro
|
||||
? routes.sodistorepro_installations
|
||||
: accessToSodistoreGrid
|
||||
? routes.sodistoregrid_installations
|
||||
: accessToSodistore
|
||||
? routes.sodistore_installations
|
||||
: accessToSalimax
|
||||
? routes.installations
|
||||
: routes.salidomo_installations;
|
||||
|
||||
const detectBrowserLanguage = (): string => {
|
||||
const browserLang = navigator.language?.toLowerCase() || '';
|
||||
if (browserLang.startsWith('de')) return 'de';
|
||||
if (browserLang.startsWith('fr')) return 'fr';
|
||||
if (browserLang.startsWith('it')) return 'it';
|
||||
return 'en';
|
||||
};
|
||||
|
||||
const [language, setLanguage] = useState<string>(
|
||||
() => localStorage.getItem('language') || currentUser?.language || detectBrowserLanguage()
|
||||
() => localStorage.getItem('language') || currentUser?.language || 'en'
|
||||
);
|
||||
|
||||
const onSelectLanguage = (lang: string) => {
|
||||
|
|
@ -120,19 +94,11 @@ function App() {
|
|||
const Login = Loader(lazy(() => import('src/components/login')));
|
||||
const Users = Loader(lazy(() => import('src/content/dashboards/Users')));
|
||||
|
||||
useEffect(() => {
|
||||
if (!username || token) return;
|
||||
|
||||
const loginToResetPassword = () => {
|
||||
axiosConfigWithoutToken
|
||||
.post('/Login', null, { params: { username, password: '' } })
|
||||
.then((response) => {
|
||||
if (response.data && response.data.token) {
|
||||
// Clear the username param from URL to prevent re-login loops
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.delete('username');
|
||||
url.searchParams.delete('reset');
|
||||
window.history.replaceState({}, '', url.pathname);
|
||||
|
||||
setNewToken(response.data.token);
|
||||
setUser(response.data.user);
|
||||
setAccessToSalimax(response.data.accessToSalimax);
|
||||
|
|
@ -140,11 +106,25 @@ function App() {
|
|||
setAccessToSodiohome(response.data.accessToSodioHome);
|
||||
setAccessToSodistore(response.data.accessToSodistoreMax);
|
||||
setAccessToSodistoreGrid(response.data.accessToSodistoreGrid);
|
||||
setAccessToSodistorePro(response.data.accessToSodistorePro);
|
||||
if (response.data.accessToSalimax) {
|
||||
navigate(routes.installations);
|
||||
} else if (response.data.accessToSalidomo) {
|
||||
navigate(routes.salidomo_installations);
|
||||
} else if (response.data.accessToSodistoreMax) {
|
||||
navigate(routes.sodistore_installations);
|
||||
} else if (response.data.accessToSodistoreGrid) {
|
||||
navigate(routes.sodistoregrid_installations);
|
||||
} else {
|
||||
navigate(routes.sodiohome_installations);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}, [username]);
|
||||
};
|
||||
|
||||
if (username) {
|
||||
loginToResetPassword();
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
return (
|
||||
|
|
@ -178,14 +158,8 @@ function App() {
|
|||
if (token && currentUser?.mustResetPassword) {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<IntlProvider
|
||||
messages={getTranslations()}
|
||||
locale={language}
|
||||
defaultLocale="en"
|
||||
>
|
||||
<CssBaseline />
|
||||
<SetNewPassword></SetNewPassword>
|
||||
</IntlProvider>
|
||||
<CssBaseline />
|
||||
<SetNewPassword></SetNewPassword>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
|
@ -203,11 +177,11 @@ function App() {
|
|||
<Routes>
|
||||
<Route
|
||||
path={''}
|
||||
element={<Navigate to={defaultRoute}></Navigate>}
|
||||
element={<Navigate to={routes.installations}></Navigate>}
|
||||
></Route>
|
||||
<Route
|
||||
path={'login'}
|
||||
element={<Navigate to={defaultRoute}></Navigate>}
|
||||
element={<Navigate to={routes.installations}></Navigate>}
|
||||
></Route>
|
||||
<Route
|
||||
path="/"
|
||||
|
|
@ -254,15 +228,6 @@ function App() {
|
|||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={routes.sodistorepro_installations + '*'}
|
||||
element={
|
||||
<AccessContextProvider>
|
||||
<SodioHomeInstallationTabs product={5} />
|
||||
</AccessContextProvider>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={routes.sodistoregrid_installations + '*'}
|
||||
element={
|
||||
|
|
@ -276,7 +241,7 @@ function App() {
|
|||
<Route path={routes.users + '*'} element={<Users />} />
|
||||
<Route
|
||||
path={'*'}
|
||||
element={<Navigate to={defaultRoute}></Navigate>}
|
||||
element={<Navigate to={routes.installations}></Navigate>}
|
||||
></Route>
|
||||
<Route path="ResetPassword" element={<ResetPassword />}></Route>
|
||||
</Route>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
"sodistore_installations": "/sodistore_installations/",
|
||||
"sodiohome_installations": "/sodiohome_installations/",
|
||||
"sodistoregrid_installations": "/sodistoregrid_installations/",
|
||||
"sodistorepro_installations": "/sodistorepro_installations/",
|
||||
"installation": "installation/",
|
||||
"login": "/login/",
|
||||
"forgotPassword": "/forgotPassword/",
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Button,
|
||||
Typography,
|
||||
Divider,
|
||||
Box
|
||||
} from '@mui/material';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
export const CURRENT_TERMS_VERSION = 2;
|
||||
|
||||
interface AcknowledgementDialogProps {
|
||||
open: boolean;
|
||||
onAcknowledge: () => void;
|
||||
}
|
||||
|
||||
const AcknowledgementDialog: React.FC<AcknowledgementDialogProps> = ({
|
||||
open,
|
||||
onAcknowledge
|
||||
}) => {
|
||||
return (
|
||||
<Dialog open={open} maxWidth="sm" fullWidth>
|
||||
<DialogTitle>
|
||||
<FormattedMessage
|
||||
id="terms_dialog_title"
|
||||
defaultMessage="Welcome to inesco energy"
|
||||
/>
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<Box mb={2}>
|
||||
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
|
||||
<FormattedMessage
|
||||
id="terms_data_heading"
|
||||
defaultMessage="Your Data"
|
||||
/>
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<FormattedMessage
|
||||
id="terms_data_body"
|
||||
defaultMessage="Your installation data is securely stored in Switzerland. We do not share your data with third parties."
|
||||
/>
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box my={2}>
|
||||
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
|
||||
<FormattedMessage
|
||||
id="terms_ai_heading"
|
||||
defaultMessage="AI-Powered Insights"
|
||||
/>
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<FormattedMessage
|
||||
id="terms_ai_body"
|
||||
defaultMessage="We use an AI service hosted in the EU to provide diagnostics and insights for your installations. AI-generated results are recommendations and should be verified by qualified personnel."
|
||||
/>
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box mt={2}>
|
||||
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
|
||||
<FormattedMessage
|
||||
id="terms_cookies_heading"
|
||||
defaultMessage="Browser Storage"
|
||||
/>
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<FormattedMessage
|
||||
id="terms_cookies_body"
|
||||
defaultMessage="This platform stores login and preference settings in your browser to keep you signed in and remember your language choice."
|
||||
/>
|
||||
</Typography>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant="contained" onClick={onAcknowledge}>
|
||||
<FormattedMessage
|
||||
id="terms_acknowledge_button"
|
||||
defaultMessage="I understand"
|
||||
/>
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default AcknowledgementDialog;
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Button,
|
||||
Typography,
|
||||
Divider,
|
||||
Box
|
||||
} from '@mui/material';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
interface DataPrivacyDialogProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const DataPrivacyDialog: React.FC<DataPrivacyDialogProps> = ({
|
||||
open,
|
||||
onClose
|
||||
}) => {
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose} maxWidth="sm" fullWidth>
|
||||
<DialogTitle>
|
||||
<FormattedMessage
|
||||
id="privacy_dialog_title"
|
||||
defaultMessage="Data & Privacy"
|
||||
/>
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<Box mb={2}>
|
||||
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
|
||||
<FormattedMessage
|
||||
id="privacy_data_heading"
|
||||
defaultMessage="Where is my data stored?"
|
||||
/>
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<FormattedMessage
|
||||
id="privacy_data_body"
|
||||
defaultMessage="Your installation data is stored on servers in Switzerland. Only authorized inesco energy personnel can access your data for support purposes."
|
||||
/>
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box my={2}>
|
||||
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
|
||||
<FormattedMessage
|
||||
id="privacy_ai_heading"
|
||||
defaultMessage="How is AI used?"
|
||||
/>
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<FormattedMessage
|
||||
id="privacy_ai_body"
|
||||
defaultMessage="We use an AI service hosted in the European Union to analyze your installation data and provide diagnostic insights. The AI processes technical data such as battery readings and error codes. AI recommendations should always be verified by qualified personnel."
|
||||
/>
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box my={2}>
|
||||
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
|
||||
<FormattedMessage
|
||||
id="privacy_browser_heading"
|
||||
defaultMessage="What does my browser store?"
|
||||
/>
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<FormattedMessage
|
||||
id="privacy_browser_body"
|
||||
defaultMessage="Your browser stores your login session to keep you signed in, and your language and theme preferences. No tracking or advertising cookies are used."
|
||||
/>
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box mt={2}>
|
||||
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
|
||||
<FormattedMessage
|
||||
id="privacy_access_heading"
|
||||
defaultMessage="Who has access to my data?"
|
||||
/>
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
<FormattedMessage
|
||||
id="privacy_access_body"
|
||||
defaultMessage="Your data is not shared with third parties. It is used solely to operate the platform and provide you with insights about your installations."
|
||||
/>
|
||||
</Typography>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant="contained" onClick={onClose}>
|
||||
<FormattedMessage
|
||||
id="privacy_close_button"
|
||||
defaultMessage="Close"
|
||||
/>
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default DataPrivacyDialog;
|
||||
|
|
@ -18,7 +18,7 @@ function Footer() {
|
|||
>
|
||||
<Box>
|
||||
<Typography variant="subtitle1">
|
||||
© 2025 - inesco energy AG
|
||||
© 2025 - Inesco Energy AG
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography
|
||||
|
|
@ -33,7 +33,7 @@ function Footer() {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
inesco energy AG
|
||||
Inesco Energy AG
|
||||
</Link>
|
||||
</Typography>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ function Logo() {
|
|||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<TooltipWrapper title="inesco energy" arrow>
|
||||
<TooltipWrapper title="inesco Energy" arrow>
|
||||
<LogoWrapper to="/overview">
|
||||
<Badge
|
||||
sx={{
|
||||
|
|
|
|||
|
|
@ -42,8 +42,7 @@ function Login() {
|
|||
setAccessToSalidomo,
|
||||
setAccessToSodiohome,
|
||||
setAccessToSodistore,
|
||||
setAccessToSodistoreGrid,
|
||||
setAccessToSodistorePro
|
||||
setAccessToSodistoreGrid
|
||||
} = useContext(ProductIdContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
|
@ -87,19 +86,16 @@ function Login() {
|
|||
setAccessToSodiohome(response.data.accessToSodioHome);
|
||||
setAccessToSodistore(response.data.accessToSodistoreMax);
|
||||
setAccessToSodistoreGrid(response.data.accessToSodistoreGrid);
|
||||
setAccessToSodistorePro(response.data.accessToSodistorePro);
|
||||
if (response.data.accessToSodioHome) {
|
||||
navigate(routes.sodiohome_installations);
|
||||
} else if (response.data.accessToSodistorePro) {
|
||||
navigate(routes.sodistorepro_installations);
|
||||
} else if (response.data.accessToSodistoreGrid) {
|
||||
navigate(routes.sodistoregrid_installations);
|
||||
if (response.data.accessToSalimax) {
|
||||
navigate(routes.installations);
|
||||
} else if (response.data.accessToSalidomo) {
|
||||
navigate(routes.salidomo_installations);
|
||||
} else if (response.data.accessToSodistoreMax) {
|
||||
navigate(routes.sodistore_installations);
|
||||
} else if (response.data.accessToSalimax) {
|
||||
navigate(routes.installations);
|
||||
} else if (response.data.accessToSodistoreGrid) {
|
||||
navigate(routes.sodistoregrid_installations);
|
||||
} else {
|
||||
navigate(routes.salidomo_installations);
|
||||
navigate(routes.sodiohome_installations);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -240,13 +240,12 @@ function BatteryViewSodioHome(props: BatteryViewSodioHomeProps) {
|
|||
align="center"
|
||||
sx={{ fontWeight: 'bold' }}
|
||||
>
|
||||
{/* Detailed battery view commented out */}
|
||||
{/*<Link
|
||||
<Link
|
||||
style={{ color: 'black' }}
|
||||
to={routes.detailed_view + BatteryId}
|
||||
>*/}
|
||||
>
|
||||
{'Battery Cluster ' + BatteryId}
|
||||
{/*</Link>*/}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{
|
||||
|
|
|
|||
|
|
@ -151,25 +151,22 @@ function MainStatsSodioHome(props: MainStatsSodioHomeProps) {
|
|||
pathsToSearch.push('Node' + i);
|
||||
}
|
||||
|
||||
const total = pathsToSearch.length;
|
||||
let i = 0;
|
||||
pathsToSearch.forEach((devicePath) => {
|
||||
if (
|
||||
Object.hasOwnProperty.call(chartData[category].data, devicePath) &&
|
||||
chartData[category].data[devicePath].data.length != 0
|
||||
) {
|
||||
// Spread color indices evenly across the palette for better contrast
|
||||
const colorIndex = total <= 1 ? 0 : Math.round(i * 9 / (total - 1));
|
||||
series.push({
|
||||
...chartData[category].data[devicePath],
|
||||
color:
|
||||
color === 'blue'
|
||||
? blueColors[colorIndex]
|
||||
? blueColors[i]
|
||||
: color === 'red'
|
||||
? redColors[colorIndex]
|
||||
? redColors[i]
|
||||
: color === 'green'
|
||||
? greenColors[colorIndex]
|
||||
: orangeColors[colorIndex]
|
||||
? greenColors[i]
|
||||
: orangeColors[i]
|
||||
});
|
||||
}
|
||||
i++;
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ function Information(props: InformationProps) {
|
|||
|
||||
const canEdit = currentUser.userType == UserType.admin;
|
||||
const isPartner = currentUser.userType == UserType.partner;
|
||||
const isSodistore = formValues.product === 3 || formValues.product === 4 || formValues.product === 5;
|
||||
const isSodistore = formValues.product === 3 || formValues.product === 4;
|
||||
|
||||
const [networkProviders, setNetworkProviders] = useState<string[]>([]);
|
||||
const [loadingProviders, setLoadingProviders] = useState(false);
|
||||
|
|
@ -426,18 +426,12 @@ function Information(props: InformationProps) {
|
|||
label="S3 Bucket Name"
|
||||
name="s3bucketname"
|
||||
value={
|
||||
formValues.product === 0 || formValues.product === 3
|
||||
formValues.product === 0 || formValues.product == 3
|
||||
? formValues.s3BucketId +
|
||||
'-3e5b3069-214a-43ee-8d85-57d72000c19d'
|
||||
: formValues.product === 4
|
||||
: formValues.product == 4
|
||||
? formValues.s3BucketId +
|
||||
'-5109c126-e141-43ab-8658-f3c44c838ae8'
|
||||
: formValues.product === 5
|
||||
? formValues.s3BucketId +
|
||||
'-325c9373-9025-4a8d-bf5a-f9eedf1f155c'
|
||||
: formValues.product === 1
|
||||
? formValues.s3BucketId +
|
||||
'-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e'
|
||||
: formValues.s3BucketId +
|
||||
'-e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa'
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,128 +0,0 @@
|
|||
export const SODIOHOME_DEVICE_TYPES = [
|
||||
{ id: 3, name: 'Growatt' },
|
||||
{ id: 4, name: 'inesco 12K - WR Hybrid' }
|
||||
] as const;
|
||||
|
||||
export const getDeviceTypeName = (deviceId: number): string =>
|
||||
SODIOHOME_DEVICE_TYPES.find(d => d.id === deviceId)?.name ?? '';
|
||||
|
||||
// [inverter][cluster] = batteryCount
|
||||
export type PresetConfig = number[][];
|
||||
|
||||
// 3D array: [inverter][cluster][batteryIndex] = serialNumber
|
||||
export type BatterySnTree = string[][][];
|
||||
|
||||
export const INSTALLATION_PRESETS: Record<string, PresetConfig> = {
|
||||
'sodistore home 9': [[1, 1]],
|
||||
'sodistore home 18': [[2, 2]],
|
||||
'sodistore home 27': [[2, 2], [1, 1]],
|
||||
'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 => {
|
||||
return preset.map((inv) =>
|
||||
inv.map((batteryCount) => Array.from({ length: batteryCount }, () => ''))
|
||||
);
|
||||
};
|
||||
|
||||
export const parseBatterySnTree = (
|
||||
raw: string,
|
||||
preset: PresetConfig
|
||||
): BatterySnTree => {
|
||||
if (!raw || raw.trim() === '') {
|
||||
return buildEmptyTree(preset);
|
||||
}
|
||||
|
||||
const isStructured = raw.includes('/') || raw.includes('|');
|
||||
|
||||
if (isStructured) {
|
||||
const inverters = raw.split('/');
|
||||
return preset.map((invPreset, invIdx) => {
|
||||
const clusterStr = inverters[invIdx] || '';
|
||||
const clusters = clusterStr ? clusterStr.split('|') : [];
|
||||
return invPreset.map((batteryCount, clIdx) => {
|
||||
const batteries = clusters[clIdx]
|
||||
? clusters[clIdx].split(',').map((s) => s.trim())
|
||||
: [];
|
||||
return Array.from({ length: batteryCount }, (_, i) => batteries[i] || '');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Legacy flat format: distribute by preset layout
|
||||
const allSns = raw
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter((s) => s !== '');
|
||||
let idx = 0;
|
||||
return preset.map((inv) =>
|
||||
inv.map((batteryCount) =>
|
||||
Array.from({ length: batteryCount }, () => allSns[idx++] || '')
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export const serializeBatterySnTree = (tree: BatterySnTree): string => {
|
||||
return tree
|
||||
.map((inv) => inv.map((cluster) => cluster.join(',')).join('|'))
|
||||
.join('/');
|
||||
};
|
||||
|
||||
export const remapTree = (
|
||||
oldTree: BatterySnTree,
|
||||
newPreset: PresetConfig
|
||||
): BatterySnTree => {
|
||||
return newPreset.map((inv, invIdx) =>
|
||||
inv.map((batteryCount, clIdx) =>
|
||||
Array.from(
|
||||
{ length: batteryCount },
|
||||
(_, batIdx) => oldTree[invIdx]?.[clIdx]?.[batIdx] || ''
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export const computeFlatValues = (
|
||||
preset: PresetConfig,
|
||||
tree: BatterySnTree
|
||||
) => {
|
||||
const totalBatteries = preset.flat().reduce((a, b) => a + b, 0);
|
||||
const totalClusters = preset.reduce((sum, inv) => sum + inv.length, 0);
|
||||
return {
|
||||
batteryNumber: totalBatteries,
|
||||
batteryClusterNumber: totalClusters,
|
||||
batterySerialNumbers: serializeBatterySnTree(tree),
|
||||
};
|
||||
};
|
||||
|
||||
export const wouldLoseData = (
|
||||
oldTree: BatterySnTree,
|
||||
newPreset: PresetConfig
|
||||
): boolean => {
|
||||
for (let invIdx = 0; invIdx < oldTree.length; invIdx++) {
|
||||
for (let clIdx = 0; clIdx < (oldTree[invIdx] || []).length; clIdx++) {
|
||||
for (
|
||||
let batIdx = 0;
|
||||
batIdx < (oldTree[invIdx][clIdx] || []).length;
|
||||
batIdx++
|
||||
) {
|
||||
const sn = oldTree[invIdx][clIdx][batIdx];
|
||||
if (sn && sn.trim() !== '') {
|
||||
if (invIdx >= newPreset.length) return true;
|
||||
if (clIdx >= (newPreset[invIdx] || []).length) return true;
|
||||
const newBatCount = newPreset[invIdx]?.[clIdx] ?? 0;
|
||||
if (batIdx >= newBatCount) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
|
@ -131,7 +131,7 @@ export const fetchAggregatedDataJson = (
|
|||
} else if (r.status === 200) {
|
||||
const jsontext = await r.text();
|
||||
|
||||
if (product === 2 || product === 5) {
|
||||
if (product === 2) {
|
||||
return parseSinexcelAggregatedData(jsontext);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -458,10 +458,6 @@ function InstallationTabs(props: InstallationTabsProps) {
|
|||
<Navigate
|
||||
to={routes.sodistoregrid_installations + routes.list}
|
||||
/>
|
||||
) : props.product === 5 ? (
|
||||
<Navigate
|
||||
to={routes.sodistorepro_installations + routes.list}
|
||||
/>
|
||||
) : (
|
||||
<Navigate
|
||||
to={routes.sodistore_installations + routes.list}
|
||||
|
|
|
|||
|
|
@ -248,9 +248,9 @@ function Log(props: LogProps) {
|
|||
if (source === 'KnowledgeBase')
|
||||
return <Chip label="Knowledge Base" size="small" sx={{ bgcolor: '#1976d2', color: '#fff', fontWeight: 'bold' }} />;
|
||||
if (source === 'MistralAI')
|
||||
return <Chip label="AI" size="small" sx={{ bgcolor: '#7b1fa2', color: '#fff', fontWeight: 'bold' }} />;
|
||||
return <Chip label="Mistral AI" size="small" sx={{ bgcolor: '#7b1fa2', color: '#fff', fontWeight: 'bold' }} />;
|
||||
if (source === 'MistralFailed')
|
||||
return <Chip label="AI failed" size="small" color="error" />;
|
||||
return <Chip label="Mistral failed" size="small" color="error" />;
|
||||
return <Chip label="Not available" size="small" color="default" />;
|
||||
};
|
||||
|
||||
|
|
@ -288,7 +288,7 @@ function Log(props: LogProps) {
|
|||
onChange={e => { setDemoAlarm(e.target.value); setDemoResult(null); }}
|
||||
sx={{ minWidth: 260 }}
|
||||
>
|
||||
<ListSubheader>inesco 12K - WR Hybrid</ListSubheader>
|
||||
<ListSubheader>Sinexcel</ListSubheader>
|
||||
{DEMO_ALARMS.sinexcel.map(a => (
|
||||
<MenuItem key={a} value={a}>{a}</MenuItem>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -107,15 +107,13 @@ function UserAccess(props: UserAccessProps) {
|
|||
|
||||
const fetchAvailableInstallations = useCallback(async () => {
|
||||
try {
|
||||
const [res0, res1, res2, res3, res4, res5] = await Promise.all([
|
||||
const [res0, res1, res2, res3] = await Promise.all([
|
||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=0`),
|
||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=1`),
|
||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=2`),
|
||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=3`),
|
||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=4`),
|
||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=5`)
|
||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=3`)
|
||||
]);
|
||||
setAvailableInstallations([...res0.data, ...res1.data, ...res2.data, ...res3.data, ...res4.data, ...res5.data]);
|
||||
setAvailableInstallations([...res0.data, ...res1.data, ...res2.data, ...res3.data]);
|
||||
} catch (err) {
|
||||
if (err.response && err.response.status === 401) removeToken();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ interface OverviewProps {
|
|||
s3Credentials: I_S3Credentials;
|
||||
id: number;
|
||||
device?: number;
|
||||
product?: number;
|
||||
connected?: boolean;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
|
@ -566,7 +565,7 @@ function Overview(props: OverviewProps) {
|
|||
>
|
||||
<FormattedMessage id="24_hours" defaultMessage="24-hours" />
|
||||
</Button>
|
||||
{props.device !== 3 && props.product !== 2 && (
|
||||
{props.device !== 3 && (
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleWeekData}
|
||||
|
|
@ -818,7 +817,7 @@ function Overview(props: OverviewProps) {
|
|||
type: 'bar',
|
||||
color: '#ff9900'
|
||||
},
|
||||
...((product !== 2 && product !== 5) ? [{
|
||||
...(product !== 2 ? [{
|
||||
name: 'Net Energy',
|
||||
color: '#e65100',
|
||||
type: 'line',
|
||||
|
|
@ -841,7 +840,7 @@ function Overview(props: OverviewProps) {
|
|||
alignItems="stretch"
|
||||
spacing={3}
|
||||
>
|
||||
{!(aggregatedData && (product === 2 || product === 5)) && (
|
||||
{!(aggregatedData && product === 2) && (
|
||||
<Grid item md={6} xs={12}>
|
||||
<Card
|
||||
sx={{
|
||||
|
|
@ -934,7 +933,7 @@ function Overview(props: OverviewProps) {
|
|||
</Card>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item md={(aggregatedData && (product === 2 || product === 5)) ? 12 : 6} xs={12}>
|
||||
<Grid item md={(aggregatedData && product === 2) ? 12 : 6} xs={12}>
|
||||
<Card
|
||||
sx={{
|
||||
overflow: 'visible',
|
||||
|
|
@ -1002,14 +1001,14 @@ function Overview(props: OverviewProps) {
|
|||
<ReactApexChart
|
||||
options={{
|
||||
...getChartOptions(
|
||||
(product === 2 || product === 5)
|
||||
product === 2
|
||||
? aggregatedDataArray[aggregatedChartState]
|
||||
.chartOverview.dcPowerWithoutHeating
|
||||
: aggregatedDataArray[aggregatedChartState]
|
||||
.chartOverview.dcPower,
|
||||
'weekly',
|
||||
aggregatedDataArray[aggregatedChartState].datelist,
|
||||
(product === 2 || product === 5)
|
||||
product === 2
|
||||
)
|
||||
}}
|
||||
series={[
|
||||
|
|
@ -1018,7 +1017,7 @@ function Overview(props: OverviewProps) {
|
|||
.chartData.dcChargingPower,
|
||||
color: '#008FFB'
|
||||
},
|
||||
...((product !== 2 && product !== 5) ? [{
|
||||
...(product !== 2 ? [{
|
||||
...aggregatedDataArray[aggregatedChartState]
|
||||
.chartData.heatingPower,
|
||||
color: '#ff9900'
|
||||
|
|
@ -1074,7 +1073,7 @@ function Overview(props: OverviewProps) {
|
|||
alignItems="stretch"
|
||||
spacing={3}
|
||||
>
|
||||
{(product !== 2 && product !== 5) && (
|
||||
{product !== 2 && (
|
||||
<Grid item md={6} xs={12}>
|
||||
<Card
|
||||
sx={{
|
||||
|
|
@ -1137,7 +1136,7 @@ function Overview(props: OverviewProps) {
|
|||
</Card>
|
||||
</Grid>
|
||||
)}
|
||||
{(product !== 2 && product !== 5) && (
|
||||
{product !== 2 && (
|
||||
<Grid item md={6} xs={12}>
|
||||
<Card
|
||||
sx={{
|
||||
|
|
@ -1393,7 +1392,7 @@ function Overview(props: OverviewProps) {
|
|||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{aggregatedData && (product === 2 || product === 5) && (
|
||||
{aggregatedData && product === 2 && (
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
|
|
@ -1458,7 +1457,7 @@ function Overview(props: OverviewProps) {
|
|||
alignItems="stretch"
|
||||
spacing={3}
|
||||
>
|
||||
<Grid item md={(product === 2 || product === 5) ? 12 : 6} xs={12}>
|
||||
<Grid item md={product === 2 ? 12 : 6} xs={12}>
|
||||
<Card
|
||||
sx={{
|
||||
overflow: 'visible',
|
||||
|
|
@ -1519,7 +1518,7 @@ function Overview(props: OverviewProps) {
|
|||
/>
|
||||
</Card>
|
||||
</Grid>
|
||||
{(product !== 2 && product !== 5) && (
|
||||
{product !== 2 && (
|
||||
<Grid item md={6} xs={12}>
|
||||
<Card
|
||||
sx={{
|
||||
|
|
|
|||
|
|
@ -19,18 +19,15 @@ import { useLocation, useNavigate } from 'react-router-dom';
|
|||
import routes from '../../../Resources/routes.json';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import BuildIcon from '@mui/icons-material/Build';
|
||||
import { getDeviceTypeName } from '../Information/installationSetupUtils';
|
||||
|
||||
interface FlatInstallationViewProps {
|
||||
installations: I_Installation[];
|
||||
product?: number;
|
||||
}
|
||||
|
||||
const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||
const navigate = useNavigate();
|
||||
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
|
||||
const currentLocation = useLocation();
|
||||
const baseRoute = props.product === 5 ? routes.sodistorepro_installations : routes.sodiohome_installations;
|
||||
//
|
||||
const sortedInstallations = [...props.installations].sort((a, b) => {
|
||||
// Compare the status field of each installation and sort them based on the status.
|
||||
|
|
@ -53,7 +50,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
setSelectedInstallation(-1);
|
||||
|
||||
navigate(
|
||||
baseRoute +
|
||||
routes.sodiohome_installations +
|
||||
routes.list +
|
||||
routes.installation +
|
||||
`${installationID}` +
|
||||
|
|
@ -84,9 +81,9 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
sx={{
|
||||
display:
|
||||
currentLocation.pathname ===
|
||||
baseRoute + 'list' ||
|
||||
routes.sodiohome_installations + 'list' ||
|
||||
currentLocation.pathname ===
|
||||
baseRoute + routes.list
|
||||
routes.sodiohome_installations + routes.list
|
||||
? 'block'
|
||||
: 'none'
|
||||
}}
|
||||
|
|
@ -99,14 +96,14 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
<TableCell>
|
||||
<FormattedMessage id="name" defaultMessage="Name" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<FormattedMessage id="location" defaultMessage="Location" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<FormattedMessage id="installationSN" defaultMessage="Installation SN" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<FormattedMessage id="DeviceType" defaultMessage="Device Type" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<FormattedMessage id="canton" defaultMessage="Canton" />
|
||||
<FormattedMessage id="region" defaultMessage="Region" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<FormattedMessage id="country" defaultMessage="Country" />
|
||||
|
|
@ -149,6 +146,19 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Typography
|
||||
variant="body2"
|
||||
fontWeight="bold"
|
||||
color="text.primary"
|
||||
gutterBottom
|
||||
noWrap
|
||||
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||
>
|
||||
{installation.location}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Typography
|
||||
variant="body2"
|
||||
|
|
@ -171,20 +181,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
noWrap
|
||||
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||
>
|
||||
{getDeviceTypeName(installation.device)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Typography
|
||||
variant="body2"
|
||||
fontWeight="bold"
|
||||
color="text.primary"
|
||||
gutterBottom
|
||||
noWrap
|
||||
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||
>
|
||||
{installation.canton || ''}
|
||||
{installation.region}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
|
||||
|
|
|
|||
|
|
@ -50,9 +50,7 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
|||
const s3Bucket =
|
||||
props.current_installation.s3BucketId.toString() +
|
||||
'-' +
|
||||
(props.current_installation.product === 5
|
||||
? '325c9373-9025-4a8d-bf5a-f9eedf1f155c'
|
||||
: 'e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa');
|
||||
'e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa';
|
||||
|
||||
const context = useContext(UserContext);
|
||||
const { currentUser } = context;
|
||||
|
|
@ -605,7 +603,6 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
|||
s3Credentials={s3Credentials}
|
||||
id={props.current_installation.id}
|
||||
device={props.current_installation.device}
|
||||
product={props.current_installation.product}
|
||||
connected={connected}
|
||||
loading={loading}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -10,14 +10,12 @@ import SodioHomeInstallation from './Installation';
|
|||
|
||||
interface installationSearchProps {
|
||||
installations: I_Installation[];
|
||||
product?: number;
|
||||
}
|
||||
|
||||
function InstallationSearch(props: installationSearchProps) {
|
||||
const intl = useIntl();
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const currentLocation = useLocation();
|
||||
const baseRoute = props.product === 5 ? routes.sodistorepro_installations : routes.sodiohome_installations;
|
||||
// const [filteredData, setFilteredData] = useState(props.installations);
|
||||
|
||||
const indexedData = useMemo(() => {
|
||||
|
|
@ -48,9 +46,9 @@ function InstallationSearch(props: installationSearchProps) {
|
|||
sx={{
|
||||
display:
|
||||
currentLocation.pathname ===
|
||||
baseRoute + 'list' ||
|
||||
routes.sodiohome_installations + 'list' ||
|
||||
currentLocation.pathname ===
|
||||
baseRoute + routes.list
|
||||
routes.sodiohome_installations + routes.list
|
||||
? 'block'
|
||||
: 'none'
|
||||
}}
|
||||
|
|
@ -81,7 +79,7 @@ function InstallationSearch(props: installationSearchProps) {
|
|||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<FlatInstallationView installations={filteredData} product={props.product} />
|
||||
<FlatInstallationView installations={filteredData} />
|
||||
<Routes>
|
||||
{filteredData.map((installation) => {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -17,32 +17,29 @@ import { Close as CloseIcon } from '@mui/icons-material';
|
|||
import { I_Installation } from 'src/interfaces/InstallationTypes';
|
||||
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { INSTALLATION_PRESETS, SODIOHOME_DEVICE_TYPES } from '../Information/installationSetupUtils';
|
||||
|
||||
interface SodistorehomeInstallationFormPros {
|
||||
cancel: () => void;
|
||||
submit: () => void;
|
||||
parentid: number;
|
||||
product?: number;
|
||||
}
|
||||
|
||||
function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros) {
|
||||
const theme = useTheme();
|
||||
const [open, setOpen] = useState(true);
|
||||
const isSodistorePro = props.product === 5;
|
||||
const [formValues, setFormValues] = useState<Partial<I_Installation>>({
|
||||
name: '',
|
||||
region: '',
|
||||
location: '',
|
||||
country: '',
|
||||
vpnIp: '',
|
||||
installationModel: '',
|
||||
externalEms: 'No',
|
||||
...(isSodistorePro ? { device: 4 } : {}),
|
||||
});
|
||||
const [inverterCount, setInverterCount] = useState('');
|
||||
const requiredFields = ['name', 'vpnIp', ...(isSodistorePro ? [] : ['installationModel'])];
|
||||
const requiredFields = ['name', 'location', 'country', 'vpnIp'];
|
||||
|
||||
const DeviceTypes = isSodistorePro
|
||||
? [{ id: 4, name: 'inesco 12K - WR Hybrid' }]
|
||||
: SODIOHOME_DEVICE_TYPES;
|
||||
const DeviceTypes = [
|
||||
{ id: 3, name: 'Growatt' },
|
||||
{ id: 4, name: 'Sinexcel' }
|
||||
];
|
||||
const installationContext = useContext(InstallationsContext);
|
||||
const { createInstallation, loading, setLoading, error, setError } =
|
||||
installationContext;
|
||||
|
|
@ -58,10 +55,7 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
|||
const handleSubmit = async (e) => {
|
||||
setLoading(true);
|
||||
formValues.parentId = props.parentid;
|
||||
formValues.product = props.product ?? 2;
|
||||
if (isSodistorePro) {
|
||||
formValues.installationModel = inverterCount;
|
||||
}
|
||||
formValues.product = 2;
|
||||
const responseData = await createInstallation(formValues);
|
||||
props.submit();
|
||||
};
|
||||
|
|
@ -75,9 +69,6 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (isSodistorePro && (!inverterCount || parseInt(inverterCount, 10) < 1)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
|
@ -133,6 +124,42 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
|||
error={formValues.name === ''}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TextField
|
||||
label={<FormattedMessage id="region" defaultMessage="Region" />}
|
||||
name="region"
|
||||
value={formValues.region}
|
||||
onChange={handleChange}
|
||||
required
|
||||
error={formValues.region === ''}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TextField
|
||||
label={
|
||||
<FormattedMessage id="location" defaultMessage="Location" />
|
||||
}
|
||||
name="location"
|
||||
value={formValues.location}
|
||||
onChange={handleChange}
|
||||
required
|
||||
error={formValues.location === ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
label={
|
||||
<FormattedMessage id="country" defaultMessage="Country" />
|
||||
}
|
||||
name="country"
|
||||
value={formValues.country}
|
||||
onChange={handleChange}
|
||||
required
|
||||
error={formValues.country === ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
label={<FormattedMessage id="VpnIp" defaultMessage="VpnIp" />}
|
||||
|
|
@ -144,67 +171,6 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
|||
/>
|
||||
</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>
|
||||
<FormControl
|
||||
fullWidth
|
||||
required
|
||||
error={formValues.installationModel === ''}
|
||||
sx={{
|
||||
marginTop: 1,
|
||||
marginBottom: 1,
|
||||
width: 390
|
||||
}}
|
||||
>
|
||||
<InputLabel
|
||||
sx={{
|
||||
fontSize: 14,
|
||||
backgroundColor: 'white'
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="installationModel"
|
||||
defaultMessage="Installation Model"
|
||||
/>
|
||||
</InputLabel>
|
||||
<Select
|
||||
name="installationModel"
|
||||
value={formValues.installationModel || ''}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{Object.keys(INSTALLATION_PRESETS).map((name) => (
|
||||
<MenuItem key={name} value={name}>
|
||||
{name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isSodistorePro && (
|
||||
<div>
|
||||
<FormControl
|
||||
fullWidth
|
||||
|
|
@ -238,7 +204,6 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
|
|||
</Select>
|
||||
</FormControl>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ import TreeView from '../Tree/treeView';
|
|||
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
|
||||
import { UserType } from '../../../interfaces/UserTypes';
|
||||
import SodioHomeInstallation from './Installation';
|
||||
import AcknowledgementDialog, { CURRENT_TERMS_VERSION } from '../../../components/AcknowledgementDialog';
|
||||
import axiosConfig from '../../../Resources/axiosConfig';
|
||||
|
||||
interface SodioHomeInstallationTabsProps {
|
||||
product: number;
|
||||
|
|
@ -23,25 +21,7 @@ interface SodioHomeInstallationTabsProps {
|
|||
function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
||||
const location = useLocation();
|
||||
const context = useContext(UserContext);
|
||||
const { currentUser, setUser } = context;
|
||||
|
||||
const showTermsDialog =
|
||||
currentUser?.acknowledgedTermsVersion == null ||
|
||||
currentUser.acknowledgedTermsVersion < CURRENT_TERMS_VERSION;
|
||||
|
||||
const handleAcknowledgeTerms = () => {
|
||||
axiosConfig
|
||||
.put('/AcknowledgeTerms', undefined, {
|
||||
params: { version: CURRENT_TERMS_VERSION }
|
||||
})
|
||||
.then(() => {
|
||||
const updatedUser = {
|
||||
...currentUser,
|
||||
acknowledgedTermsVersion: CURRENT_TERMS_VERSION
|
||||
};
|
||||
setUser(updatedUser);
|
||||
});
|
||||
};
|
||||
const { currentUser } = context;
|
||||
const tabList = [
|
||||
'live',
|
||||
'overview',
|
||||
|
|
@ -60,15 +40,12 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
|||
useState<boolean>(false);
|
||||
const {
|
||||
sodiohomeInstallations,
|
||||
sodistoreProInstallations,
|
||||
fetchAllInstallations,
|
||||
socket,
|
||||
openSocket,
|
||||
closeSocket
|
||||
} = useContext(InstallationsContext);
|
||||
const { product, setProduct } = useContext(ProductIdContext);
|
||||
const baseRoute = props.product === 5 ? routes.sodistorepro_installations : routes.sodiohome_installations;
|
||||
const installations = props.product === 5 ? sodistoreProInstallations : sodiohomeInstallations;
|
||||
|
||||
useEffect(() => {
|
||||
let path = location.pathname.split('/');
|
||||
|
|
@ -260,11 +237,11 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
|||
!location.pathname.includes('folder');
|
||||
|
||||
// Determine if current installation is Growatt (device=3) to hide report tab
|
||||
const currentInstallation = installations.find((i) =>
|
||||
const currentInstallation = sodiohomeInstallations.find((i) =>
|
||||
location.pathname.includes(`/${i.id}/`)
|
||||
);
|
||||
const isGrowatt = currentInstallation?.device === 3
|
||||
|| (installations.length === 1 && installations[0].device === 3);
|
||||
|| (sodiohomeInstallations.length === 1 && sodiohomeInstallations[0].device === 3);
|
||||
|
||||
const tabs = inInstallationView && currentUser.userType == UserType.admin
|
||||
? [
|
||||
|
|
@ -436,12 +413,8 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
|||
}
|
||||
];
|
||||
|
||||
return installations.length > 1 ? (
|
||||
return sodiohomeInstallations.length > 1 ? (
|
||||
<>
|
||||
<AcknowledgementDialog
|
||||
open={showTermsDialog}
|
||||
onAcknowledge={handleAcknowledgeTerms}
|
||||
/>
|
||||
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
|
||||
<TabsContainerWrapper>
|
||||
<Tabs
|
||||
|
|
@ -486,8 +459,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
|||
<Grid item xs={12}>
|
||||
<Box p={4}>
|
||||
<InstallationSearch
|
||||
installations={installations}
|
||||
product={props.product}
|
||||
installations={sodiohomeInstallations}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
|
@ -500,7 +472,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
|||
path={'*'}
|
||||
element={
|
||||
<Navigate
|
||||
to={baseRoute + routes.list}
|
||||
to={routes.sodiohome_installations + routes.list}
|
||||
></Navigate>
|
||||
}
|
||||
></Route>
|
||||
|
|
@ -509,12 +481,9 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
|||
</Card>
|
||||
</Container>
|
||||
</>
|
||||
) : installations.length === 1 ? (
|
||||
) : sodiohomeInstallations.length === 1 ? (
|
||||
<>
|
||||
<AcknowledgementDialog
|
||||
open={showTermsDialog}
|
||||
onAcknowledge={handleAcknowledgeTerms}
|
||||
/>
|
||||
{' '}
|
||||
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
|
||||
<TabsContainerWrapper>
|
||||
<Tabs
|
||||
|
|
@ -554,7 +523,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
|
|||
<Grid item xs={12}>
|
||||
<Box p={4}>
|
||||
<SodioHomeInstallation
|
||||
current_installation={installations[0]}
|
||||
current_installation={sodiohomeInstallations[0]}
|
||||
type="installation"
|
||||
></SodioHomeInstallation>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -37,8 +37,7 @@ const productOptions = [
|
|||
{ value: 1, label: 'Salidomo' },
|
||||
{ value: 2, label: 'Sodistore Home' },
|
||||
{ value: 3, label: 'Sodistore Max' },
|
||||
{ value: 4, label: 'Sodistore Grid' },
|
||||
{ value: 5, label: 'Sodistore Pro' }
|
||||
{ value: 4, label: 'Sodistore Grid' }
|
||||
];
|
||||
|
||||
const deviceOptionsByProduct: Record<number, { value: number; label: string }[]> = {
|
||||
|
|
@ -48,7 +47,7 @@ const deviceOptionsByProduct: Record<number, { value: number; label: string }[]>
|
|||
],
|
||||
2: [
|
||||
{ value: 3, label: 'Growatt' },
|
||||
{ value: 4, label: 'inesco 12K - WR Hybrid' }
|
||||
{ value: 4, label: 'Sinexcel' }
|
||||
]
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -742,8 +742,7 @@ function TicketDetailPage() {
|
|||
1: routes.salidomo_installations,
|
||||
2: routes.sodiohome_installations,
|
||||
3: routes.sodistore_installations,
|
||||
4: routes.sodistoregrid_installations,
|
||||
5: routes.sodistorepro_installations
|
||||
4: routes.sodistoregrid_installations
|
||||
};
|
||||
const prefix = productRoutes[detail.installationProduct] ?? routes.installations;
|
||||
navigate(
|
||||
|
|
|
|||
|
|
@ -60,10 +60,6 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
|||
? routes.salidomo_installations
|
||||
: installation.product == 2
|
||||
? routes.sodiohome_installations
|
||||
: installation.product == 4
|
||||
? routes.sodistoregrid_installations
|
||||
: installation.product == 5
|
||||
? routes.sodistorepro_installations
|
||||
: routes.sodistore_installations;
|
||||
|
||||
let folder_path =
|
||||
|
|
@ -73,10 +69,6 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
|||
? routes.salidomo_installations
|
||||
: product == 2
|
||||
? routes.sodiohome_installations
|
||||
: product == 4
|
||||
? routes.sodistoregrid_installations
|
||||
: product == 5
|
||||
? routes.sodistorepro_installations
|
||||
: routes.sodistore_installations;
|
||||
|
||||
if (installation.type != 'Folder') {
|
||||
|
|
@ -217,10 +209,6 @@ function CustomTreeItem(props: CustomTreeItemProps) {
|
|||
currentLocation.pathname === routes.installations + routes.tree ||
|
||||
currentLocation.pathname ===
|
||||
routes.sodiohome_installations + routes.tree ||
|
||||
currentLocation.pathname ===
|
||||
routes.sodistoregrid_installations + routes.tree ||
|
||||
currentLocation.pathname ===
|
||||
routes.sodistorepro_installations + routes.tree ||
|
||||
currentLocation.pathname.includes('folder')
|
||||
? 'block'
|
||||
: 'none',
|
||||
|
|
|
|||
|
|
@ -59,18 +59,17 @@ function TreeInformation(props: TreeInformationProps) {
|
|||
fetchAllInstallations
|
||||
} = installationContext;
|
||||
|
||||
const [product, setProduct] = useState('SodistoreHome');
|
||||
const [product, setProduct] = useState('Salimax');
|
||||
|
||||
const handleChangeInstallationChoice = (e) => {
|
||||
setProduct(e.target.value); // Directly update the product state
|
||||
};
|
||||
|
||||
const ProductTypes = ['SodistoreHome', 'SodistorePro', 'SodistoreGrid', 'SodistoreMax', 'Salimax', 'Salidomo'];
|
||||
const ProductTypes = ['Salimax', 'Salidomo', 'SodistoreHome', 'SodistoreMax', 'SodistoreGrid'];
|
||||
const ProductDisplayNames: Record<string, string> = {
|
||||
'SodistoreHome': 'Sodistore Home',
|
||||
'SodistoreMax': 'Sodistore Max',
|
||||
'SodistoreGrid': 'Sodistore Grid',
|
||||
'SodistorePro': 'Sodistore Pro'
|
||||
'SodistoreGrid': 'Sodistore Grid'
|
||||
};
|
||||
|
||||
const isMobile = window.innerWidth <= 1490;
|
||||
|
|
@ -346,12 +345,11 @@ function TreeInformation(props: TreeInformationProps) {
|
|||
/>
|
||||
)}
|
||||
|
||||
{openModalInstallation && (product == 'SodistoreHome' || product == 'SodistorePro') && (
|
||||
{openModalInstallation && product == 'SodistoreHome' && (
|
||||
<SodiostorehomeInstallationForm
|
||||
cancel={handleFormCancel}
|
||||
submit={handleInstallationFormSubmit}
|
||||
parentid={props.folder.id}
|
||||
product={product == 'SodistorePro' ? 5 : undefined}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,24 +72,22 @@ function userForm(props: userFormProps) {
|
|||
setLoading(true);
|
||||
|
||||
try {
|
||||
const [res0, res1, res2, res3, res4, res5] = await Promise.all([
|
||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=0`),
|
||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=1`),
|
||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=2`),
|
||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=3`),
|
||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=4`),
|
||||
axiosConfig.get(`/GetAllInstallationsFromProduct?product=5`)
|
||||
]);
|
||||
// fetch product 0
|
||||
const res0 = await axiosConfig.get(
|
||||
`/GetAllInstallationsFromProduct?product=0`
|
||||
);
|
||||
const installations0 = res0.data;
|
||||
|
||||
const combined = [
|
||||
...res0.data,
|
||||
...res1.data,
|
||||
...res2.data,
|
||||
...res3.data,
|
||||
...res4.data,
|
||||
...res5.data
|
||||
];
|
||||
// fetch product 1
|
||||
const res1 = await axiosConfig.get(
|
||||
`/GetAllInstallationsFromProduct?product=3`
|
||||
);
|
||||
const installations1 = res1.data;
|
||||
|
||||
// aggregate
|
||||
const combined = [...installations0, ...installations1];
|
||||
|
||||
// update
|
||||
setInstallations(combined);
|
||||
} catch (err) {
|
||||
if (err.response && err.response.status === 401) {
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ function Status500() {
|
|||
<Container maxWidth="sm">
|
||||
<Box textAlign="center">
|
||||
<TypographyPrimary variant="h1" sx={{ my: 2 }}>
|
||||
inesco energy{' '}
|
||||
inesco Energy{' '}
|
||||
</TypographyPrimary>
|
||||
<TypographySecondary
|
||||
variant="h4"
|
||||
|
|
|
|||
|
|
@ -34,9 +34,6 @@ const InstallationsContextProvider = ({
|
|||
const [sodistoreGridInstallations, setSodistoreGridInstallations] = useState<
|
||||
I_Installation[]
|
||||
>([]);
|
||||
const [sodistoreProInstallations, setSodistoreProInstallations] = useState<
|
||||
I_Installation[]
|
||||
>([]);
|
||||
const [foldersAndInstallations, setFoldersAndInstallations] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
|
|
@ -108,24 +105,10 @@ const InstallationsContextProvider = ({
|
|||
}
|
||||
);
|
||||
|
||||
const updatedSodistorePro = sodistoreProInstallations.map(
|
||||
(installation) => {
|
||||
const update = pendingUpdates.current[installation.id];
|
||||
return update
|
||||
? {
|
||||
...installation,
|
||||
status: update.status,
|
||||
testingMode: update.testingMode
|
||||
}
|
||||
: installation;
|
||||
}
|
||||
);
|
||||
|
||||
setSalidomoInstallations(updatedSalidomo);
|
||||
setSalimax_Or_Sodistore_Installations(updatedSalimax);
|
||||
setSodiohomeInstallations(updatedSodiohome);
|
||||
setSodistoreGridInstallations(updatedSodistoreGrid);
|
||||
setSodistoreProInstallations(updatedSodistorePro);
|
||||
|
||||
// Clear the pending updates after applying
|
||||
pendingUpdates.current = {};
|
||||
|
|
@ -133,8 +116,7 @@ const InstallationsContextProvider = ({
|
|||
salidomoInstallations,
|
||||
salimax_or_sodistore_Installations,
|
||||
sodiohomeInstallations,
|
||||
sodistoreGridInstallations,
|
||||
sodistoreProInstallations
|
||||
sodistoreGridInstallations
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -211,8 +193,6 @@ const InstallationsContextProvider = ({
|
|||
|
||||
if (product === 2) {
|
||||
setSodiohomeInstallations(res.data);
|
||||
} else if (product === 5) {
|
||||
setSodistoreProInstallations(res.data);
|
||||
} else if (product === 1) {
|
||||
setSalidomoInstallations(res.data);
|
||||
} else if (product === 0 || product === 3) {
|
||||
|
|
@ -438,7 +418,6 @@ const InstallationsContextProvider = ({
|
|||
salidomoInstallations,
|
||||
sodiohomeInstallations,
|
||||
sodistoreGridInstallations,
|
||||
sodistoreProInstallations,
|
||||
foldersAndInstallations,
|
||||
fetchAllInstallations,
|
||||
fetchAllFoldersAndInstallations,
|
||||
|
|
@ -466,7 +445,6 @@ const InstallationsContextProvider = ({
|
|||
salidomoInstallations,
|
||||
sodiohomeInstallations,
|
||||
sodistoreGridInstallations,
|
||||
sodistoreProInstallations,
|
||||
foldersAndInstallations,
|
||||
loading,
|
||||
error,
|
||||
|
|
|
|||
|
|
@ -10,13 +10,11 @@ interface ProductIdContextType {
|
|||
accessToSodiohome: boolean;
|
||||
accessToSodistore: boolean;
|
||||
accessToSodistoreGrid: boolean;
|
||||
accessToSodistorePro: boolean;
|
||||
setAccessToSalimax: (access: boolean) => void;
|
||||
setAccessToSalidomo: (access: boolean) => void;
|
||||
setAccessToSodiohome: (access: boolean) => void;
|
||||
setAccessToSodistore: (access: boolean) => void;
|
||||
setAccessToSodistoreGrid: (access: boolean) => void;
|
||||
setAccessToSodistorePro: (access: boolean) => void;
|
||||
}
|
||||
|
||||
// Create the context.
|
||||
|
|
@ -51,10 +49,6 @@ export const ProductIdContextProvider = ({
|
|||
const storedValue = localStorage.getItem('accessToSodistoreGrid');
|
||||
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<number>(
|
||||
// productMapping[Object.keys(productMapping).find((key) => location.includes(key)) || ''] || -1
|
||||
|
|
@ -62,8 +56,6 @@ export const ProductIdContextProvider = ({
|
|||
const [product, setProduct] = useState<number>(() => {
|
||||
if (location.includes('salidomo')) {
|
||||
return 1;
|
||||
} else if (location.includes('sodistorepro')) {
|
||||
return 5;
|
||||
} else if (location.includes('sodiohome')) {
|
||||
return 2;
|
||||
} else if (location.includes('sodistoregrid')) {
|
||||
|
|
@ -100,10 +92,6 @@ export const ProductIdContextProvider = ({
|
|||
setAccessToSodistoreGrid(access);
|
||||
localStorage.setItem('accessToSodistoreGrid', JSON.stringify(access));
|
||||
};
|
||||
const changeAccessSodistorePro = (access: boolean) => {
|
||||
setAccessToSodistorePro(access);
|
||||
localStorage.setItem('accessToSodistorePro', JSON.stringify(access));
|
||||
};
|
||||
|
||||
return (
|
||||
<ProductIdContext.Provider
|
||||
|
|
@ -115,13 +103,11 @@ export const ProductIdContextProvider = ({
|
|||
accessToSodiohome,
|
||||
accessToSodistore,
|
||||
accessToSodistoreGrid,
|
||||
accessToSodistorePro,
|
||||
setAccessToSalimax: changeAccessSalimax,
|
||||
setAccessToSalidomo: changeAccessSalidomo,
|
||||
setAccessToSodiohome: changeAccessSodiohome,
|
||||
setAccessToSodistore: changeAccessSodistore,
|
||||
setAccessToSodistoreGrid: changeAccessSodistoreGrid,
|
||||
setAccessToSodistorePro: changeAccessSodistorePro
|
||||
setAccessToSodistoreGrid: changeAccessSodistoreGrid
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ export const transformInputToBatteryViewDataJson = async (
|
|||
}> => {
|
||||
const prefixes = ['', 'k', 'M', 'G', 'T'];
|
||||
const MAX_NUMBER = 9999999;
|
||||
const isSodioHome = product === 2 || product === 5;
|
||||
const isSodioHome = product === 2;
|
||||
const categories = isSodioHome
|
||||
? ['Soc', 'Power', 'Voltage', 'Current', 'Soh']
|
||||
: ['Soc', 'Temperature', 'Power', 'Voltage', 'Current'];
|
||||
|
|
@ -169,7 +169,7 @@ export const transformInputToBatteryViewDataJson = async (
|
|||
);
|
||||
|
||||
const adjustedTimestamp =
|
||||
product == 0 || product == 2 || product == 3 || product == 4 || product == 5
|
||||
product == 0 || product == 2 || product == 3 || product == 4
|
||||
? new Date(timestampArray[i] * 1000)
|
||||
: new Date(timestampArray[i] * 100000);
|
||||
//Timezone offset is negative, so we convert the timestamp to the current zone by subtracting the corresponding offset
|
||||
|
|
@ -226,52 +226,26 @@ export const transformInputToBatteryViewDataJson = async (
|
|||
categories.forEach((category) => {
|
||||
pathsToSave.forEach((path) => {
|
||||
if (pathsToSave.indexOf(path) >= old_length) {
|
||||
const displayIndex = pathsToSave.indexOf(path);
|
||||
chartData[category].data[path] = { name: 'Battery Cluster ' + (displayIndex + 1), data: [] };
|
||||
chartData[category].data[path] = { name: path, data: [] };
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const hasDevices = !!inv?.Devices;
|
||||
|
||||
// Sinexcel field suffixes differ from Growatt for Voltage/Current
|
||||
const categoryFieldMapGrowatt = {
|
||||
// Map category names to InverterRecord field suffixes
|
||||
const categoryFieldMap = {
|
||||
Soc: 'Soc',
|
||||
Power: 'Power',
|
||||
Voltage: 'Voltage',
|
||||
Current: 'Current',
|
||||
Soh: 'Soh'
|
||||
};
|
||||
const categoryFieldMapSinexcel = {
|
||||
Soc: 'Soc',
|
||||
Power: 'Power',
|
||||
Voltage: 'PackTotalVoltage',
|
||||
Current: 'PackTotalCurrent',
|
||||
Soh: 'Soh'
|
||||
};
|
||||
|
||||
for (let j = 0; j < pathsToSave.length; j++) {
|
||||
const batteryIndex = j + 1; // Battery1, Battery2, ...
|
||||
categories.forEach((category) => {
|
||||
let value: number | undefined;
|
||||
|
||||
if (hasDevices) {
|
||||
// Sinexcel: nested under Devices — 0→D1/B1, 1→D1/B2, 2→D2/B1, ...
|
||||
const deviceId = String(Math.floor(j / 2) + 1);
|
||||
const bi = (j % 2) + 1;
|
||||
const device = inv.Devices[deviceId];
|
||||
const fieldName = `Battery${bi}${categoryFieldMapSinexcel[category]}`;
|
||||
value = device?.[fieldName];
|
||||
// Fallback for Soc
|
||||
if ((value === undefined || value === null) && category === 'Soc') {
|
||||
value = device?.[`Battery${bi}SocSecondvalue`];
|
||||
}
|
||||
} else {
|
||||
// Growatt: flat Battery1Soc, Battery2Voltage, ...
|
||||
const batteryIndex = j + 1;
|
||||
const fieldName = `Battery${batteryIndex}${categoryFieldMapGrowatt[category]}`;
|
||||
value = inv[fieldName];
|
||||
}
|
||||
const fieldName = `Battery${batteryIndex}${categoryFieldMap[category]}`;
|
||||
const value = inv[fieldName];
|
||||
|
||||
if (value !== undefined && value !== null) {
|
||||
if (value < chartOverview[category].min) {
|
||||
|
|
@ -419,7 +393,7 @@ export const transformInputToDailyDataJson = async (
|
|||
// custom fallback logic to handle differences between Growatt and Sinexcel.
|
||||
// Growatt has: Battery1AmbientTemperature, GridPower, PvPower
|
||||
// Sinexcel has: Battery1Temperature, TotalGridPower (meter may be offline), PvPower1-4
|
||||
const pathsToSearch = (product == 2 || product == 5)
|
||||
const pathsToSearch = product == 2
|
||||
? [
|
||||
'SODIOHOME_SOC',
|
||||
'SODIOHOME_TEMPERATURE',
|
||||
|
|
@ -542,8 +516,8 @@ export const transformInputToDailyDataJson = async (
|
|||
|
||||
let value: number | undefined = undefined;
|
||||
|
||||
if (product === 2 || product === 5) {
|
||||
// SodioHome/SodistorePro: use top-level aggregated values (Sinexcel multi-inverter)
|
||||
if (product === 2) {
|
||||
// SodioHome: use top-level aggregated values (Sinexcel multi-inverter)
|
||||
const inv = result?.InverterRecord;
|
||||
if (inv) {
|
||||
switch (category_index) {
|
||||
|
|
@ -761,7 +735,7 @@ export const transformInputToAggregatedDataJson = async (
|
|||
const timestampPromises = [];
|
||||
|
||||
while (currentDay.isBefore(end_date)) {
|
||||
const dateFormat = (product === 2 || product === 5)
|
||||
const dateFormat = product === 2
|
||||
? currentDay.format('DDMMYYYY')
|
||||
: currentDay.format('YYYY-MM-DD');
|
||||
timestampPromises.push(
|
||||
|
|
|
|||
|
|
@ -7,13 +7,6 @@ export interface I_Installation extends I_S3Credentials {
|
|||
location: string;
|
||||
region: string;
|
||||
country: string;
|
||||
street?: string;
|
||||
postCode?: string;
|
||||
city?: string;
|
||||
canton?: string;
|
||||
distributionPartner?: string;
|
||||
inverterFirmwareVersion?: string;
|
||||
batteryFirmwareVersion?: string;
|
||||
installationName: string;
|
||||
vpnIp: string;
|
||||
orderNumbers: string[] | string;
|
||||
|
|
@ -25,11 +18,6 @@ export interface I_Installation extends I_S3Credentials {
|
|||
batteryClusterNumber: number;
|
||||
batteryNumber: number;
|
||||
batterySerialNumbers: string;
|
||||
pvStringsPerInverter: string;
|
||||
installationModel: string;
|
||||
externalEms: string;
|
||||
couplingType: string;
|
||||
|
||||
parentId: number;
|
||||
s3WriteKey: string;
|
||||
s3WriteSecret: string;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ export type InnovEnergyUser = {
|
|||
type: string;
|
||||
folderIds?: number[];
|
||||
mustResetPassword: boolean;
|
||||
acknowledgedTermsVersion?: number;
|
||||
};
|
||||
|
||||
export interface I_UserWithInheritedAccess {
|
||||
|
|
|
|||
|
|
@ -6,13 +6,6 @@
|
|||
"alarms": "Alarme",
|
||||
"applyChanges": "Änderungen speichern",
|
||||
"country": "Land",
|
||||
"street": "Strasse",
|
||||
"postCode": "PLZ",
|
||||
"city": "Ort",
|
||||
"canton": "Kanton",
|
||||
"distributionPartner": "Vertriebspartner",
|
||||
"inverterFirmwareVersion": "Wechselrichter-Firmware-Version",
|
||||
"batteryFirmwareVersion": "Batterie-Firmware-Version",
|
||||
"networkProvider": "Netzbetreiber",
|
||||
"createNewFolder": "Neuer Ordner",
|
||||
"createNewUser": "Neuer Benutzer",
|
||||
|
|
@ -80,27 +73,6 @@
|
|||
"live": "Live Daten",
|
||||
"deleteInstallation": "Installation löschen",
|
||||
"confirmDeleteInstallation": "Möchten Sie diese Installation löschen?",
|
||||
"installationModel": "Installationsmodell",
|
||||
"externalEms": "Externes EMS",
|
||||
"externalEmsOther": "Externes EMS (angeben)",
|
||||
"emsNo": "Nein",
|
||||
"emsOther": "Andere",
|
||||
"generalInfo": "Allgemeine Informationen",
|
||||
"installationSetup": "Installationseinrichtung",
|
||||
"couplingType": "AC/DC-Kopplung",
|
||||
"couplingAC": "AC-gekoppelt",
|
||||
"couplingDC": "DC-gekoppelt",
|
||||
"selectModel": "Modell auswählen...",
|
||||
"inverterN": "Wechselrichter {n}",
|
||||
"clusterN": "Cluster {n}",
|
||||
"clustersBatteriesSummary": "{filledClusters}/{totalClusters} Cluster, {filledBat}/{totalBat} Batterien",
|
||||
"batteriesSummary": "{filled}/{total} Batterien",
|
||||
"inverterNSerialNumber": "Wechselrichter {n} Seriennummer",
|
||||
"dataloggerNSerialNumber": "Datenlogger {n} Seriennummer",
|
||||
"pvStringsOnInverterN": "Anzahl PV-Strings an Wechselrichter {n}",
|
||||
"batteryNSerialNumber": "Batterie {n} Seriennummer",
|
||||
"adminSection": "Admin",
|
||||
"confirmPresetSwitch": "Der Wechsel zu einer kleineren Konfiguration entfernt einige Batterie-Seriennummern. Fortfahren?",
|
||||
"bucketLabel": "Bucket",
|
||||
"deleteInstallationWarning": "Bitte notieren Sie den Bucket-Namen oben. Das Löschen der S3-Daten kann mehrere Minuten dauern. Überprüfen Sie nach dem Löschen in Exoscale, ob der Bucket entfernt wurde. Falls nicht, leeren und löschen Sie den Bucket manuell.",
|
||||
"errorOccured": "Ein Fehler ist aufgetreten",
|
||||
|
|
@ -113,7 +85,6 @@
|
|||
"noUsersWithDirectAccessToThis": "Keine Benutzer mit direktem Zugriff",
|
||||
"selectUsers": "Benutzer auswählen",
|
||||
"cancel": "Abbrechen",
|
||||
"continue": "Fortfahren",
|
||||
"addNewFolder": "Neuen Ordner hinzufügen",
|
||||
"addNewInstallation": "Neue Installation hinzufügen",
|
||||
"deleteFolder": "Ordner löschen",
|
||||
|
|
@ -214,7 +185,7 @@
|
|||
"demo_test_button": "KI-Diagnose",
|
||||
"demo_hide_button": "KI-Diagnose ausblenden",
|
||||
"demo_panel_title": "KI-Diagnose",
|
||||
"demo_custom_group": "Benutzerdefiniert (kann KI verwenden)",
|
||||
"demo_custom_group": "Benutzerdefiniert (kann Mistral KI verwenden)",
|
||||
"demo_custom_option": "Benutzerdefinierten Alarm eingeben…",
|
||||
"demo_custom_placeholder": "z.B. UnknownBatteryFault",
|
||||
"demo_diagnose_button": "Diagnostizieren",
|
||||
|
|
@ -643,26 +614,5 @@
|
|||
"timelineAiDiagnosisCompletedDesc": "KI-Diagnose abgeschlossen.",
|
||||
"timelineAiDiagnosisFailedDesc": "KI-Diagnose fehlgeschlagen.",
|
||||
"timelineEscalatedDesc": "Ticket eskaliert.",
|
||||
"timelineResolutionAddedDesc": "Lösung hinzugefügt von {name}.",
|
||||
"terms_dialog_title": "Willkommen bei inesco energy",
|
||||
"terms_data_heading": "Ihre Daten",
|
||||
"terms_data_body": "Ihre Installationsdaten werden sicher in der Schweiz gespeichert. Wir geben Ihre Daten nicht an Dritte weiter.",
|
||||
"terms_ai_heading": "KI-gestützte Einblicke",
|
||||
"terms_ai_body": "Wir nutzen einen in der EU gehosteten KI-Dienst, um Diagnosen und Einblicke für Ihre Installationen bereitzustellen. KI-generierte Ergebnisse sind Empfehlungen und sollten von qualifiziertem Personal überprüft werden.",
|
||||
"terms_cookies_heading": "Browser-Speicher",
|
||||
"terms_cookies_body": "Diese Plattform speichert Anmelde- und Einstellungsdaten in Ihrem Browser, um Sie angemeldet zu halten und Ihre Sprachauswahl zu speichern.",
|
||||
"terms_acknowledge_button": "Ich verstehe",
|
||||
"privacy_menu_item": "Daten & Datenschutz",
|
||||
"privacy_dialog_title": "Daten & Datenschutz",
|
||||
"privacy_data_heading": "Wo werden meine Daten gespeichert?",
|
||||
"privacy_data_body": "Ihre Installationsdaten werden auf Servern in der Schweiz gespeichert. Nur autorisiertes Personal von inesco energy kann zu Supportzwecken auf Ihre Daten zugreifen.",
|
||||
"privacy_ai_heading": "Wie wird KI eingesetzt?",
|
||||
"privacy_ai_body": "Wir nutzen einen in der Europäischen Union gehosteten KI-Dienst, um Ihre Installationsdaten zu analysieren und diagnostische Einblicke zu liefern. Die KI verarbeitet technische Daten wie Batteriemesswerte und Fehlercodes. KI-Empfehlungen sollten stets von qualifiziertem Personal überprüft werden.",
|
||||
"privacy_browser_heading": "Was speichert mein Browser?",
|
||||
"privacy_browser_body": "Ihr Browser speichert Ihre Anmeldesitzung, um Sie angemeldet zu halten, sowie Ihre Sprach- und Designeinstellungen. Es werden keine Tracking- oder Werbe-Cookies verwendet.",
|
||||
"privacy_access_heading": "Wer hat Zugriff auf meine Daten?",
|
||||
"privacy_access_body": "Ihre Daten werden nicht an Dritte weitergegeben. Sie werden ausschliesslich für den Betrieb der Plattform und zur Bereitstellung von Einblicken in Ihre Installationen verwendet.",
|
||||
"privacy_close_button": "Schliessen",
|
||||
"sodistorepro": "Sodistore Pro",
|
||||
"numberOfInverters": "Anzahl der Wechselrichter"
|
||||
"timelineResolutionAddedDesc": "Lösung hinzugefügt von {name}."
|
||||
}
|
||||
|
|
@ -2,13 +2,6 @@
|
|||
"allInstallations": "All installations",
|
||||
"applyChanges": "Apply changes",
|
||||
"country": "Country",
|
||||
"street": "Street",
|
||||
"postCode": "Postcode",
|
||||
"city": "City",
|
||||
"canton": "Canton",
|
||||
"distributionPartner": "Distribution Partner",
|
||||
"inverterFirmwareVersion": "Inverter Firmware Version",
|
||||
"batteryFirmwareVersion": "Battery Firmware Version",
|
||||
"networkProvider": "Network Provider",
|
||||
"customerName": "Customer name",
|
||||
"english": "English",
|
||||
|
|
@ -62,27 +55,6 @@
|
|||
"live": "Live View",
|
||||
"deleteInstallation": "Delete Installation",
|
||||
"confirmDeleteInstallation": "Do you want to delete this installation?",
|
||||
"installationModel": "Installation Model",
|
||||
"externalEms": "External EMS",
|
||||
"externalEmsOther": "External EMS (specify)",
|
||||
"emsNo": "No",
|
||||
"emsOther": "Other",
|
||||
"generalInfo": "General Info",
|
||||
"installationSetup": "Installation Setup",
|
||||
"couplingType": "AC/DC Coupling",
|
||||
"couplingAC": "AC-coupled",
|
||||
"couplingDC": "DC-coupled",
|
||||
"selectModel": "Select model...",
|
||||
"inverterN": "Inverter {n}",
|
||||
"clusterN": "Cluster {n}",
|
||||
"clustersBatteriesSummary": "{filledClusters}/{totalClusters} clusters, {filledBat}/{totalBat} batteries",
|
||||
"batteriesSummary": "{filled}/{total} batteries",
|
||||
"inverterNSerialNumber": "Inverter {n} Serial Number",
|
||||
"dataloggerNSerialNumber": "Datalogger {n} Serial Number",
|
||||
"pvStringsOnInverterN": "Number of PV Strings on Inverter {n}",
|
||||
"batteryNSerialNumber": "Battery {n} Serial Number",
|
||||
"adminSection": "Admin",
|
||||
"confirmPresetSwitch": "Switching to a smaller configuration will remove some battery serial number entries. Continue?",
|
||||
"bucketLabel": "Bucket",
|
||||
"deleteInstallationWarning": "Please note the bucket name above. Purging S3 data may take several minutes. After deletion, verify in Exoscale that the bucket has been removed. If not, purge and delete the bucket manually.",
|
||||
"errorOccured": "An error has occurred",
|
||||
|
|
@ -95,7 +67,6 @@
|
|||
"noUsersWithDirectAccessToThis": "No users with direct access to this ",
|
||||
"selectUsers": "Select Users",
|
||||
"cancel": "Cancel",
|
||||
"continue": "Continue",
|
||||
"addNewFolder": "Add new Folder",
|
||||
"addNewInstallation": "Add new Installation",
|
||||
"deleteFolder": "Delete Folder",
|
||||
|
|
@ -196,7 +167,7 @@
|
|||
"demo_test_button": "AI Diagnosis",
|
||||
"demo_hide_button": "Hide AI Diagnosis",
|
||||
"demo_panel_title": "AI Diagnosis",
|
||||
"demo_custom_group": "Custom (may use AI)",
|
||||
"demo_custom_group": "Custom (may use Mistral AI)",
|
||||
"demo_custom_option": "Type custom alarm below…",
|
||||
"demo_custom_placeholder": "e.g. UnknownBatteryFault",
|
||||
"demo_diagnose_button": "Diagnose",
|
||||
|
|
@ -391,26 +362,5 @@
|
|||
"timelineAiDiagnosisCompletedDesc": "AI diagnosis completed.",
|
||||
"timelineAiDiagnosisFailedDesc": "AI diagnosis failed.",
|
||||
"timelineEscalatedDesc": "Ticket escalated.",
|
||||
"timelineResolutionAddedDesc": "Resolution added by {name}.",
|
||||
"terms_dialog_title": "Welcome to inesco energy",
|
||||
"terms_data_heading": "Your Data",
|
||||
"terms_data_body": "Your installation data is securely stored in Switzerland. We do not share your data with third parties.",
|
||||
"terms_ai_heading": "AI-Powered Insights",
|
||||
"terms_ai_body": "We use an AI service hosted in the EU to provide diagnostics and insights for your installations. AI-generated results are recommendations and should be verified by qualified personnel.",
|
||||
"terms_cookies_heading": "Browser Storage",
|
||||
"terms_cookies_body": "This platform stores login and preference settings in your browser to keep you signed in and remember your language choice.",
|
||||
"terms_acknowledge_button": "I understand",
|
||||
"privacy_menu_item": "Data & Privacy",
|
||||
"privacy_dialog_title": "Data & Privacy",
|
||||
"privacy_data_heading": "Where is my data stored?",
|
||||
"privacy_data_body": "Your installation data is stored on servers in Switzerland. Only authorized inesco energy personnel can access your data for support purposes.",
|
||||
"privacy_ai_heading": "How is AI used?",
|
||||
"privacy_ai_body": "We use an AI service hosted in the European Union to analyze your installation data and provide diagnostic insights. The AI processes technical data such as battery readings and error codes. AI recommendations should always be verified by qualified personnel.",
|
||||
"privacy_browser_heading": "What does my browser store?",
|
||||
"privacy_browser_body": "Your browser stores your login session to keep you signed in, and your language and theme preferences. No tracking or advertising cookies are used.",
|
||||
"privacy_access_heading": "Who has access to my data?",
|
||||
"privacy_access_body": "Your data is not shared with third parties. It is used solely to operate the platform and provide you with insights about your installations.",
|
||||
"privacy_close_button": "Close",
|
||||
"sodistorepro": "Sodistore Pro",
|
||||
"numberOfInverters": "Number of Inverters"
|
||||
"timelineResolutionAddedDesc": "Resolution added by {name}."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,6 @@
|
|||
"alarms": "Alarmes",
|
||||
"applyChanges": "Appliquer",
|
||||
"country": "Pays",
|
||||
"street": "Rue",
|
||||
"postCode": "Code postal",
|
||||
"city": "Ville",
|
||||
"canton": "Canton",
|
||||
"distributionPartner": "Partenaire de distribution",
|
||||
"inverterFirmwareVersion": "Version firmware onduleur",
|
||||
"batteryFirmwareVersion": "Version firmware batterie",
|
||||
"networkProvider": "Gestionnaire de réseau",
|
||||
"createNewFolder": "Nouveau dossier",
|
||||
"createNewUser": "Nouvel utilisateur",
|
||||
|
|
@ -74,27 +67,6 @@
|
|||
"live": "Diffusion en direct",
|
||||
"deleteInstallation": "Supprimer l'installation",
|
||||
"confirmDeleteInstallation": "Voulez-vous supprimer cette installation ?",
|
||||
"installationModel": "Modèle d'installation",
|
||||
"externalEms": "EMS externe",
|
||||
"externalEmsOther": "EMS externe (préciser)",
|
||||
"emsNo": "Non",
|
||||
"emsOther": "Autre",
|
||||
"generalInfo": "Informations générales",
|
||||
"installationSetup": "Configuration de l'installation",
|
||||
"couplingType": "Couplage AC/DC",
|
||||
"couplingAC": "Couplage AC",
|
||||
"couplingDC": "Couplage DC",
|
||||
"selectModel": "Sélectionner le modèle...",
|
||||
"inverterN": "Onduleur {n}",
|
||||
"clusterN": "Cluster {n}",
|
||||
"clustersBatteriesSummary": "{filledClusters}/{totalClusters} clusters, {filledBat}/{totalBat} batteries",
|
||||
"batteriesSummary": "{filled}/{total} batteries",
|
||||
"inverterNSerialNumber": "Numéro de série onduleur {n}",
|
||||
"dataloggerNSerialNumber": "Numéro de série datalogger {n}",
|
||||
"pvStringsOnInverterN": "Nombre de chaînes PV sur onduleur {n}",
|
||||
"batteryNSerialNumber": "Numéro de série batterie {n}",
|
||||
"adminSection": "Admin",
|
||||
"confirmPresetSwitch": "Le passage à une configuration plus petite supprimera certains numéros de série de batteries. Continuer ?",
|
||||
"bucketLabel": "Bucket",
|
||||
"deleteInstallationWarning": "Veuillez noter le nom du bucket ci-dessus. La purge des données S3 peut prendre plusieurs minutes. Après la suppression, vérifiez dans Exoscale que le bucket a bien été supprimé. Sinon, purgez et supprimez le bucket manuellement.",
|
||||
"errorOccured": "Une erreur s'est produite",
|
||||
|
|
@ -107,7 +79,6 @@
|
|||
"noUsersWithDirectAccessToThis": "Aucun utilisateur ayant un accès direct",
|
||||
"selectUsers": "Sélectionnez les utilisateurs",
|
||||
"cancel": "Annuler",
|
||||
"continue": "Continuer",
|
||||
"addNewFolder": "Ajouter un nouveau dossier",
|
||||
"addNewInstallation": "Ajouter une nouvelle installation",
|
||||
"deleteFolder": "Supprimer le dossier",
|
||||
|
|
@ -208,7 +179,7 @@
|
|||
"demo_test_button": "Diagnostic IA",
|
||||
"demo_hide_button": "Masquer le diagnostic IA",
|
||||
"demo_panel_title": "Diagnostic IA",
|
||||
"demo_custom_group": "Personnalisé (peut utiliser IA)",
|
||||
"demo_custom_group": "Personnalisé (peut utiliser Mistral IA)",
|
||||
"demo_custom_option": "Saisir une alarme personnalisée…",
|
||||
"demo_custom_placeholder": "ex. UnknownBatteryFault",
|
||||
"demo_diagnose_button": "Diagnostiquer",
|
||||
|
|
@ -643,26 +614,5 @@
|
|||
"timelineAiDiagnosisCompletedDesc": "Diagnostic IA terminé.",
|
||||
"timelineAiDiagnosisFailedDesc": "Diagnostic IA échoué.",
|
||||
"timelineEscalatedDesc": "Ticket escaladé.",
|
||||
"timelineResolutionAddedDesc": "Résolution ajoutée par {name}.",
|
||||
"terms_dialog_title": "Bienvenue chez inesco energy",
|
||||
"terms_data_heading": "Vos données",
|
||||
"terms_data_body": "Les données de votre installation sont stockées en toute sécurité en Suisse. Nous ne partageons pas vos données avec des tiers.",
|
||||
"terms_ai_heading": "Analyses basées sur l'IA",
|
||||
"terms_ai_body": "Nous utilisons un service d'IA hébergé dans l'UE pour fournir des diagnostics et des analyses pour vos installations. Les résultats générés par l'IA sont des recommandations et doivent être vérifiés par du personnel qualifié.",
|
||||
"terms_cookies_heading": "Stockage du navigateur",
|
||||
"terms_cookies_body": "Cette plateforme enregistre vos paramètres de connexion et de préférences dans votre navigateur pour maintenir votre session et mémoriser votre choix de langue.",
|
||||
"terms_acknowledge_button": "Je comprends",
|
||||
"privacy_menu_item": "Données et confidentialité",
|
||||
"privacy_dialog_title": "Données et confidentialité",
|
||||
"privacy_data_heading": "Où sont stockées mes données ?",
|
||||
"privacy_data_body": "Les données de votre installation sont stockées sur des serveurs en Suisse. Seul le personnel autorisé d'inesco energy peut accéder à vos données à des fins d'assistance.",
|
||||
"privacy_ai_heading": "Comment l'IA est-elle utilisée ?",
|
||||
"privacy_ai_body": "Nous utilisons un service d'IA hébergé dans l'Union européenne pour analyser les données de votre installation et fournir des informations diagnostiques. L'IA traite des données techniques telles que les relevés de batterie et les codes d'erreur. Les recommandations de l'IA doivent toujours être vérifiées par du personnel qualifié.",
|
||||
"privacy_browser_heading": "Que stocke mon navigateur ?",
|
||||
"privacy_browser_body": "Votre navigateur stocke votre session de connexion pour vous maintenir connecté, ainsi que vos préférences de langue et de thème. Aucun cookie de suivi ou publicitaire n'est utilisé.",
|
||||
"privacy_access_heading": "Qui a accès à mes données ?",
|
||||
"privacy_access_body": "Vos données ne sont pas partagées avec des tiers. Elles sont utilisées uniquement pour le fonctionnement de la plateforme et pour vous fournir des informations sur vos installations.",
|
||||
"privacy_close_button": "Fermer",
|
||||
"sodistorepro": "Sodistore Pro",
|
||||
"numberOfInverters": "Nombre d'onduleurs"
|
||||
"timelineResolutionAddedDesc": "Résolution ajoutée par {name}."
|
||||
}
|
||||
|
|
@ -2,13 +2,6 @@
|
|||
"allInstallations": "Tutte le installazioni",
|
||||
"applyChanges": "Applica modifiche",
|
||||
"country": "Paese",
|
||||
"street": "Via",
|
||||
"postCode": "CAP",
|
||||
"city": "Città",
|
||||
"canton": "Cantone",
|
||||
"distributionPartner": "Partner di distribuzione",
|
||||
"inverterFirmwareVersion": "Versione firmware inverter",
|
||||
"batteryFirmwareVersion": "Versione firmware batteria",
|
||||
"networkProvider": "Gestore di rete",
|
||||
"customerName": "Nome cliente",
|
||||
"english": "Inglese",
|
||||
|
|
@ -62,27 +55,6 @@
|
|||
"live": "Vista in diretta",
|
||||
"deleteInstallation": "Elimina installazione",
|
||||
"confirmDeleteInstallation": "Vuoi eliminare questa installazione?",
|
||||
"installationModel": "Modello di installazione",
|
||||
"externalEms": "EMS esterno",
|
||||
"externalEmsOther": "EMS esterno (specificare)",
|
||||
"emsNo": "No",
|
||||
"emsOther": "Altro",
|
||||
"generalInfo": "Informazioni generali",
|
||||
"installationSetup": "Configurazione installazione",
|
||||
"couplingType": "Accoppiamento AC/DC",
|
||||
"couplingAC": "Accoppiamento AC",
|
||||
"couplingDC": "Accoppiamento DC",
|
||||
"selectModel": "Seleziona modello...",
|
||||
"inverterN": "Inverter {n}",
|
||||
"clusterN": "Cluster {n}",
|
||||
"clustersBatteriesSummary": "{filledClusters}/{totalClusters} cluster, {filledBat}/{totalBat} batterie",
|
||||
"batteriesSummary": "{filled}/{total} batterie",
|
||||
"inverterNSerialNumber": "Numero di serie inverter {n}",
|
||||
"dataloggerNSerialNumber": "Numero di serie datalogger {n}",
|
||||
"pvStringsOnInverterN": "Numero di stringhe PV sull'inverter {n}",
|
||||
"batteryNSerialNumber": "Numero di serie batteria {n}",
|
||||
"adminSection": "Admin",
|
||||
"confirmPresetSwitch": "Il passaggio a una configurazione più piccola rimuoverà alcuni numeri di serie delle batterie. Continuare?",
|
||||
"bucketLabel": "Bucket",
|
||||
"deleteInstallationWarning": "Prendi nota del nome del bucket qui sopra. L'eliminazione dei dati S3 potrebbe richiedere diversi minuti. Dopo l'eliminazione, verifica in Exoscale che il bucket sia stato rimosso. In caso contrario, svuota ed elimina il bucket manualmente.",
|
||||
"errorOccured": "Si è verificato un errore",
|
||||
|
|
@ -95,7 +67,6 @@
|
|||
"noUsersWithDirectAccessToThis": "Nessun utente con accesso diretto a questo",
|
||||
"selectUsers": "Seleziona utenti",
|
||||
"cancel": "Annulla",
|
||||
"continue": "Continua",
|
||||
"addNewFolder": "Aggiungi nuova cartella",
|
||||
"addNewInstallation": "Aggiungi nuova installazione",
|
||||
"deleteFolder": "Elimina cartella",
|
||||
|
|
@ -219,7 +190,7 @@
|
|||
"demo_test_button": "Diagnosi IA",
|
||||
"demo_hide_button": "Nascondi diagnosi IA",
|
||||
"demo_panel_title": "Diagnosi IA",
|
||||
"demo_custom_group": "Personalizzato (potrebbe usare IA)",
|
||||
"demo_custom_group": "Personalizzato (potrebbe usare Mistral IA)",
|
||||
"demo_custom_option": "Inserisci allarme personalizzato…",
|
||||
"demo_custom_placeholder": "es. UnknownBatteryFault",
|
||||
"demo_diagnose_button": "Diagnostica",
|
||||
|
|
@ -643,26 +614,5 @@
|
|||
"timelineAiDiagnosisCompletedDesc": "Diagnosi IA completata.",
|
||||
"timelineAiDiagnosisFailedDesc": "Diagnosi IA fallita.",
|
||||
"timelineEscalatedDesc": "Ticket escalato.",
|
||||
"timelineResolutionAddedDesc": "Risoluzione aggiunta da {name}.",
|
||||
"terms_dialog_title": "Benvenuto su inesco energy",
|
||||
"terms_data_heading": "I tuoi dati",
|
||||
"terms_data_body": "I dati della tua installazione sono archiviati in modo sicuro in Svizzera. Non condividiamo i tuoi dati con terze parti.",
|
||||
"terms_ai_heading": "Analisi basate sull'IA",
|
||||
"terms_ai_body": "Utilizziamo un servizio di IA ospitato nell'UE per fornire diagnostica e analisi per le tue installazioni. I risultati generati dall'IA sono raccomandazioni e devono essere verificati da personale qualificato.",
|
||||
"terms_cookies_heading": "Archiviazione del browser",
|
||||
"terms_cookies_body": "Questa piattaforma memorizza le impostazioni di accesso e le preferenze nel browser per mantenerti connesso e ricordare la tua scelta linguistica.",
|
||||
"terms_acknowledge_button": "Ho capito",
|
||||
"privacy_menu_item": "Dati e privacy",
|
||||
"privacy_dialog_title": "Dati e privacy",
|
||||
"privacy_data_heading": "Dove vengono archiviati i miei dati?",
|
||||
"privacy_data_body": "I dati della tua installazione sono archiviati su server in Svizzera. Solo il personale autorizzato di inesco energy può accedere ai tuoi dati per scopi di assistenza.",
|
||||
"privacy_ai_heading": "Come viene utilizzata l'IA?",
|
||||
"privacy_ai_body": "Utilizziamo un servizio di IA ospitato nell'Unione Europea per analizzare i dati della tua installazione e fornire informazioni diagnostiche. L'IA elabora dati tecnici come le letture delle batterie e i codici di errore. Le raccomandazioni dell'IA devono sempre essere verificate da personale qualificato.",
|
||||
"privacy_browser_heading": "Cosa memorizza il mio browser?",
|
||||
"privacy_browser_body": "Il tuo browser memorizza la sessione di accesso per mantenerti connesso e le tue preferenze di lingua e tema. Non vengono utilizzati cookie di tracciamento o pubblicitari.",
|
||||
"privacy_access_heading": "Chi ha accesso ai miei dati?",
|
||||
"privacy_access_body": "I tuoi dati non vengono condivisi con terze parti. Vengono utilizzati esclusivamente per il funzionamento della piattaforma e per fornirti informazioni sulle tue installazioni.",
|
||||
"privacy_close_button": "Chiudi",
|
||||
"sodistorepro": "Sodistore Pro",
|
||||
"numberOfInverters": "Numero di inverter"
|
||||
"timelineResolutionAddedDesc": "Risoluzione aggiunta da {name}."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,9 @@ import {
|
|||
import React, { useContext, useRef, useState } from 'react';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import ExpandMoreTwoToneIcon from '@mui/icons-material/ExpandMoreTwoTone';
|
||||
import ShieldOutlinedIcon from '@mui/icons-material/ShieldOutlined';
|
||||
import { ThemeContext } from '../../../../theme/ThemeProvider';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import '../../../../App.css';
|
||||
import DataPrivacyDialog from '../../../../components/DataPrivacyDialog';
|
||||
|
||||
interface HeaderButtonsProps {
|
||||
language: string;
|
||||
|
|
@ -81,7 +79,6 @@ function HeaderMenu(props: HeaderButtonsProps) {
|
|||
const setThemeName = themeContext;
|
||||
|
||||
const [darkState, setDarkState] = useState(false);
|
||||
const [privacyOpen, setPrivacyOpen] = useState(false);
|
||||
|
||||
const handleThemeChange = () => {
|
||||
setDarkState(!darkState);
|
||||
|
|
@ -135,20 +132,6 @@ function HeaderMenu(props: HeaderButtonsProps) {
|
|||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
classes={{ root: 'MuiListItem-indicators' }}
|
||||
onClick={() => setPrivacyOpen(true)}
|
||||
>
|
||||
<ListItemText
|
||||
primaryTypographyProps={{ noWrap: true }}
|
||||
primary={
|
||||
<Box display="flex" alignItems="center">
|
||||
<ShieldOutlinedIcon fontSize="small" sx={{ mr: 0.5 }} />
|
||||
<FormattedMessage id="privacy_menu_item" defaultMessage="Data & Privacy" />
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
</ListWrapper>
|
||||
<div
|
||||
|
|
@ -169,10 +152,6 @@ function HeaderMenu(props: HeaderButtonsProps) {
|
|||
</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
<DataPrivacyDialog
|
||||
open={privacyOpen}
|
||||
onClose={() => setPrivacyOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,8 +170,7 @@ function SidebarMenu() {
|
|||
accessToSodistore,
|
||||
accessToSalidomo,
|
||||
accessToSodiohome,
|
||||
accessToSodistoreGrid,
|
||||
accessToSodistorePro
|
||||
accessToSodistoreGrid
|
||||
} = useContext(ProductIdContext);
|
||||
|
||||
return (
|
||||
|
|
@ -186,20 +185,37 @@ function SidebarMenu() {
|
|||
}
|
||||
>
|
||||
<SubMenuWrapper>
|
||||
{accessToSodiohome && (
|
||||
{accessToSalimax && (
|
||||
<List component="div">
|
||||
<ListItem component="div">
|
||||
<Button
|
||||
disableRipple
|
||||
component={RouterLink}
|
||||
onClick={closeSidebar}
|
||||
to="/sodiohome_installations"
|
||||
to="/installations"
|
||||
startIcon={<BrightnessLowTwoToneIcon />}
|
||||
>
|
||||
<Box sx={{ marginTop: '3px' }}>
|
||||
<FormattedMessage id="salimax" defaultMessage="Salimax" />
|
||||
</Box>
|
||||
</Button>
|
||||
</ListItem>
|
||||
</List>
|
||||
)}
|
||||
{accessToSodistore && (
|
||||
<List component="div">
|
||||
<ListItem component="div">
|
||||
<Button
|
||||
disableRipple
|
||||
component={RouterLink}
|
||||
onClick={closeSidebar}
|
||||
to="/sodistore_installations"
|
||||
startIcon={<BrightnessLowTwoToneIcon />}
|
||||
>
|
||||
<Box sx={{ marginTop: '3px' }}>
|
||||
<FormattedMessage
|
||||
id="sodiohome"
|
||||
defaultMessage="Sodistore Home"
|
||||
id="sodistore"
|
||||
defaultMessage="Sodistore Max"
|
||||
/>
|
||||
</Box>
|
||||
</Button>
|
||||
|
|
@ -207,20 +223,20 @@ function SidebarMenu() {
|
|||
</List>
|
||||
)}
|
||||
|
||||
{accessToSodistorePro && (
|
||||
{accessToSalidomo && (
|
||||
<List component="div">
|
||||
<ListItem component="div">
|
||||
<Button
|
||||
disableRipple
|
||||
component={RouterLink}
|
||||
onClick={closeSidebar}
|
||||
to="/sodistorepro_installations"
|
||||
to="/salidomo_installations"
|
||||
startIcon={<BrightnessLowTwoToneIcon />}
|
||||
>
|
||||
<Box sx={{ marginTop: '3px' }}>
|
||||
<FormattedMessage
|
||||
id="sodistorepro"
|
||||
defaultMessage="Sodistore Pro"
|
||||
id="salidomo"
|
||||
defaultMessage="Salidomo"
|
||||
/>
|
||||
</Box>
|
||||
</Button>
|
||||
|
|
@ -249,59 +265,20 @@ function SidebarMenu() {
|
|||
</List>
|
||||
)}
|
||||
|
||||
{accessToSodistore && (
|
||||
{accessToSodiohome && (
|
||||
<List component="div">
|
||||
<ListItem component="div">
|
||||
<Button
|
||||
disableRipple
|
||||
component={RouterLink}
|
||||
onClick={closeSidebar}
|
||||
to="/sodistore_installations"
|
||||
to="/sodiohome_installations"
|
||||
startIcon={<BrightnessLowTwoToneIcon />}
|
||||
>
|
||||
<Box sx={{ marginTop: '3px' }}>
|
||||
<FormattedMessage
|
||||
id="sodistore"
|
||||
defaultMessage="Sodistore Max"
|
||||
/>
|
||||
</Box>
|
||||
</Button>
|
||||
</ListItem>
|
||||
</List>
|
||||
)}
|
||||
|
||||
{accessToSalimax && (
|
||||
<List component="div">
|
||||
<ListItem component="div">
|
||||
<Button
|
||||
disableRipple
|
||||
component={RouterLink}
|
||||
onClick={closeSidebar}
|
||||
to="/installations"
|
||||
startIcon={<BrightnessLowTwoToneIcon />}
|
||||
>
|
||||
<Box sx={{ marginTop: '3px' }}>
|
||||
<FormattedMessage id="salimax" defaultMessage="Salimax" />
|
||||
</Box>
|
||||
</Button>
|
||||
</ListItem>
|
||||
</List>
|
||||
)}
|
||||
|
||||
{accessToSalidomo && (
|
||||
<List component="div">
|
||||
<ListItem component="div">
|
||||
<Button
|
||||
disableRipple
|
||||
component={RouterLink}
|
||||
onClick={closeSidebar}
|
||||
to="/salidomo_installations"
|
||||
startIcon={<BrightnessLowTwoToneIcon />}
|
||||
>
|
||||
<Box sx={{ marginTop: '3px' }}>
|
||||
<FormattedMessage
|
||||
id="salidomo"
|
||||
defaultMessage="Salidomo"
|
||||
id="sodiohome"
|
||||
defaultMessage="Sodistore Home"
|
||||
/>
|
||||
</Box>
|
||||
</Button>
|
||||
|
|
|
|||
Loading…
Reference in New Issue