From baaabbecd068bdb0f0a6867edbcbd881b346e86b Mon Sep 17 00:00:00 2001 From: Yinyin Liu Date: Tue, 24 Mar 2026 12:35:50 +0100 Subject: [PATCH 01/30] add AI and cookies usage acknowledgement --- csharp/App/Backend/Controller.cs | 13 +++ csharp/App/Backend/DataTypes/User.cs | 1 + .../src/components/AcknowledgementDialog.tsx | 95 +++++++++++++++++++ .../src/content/dashboards/Log/Log.tsx | 4 +- .../SodiohomeInstallations/index.tsx | 31 +++++- .../src/interfaces/UserTypes.tsx | 1 + typescript/frontend-marios2/src/lang/de.json | 12 ++- typescript/frontend-marios2/src/lang/en.json | 12 ++- typescript/frontend-marios2/src/lang/fr.json | 12 ++- typescript/frontend-marios2/src/lang/it.json | 12 ++- 10 files changed, 181 insertions(+), 12 deletions(-) create mode 100644 typescript/frontend-marios2/src/components/AcknowledgementDialog.tsx diff --git a/csharp/App/Backend/Controller.cs b/csharp/App/Backend/Controller.cs index 69d76a324..8e1edc6f5 100644 --- a/csharp/App/Backend/Controller.cs +++ b/csharp/App/Backend/Controller.cs @@ -2309,4 +2309,17 @@ 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); + } + } diff --git a/csharp/App/Backend/DataTypes/User.cs b/csharp/App/Backend/DataTypes/User.cs index a5ec78224..95aebc50a 100644 --- a/csharp/App/Backend/DataTypes/User.cs +++ b/csharp/App/Backend/DataTypes/User.cs @@ -11,6 +11,7 @@ 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!; diff --git a/typescript/frontend-marios2/src/components/AcknowledgementDialog.tsx b/typescript/frontend-marios2/src/components/AcknowledgementDialog.tsx new file mode 100644 index 000000000..c4a035747 --- /dev/null +++ b/typescript/frontend-marios2/src/components/AcknowledgementDialog.tsx @@ -0,0 +1,95 @@ +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 = 1; + +interface AcknowledgementDialogProps { + open: boolean; + onAcknowledge: () => void; +} + +const AcknowledgementDialog: React.FC = ({ + open, + onAcknowledge +}) => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default AcknowledgementDialog; diff --git a/typescript/frontend-marios2/src/content/dashboards/Log/Log.tsx b/typescript/frontend-marios2/src/content/dashboards/Log/Log.tsx index f2e7373f1..2e4e5853e 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Log/Log.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Log/Log.tsx @@ -248,9 +248,9 @@ function Log(props: LogProps) { if (source === 'KnowledgeBase') return ; if (source === 'MistralAI') - return ; + return ; if (source === 'MistralFailed') - return ; + return ; return ; }; diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx index 398959ea3..bd4adaba6 100644 --- a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx @@ -13,6 +13,8 @@ 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; @@ -21,7 +23,25 @@ interface SodioHomeInstallationTabsProps { function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) { const location = useLocation(); const context = useContext(UserContext); - const { currentUser } = context; + 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 tabList = [ 'live', 'overview', @@ -415,6 +435,10 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) { return sodiohomeInstallations.length > 1 ? ( <> + ) : sodiohomeInstallations.length === 1 ? ( <> - {' '} + Date: Tue, 24 Mar 2026 13:42:36 +0100 Subject: [PATCH 02/30] update sodistore home information tab based on weekly meeting's feedback --- csharp/App/Backend/DataTypes/Installation.cs | 7 ++ .../Services/ReportAggregationService.cs | 9 +- .../Backend/Services/WeeklyReportService.cs | 4 +- .../Information/InformationSodistoreHome.tsx | 87 +++++++++++++++---- .../src/content/dashboards/Log/Log.tsx | 2 +- .../FlatInstallationView.tsx | 36 ++++---- .../SodistorehomeInstallationForm.tsx | 43 +-------- .../dashboards/Tickets/CreateTicketModal.tsx | 2 +- .../src/interfaces/InstallationTypes.tsx | 7 ++ typescript/frontend-marios2/src/lang/de.json | 7 ++ typescript/frontend-marios2/src/lang/en.json | 7 ++ typescript/frontend-marios2/src/lang/fr.json | 7 ++ typescript/frontend-marios2/src/lang/it.json | 7 ++ 13 files changed, 142 insertions(+), 83 deletions(-) diff --git a/csharp/App/Backend/DataTypes/Installation.cs b/csharp/App/Backend/DataTypes/Installation.cs index bdf63571d..0345e3292 100644 --- a/csharp/App/Backend/DataTypes/Installation.cs +++ b/csharp/App/Backend/DataTypes/Installation.cs @@ -27,6 +27,13 @@ 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; } = ""; diff --git a/csharp/App/Backend/Services/ReportAggregationService.cs b/csharp/App/Backend/Services/ReportAggregationService.cs index 4dbab2985..50614e28c 100644 --- a/csharp/App/Backend/Services/ReportAggregationService.cs +++ b/csharp/App/Backend/Services/ReportAggregationService.cs @@ -364,12 +364,15 @@ 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, - installation?.Location, installation?.Country, installation?.Region); + weatherCity, installation?.Country, weatherRegion); var monthlySummary = new MonthlyReportSummary { @@ -591,6 +594,8 @@ 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, @@ -599,7 +604,7 @@ public static class ReportAggregationService report.TotalBatteryCharged, report.TotalBatteryDischarged, report.TotalEnergySaved, report.TotalSavingsCHF, report.SelfSufficiencyPercent, report.BatteryEfficiencyPercent, language, - installation?.Location, installation?.Country, installation?.Region)); + weatherCity, installation?.Country, weatherRegion)); } /// Cached-or-generated AI insight for a stored YearlyReportSummary. diff --git a/csharp/App/Backend/Services/WeeklyReportService.cs b/csharp/App/Backend/Services/WeeklyReportService.cs index 2fe3e8047..67c1b07b3 100644 --- a/csharp/App/Backend/Services/WeeklyReportService.cs +++ b/csharp/App/Backend/Services/WeeklyReportService.cs @@ -179,9 +179,9 @@ public static class WeeklyReportService // 4. Get installation location for weather forecast var installation = Db.GetInstallationById(installationId); - var location = installation?.Location; + var location = !string.IsNullOrWhiteSpace(installation?.City) ? installation.City : installation?.Location; var country = installation?.Country; - var region = installation?.Region; + var region = !string.IsNullOrWhiteSpace(installation?.Canton) ? installation.Canton : installation?.Region; Console.WriteLine($"[WeeklyReportService] Installation {installationId}: Location='{location}', Region='{region}', Country='{country}', HourlyRecords={currentHourlyData.Count}"); return await GenerateReportFromDataAsync( diff --git a/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx b/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx index 3cb43e526..a94a91042 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx @@ -60,7 +60,7 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) { const theme = useTheme(); const intl = useIntl(); const [formValues, setFormValues] = useState(props.values); - const requiredFields = ['name', 'region', 'location', 'country']; + const requiredFields = ['name']; const [openModalDeleteInstallation, setOpenModalDeleteInstallation] = useState(false); const [pendingPreset, setPendingPreset] = useState(null); @@ -95,7 +95,7 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) { const DeviceTypes = [ { id: 3, name: 'Growatt' }, - { id: 4, name: 'Sinexcel' } + { id: 4, name: 'inesco 12K - WR Hybrid' } ]; // Preset state — initializes from persisted installationModel, empty for legacy @@ -533,27 +533,45 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
} - name="region" - value={formValues.region} + label={} + name="street" + value={formValues.street || ''} onChange={handleChange} variant="outlined" fullWidth - required={canEdit} - error={canEdit && formValues.region === ''} inputProps={{ readOnly: !canEdit }} />
} - name="location" - value={formValues.location} + label={} + name="postCode" + value={formValues.postCode || ''} + onChange={handleChange} + variant="outlined" + fullWidth + inputProps={{ readOnly: !canEdit }} + /> +
+
+ } + name="city" + value={formValues.city || ''} + onChange={handleChange} + variant="outlined" + fullWidth + inputProps={{ readOnly: !canEdit }} + /> +
+
+ } + name="canton" + value={formValues.canton || ''} onChange={handleChange} variant="outlined" fullWidth - required={canEdit} - error={canEdit && formValues.location === ''} inputProps={{ readOnly: !canEdit }} />
@@ -561,12 +579,21 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) { } name="country" - value={formValues.country} + value={formValues.country || ''} + onChange={handleChange} + variant="outlined" + fullWidth + inputProps={{ readOnly: !canEdit }} + /> + +
+ } + name="distributionPartner" + value={formValues.distributionPartner || ''} onChange={handleChange} variant="outlined" fullWidth - required={canEdit} - error={canEdit && formValues.country === ''} inputProps={{ readOnly: !canEdit }} />
@@ -696,6 +723,18 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) { +
+ } + name="serialNumber" + value={formValues.serialNumber || ''} + onChange={handleChange} + variant="outlined" + fullWidth + inputProps={{ readOnly: !canEdit }} + /> +
+
} - name="serialNumber" - value={formValues.serialNumber} + label={} + name="inverterFirmwareVersion" + value={formValues.inverterFirmwareVersion || ''} + onChange={handleChange} + variant="outlined" + fullWidth + inputProps={{ readOnly: !canEdit }} + /> +
+ +
+ } + name="batteryFirmwareVersion" + value={formValues.batteryFirmwareVersion || ''} onChange={handleChange} variant="outlined" fullWidth diff --git a/typescript/frontend-marios2/src/content/dashboards/Log/Log.tsx b/typescript/frontend-marios2/src/content/dashboards/Log/Log.tsx index 2e4e5853e..c883c9f6b 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Log/Log.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Log/Log.tsx @@ -288,7 +288,7 @@ function Log(props: LogProps) { onChange={e => { setDemoAlarm(e.target.value); setDemoResult(null); }} sx={{ minWidth: 260 }} > - Sinexcel + inesco 12K - WR Hybrid {DEMO_ALARMS.sinexcel.map(a => ( {a} ))} diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/FlatInstallationView.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/FlatInstallationView.tsx index 982e187b4..bd5a19d79 100644 --- a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/FlatInstallationView.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/FlatInstallationView.tsx @@ -96,14 +96,14 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => { - - - - + + + + @@ -146,19 +146,6 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => { - - - {installation.location} - - - { noWrap sx={{ marginTop: '10px', fontSize: 'small' }} > - {installation.region} + {installation.device === 3 ? 'Growatt' : installation.device === 4 ? 'inesco 12K - WR Hybrid' : ''} + + + + + + {installation.canton || ''} diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/SodistorehomeInstallationForm.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/SodistorehomeInstallationForm.tsx index 764189e54..be7804ac0 100644 --- a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/SodistorehomeInstallationForm.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/SodistorehomeInstallationForm.tsx @@ -30,18 +30,15 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros) const [open, setOpen] = useState(true); const [formValues, setFormValues] = useState>({ name: '', - region: '', - location: '', - country: '', vpnIp: '', installationModel: '', externalEms: 'No', }); - const requiredFields = ['name', 'location', 'country', 'vpnIp', 'installationModel']; + const requiredFields = ['name', 'vpnIp', 'installationModel']; const DeviceTypes = [ { id: 3, name: 'Growatt' }, - { id: 4, name: 'Sinexcel' } + { id: 4, name: 'inesco 12K - WR Hybrid' } ]; const installationContext = useContext(InstallationsContext); const { createInstallation, loading, setLoading, error, setError } = @@ -127,42 +124,6 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros) error={formValues.name === ''} />
-
- } - name="region" - value={formValues.region} - onChange={handleChange} - required - error={formValues.region === ''} - /> -
-
- - } - name="location" - value={formValues.location} - onChange={handleChange} - required - error={formValues.location === ''} - /> -
- -
- - } - name="country" - value={formValues.country} - onChange={handleChange} - required - error={formValues.country === ''} - /> -
-
} diff --git a/typescript/frontend-marios2/src/content/dashboards/Tickets/CreateTicketModal.tsx b/typescript/frontend-marios2/src/content/dashboards/Tickets/CreateTicketModal.tsx index f65602627..a6baa3296 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Tickets/CreateTicketModal.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Tickets/CreateTicketModal.tsx @@ -47,7 +47,7 @@ const deviceOptionsByProduct: Record ], 2: [ { value: 3, label: 'Growatt' }, - { value: 4, label: 'Sinexcel' } + { value: 4, label: 'inesco 12K - WR Hybrid' } ] }; diff --git a/typescript/frontend-marios2/src/interfaces/InstallationTypes.tsx b/typescript/frontend-marios2/src/interfaces/InstallationTypes.tsx index f5f1f40b5..01f8a3d4c 100644 --- a/typescript/frontend-marios2/src/interfaces/InstallationTypes.tsx +++ b/typescript/frontend-marios2/src/interfaces/InstallationTypes.tsx @@ -7,6 +7,13 @@ 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; diff --git a/typescript/frontend-marios2/src/lang/de.json b/typescript/frontend-marios2/src/lang/de.json index bcaf03868..9bf46ca08 100644 --- a/typescript/frontend-marios2/src/lang/de.json +++ b/typescript/frontend-marios2/src/lang/de.json @@ -6,6 +6,13 @@ "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", diff --git a/typescript/frontend-marios2/src/lang/en.json b/typescript/frontend-marios2/src/lang/en.json index bd17ce25e..b8e9aa4a1 100644 --- a/typescript/frontend-marios2/src/lang/en.json +++ b/typescript/frontend-marios2/src/lang/en.json @@ -2,6 +2,13 @@ "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", diff --git a/typescript/frontend-marios2/src/lang/fr.json b/typescript/frontend-marios2/src/lang/fr.json index 1774b7bf8..8357a50d6 100644 --- a/typescript/frontend-marios2/src/lang/fr.json +++ b/typescript/frontend-marios2/src/lang/fr.json @@ -4,6 +4,13 @@ "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", diff --git a/typescript/frontend-marios2/src/lang/it.json b/typescript/frontend-marios2/src/lang/it.json index cf7575fd6..9759bb9a0 100644 --- a/typescript/frontend-marios2/src/lang/it.json +++ b/typescript/frontend-marios2/src/lang/it.json @@ -2,6 +2,13 @@ "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", From 4bb35c6951ba945caa58355a7cd605ceed1cc5e4 Mon Sep 17 00:00:00 2001 From: Yinyin Liu Date: Tue, 24 Mar 2026 14:38:23 +0100 Subject: [PATCH 03/30] frontend bug fix --- .../dashboards/Information/InformationSodistoreHome.tsx | 8 +++----- .../dashboards/Information/installationSetupUtils.ts | 8 ++++++++ .../SodiohomeInstallations/FlatInstallationView.tsx | 3 ++- .../SodistorehomeInstallationForm.tsx | 7 ++----- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx b/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx index a94a91042..c96266492 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx @@ -42,6 +42,7 @@ import { remapTree, computeFlatValues, wouldLoseData, + SODIOHOME_DEVICE_TYPES, } from './installationSetupUtils'; interface InformationSodistorehomeProps { @@ -93,10 +94,7 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) { return [value.trim()]; }; - const DeviceTypes = [ - { id: 3, name: 'Growatt' }, - { id: 4, name: 'inesco 12K - WR Hybrid' } - ]; + const DeviceTypes = SODIOHOME_DEVICE_TYPES; // Preset state — initializes from persisted installationModel, empty for legacy const [selectedPreset, setSelectedPreset] = useState( @@ -741,7 +739,7 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) { shrink sx={{ fontSize: 14, backgroundColor: 'white', px: 0.5 }} > - Installation Model +
+ )}
+ {isSodistorePro ? ( +
+ } + type="text" + value={inverterCount} + onChange={(e) => handleInverterCountChange(e.target.value)} + variant="outlined" + fullWidth + inputProps={{ readOnly: !canEdit }} + /> +
+ ) : (
+ )}
diff --git a/typescript/frontend-marios2/src/content/dashboards/Information/installationSetupUtils.ts b/typescript/frontend-marios2/src/content/dashboards/Information/installationSetupUtils.ts index daf7c966a..6c4f4beb1 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Information/installationSetupUtils.ts +++ b/typescript/frontend-marios2/src/content/dashboards/Information/installationSetupUtils.ts @@ -19,6 +19,14 @@ export const INSTALLATION_PRESETS: Record = { '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 }, () => '')) diff --git a/typescript/frontend-marios2/src/content/dashboards/Installations/fetchData.tsx b/typescript/frontend-marios2/src/content/dashboards/Installations/fetchData.tsx index 9f69c4fe5..0fd655380 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Installations/fetchData.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Installations/fetchData.tsx @@ -131,7 +131,7 @@ export const fetchAggregatedDataJson = ( } else if (r.status === 200) { const jsontext = await r.text(); - if (product === 2) { + if (product === 2 || product === 5) { return parseSinexcelAggregatedData(jsontext); } diff --git a/typescript/frontend-marios2/src/content/dashboards/ManageAccess/UserAccess.tsx b/typescript/frontend-marios2/src/content/dashboards/ManageAccess/UserAccess.tsx index 3f035d2ae..4021a2cad 100644 --- a/typescript/frontend-marios2/src/content/dashboards/ManageAccess/UserAccess.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/ManageAccess/UserAccess.tsx @@ -107,13 +107,15 @@ function UserAccess(props: UserAccessProps) { const fetchAvailableInstallations = useCallback(async () => { try { - const [res0, res1, res2, res3] = await Promise.all([ + const [res0, res1, res2, res3, res4, res5] = await Promise.all([ axiosConfig.get(`/GetAllInstallationsFromProduct?product=0`), axiosConfig.get(`/GetAllInstallationsFromProduct?product=1`), axiosConfig.get(`/GetAllInstallationsFromProduct?product=2`), - axiosConfig.get(`/GetAllInstallationsFromProduct?product=3`) + axiosConfig.get(`/GetAllInstallationsFromProduct?product=3`), + axiosConfig.get(`/GetAllInstallationsFromProduct?product=4`), + axiosConfig.get(`/GetAllInstallationsFromProduct?product=5`) ]); - setAvailableInstallations([...res0.data, ...res1.data, ...res2.data, ...res3.data]); + setAvailableInstallations([...res0.data, ...res1.data, ...res2.data, ...res3.data, ...res4.data, ...res5.data]); } catch (err) { if (err.response && err.response.status === 401) removeToken(); } diff --git a/typescript/frontend-marios2/src/content/dashboards/Overview/overview.tsx b/typescript/frontend-marios2/src/content/dashboards/Overview/overview.tsx index 023be7bd5..e3c88ae09 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Overview/overview.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Overview/overview.tsx @@ -817,7 +817,7 @@ function Overview(props: OverviewProps) { type: 'bar', color: '#ff9900' }, - ...(product !== 2 ? [{ + ...((product !== 2 && product !== 5) ? [{ name: 'Net Energy', color: '#e65100', type: 'line', @@ -840,7 +840,7 @@ function Overview(props: OverviewProps) { alignItems="stretch" spacing={3} > - {!(aggregatedData && product === 2) && ( + {!(aggregatedData && (product === 2 || product === 5)) && ( )} - + - {product !== 2 && ( + {(product !== 2 && product !== 5) && ( )} - {product !== 2 && ( + {(product !== 2 && product !== 5) && ( - {aggregatedData && product === 2 && ( + {aggregatedData && (product === 2 || product === 5) && ( - + - {product !== 2 && ( + {(product !== 2 && product !== 5) && ( { const navigate = useNavigate(); const [selectedInstallation, setSelectedInstallation] = useState(-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. @@ -51,7 +53,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => { setSelectedInstallation(-1); navigate( - routes.sodiohome_installations + + baseRoute + routes.list + routes.installation + `${installationID}` + @@ -82,9 +84,9 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => { sx={{ display: currentLocation.pathname === - routes.sodiohome_installations + 'list' || + baseRoute + 'list' || currentLocation.pathname === - routes.sodiohome_installations + routes.list + baseRoute + routes.list ? 'block' : 'none' }} diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx index 91c3bc9d3..00631620d 100644 --- a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/Installation.tsx @@ -50,7 +50,9 @@ function SodioHomeInstallation(props: singleInstallationProps) { const s3Bucket = props.current_installation.s3BucketId.toString() + '-' + - 'e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa'; + (props.current_installation.product === 5 + ? '325c9373-9025-4a8d-bf5a-f9eedf1f155c' + : 'e7b9a240-3c5d-4d2e-a019-6d8b1f7b73fa'); const context = useContext(UserContext); const { currentUser } = context; diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/InstallationSearch.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/InstallationSearch.tsx index 521f4622e..eac127311 100644 --- a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/InstallationSearch.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/InstallationSearch.tsx @@ -10,12 +10,14 @@ 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(() => { @@ -46,9 +48,9 @@ function InstallationSearch(props: installationSearchProps) { sx={{ display: currentLocation.pathname === - routes.sodiohome_installations + 'list' || + baseRoute + 'list' || currentLocation.pathname === - routes.sodiohome_installations + routes.list + baseRoute + routes.list ? 'block' : 'none' }} @@ -79,7 +81,7 @@ function InstallationSearch(props: installationSearchProps) { - + {filteredData.map((installation) => { return ( diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/SodistorehomeInstallationForm.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/SodistorehomeInstallationForm.tsx index 038f7bb42..88e2814e3 100644 --- a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/SodistorehomeInstallationForm.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/SodistorehomeInstallationForm.tsx @@ -23,20 +23,26 @@ 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>({ name: '', vpnIp: '', installationModel: '', externalEms: 'No', + ...(isSodistorePro ? { device: 4 } : {}), }); - const requiredFields = ['name', 'vpnIp', 'installationModel']; + const [inverterCount, setInverterCount] = useState(''); + const requiredFields = ['name', 'vpnIp', ...(isSodistorePro ? [] : ['installationModel'])]; - const DeviceTypes = SODIOHOME_DEVICE_TYPES; + const DeviceTypes = isSodistorePro + ? [{ id: 4, name: 'inesco 12K - WR Hybrid' }] + : SODIOHOME_DEVICE_TYPES; const installationContext = useContext(InstallationsContext); const { createInstallation, loading, setLoading, error, setError } = installationContext; @@ -52,7 +58,10 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros) const handleSubmit = async (e) => { setLoading(true); formValues.parentId = props.parentid; - formValues.product = 2; + formValues.product = props.product ?? 2; + if (isSodistorePro) { + formValues.installationModel = inverterCount; + } const responseData = await createInstallation(formValues); props.submit(); }; @@ -66,6 +75,9 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros) return false; } } + if (isSodistorePro && (!inverterCount || parseInt(inverterCount, 10) < 1)) { + return false; + } return true; }; @@ -132,6 +144,29 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros) />
+ {isSodistorePro ? ( +
+ + } + 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} + /> +
+ ) : (
+ )} + {!isSodistorePro && (
+ )}
{ let path = location.pathname.split('/'); @@ -484,6 +485,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) { @@ -496,7 +498,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) { path={'*'} element={ } > diff --git a/typescript/frontend-marios2/src/content/dashboards/Tickets/TicketDetail.tsx b/typescript/frontend-marios2/src/content/dashboards/Tickets/TicketDetail.tsx index 60c35f955..181791aab 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Tickets/TicketDetail.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Tickets/TicketDetail.tsx @@ -742,7 +742,8 @@ function TicketDetailPage() { 1: routes.salidomo_installations, 2: routes.sodiohome_installations, 3: routes.sodistore_installations, - 4: routes.sodistoregrid_installations + 4: routes.sodistoregrid_installations, + 5: routes.sodistorepro_installations }; const prefix = productRoutes[detail.installationProduct] ?? routes.installations; navigate( diff --git a/typescript/frontend-marios2/src/content/dashboards/Tree/CustomTreeItem.tsx b/typescript/frontend-marios2/src/content/dashboards/Tree/CustomTreeItem.tsx index 469d9658d..b125a9f40 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Tree/CustomTreeItem.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Tree/CustomTreeItem.tsx @@ -60,6 +60,8 @@ function CustomTreeItem(props: CustomTreeItemProps) { ? routes.salidomo_installations : installation.product == 2 ? routes.sodiohome_installations + : installation.product == 5 + ? routes.sodistorepro_installations : routes.sodistore_installations; let folder_path = @@ -69,6 +71,8 @@ function CustomTreeItem(props: CustomTreeItemProps) { ? routes.salidomo_installations : product == 2 ? routes.sodiohome_installations + : product == 5 + ? routes.sodistorepro_installations : routes.sodistore_installations; if (installation.type != 'Folder') { diff --git a/typescript/frontend-marios2/src/content/dashboards/Tree/Information.tsx b/typescript/frontend-marios2/src/content/dashboards/Tree/Information.tsx index 3e8c936f6..8a07848b3 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Tree/Information.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Tree/Information.tsx @@ -65,11 +65,12 @@ function TreeInformation(props: TreeInformationProps) { setProduct(e.target.value); // Directly update the product state }; - const ProductTypes = ['Salimax', 'Salidomo', 'SodistoreHome', 'SodistoreMax', 'SodistoreGrid']; + const ProductTypes = ['Salimax', 'Salidomo', 'SodistoreHome', 'SodistoreMax', 'SodistoreGrid', 'SodistorePro']; const ProductDisplayNames: Record = { 'SodistoreHome': 'Sodistore Home', 'SodistoreMax': 'Sodistore Max', - 'SodistoreGrid': 'Sodistore Grid' + 'SodistoreGrid': 'Sodistore Grid', + 'SodistorePro': 'Sodistore Pro' }; const isMobile = window.innerWidth <= 1490; @@ -345,11 +346,12 @@ function TreeInformation(props: TreeInformationProps) { /> )} - {openModalInstallation && product == 'SodistoreHome' && ( + {openModalInstallation && (product == 'SodistoreHome' || product == 'SodistorePro') && ( )} diff --git a/typescript/frontend-marios2/src/contexts/InstallationsContextProvider.tsx b/typescript/frontend-marios2/src/contexts/InstallationsContextProvider.tsx index a663f4ae3..926689b19 100644 --- a/typescript/frontend-marios2/src/contexts/InstallationsContextProvider.tsx +++ b/typescript/frontend-marios2/src/contexts/InstallationsContextProvider.tsx @@ -191,7 +191,7 @@ const InstallationsContextProvider = ({ `/GetAllInstallationsFromProduct?product=${product}` ); - if (product === 2) { + if (product === 2 || product === 5) { setSodiohomeInstallations(res.data); } else if (product === 1) { setSalidomoInstallations(res.data); diff --git a/typescript/frontend-marios2/src/contexts/ProductIdContextProvider.tsx b/typescript/frontend-marios2/src/contexts/ProductIdContextProvider.tsx index 0b14ffb7c..cfe6720a9 100644 --- a/typescript/frontend-marios2/src/contexts/ProductIdContextProvider.tsx +++ b/typescript/frontend-marios2/src/contexts/ProductIdContextProvider.tsx @@ -10,11 +10,13 @@ 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. @@ -49,6 +51,10 @@ 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( // productMapping[Object.keys(productMapping).find((key) => location.includes(key)) || ''] || -1 @@ -56,6 +62,8 @@ export const ProductIdContextProvider = ({ const [product, setProduct] = useState(() => { 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')) { @@ -92,6 +100,10 @@ export const ProductIdContextProvider = ({ setAccessToSodistoreGrid(access); localStorage.setItem('accessToSodistoreGrid', JSON.stringify(access)); }; + const changeAccessSodistorePro = (access: boolean) => { + setAccessToSodistorePro(access); + localStorage.setItem('accessToSodistorePro', JSON.stringify(access)); + }; return ( {children} diff --git a/typescript/frontend-marios2/src/interfaces/Chart.tsx b/typescript/frontend-marios2/src/interfaces/Chart.tsx index 7a331de8e..754611b06 100644 --- a/typescript/frontend-marios2/src/interfaces/Chart.tsx +++ b/typescript/frontend-marios2/src/interfaces/Chart.tsx @@ -86,7 +86,7 @@ export const transformInputToBatteryViewDataJson = async ( }> => { const prefixes = ['', 'k', 'M', 'G', 'T']; const MAX_NUMBER = 9999999; - const isSodioHome = product === 2; + const isSodioHome = product === 2 || product === 5; 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 == 0 || product == 2 || product == 3 || product == 4 || product == 5 ? 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 @@ -393,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 + const pathsToSearch = (product == 2 || product == 5) ? [ 'SODIOHOME_SOC', 'SODIOHOME_TEMPERATURE', @@ -516,8 +516,8 @@ export const transformInputToDailyDataJson = async ( let value: number | undefined = undefined; - if (product === 2) { - // SodioHome: use top-level aggregated values (Sinexcel multi-inverter) + if (product === 2 || product === 5) { + // SodioHome/SodistorePro: use top-level aggregated values (Sinexcel multi-inverter) const inv = result?.InverterRecord; if (inv) { switch (category_index) { @@ -735,7 +735,7 @@ export const transformInputToAggregatedDataJson = async ( const timestampPromises = []; while (currentDay.isBefore(end_date)) { - const dateFormat = product === 2 + const dateFormat = (product === 2 || product === 5) ? currentDay.format('DDMMYYYY') : currentDay.format('YYYY-MM-DD'); timestampPromises.push( diff --git a/typescript/frontend-marios2/src/lang/de.json b/typescript/frontend-marios2/src/lang/de.json index 9bf46ca08..333a65ef5 100644 --- a/typescript/frontend-marios2/src/lang/de.json +++ b/typescript/frontend-marios2/src/lang/de.json @@ -648,5 +648,7 @@ "terms_cookies_body": "Browser-Speicher wird für Anmeldesitzungen und Benutzereinstellungen verwendet. Dies ist für die korrekte Funktion der Plattform erforderlich.", "terms_usage_heading": "Nutzungsbedingungen", "terms_usage_body": "Durch die Nutzung dieser Plattform erkennen Sie die allgemeinen Nutzungsbedingungen von inesco Energy an. Bei Fragen wenden Sie sich bitte an Ihren Systemadministrator.", - "terms_acknowledge_button": "Ich verstehe" + "terms_acknowledge_button": "Ich verstehe", + "sodistorepro": "Sodistore Pro", + "numberOfInverters": "Anzahl der Wechselrichter" } \ No newline at end of file diff --git a/typescript/frontend-marios2/src/lang/en.json b/typescript/frontend-marios2/src/lang/en.json index b8e9aa4a1..44bd3d72a 100644 --- a/typescript/frontend-marios2/src/lang/en.json +++ b/typescript/frontend-marios2/src/lang/en.json @@ -396,5 +396,7 @@ "terms_cookies_body": "Browser storage is used for login sessions and user preferences. This is required for the platform to function correctly.", "terms_usage_heading": "Terms of Use", "terms_usage_body": "By using this platform, you acknowledge the general terms of use of inesco Energy. For questions, please contact your system administrator.", - "terms_acknowledge_button": "I understand" + "terms_acknowledge_button": "I understand", + "sodistorepro": "Sodistore Pro", + "numberOfInverters": "Number of Inverters" } diff --git a/typescript/frontend-marios2/src/lang/fr.json b/typescript/frontend-marios2/src/lang/fr.json index 8357a50d6..97a5cedf7 100644 --- a/typescript/frontend-marios2/src/lang/fr.json +++ b/typescript/frontend-marios2/src/lang/fr.json @@ -648,5 +648,7 @@ "terms_cookies_body": "Le stockage du navigateur est utilisé pour les sessions de connexion et les préférences utilisateur. Ceci est nécessaire au bon fonctionnement de la plateforme.", "terms_usage_heading": "Conditions d'utilisation", "terms_usage_body": "En utilisant cette plateforme, vous reconnaissez les conditions générales d'utilisation d'inesco Energy. Pour toute question, veuillez contacter votre administrateur système.", - "terms_acknowledge_button": "Je comprends" + "terms_acknowledge_button": "Je comprends", + "sodistorepro": "Sodistore Pro", + "numberOfInverters": "Nombre d'onduleurs" } \ No newline at end of file diff --git a/typescript/frontend-marios2/src/lang/it.json b/typescript/frontend-marios2/src/lang/it.json index 9759bb9a0..e68e4696d 100644 --- a/typescript/frontend-marios2/src/lang/it.json +++ b/typescript/frontend-marios2/src/lang/it.json @@ -648,5 +648,7 @@ "terms_cookies_body": "L'archiviazione del browser viene utilizzata per le sessioni di accesso e le preferenze utente. Questo è necessario per il corretto funzionamento della piattaforma.", "terms_usage_heading": "Condizioni d'uso", "terms_usage_body": "Utilizzando questa piattaforma, si riconoscono le condizioni generali d'uso di inesco Energy. Per domande, contattare l'amministratore di sistema.", - "terms_acknowledge_button": "Ho capito" + "terms_acknowledge_button": "Ho capito", + "sodistorepro": "Sodistore Pro", + "numberOfInverters": "Numero di inverter" } diff --git a/typescript/frontend-marios2/src/layouts/SidebarLayout/Sidebar/SidebarMenu/index.tsx b/typescript/frontend-marios2/src/layouts/SidebarLayout/Sidebar/SidebarMenu/index.tsx index 3a25cf5ef..402a44b25 100644 --- a/typescript/frontend-marios2/src/layouts/SidebarLayout/Sidebar/SidebarMenu/index.tsx +++ b/typescript/frontend-marios2/src/layouts/SidebarLayout/Sidebar/SidebarMenu/index.tsx @@ -170,7 +170,8 @@ function SidebarMenu() { accessToSodistore, accessToSalidomo, accessToSodiohome, - accessToSodistoreGrid + accessToSodistoreGrid, + accessToSodistorePro } = useContext(ProductIdContext); return ( @@ -285,6 +286,27 @@ function SidebarMenu() { )} + + {accessToSodistorePro && ( + + + + + + )} From 2681248bdcd554cec656a4d4920e919b23880bfb Mon Sep 17 00:00:00 2001 From: Yinyin Liu Date: Thu, 26 Mar 2026 08:52:43 +0100 Subject: [PATCH 06/30] fix tree path issues of all products --- typescript/frontend-marios2/src/App.tsx | 39 +++++++++---------- .../dashboards/Information/Information.tsx | 12 ++++-- .../dashboards/Installations/index.tsx | 4 ++ .../SodiohomeInstallations/index.tsx | 14 ++++--- .../dashboards/Tickets/CreateTicketModal.tsx | 3 +- .../dashboards/Tree/CustomTreeItem.tsx | 8 ++++ .../src/content/dashboards/Users/userForm.tsx | 30 +++++++------- .../contexts/InstallationsContextProvider.tsx | 26 ++++++++++++- 8 files changed, 89 insertions(+), 47 deletions(-) diff --git a/typescript/frontend-marios2/src/App.tsx b/typescript/frontend-marios2/src/App.tsx index 368465a5f..088283b84 100644 --- a/typescript/frontend-marios2/src/App.tsx +++ b/typescript/frontend-marios2/src/App.tsx @@ -95,11 +95,19 @@ function App() { const Login = Loader(lazy(() => import('src/components/login'))); const Users = Loader(lazy(() => import('src/content/dashboards/Users'))); - const loginToResetPassword = () => { + useEffect(() => { + if (!username || token) return; + 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); @@ -108,27 +116,10 @@ function App() { 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 if (response.data.accessToSodistorePro) { - navigate(routes.sodistorepro_installations); - } else { - navigate(routes.sodiohome_installations); - } } }) .catch(() => {}); - }; - - if (username) { - loginToResetPassword(); - } + }, [username]); if (!token) { return ( @@ -162,8 +153,14 @@ function App() { if (token && currentUser?.mustResetPassword) { return ( - - + + + + ); } diff --git a/typescript/frontend-marios2/src/content/dashboards/Information/Information.tsx b/typescript/frontend-marios2/src/content/dashboards/Information/Information.tsx index 01667ed5c..99b3144dd 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Information/Information.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Information/Information.tsx @@ -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; + const isSodistore = formValues.product === 3 || formValues.product === 4 || formValues.product === 5; const [networkProviders, setNetworkProviders] = useState([]); const [loadingProviders, setLoadingProviders] = useState(false); @@ -426,12 +426,18 @@ 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' } diff --git a/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx b/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx index 8db02e68a..500088bde 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx @@ -458,6 +458,10 @@ function InstallationTabs(props: InstallationTabsProps) { + ) : props.product === 5 ? ( + ) : ( (false); const { sodiohomeInstallations, + sodistoreProInstallations, fetchAllInstallations, socket, openSocket, @@ -67,6 +68,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) { } = 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('/'); @@ -258,11 +260,11 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) { !location.pathname.includes('folder'); // Determine if current installation is Growatt (device=3) to hide report tab - const currentInstallation = sodiohomeInstallations.find((i) => + const currentInstallation = installations.find((i) => location.pathname.includes(`/${i.id}/`) ); const isGrowatt = currentInstallation?.device === 3 - || (sodiohomeInstallations.length === 1 && sodiohomeInstallations[0].device === 3); + || (installations.length === 1 && installations[0].device === 3); const tabs = inInstallationView && currentUser.userType == UserType.admin ? [ @@ -434,7 +436,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) { } ]; - return sodiohomeInstallations.length > 1 ? ( + return installations.length > 1 ? ( <> @@ -507,7 +509,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) { - ) : sodiohomeInstallations.length === 1 ? ( + ) : installations.length === 1 ? ( <> diff --git a/typescript/frontend-marios2/src/content/dashboards/Tickets/CreateTicketModal.tsx b/typescript/frontend-marios2/src/content/dashboards/Tickets/CreateTicketModal.tsx index a6baa3296..d10927ea3 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Tickets/CreateTicketModal.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Tickets/CreateTicketModal.tsx @@ -37,7 +37,8 @@ const productOptions = [ { value: 1, label: 'Salidomo' }, { value: 2, label: 'Sodistore Home' }, { value: 3, label: 'Sodistore Max' }, - { value: 4, label: 'Sodistore Grid' } + { value: 4, label: 'Sodistore Grid' }, + { value: 5, label: 'Sodistore Pro' } ]; const deviceOptionsByProduct: Record = { diff --git a/typescript/frontend-marios2/src/content/dashboards/Tree/CustomTreeItem.tsx b/typescript/frontend-marios2/src/content/dashboards/Tree/CustomTreeItem.tsx index b125a9f40..06ba87bc0 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Tree/CustomTreeItem.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Tree/CustomTreeItem.tsx @@ -60,6 +60,8 @@ 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; @@ -71,6 +73,8 @@ 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; @@ -213,6 +217,10 @@ 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', diff --git a/typescript/frontend-marios2/src/content/dashboards/Users/userForm.tsx b/typescript/frontend-marios2/src/content/dashboards/Users/userForm.tsx index 652461f34..1d849e3f5 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Users/userForm.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Users/userForm.tsx @@ -72,22 +72,24 @@ function userForm(props: userFormProps) { setLoading(true); try { - // fetch product 0 - const res0 = await axiosConfig.get( - `/GetAllInstallationsFromProduct?product=0` - ); - const installations0 = res0.data; + 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 1 - const res1 = await axiosConfig.get( - `/GetAllInstallationsFromProduct?product=3` - ); - const installations1 = res1.data; + const combined = [ + ...res0.data, + ...res1.data, + ...res2.data, + ...res3.data, + ...res4.data, + ...res5.data + ]; - // aggregate - const combined = [...installations0, ...installations1]; - - // update setInstallations(combined); } catch (err) { if (err.response && err.response.status === 401) { diff --git a/typescript/frontend-marios2/src/contexts/InstallationsContextProvider.tsx b/typescript/frontend-marios2/src/contexts/InstallationsContextProvider.tsx index 926689b19..1efe81d81 100644 --- a/typescript/frontend-marios2/src/contexts/InstallationsContextProvider.tsx +++ b/typescript/frontend-marios2/src/contexts/InstallationsContextProvider.tsx @@ -34,6 +34,9 @@ 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); @@ -105,10 +108,24 @@ 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 = {}; @@ -116,7 +133,8 @@ const InstallationsContextProvider = ({ salidomoInstallations, salimax_or_sodistore_Installations, sodiohomeInstallations, - sodistoreGridInstallations + sodistoreGridInstallations, + sodistoreProInstallations ]); useEffect(() => { @@ -191,8 +209,10 @@ const InstallationsContextProvider = ({ `/GetAllInstallationsFromProduct?product=${product}` ); - if (product === 2 || product === 5) { + 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) { @@ -418,6 +438,7 @@ const InstallationsContextProvider = ({ salidomoInstallations, sodiohomeInstallations, sodistoreGridInstallations, + sodistoreProInstallations, foldersAndInstallations, fetchAllInstallations, fetchAllFoldersAndInstallations, @@ -445,6 +466,7 @@ const InstallationsContextProvider = ({ salidomoInstallations, sodiohomeInstallations, sodistoreGridInstallations, + sodistoreProInstallations, foldersAndInstallations, loading, error, From 8c58ce45f68a1f4c9589538698cefa273da1e853 Mon Sep 17 00:00:00 2001 From: Yinyin Liu Date: Thu, 26 Mar 2026 09:08:49 +0100 Subject: [PATCH 07/30] add DC or AC Coupled option for Sodistore Home and Sodistore Pro --- csharp/App/Backend/DataTypes/Installation.cs | 1 + .../Information/InformationSodistoreHome.tsx | 26 +++++++++++++++++++ .../src/interfaces/InstallationTypes.tsx | 1 + typescript/frontend-marios2/src/lang/de.json | 3 +++ typescript/frontend-marios2/src/lang/en.json | 3 +++ typescript/frontend-marios2/src/lang/fr.json | 3 +++ typescript/frontend-marios2/src/lang/it.json | 3 +++ 7 files changed, 40 insertions(+) diff --git a/csharp/App/Backend/DataTypes/Installation.cs b/csharp/App/Backend/DataTypes/Installation.cs index 0902d74a0..a7fb0fb39 100644 --- a/csharp/App/Backend/DataTypes/Installation.cs +++ b/csharp/App/Backend/DataTypes/Installation.cs @@ -60,6 +60,7 @@ public class Installation : TreeNode 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; } diff --git a/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx b/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx index a6da44bd0..41e83f26d 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx @@ -780,6 +780,32 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) { />
+
+ + + + + + +
+ {isSodistorePro ? (
Date: Thu, 26 Mar 2026 09:11:13 +0100 Subject: [PATCH 08/30] the default Installation Setup details in Information tab of Sodistore Pro should be not expanded --- .../dashboards/Information/InformationSodistoreHome.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx b/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx index 41e83f26d..9618d2a5a 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Information/InformationSodistoreHome.tsx @@ -882,7 +882,7 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) { return ( Date: Thu, 26 Mar 2026 09:17:40 +0100 Subject: [PATCH 09/30] reorder products order showing on monitor --- .../frontend-marios2/src/components/login.tsx | 18 +- .../content/dashboards/Tree/Information.tsx | 4 +- .../Sidebar/SidebarMenu/index.tsx | 161 +++++++++--------- 3 files changed, 92 insertions(+), 91 deletions(-) diff --git a/typescript/frontend-marios2/src/components/login.tsx b/typescript/frontend-marios2/src/components/login.tsx index 99c8f5014..06e8d7bb9 100644 --- a/typescript/frontend-marios2/src/components/login.tsx +++ b/typescript/frontend-marios2/src/components/login.tsx @@ -88,18 +88,18 @@ function Login() { 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); + 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); + } else if (response.data.accessToSodistoreMax) { + navigate(routes.sodistore_installations); + } else if (response.data.accessToSalimax) { + navigate(routes.installations); } else { - navigate(routes.sodiohome_installations); + navigate(routes.salidomo_installations); } } }) diff --git a/typescript/frontend-marios2/src/content/dashboards/Tree/Information.tsx b/typescript/frontend-marios2/src/content/dashboards/Tree/Information.tsx index 8a07848b3..6dde96e5f 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Tree/Information.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Tree/Information.tsx @@ -59,13 +59,13 @@ function TreeInformation(props: TreeInformationProps) { fetchAllInstallations } = installationContext; - const [product, setProduct] = useState('Salimax'); + const [product, setProduct] = useState('SodistoreHome'); const handleChangeInstallationChoice = (e) => { setProduct(e.target.value); // Directly update the product state }; - const ProductTypes = ['Salimax', 'Salidomo', 'SodistoreHome', 'SodistoreMax', 'SodistoreGrid', 'SodistorePro']; + const ProductTypes = ['SodistoreHome', 'SodistorePro', 'SodistoreGrid', 'SodistoreMax', 'Salimax', 'Salidomo']; const ProductDisplayNames: Record = { 'SodistoreHome': 'Sodistore Home', 'SodistoreMax': 'Sodistore Max', diff --git a/typescript/frontend-marios2/src/layouts/SidebarLayout/Sidebar/SidebarMenu/index.tsx b/typescript/frontend-marios2/src/layouts/SidebarLayout/Sidebar/SidebarMenu/index.tsx index 402a44b25..61304f5ad 100644 --- a/typescript/frontend-marios2/src/layouts/SidebarLayout/Sidebar/SidebarMenu/index.tsx +++ b/typescript/frontend-marios2/src/layouts/SidebarLayout/Sidebar/SidebarMenu/index.tsx @@ -186,86 +186,6 @@ function SidebarMenu() { } > - {accessToSalimax && ( - - - - - - )} - {accessToSodistore && ( - - - - - - )} - - {accessToSalidomo && ( - - - - - - )} - - {accessToSodistoreGrid && ( - - - - - - )} - {accessToSodiohome && ( @@ -307,6 +227,87 @@ function SidebarMenu() { )} + + {accessToSodistoreGrid && ( + + + + + + )} + + {accessToSodistore && ( + + + + + + )} + + {accessToSalimax && ( + + + + + + )} + + {accessToSalidomo && ( + + + + + + )} From 1683ab9b9a91878e28cbd8ad3bda36be79b672bc Mon Sep 17 00:00:00 2001 From: Yinyin Liu Date: Thu, 26 Mar 2026 09:33:35 +0100 Subject: [PATCH 10/30] make inesco energy as the default naming --- csharp/App/Backend/DataTypes/Methods/User.cs | 40 +++++++++---------- csharp/App/Backend/Database/Db.cs | 8 ++-- csharp/App/Backend/MailerConfig.json | 2 +- .../Backend/Services/AlarmReviewService.cs | 8 ++-- .../Backend/Services/ReportEmailService.cs | 30 +++++++------- .../App/Backend/Websockets/RabbitMQManager.cs | 4 +- typescript/frontend-marios2/public/index.html | 2 +- .../src/components/AcknowledgementDialog.tsx | 4 +- .../src/components/Footer/index.tsx | 4 +- .../src/components/LogoSign/index.tsx | 2 +- .../content/pages/Status/Status500/index.tsx | 2 +- typescript/frontend-marios2/src/lang/de.json | 4 +- typescript/frontend-marios2/src/lang/en.json | 4 +- typescript/frontend-marios2/src/lang/fr.json | 4 +- typescript/frontend-marios2/src/lang/it.json | 4 +- 15 files changed, 62 insertions(+), 60 deletions(-) diff --git a/csharp/App/Backend/DataTypes/Methods/User.cs b/csharp/App/Backend/DataTypes/Methods/User.cs index 9448176df..6eb13cbaa 100644 --- a/csharp/App/Backend/DataTypes/Methods/User.cs +++ b/csharp/App/Backend/DataTypes/Methods/User.cs @@ -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,24 +274,24 @@ 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}" ) }; @@ -307,7 +307,7 @@ public static class UserMethods var (subject, body) = (user.Language ?? "en") switch { "de" => ( - $"inesco Energy – Ticket #{ticket.Id} wurde Ihnen zugewiesen", + $"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" + @@ -316,10 +316,10 @@ public static class UserMethods $"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" + "Mit freundlichen Grüssen\ninesco energy Monitor" ), "fr" => ( - $"inesco Energy – Le ticket #{ticket.Id} vous a été attribué", + $"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" + @@ -328,10 +328,10 @@ public static class UserMethods $"Catégorie : {category}\n\n" + $"Description :\n{ticket.Description}\n\n" + $"Ouvrir le ticket : {ticketLink}\n\n" + - "Cordialement,\ninesco Energy Monitor" + "Cordialement,\ninesco energy Monitor" ), "it" => ( - $"inesco Energy – Il ticket #{ticket.Id} le è stato assegnato", + $"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" + @@ -340,10 +340,10 @@ public static class UserMethods $"Categoria: {category}\n\n" + $"Descrizione:\n{ticket.Description}\n\n" + $"Aprire il ticket: {ticketLink}\n\n" + - "Cordiali saluti,\ninesco Energy Monitor" + "Cordiali saluti,\ninesco energy Monitor" ), _ => ( - $"inesco Energy – Ticket #{ticket.Id} has been assigned to you", + $"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" + @@ -352,7 +352,7 @@ public static class UserMethods $"Category: {category}\n\n" + $"Description:\n{ticket.Description}\n\n" + $"Open the ticket: {ticketLink}\n\n" + - "Best regards,\ninesco Energy Monitor" + "Best regards,\ninesco energy Monitor" ) }; diff --git a/csharp/App/Backend/Database/Db.cs b/csharp/App/Backend/Database/Db.cs index e8a197206..1350caa91 100644 --- a/csharp/App/Backend/Database/Db.cs +++ b/csharp/App/Backend/Database/Db.cs @@ -83,10 +83,12 @@ 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'"); + // 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'"); 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 = 'InnovEnergy Master Admin'"); + Connection.Execute("UPDATE User SET Name = 'inesco energy Master Admin' WHERE Name = 'inesco Energy Master Admin'"); //UpdateKeys(); CleanupSessions().SupressAwaitWarning(); diff --git a/csharp/App/Backend/MailerConfig.json b/csharp/App/Backend/MailerConfig.json index 430e85f31..9a4a7f458 100644 --- a/csharp/App/Backend/MailerConfig.json +++ b/csharp/App/Backend/MailerConfig.json @@ -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" } diff --git a/csharp/App/Backend/Services/AlarmReviewService.cs b/csharp/App/Backend/Services/AlarmReviewService.cs index ff278c407..28c892400 100644 --- a/csharp/App/Backend/Services/AlarmReviewService.cs +++ b/csharp/App/Backend/Services/AlarmReviewService.cs @@ -1223,7 +1223,7 @@ textarea:focus{outline:none;border-color:#3498db}
Vielen Dank für Ihre Zeit — Ihr Beitrag macht einen echten Unterschied für unsere Kunden. 🙏
-
inesco Energy Monitor
+
inesco energy Monitor