Update frontend to support fast search in salidomo.
Also, the user can sort based on status and action flag Update aggregator for Salidomo so that it uses json
This commit is contained in:
parent
41ca486edb
commit
96359fab08
|
|
@ -137,9 +137,6 @@ public static class Aggregator
|
||||||
var jsonObject = JObject.Parse(jsonData);
|
var jsonObject = JObject.Parse(jsonData);
|
||||||
|
|
||||||
//Console.WriteLine(jsonObject);
|
//Console.WriteLine(jsonObject);
|
||||||
// Console.WriteLine(jsonObject["GridMeter"]);
|
|
||||||
|
|
||||||
|
|
||||||
if (jsonObject["Battery"] != null && jsonObject["Battery"]["Soc"] != null)
|
if (jsonObject["Battery"] != null && jsonObject["Battery"]["Soc"] != null)
|
||||||
{
|
{
|
||||||
batterySoc.Add((double)jsonObject["Battery"]["Soc"]);
|
batterySoc.Add((double)jsonObject["Battery"]["Soc"]);
|
||||||
|
|
@ -166,86 +163,11 @@ public static class Aggregator
|
||||||
Console.WriteLine("power import is "+jsonObject["GridMeter"]["ActivePowerImportT3"]);
|
Console.WriteLine("power import is "+jsonObject["GridMeter"]["ActivePowerImportT3"]);
|
||||||
gridPowerImport.Add((double)jsonObject["GridMeter"]["ActivePowerImportT3"]);
|
gridPowerImport.Add((double)jsonObject["GridMeter"]["ActivePowerImportT3"]);
|
||||||
}
|
}
|
||||||
if (jsonObject["Battery"] != null && jsonObject["Battery"]["HeatingPower"] != null)
|
|
||||||
{
|
|
||||||
heatingPower.Add((double)jsonObject["Battery"]["HeatingPower"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Failed to parse JSON file {jsonFile}: {e.Message}");
|
Console.WriteLine($"Failed to parse JSON file {jsonFile}: {e.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// using var reader = new StreamReader(jsonFile);
|
|
||||||
//
|
|
||||||
// while (!reader.EndOfStream)
|
|
||||||
// {
|
|
||||||
//
|
|
||||||
// var line = reader.ReadLine();
|
|
||||||
// var lines = line?.Split(';');
|
|
||||||
//
|
|
||||||
// // Assuming there are always three columns (variable name and its value)
|
|
||||||
// if (lines is { Length: 3 })
|
|
||||||
// {
|
|
||||||
// var variableName = lines[0].Trim();
|
|
||||||
//
|
|
||||||
// if (TryParse(lines[1].Trim(), out var value))
|
|
||||||
// {
|
|
||||||
// switch (variableName)
|
|
||||||
// {
|
|
||||||
// case "/Battery/Soc":
|
|
||||||
// batterySoc.Add(value);
|
|
||||||
// break;
|
|
||||||
//
|
|
||||||
// case "/PvOnDc/DcWh" :
|
|
||||||
// pvPowerSum.Add(value);
|
|
||||||
// break;
|
|
||||||
//
|
|
||||||
// case "/Battery/Dc/Power":
|
|
||||||
//
|
|
||||||
// if (value < 0)
|
|
||||||
// {
|
|
||||||
// batteryDischargePower.Add(value);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// batteryChargePower.Add(value);
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
//
|
|
||||||
// case "/GridMeter/ActivePowerExportT3":
|
|
||||||
// // we are using different register to check which value from the grid meter we need to use
|
|
||||||
// // At the moment register 8002 amd 8012. in KWh
|
|
||||||
// gridPowerExport.Add(value);
|
|
||||||
// break;
|
|
||||||
// case "/GridMeter/ActivePowerImportT3":
|
|
||||||
// gridPowerImport.Add(value);
|
|
||||||
// break;
|
|
||||||
// case "/Battery/HeatingPower":
|
|
||||||
// heatingPower.Add(value);
|
|
||||||
// break;
|
|
||||||
// // Add more cases as needed
|
|
||||||
// default:
|
|
||||||
// // Code to execute when variableName doesn't match any condition
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// //Handle cases where variableValue is not a valid number
|
|
||||||
// // Console.WriteLine(
|
|
||||||
// // $"Invalid numeric value for variable {variableName}:{lines[1].Trim()}");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// // Handle invalid column format
|
|
||||||
// //Console.WriteLine("Invalid format in column");
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -262,7 +184,6 @@ public static class Aggregator
|
||||||
|
|
||||||
var dischargingEnergy = (batteryDischargePower.Any() ? batteryDischargePower.Average() : 0.0) / 3600;
|
var dischargingEnergy = (batteryDischargePower.Any() ? batteryDischargePower.Average() : 0.0) / 3600;
|
||||||
var chargingEnergy = (batteryChargePower.Any() ? batteryChargePower.Average() : 0.0) / 3600;
|
var chargingEnergy = (batteryChargePower.Any() ? batteryChargePower.Average() : 0.0) / 3600;
|
||||||
var heatingPowerAvg = (heatingPower.Any() ? heatingPower.Average() : 0.0) / 3600;
|
|
||||||
|
|
||||||
var dMaxSoc = batterySoc.Any() ? batterySoc.Max() : 0.0;
|
var dMaxSoc = batterySoc.Any() ? batterySoc.Max() : 0.0;
|
||||||
var dMinSoc = batterySoc.Any() ? batterySoc.Min() : 0.0;
|
var dMinSoc = batterySoc.Any() ? batterySoc.Min() : 0.0;
|
||||||
|
|
@ -280,7 +201,7 @@ public static class Aggregator
|
||||||
GridExportPower = dSumGridExportPower,
|
GridExportPower = dSumGridExportPower,
|
||||||
GridImportPower = dSumGridImportPower,
|
GridImportPower = dSumGridImportPower,
|
||||||
PvPower = dSumPvPower,
|
PvPower = dSumPvPower,
|
||||||
HeatingPower = heatingPowerAvg
|
HeatingPower = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
// Print the stored JSON data for verification
|
// Print the stored JSON data for verification
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
|
FormControl,
|
||||||
Grid,
|
Grid,
|
||||||
|
InputAdornment,
|
||||||
|
InputLabel,
|
||||||
|
MenuItem,
|
||||||
|
Select,
|
||||||
styled,
|
styled,
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
|
@ -10,6 +15,7 @@ import {
|
||||||
TableContainer,
|
TableContainer,
|
||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableRow,
|
||||||
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
useTheme
|
useTheme
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
@ -19,42 +25,23 @@ import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
import routes from '../../../Resources/routes.json';
|
import routes from '../../../Resources/routes.json';
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import BuildIcon from '@mui/icons-material/Build';
|
import BuildIcon from '@mui/icons-material/Build';
|
||||||
|
import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone';
|
||||||
|
|
||||||
interface FlatInstallationViewProps {
|
interface FlatInstallationViewProps {
|
||||||
installations: I_Installation[];
|
installations: I_Installation[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
// const webSocketContext = useContext(WebSocketContext);
|
|
||||||
// const { getSortedInstallations } = webSocketContext;
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
|
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
|
||||||
const currentLocation = useLocation();
|
const currentLocation = useLocation();
|
||||||
//
|
const BATCH_SIZE = 50;
|
||||||
const sortedInstallations = [...props.installations].sort((a, b) => {
|
const [visibleCount, setVisibleCount] = useState(BATCH_SIZE);
|
||||||
// Compare the status field of each installation and sort them based on the status.
|
const observerRef = useRef<IntersectionObserver | null>(null);
|
||||||
//Installations with alarms go first
|
const loadMoreRef = useRef<HTMLDivElement>(null);
|
||||||
let a_status = a.status;
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
let b_status = b.status;
|
const [sortByStatus, setSortByStatus] = useState('All Installations');
|
||||||
|
const [sortByAction, setSortByAction] = useState('All Installations');
|
||||||
if (a_status > b_status) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (a_status < b_status) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
// const sortedInstallations = useMemo(() => {
|
|
||||||
// return [...props.installations].sort((a, b) => {
|
|
||||||
// const a_status = getStatus(a.id) || 0;
|
|
||||||
// const b_status = getStatus(b.id) || 0;
|
|
||||||
// return b_status - a_status;
|
|
||||||
// });
|
|
||||||
// }, [props.installations, getStatus]);
|
|
||||||
|
|
||||||
// const sortedInstallations = getSortedInstallations();
|
|
||||||
|
|
||||||
const handleSelectOneInstallation = (installationID: number): void => {
|
const handleSelectOneInstallation = (installationID: number): void => {
|
||||||
if (selectedInstallation != installationID) {
|
if (selectedInstallation != installationID) {
|
||||||
|
|
@ -86,265 +73,454 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const indexedData = useMemo(() => {
|
||||||
|
return props.installations.map((item) => ({
|
||||||
|
...item,
|
||||||
|
nameLower: item.name.toLowerCase(),
|
||||||
|
locationLower: item.location.toLowerCase(),
|
||||||
|
regionLower: item.region.toLowerCase()
|
||||||
|
}));
|
||||||
|
}, [props.installations]);
|
||||||
|
|
||||||
|
const sortedInstallations = useMemo(() => {
|
||||||
|
let filtered = indexedData.filter(
|
||||||
|
(item) =>
|
||||||
|
item.nameLower.includes(searchTerm.toLowerCase()) ||
|
||||||
|
item.locationLower.includes(searchTerm.toLowerCase()) ||
|
||||||
|
item.regionLower.includes(searchTerm.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
// Apply the 'showOnly' filter
|
||||||
|
switch (sortByStatus) {
|
||||||
|
case 'Installations With Alarm':
|
||||||
|
filtered = filtered.filter((i) => i.status === 2);
|
||||||
|
break;
|
||||||
|
case 'Installations with Warning':
|
||||||
|
filtered = filtered.filter((i) => i.status === 1);
|
||||||
|
break;
|
||||||
|
case 'Functional Installations':
|
||||||
|
filtered = filtered.filter((i) => i.status === 0);
|
||||||
|
break;
|
||||||
|
case 'Offline Installations':
|
||||||
|
filtered = filtered.filter((i) => i.status === -1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (sortByAction) {
|
||||||
|
case 'Installations With Action Flag':
|
||||||
|
filtered = filtered.filter((i) => i.testingMode === true);
|
||||||
|
break;
|
||||||
|
case 'Installations Without Action Flag':
|
||||||
|
filtered = filtered.filter((i) => i.testingMode === false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by status (alarms first)
|
||||||
|
return filtered.sort((a, b) => {
|
||||||
|
const a_status = a.status;
|
||||||
|
const b_status = b.status;
|
||||||
|
|
||||||
|
if (a_status > b_status) return -1;
|
||||||
|
if (a_status < b_status) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}, [searchTerm, indexedData, sortByAction, sortByStatus]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (observerRef.current) {
|
||||||
|
observerRef.current.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
observerRef.current = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
if (entries[0].isIntersecting) {
|
||||||
|
setVisibleCount((prev) => prev + BATCH_SIZE);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rootMargin: '600px', // triggers before the element fully enters view
|
||||||
|
threshold: 0.5 // triggers when 10% of it is visible
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (loadMoreRef.current) {
|
||||||
|
observerRef.current.observe(loadMoreRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (observerRef.current) {
|
||||||
|
observerRef.current.disconnect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={1} sx={{ marginTop: 0.1 }}>
|
<>
|
||||||
<Grid
|
<Grid container spacing={1}>
|
||||||
item
|
<Grid
|
||||||
sx={{
|
item
|
||||||
display:
|
xs={12}
|
||||||
currentLocation.pathname ===
|
md={6}
|
||||||
routes.salidomo_installations + 'list' ||
|
sx={{
|
||||||
currentLocation.pathname ===
|
display:
|
||||||
routes.salidomo_installations + routes.list
|
currentLocation.pathname ===
|
||||||
? 'block'
|
routes.salidomo_installations + 'list' ||
|
||||||
: 'none'
|
currentLocation.pathname ===
|
||||||
}}
|
routes.salidomo_installations + routes.list
|
||||||
>
|
? 'block'
|
||||||
<Card>
|
: 'none'
|
||||||
<TableContainer>
|
}}
|
||||||
<Table>
|
>
|
||||||
<TableHead>
|
<div
|
||||||
<TableRow>
|
style={{
|
||||||
<TableCell>
|
display: 'flex',
|
||||||
<FormattedMessage id="name" defaultMessage="Name" />
|
flexDirection: 'row',
|
||||||
</TableCell>
|
alignItems: 'center',
|
||||||
<TableCell>
|
gap: '16px', // spacing between elements
|
||||||
<FormattedMessage id="location" defaultMessage="Location" />
|
width: '100%'
|
||||||
</TableCell>
|
}}
|
||||||
<TableCell>
|
>
|
||||||
<FormattedMessage id="region" defaultMessage="Region" />
|
<FormControl sx={{ flex: 1 }}>
|
||||||
</TableCell>
|
<TextField
|
||||||
<TableCell>
|
placeholder="Search"
|
||||||
<FormattedMessage id="country" defaultMessage="Country" />
|
value={searchTerm}
|
||||||
</TableCell>
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
<TableCell>
|
fullWidth
|
||||||
<FormattedMessage id="VRM Link" defaultMessage="VRM Link" />
|
InputProps={{
|
||||||
</TableCell>
|
startAdornment: (
|
||||||
<TableCell>
|
<InputAdornment position="start">
|
||||||
<FormattedMessage id="Device" defaultMessage="Device" />
|
<SearchTwoToneIcon />
|
||||||
</TableCell>
|
</InputAdornment>
|
||||||
<TableCell>
|
)
|
||||||
<FormattedMessage id="status" defaultMessage="Status" />
|
}}
|
||||||
</TableCell>
|
/>
|
||||||
</TableRow>
|
</FormControl>
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
|
||||||
{sortedInstallations
|
|
||||||
// .filter(
|
|
||||||
// (installation) =>
|
|
||||||
// installation.status === -1 &&
|
|
||||||
// installation.testingMode == false
|
|
||||||
// )
|
|
||||||
.map((installation) => {
|
|
||||||
const isInstallationSelected =
|
|
||||||
installation.id === selectedInstallation;
|
|
||||||
|
|
||||||
const status = installation.status;
|
<FormControl sx={{ width: 240 }}>
|
||||||
|
<InputLabel>
|
||||||
|
<FormattedMessage
|
||||||
|
id="sortByStatus"
|
||||||
|
defaultMessage="Sort By Status"
|
||||||
|
/>
|
||||||
|
</InputLabel>
|
||||||
|
<Select
|
||||||
|
value={sortByStatus}
|
||||||
|
onChange={(e) => setSortByStatus(e.target.value)}
|
||||||
|
label="Show Only"
|
||||||
|
>
|
||||||
|
{[
|
||||||
|
'All Installations',
|
||||||
|
'Installations With Alarm',
|
||||||
|
'Installations with Warning',
|
||||||
|
'Functional Installations',
|
||||||
|
'Offline Installations'
|
||||||
|
].map((type) => (
|
||||||
|
<MenuItem key={type} value={type}>
|
||||||
|
{type}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
return (
|
<FormControl sx={{ width: 240 }}>
|
||||||
<HoverableTableRow
|
<InputLabel>
|
||||||
key={installation.id}
|
<FormattedMessage
|
||||||
onClick={() =>
|
id="sortByActionFlag"
|
||||||
handleSelectOneInstallation(installation.id)
|
defaultMessage="Sort By Action Flag"
|
||||||
}
|
/>
|
||||||
>
|
</InputLabel>
|
||||||
<TableCell>
|
<Select
|
||||||
<Typography
|
value={sortByAction}
|
||||||
variant="body2"
|
onChange={(e) => setSortByAction(e.target.value)}
|
||||||
fontWeight="bold"
|
label="Show Only"
|
||||||
color="text.primary"
|
>
|
||||||
gutterBottom
|
{[
|
||||||
noWrap
|
'All Installations',
|
||||||
sx={{ marginTop: '10px', fontSize: 'small' }}
|
'Installations With Action Flag',
|
||||||
>
|
'Installations Without Action Flag'
|
||||||
{installation.name}
|
].map((type) => (
|
||||||
</Typography>
|
<MenuItem key={type} value={type}>
|
||||||
</TableCell>
|
{type}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<TableCell>
|
<Grid container spacing={1} sx={{ marginTop: 0.1 }}>
|
||||||
<Typography
|
<Grid
|
||||||
variant="body2"
|
item
|
||||||
fontWeight="bold"
|
sx={{
|
||||||
color="text.primary"
|
display:
|
||||||
gutterBottom
|
currentLocation.pathname ===
|
||||||
noWrap
|
routes.salidomo_installations + 'list' ||
|
||||||
sx={{ marginTop: '10px', fontSize: 'small' }}
|
currentLocation.pathname ===
|
||||||
>
|
routes.salidomo_installations + routes.list
|
||||||
{installation.location}
|
? 'block'
|
||||||
</Typography>
|
: 'none'
|
||||||
</TableCell>
|
}}
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<TableContainer>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage id="name" defaultMessage="Name" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage
|
||||||
|
id="location"
|
||||||
|
defaultMessage="Location"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage id="region" defaultMessage="Region" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage id="country" defaultMessage="Country" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage
|
||||||
|
id="VRM Link"
|
||||||
|
defaultMessage="VRM Link"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage id="Device" defaultMessage="Device" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage id="status" defaultMessage="Status" />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{sortedInstallations
|
||||||
|
.slice(0, visibleCount)
|
||||||
|
|
||||||
<TableCell>
|
// .filter(
|
||||||
<Typography
|
// (installation) =>
|
||||||
variant="body2"
|
// installation.status === -1 &&
|
||||||
fontWeight="bold"
|
// installation.testingMode == false
|
||||||
color="text.primary"
|
// )
|
||||||
gutterBottom
|
.map((installation) => {
|
||||||
noWrap
|
const isInstallationSelected =
|
||||||
sx={{ marginTop: '10px', fontSize: 'small' }}
|
installation.id === selectedInstallation;
|
||||||
>
|
|
||||||
{installation.region}
|
|
||||||
</Typography>
|
|
||||||
</TableCell>
|
|
||||||
|
|
||||||
<TableCell>
|
const status = installation.status;
|
||||||
<Typography
|
|
||||||
variant="body2"
|
|
||||||
fontWeight="bold"
|
|
||||||
color="text.primary"
|
|
||||||
gutterBottom
|
|
||||||
noWrap
|
|
||||||
sx={{ marginTop: '10px', fontSize: 'small' }}
|
|
||||||
>
|
|
||||||
{installation.country}
|
|
||||||
</Typography>
|
|
||||||
</TableCell>
|
|
||||||
|
|
||||||
<TableCell>
|
return (
|
||||||
<Typography
|
<HoverableTableRow
|
||||||
variant="body2"
|
key={installation.id}
|
||||||
fontWeight="bold"
|
onClick={() =>
|
||||||
color="text.primary"
|
handleSelectOneInstallation(installation.id)
|
||||||
gutterBottom
|
}
|
||||||
noWrap
|
>
|
||||||
sx={{ marginTop: '10px', fontSize: 'small' }}
|
<TableCell>
|
||||||
>
|
<Typography
|
||||||
<a
|
variant="body2"
|
||||||
href={
|
fontWeight="bold"
|
||||||
'https://vrm.victronenergy.com/installation/' +
|
color="text.primary"
|
||||||
installation.vrmLink +
|
gutterBottom
|
||||||
'/dashboard'
|
noWrap
|
||||||
}
|
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
style={{
|
|
||||||
display: 'inline-block',
|
|
||||||
padding: '0'
|
|
||||||
}} // Style the link
|
|
||||||
onClick={(e) => e.stopPropagation()} // Prevent the click event from bubbling up to the table row
|
|
||||||
>
|
>
|
||||||
VRM link
|
{installation.name}
|
||||||
</a>
|
</Typography>
|
||||||
</Typography>
|
</TableCell>
|
||||||
</TableCell>
|
|
||||||
|
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div
|
<Typography
|
||||||
style={{
|
variant="body2"
|
||||||
display: 'flex',
|
fontWeight="bold"
|
||||||
alignItems: 'center',
|
color="text.primary"
|
||||||
marginLeft: '5px'
|
gutterBottom
|
||||||
}}
|
noWrap
|
||||||
>
|
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||||
{installation.device === 1 ? (
|
>
|
||||||
<Typography
|
{installation.location}
|
||||||
variant="body2"
|
</Typography>
|
||||||
fontWeight="bold"
|
</TableCell>
|
||||||
color="text.primary"
|
|
||||||
gutterBottom
|
|
||||||
noWrap
|
|
||||||
sx={{ marginTop: '10px', fontSize: 'small' }}
|
|
||||||
>
|
|
||||||
Cerbo
|
|
||||||
</Typography>
|
|
||||||
) : installation.device === 2 ? (
|
|
||||||
<Typography
|
|
||||||
variant="body2"
|
|
||||||
fontWeight="bold"
|
|
||||||
color="text.primary"
|
|
||||||
gutterBottom
|
|
||||||
noWrap
|
|
||||||
sx={{ marginTop: '10px', fontSize: 'small' }}
|
|
||||||
>
|
|
||||||
Venus
|
|
||||||
</Typography>
|
|
||||||
) : (
|
|
||||||
<Typography
|
|
||||||
variant="body2"
|
|
||||||
fontWeight="bold"
|
|
||||||
color="text.primary"
|
|
||||||
gutterBottom
|
|
||||||
noWrap
|
|
||||||
sx={{ marginTop: '10px', fontSize: 'small' }}
|
|
||||||
>
|
|
||||||
Device not specified
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</TableCell>
|
|
||||||
|
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div
|
<Typography
|
||||||
style={{
|
variant="body2"
|
||||||
display: 'flex',
|
fontWeight="bold"
|
||||||
alignItems: 'center',
|
color="text.primary"
|
||||||
marginLeft: '15px'
|
gutterBottom
|
||||||
}}
|
noWrap
|
||||||
>
|
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||||
{status === -1 ? (
|
>
|
||||||
<CancelIcon
|
{installation.region}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||||
|
>
|
||||||
|
{installation.country}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={
|
||||||
|
'https://vrm.victronenergy.com/installation/' +
|
||||||
|
installation.vrmLink +
|
||||||
|
'/dashboard'
|
||||||
|
}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
style={{
|
style={{
|
||||||
width: '23px',
|
display: 'inline-block',
|
||||||
height: '23px',
|
padding: '0'
|
||||||
color: 'red',
|
}} // Style the link
|
||||||
borderRadius: '50%'
|
onClick={(e) => e.stopPropagation()} // Prevent the click event from bubbling up to the table row
|
||||||
}}
|
>
|
||||||
/>
|
VRM link
|
||||||
) : (
|
</a>
|
||||||
''
|
</Typography>
|
||||||
)}
|
</TableCell>
|
||||||
|
|
||||||
{status === -2 ? (
|
|
||||||
<CircularProgress
|
|
||||||
size={20}
|
|
||||||
sx={{
|
|
||||||
color: '#f7b34d'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
<TableCell>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: '20px',
|
display: 'flex',
|
||||||
height: '20px',
|
alignItems: 'center',
|
||||||
marginLeft: '2px',
|
marginLeft: '5px'
|
||||||
borderRadius: '50%',
|
|
||||||
backgroundColor:
|
|
||||||
status === 2
|
|
||||||
? 'red'
|
|
||||||
: status === 1
|
|
||||||
? 'orange'
|
|
||||||
: status === -1 || status === -2
|
|
||||||
? 'transparent'
|
|
||||||
: 'green'
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
{installation.testingMode && (
|
{installation.device === 1 ? (
|
||||||
<BuildIcon
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||||
|
>
|
||||||
|
Cerbo
|
||||||
|
</Typography>
|
||||||
|
) : installation.device === 2 ? (
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||||
|
>
|
||||||
|
Venus
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||||
|
>
|
||||||
|
Device not specified
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginLeft: '15px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{status === -1 ? (
|
||||||
|
<CancelIcon
|
||||||
|
style={{
|
||||||
|
width: '23px',
|
||||||
|
height: '23px',
|
||||||
|
color: 'red',
|
||||||
|
borderRadius: '50%'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
|
||||||
|
{status === -2 ? (
|
||||||
|
<CircularProgress
|
||||||
|
size={20}
|
||||||
|
sx={{
|
||||||
|
color: '#f7b34d'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: '23px',
|
width: '20px',
|
||||||
height: '23px',
|
height: '20px',
|
||||||
color: 'purple',
|
marginLeft: '2px',
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
position: 'relative',
|
backgroundColor:
|
||||||
zIndex: 1,
|
status === 2
|
||||||
marginLeft: status != -1 ? '25px' : '0px'
|
? 'red'
|
||||||
|
: status === 1
|
||||||
|
? 'orange'
|
||||||
|
: status === -1 || status === -2
|
||||||
|
? 'transparent'
|
||||||
|
: 'green'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
{installation.testingMode && (
|
||||||
</div>
|
<BuildIcon
|
||||||
</TableCell>
|
style={{
|
||||||
</HoverableTableRow>
|
width: '23px',
|
||||||
);
|
height: '23px',
|
||||||
})}
|
color: 'purple',
|
||||||
</TableBody>
|
borderRadius: '50%',
|
||||||
</Table>
|
position: 'relative',
|
||||||
</TableContainer>
|
zIndex: 1,
|
||||||
</Card>
|
marginLeft: status != -1 ? '25px' : '0px'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</HoverableTableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={7} align="center">
|
||||||
|
<div ref={loadMoreRef} style={{ height: '40px' }} />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
import React, { useMemo, useState } from 'react';
|
import React from 'react';
|
||||||
import { FormControl, Grid, InputAdornment, TextField } from '@mui/material';
|
|
||||||
import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone';
|
|
||||||
import FlatInstallationView from './FlatInstallationView';
|
import FlatInstallationView from './FlatInstallationView';
|
||||||
import { I_Installation } from '../../../interfaces/InstallationTypes';
|
import { I_Installation } from '../../../interfaces/InstallationTypes';
|
||||||
import { Route, Routes, useLocation } from 'react-router-dom';
|
import { Route, Routes } from 'react-router-dom';
|
||||||
import routes from '../../../Resources/routes.json';
|
import routes from '../../../Resources/routes.json';
|
||||||
import SalidomoInstallation from './Installation';
|
import SalidomoInstallation from './Installation';
|
||||||
|
|
||||||
|
|
@ -12,74 +10,11 @@ interface installationSearchProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function InstallationSearch(props: installationSearchProps) {
|
function InstallationSearch(props: installationSearchProps) {
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
|
||||||
const currentLocation = useLocation();
|
|
||||||
// const [filteredData, setFilteredData] = useState(props.installations);
|
|
||||||
|
|
||||||
const indexedData = useMemo(() => {
|
|
||||||
return props.installations.map((item) => ({
|
|
||||||
...item,
|
|
||||||
nameLower: item.name.toLowerCase(),
|
|
||||||
locationLower: item.location.toLowerCase(),
|
|
||||||
regionLower: item.region.toLowerCase()
|
|
||||||
}));
|
|
||||||
}, [props.installations]);
|
|
||||||
|
|
||||||
const filteredData = useMemo(() => {
|
|
||||||
return indexedData.filter(
|
|
||||||
(item) =>
|
|
||||||
item.nameLower.includes(searchTerm.toLowerCase()) ||
|
|
||||||
item.locationLower.includes(searchTerm.toLowerCase()) ||
|
|
||||||
item.regionLower.includes(searchTerm.toLowerCase())
|
|
||||||
);
|
|
||||||
}, [searchTerm, indexedData]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid container>
|
<FlatInstallationView installations={props.installations} />
|
||||||
<Grid
|
|
||||||
item
|
|
||||||
xs={12}
|
|
||||||
md={6}
|
|
||||||
sx={{
|
|
||||||
display:
|
|
||||||
currentLocation.pathname ===
|
|
||||||
routes.salidomo_installations + 'list' ||
|
|
||||||
currentLocation.pathname ===
|
|
||||||
routes.salidomo_installations + routes.list
|
|
||||||
? 'block'
|
|
||||||
: 'none'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'flex-start'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FormControl variant="outlined">
|
|
||||||
<TextField
|
|
||||||
placeholder="Search"
|
|
||||||
value={searchTerm}
|
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
|
||||||
fullWidth
|
|
||||||
InputProps={{
|
|
||||||
startAdornment: (
|
|
||||||
<InputAdornment position="start">
|
|
||||||
<SearchTwoToneIcon />
|
|
||||||
</InputAdornment>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</div>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<FlatInstallationView installations={filteredData} />
|
|
||||||
<Routes>
|
<Routes>
|
||||||
{filteredData.map((installation) => {
|
{props.installations.map((installation) => {
|
||||||
return (
|
return (
|
||||||
<Route
|
<Route
|
||||||
key={installation.id}
|
key={installation.id}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue