add filters to fast search and disable data collection installations go bottom

This commit is contained in:
Yinyin Liu 2026-04-16 14:21:24 +02:00
parent 3fbb2eeee0
commit 4a6caa9ed3
5 changed files with 166 additions and 73 deletions

View File

@ -96,8 +96,12 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
break;
}
// Sort by status (alarms first)
// Sort by status (alarms first); data-collection-disabled sinks below offline.
return filtered.sort((a, b) => {
const aDisabled = a.dataCollectionEnabled === false;
const bDisabled = b.dataCollectionEnabled === false;
if (aDisabled !== bDisabled) return aDisabled ? 1 : -1;
const a_status = a.status;
const b_status = b.status;

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import {
Card,
CircularProgress,
@ -31,34 +31,40 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
const currentLocation = useLocation();
const baseRoute = props.product === 5 ? routes.sodistorepro_installations : routes.sodiohome_installations;
//
const sortedInstallations = [...props.installations].sort((a, b) => {
// Compare the status field of each installation and sort them based on the status.
//Installations with alarms go first
let a_status = a.status;
let b_status = b.status;
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) => {
// Data-collection-disabled installations sink below everything (even offline).
const aDisabled = a.dataCollectionEnabled === false;
const bDisabled = b.dataCollectionEnabled === false;
if (aDisabled !== bDisabled) return aDisabled ? 1 : -1;
// Then sort by status (alarms first)
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;
});
}, [props.installations]);
const handleSelectOneInstallation = (installationID: number): void => {
if (selectedInstallation != installationID) {
setSelectedInstallation(installationID);
setSelectedInstallation(-1);
const target = props.installations.find((i) => i.id === installationID);
const landingTab =
target?.dataCollectionEnabled === false ? routes.information : routes.live;
navigate(
baseRoute +
routes.list +
routes.installation +
`${installationID}` +
'/' +
routes.live,
landingTab,
{
replace: true
}
@ -77,18 +83,16 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
}
}));
const isListView =
currentLocation.pathname === baseRoute + 'list' ||
currentLocation.pathname === baseRoute + routes.list;
return (
<Grid container spacing={1} sx={{ marginTop: 0.1 }}>
<Grid
item
sx={{
display:
currentLocation.pathname ===
baseRoute + 'list' ||
currentLocation.pathname ===
baseRoute + routes.list
? 'block'
: 'none'
display: isListView ? 'block' : 'none'
}}
>
<Card>

View File

@ -489,12 +489,14 @@ function SodioHomeInstallation(props: singleInstallationProps) {
</div>
</div>
{loading &&
!dataCollectionDisabled &&
currentTab != 'information' &&
// currentTab != 'manage' &&
currentTab != 'history' &&
currentTab != 'log' &&
currentTab != 'report' &&
currentTab != 'installationTickets' && (
currentTab != 'installationTickets' &&
currentTab != 'documents' && (
<Container
maxWidth="xl"
sx={{
@ -670,7 +672,7 @@ function SodioHomeInstallation(props: singleInstallationProps) {
<Route
path={'*'}
element={<Navigate to={routes.live}></Navigate>}
element={<Navigate to={dataCollectionDisabled ? routes.information : routes.live}></Navigate>}
/>
</Routes>
</Grid>

View File

@ -1,7 +1,15 @@
import React, { useMemo, useState } from 'react';
import { FormControl, Grid, InputAdornment, TextField } from '@mui/material';
import {
FormControl,
Grid,
InputAdornment,
InputLabel,
MenuItem,
Select,
TextField
} from '@mui/material';
import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone';
import { useIntl } from 'react-intl';
import { FormattedMessage, useIntl } from 'react-intl';
import { I_Installation } from '../../../interfaces/InstallationTypes';
import { Route, Routes, useLocation } from 'react-router-dom';
import routes from '../../../Resources/routes.json';
@ -16,9 +24,10 @@ interface installationSearchProps {
function InstallationSearch(props: installationSearchProps) {
const intl = useIntl();
const [searchTerm, setSearchTerm] = useState('');
const [sortByStatus, setSortByStatus] = useState('All Installations');
const [sortByAction, setSortByAction] = useState('All Installations');
const currentLocation = useLocation();
const baseRoute = props.product === 5 ? routes.sodistorepro_installations : routes.sodiohome_installations;
// const [filteredData, setFilteredData] = useState(props.installations);
const indexedData = useMemo(() => {
return props.installations.map((item) => ({
@ -30,56 +39,126 @@ function InstallationSearch(props: installationSearchProps) {
}, [props.installations]);
const filteredData = useMemo(() => {
return indexedData.filter(
let list = indexedData.filter(
(item) =>
item.nameLower.includes(searchTerm.toLowerCase()) ||
item.locationLower.includes(searchTerm.toLowerCase()) ||
item.regionLower.includes(searchTerm.toLowerCase())
);
}, [searchTerm, indexedData]);
switch (sortByStatus) {
case 'Installations With Alarm':
list = list.filter((i) => i.status === 2);
break;
case 'Installations with Warning':
list = list.filter((i) => i.status === 1);
break;
case 'Functional Installations':
list = list.filter((i) => i.status === 0);
break;
case 'Offline Installations':
list = list.filter((i) => i.status === -1);
break;
case 'Installations Without Data Collection':
list = list.filter((i) => i.dataCollectionEnabled === false);
break;
}
switch (sortByAction) {
case 'Installations With Action Flag':
list = list.filter((i) => i.testingMode === true);
break;
case 'Installations Without Action Flag':
list = list.filter((i) => i.testingMode === false);
break;
}
return list;
}, [searchTerm, indexedData, sortByStatus, sortByAction]);
const isListView =
currentLocation.pathname === baseRoute + 'list' ||
currentLocation.pathname === baseRoute + routes.list;
return (
<>
<Grid container>
<Grid
item
xs={12}
md={6}
sx={{
display:
currentLocation.pathname ===
baseRoute + 'list' ||
currentLocation.pathname ===
baseRoute + routes.list
? 'block'
: 'none'
}}
>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start'
}}
>
<FormControl variant="outlined">
<TextField
placeholder={intl.formatMessage({ id: 'search' })}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
fullWidth
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchTwoToneIcon />
</InputAdornment>
)
}}
/>
</FormControl>
</div>
{isListView && (
<Grid container>
<Grid item xs={12}>
<div
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: '16px',
width: '100%',
flexWrap: 'wrap'
}}
>
<FormControl variant="outlined" sx={{ width: 280 }}>
<TextField
placeholder={intl.formatMessage({ id: 'search' })}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
fullWidth
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchTwoToneIcon />
</InputAdornment>
)
}}
/>
</FormControl>
<FormControl sx={{ width: 280 }}>
<InputLabel>
<FormattedMessage id="sortByStatus" defaultMessage="Sort By Status" />
</InputLabel>
<Select
value={sortByStatus}
onChange={(e) => setSortByStatus(e.target.value)}
label={intl.formatMessage({ id: 'sortByStatus' })}
>
{[
'All Installations',
'Installations With Alarm',
'Installations with Warning',
'Functional Installations',
'Offline Installations',
'Installations Without Data Collection'
].map((type) => (
<MenuItem key={type} value={type}>
{type}
</MenuItem>
))}
</Select>
</FormControl>
<FormControl sx={{ width: 280 }}>
<InputLabel>
<FormattedMessage id="sortByActionFlag" defaultMessage="Sort By Action Flag" />
</InputLabel>
<Select
value={sortByAction}
onChange={(e) => setSortByAction(e.target.value)}
label={intl.formatMessage({ id: 'sortByActionFlag' })}
>
{[
'All Installations',
'Installations With Action Flag',
'Installations Without Action Flag'
].map((type) => (
<MenuItem key={type} value={type}>
{type}
</MenuItem>
))}
</Select>
</FormControl>
</div>
</Grid>
</Grid>
</Grid>
)}
<FlatInstallationView installations={filteredData} product={props.product} />
<Routes>

View File

@ -17,12 +17,16 @@ function InstallationTree() {
useContext(InstallationsContext);
const sortedInstallations = [...foldersAndInstallations].sort((a, b) => {
// Compare the status field of each installation and sort them based on the status.
//Installations with alarms go first
// Folders stay on top (existing behavior).
if (a.type == 'Folder') {
return -1;
}
// Data-collection-disabled installations sink below everything (even offline).
const aDisabled = (a as any).dataCollectionEnabled === false;
const bDisabled = (b as any).dataCollectionEnabled === false;
if (aDisabled !== bDisabled) return aDisabled ? 1 : -1;
// Then sort by status (alarms first).
let a_status = a.status;
let b_status = b.status;