update sodistore home information tab based on weekly meeting's feedback

This commit is contained in:
Yinyin Liu 2026-03-24 13:42:36 +01:00
parent baaabbecd0
commit 0657a5fb82
13 changed files with 142 additions and 83 deletions

View File

@ -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; } = "";

View File

@ -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));
}
/// <summary>Cached-or-generated AI insight for a stored YearlyReportSummary.</summary>

View File

@ -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(

View File

@ -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<string | null>(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) {
</div>
<div>
<TextField
label={<FormattedMessage id="region" defaultMessage="Region" />}
name="region"
value={formValues.region}
label={<FormattedMessage id="street" defaultMessage="Street" />}
name="street"
value={formValues.street || ''}
onChange={handleChange}
variant="outlined"
fullWidth
required={canEdit}
error={canEdit && formValues.region === ''}
inputProps={{ readOnly: !canEdit }}
/>
</div>
<div>
<TextField
label={<FormattedMessage id="location" defaultMessage="Location" />}
name="location"
value={formValues.location}
label={<FormattedMessage id="postCode" defaultMessage="Postcode" />}
name="postCode"
value={formValues.postCode || ''}
onChange={handleChange}
variant="outlined"
fullWidth
inputProps={{ readOnly: !canEdit }}
/>
</div>
<div>
<TextField
label={<FormattedMessage id="city" defaultMessage="City" />}
name="city"
value={formValues.city || ''}
onChange={handleChange}
variant="outlined"
fullWidth
inputProps={{ readOnly: !canEdit }}
/>
</div>
<div>
<TextField
label={<FormattedMessage id="canton" defaultMessage="Canton" />}
name="canton"
value={formValues.canton || ''}
onChange={handleChange}
variant="outlined"
fullWidth
required={canEdit}
error={canEdit && formValues.location === ''}
inputProps={{ readOnly: !canEdit }}
/>
</div>
@ -561,12 +579,21 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
<TextField
label={<FormattedMessage id="country" defaultMessage="Country" />}
name="country"
value={formValues.country}
value={formValues.country || ''}
onChange={handleChange}
variant="outlined"
fullWidth
inputProps={{ readOnly: !canEdit }}
/>
</div>
<div>
<TextField
label={<FormattedMessage id="distributionPartner" defaultMessage="Distribution Partner" />}
name="distributionPartner"
value={formValues.distributionPartner || ''}
onChange={handleChange}
variant="outlined"
fullWidth
required={canEdit}
error={canEdit && formValues.country === ''}
inputProps={{ readOnly: !canEdit }}
/>
</div>
@ -696,6 +723,18 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
<FormattedMessage id="installationSetup" defaultMessage="Installation Setup" />
</Typography>
<div>
<TextField
label={<FormattedMessage id="installationSerialNumber" defaultMessage="Installation Serial Number" />}
name="serialNumber"
value={formValues.serialNumber || ''}
onChange={handleChange}
variant="outlined"
fullWidth
inputProps={{ readOnly: !canEdit }}
/>
</div>
<div>
<FormControl sx={{ m: 1, width: '50ch' }}>
<InputLabel
@ -725,9 +764,21 @@ function InformationSodistorehome(props: InformationSodistorehomeProps) {
<div>
<TextField
label={<FormattedMessage id="installationSerialNumber" defaultMessage="Installation Serial Number" />}
name="serialNumber"
value={formValues.serialNumber}
label={<FormattedMessage id="inverterFirmwareVersion" defaultMessage="Inverter Firmware Version" />}
name="inverterFirmwareVersion"
value={formValues.inverterFirmwareVersion || ''}
onChange={handleChange}
variant="outlined"
fullWidth
inputProps={{ readOnly: !canEdit }}
/>
</div>
<div>
<TextField
label={<FormattedMessage id="batteryFirmwareVersion" defaultMessage="Battery Firmware Version" />}
name="batteryFirmwareVersion"
value={formValues.batteryFirmwareVersion || ''}
onChange={handleChange}
variant="outlined"
fullWidth

View File

@ -288,7 +288,7 @@ function Log(props: LogProps) {
onChange={e => { setDemoAlarm(e.target.value); setDemoResult(null); }}
sx={{ minWidth: 260 }}
>
<ListSubheader>Sinexcel</ListSubheader>
<ListSubheader>inesco 12K - WR Hybrid</ListSubheader>
{DEMO_ALARMS.sinexcel.map(a => (
<MenuItem key={a} value={a}>{a}</MenuItem>
))}

View File

@ -96,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="region" defaultMessage="Region" />
<FormattedMessage id="DeviceType" defaultMessage="Device Type" />
</TableCell>
<TableCell>
<FormattedMessage id="canton" defaultMessage="Canton" />
</TableCell>
<TableCell>
<FormattedMessage id="country" defaultMessage="Country" />
@ -146,19 +146,6 @@ 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"
@ -181,7 +168,20 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
noWrap
sx={{ marginTop: '10px', fontSize: 'small' }}
>
{installation.region}
{installation.device === 3 ? 'Growatt' : installation.device === 4 ? 'inesco 12K - WR Hybrid' : ''}
</Typography>
</TableCell>
<TableCell>
<Typography
variant="body2"
fontWeight="bold"
color="text.primary"
gutterBottom
noWrap
sx={{ marginTop: '10px', fontSize: 'small' }}
>
{installation.canton || ''}
</Typography>
</TableCell>

View File

@ -30,18 +30,15 @@ function SodistorehomeInstallationForm(props: SodistorehomeInstallationFormPros)
const [open, setOpen] = useState(true);
const [formValues, setFormValues] = useState<Partial<I_Installation>>({
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 === ''}
/>
</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" />}

View File

@ -47,7 +47,7 @@ const deviceOptionsByProduct: Record<number, { value: number; label: string }[]>
],
2: [
{ value: 3, label: 'Growatt' },
{ value: 4, label: 'Sinexcel' }
{ value: 4, label: 'inesco 12K - WR Hybrid' }
]
};

View File

@ -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;

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",