finished refactoring the object alerts into object interactions

This commit is contained in:
csawatzky 2025-12-01 14:38:51 -06:00
parent bb92077d0b
commit c9d2eac6c3
5 changed files with 86 additions and 28 deletions

View file

@ -1,5 +1,5 @@
import { Add, ExpandMore } from "@mui/icons-material";
import { Accordion, AccordionDetails, AccordionSummary, Box, Grid2, IconButton, List, ListItem, ListSubheader, Typography } from "@mui/material";
import { Accordion, AccordionDetails, AccordionSummary, Box, Button, Grid2, IconButton, List, ListItem, ListSubheader, Typography } from "@mui/material";
import { Component } from "models";
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
import { pond, quack } from "protobuf-ts/pond";
@ -71,7 +71,13 @@ export default function Alerts(props: Props){
Alerts
</Typography>
{addNew &&
<IconButton disabled={!permissions.includes(pond.Permission.PERMISSION_WRITE)} onClick={()=>{addNew()}} color="primary"><Add /></IconButton>
<Button
disabled={!permissions.includes(pond.Permission.PERMISSION_WRITE)}
onClick={()=>{addNew()}}
variant="contained"
color="primary">
<Add /> Alert
</Button>
}
</ListSubheader>
{alerts.map((alert, i) => (

View file

@ -1,7 +1,9 @@
import { Add } from "@mui/icons-material";
import { Accordion, AccordionDetails, AccordionSummary, Divider, IconButton, List, ListItem, ListSubheader, Menu, MenuItem, Typography } from "@mui/material";
import { Component, Device } from "models";
import { pond } from "protobuf-ts/pond";
import { Add, ExpandMore } from "@mui/icons-material";
import { Accordion, AccordionDetails, AccordionSummary, Box, Button, Divider, IconButton, List, ListItem, ListSubheader, Menu, MenuItem, Typography } from "@mui/material";
import { Component, Device, Interaction } from "models";
import { interactionResultText } from "pbHelpers/Interaction";
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
import { pond, quack } from "protobuf-ts/pond";
import React, { useEffect, useState } from "react";
export interface Control {
@ -39,11 +41,49 @@ export default function Controls(props: Props){
setDeviceControls(map)
},[controls])
const readableConditions = (control: Control) => {
let conditions: string = ""
let firstSource = control.sourceComponents[0]
control.conditions.forEach((condition, index) => {
let describer = describeMeasurement(condition.measurementType, firstSource.type(), firstSource.subType());
let type = describer.label();
let comparison =
condition.comparison === quack.RelationalOperator.RELATIONAL_OPERATOR_EQUAL_TO
? "Exactly"
: condition.comparison === quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN
? "Above"
: "Below";
let value = describer.toDisplay(condition.value);
conditions = conditions + (index !== 0 ? " and " : "") + type + " " + comparison + " " + value + describer.GetUnit()
})
return conditions
};
const controlAccordion = (control: Control) => {
let tempInteraction = Interaction.create()
tempInteraction.settings.result = control.result
let resultText = interactionResultText(tempInteraction, control.sinkComponent)
return (
<Accordion>
<AccordionSummary>Show what the control does</AccordionSummary>
<AccordionDetails>show the components that are doing it</AccordionDetails>
<Accordion sx={{width: "100%"}}>
<AccordionSummary expandIcon={<ExpandMore />}>
<Box>
<Typography>
{resultText}:
</Typography>
<Typography>
{readableConditions(control)}
</Typography>
</Box>
</AccordionSummary>
<AccordionDetails>
<List>
<ListSubheader>Controlling Components</ListSubheader>
{control.sourceComponents.map((comp, i) => (
<ListItem key={i}>{comp.name()}</ListItem>
))}
</List>
</AccordionDetails>
</Accordion>
)
}
@ -79,17 +119,20 @@ export default function Controls(props: Props){
<List style={{ margin: 5 }}>
<ListSubheader sx={{paddingY: 1, display: "flex", justifyContent: "space-between", alignItems: "center"}}>
<Typography>
Alerts
Controls
</Typography>
<IconButton
{addNew &&
<Button
disabled={!permissions.includes(pond.Permission.PERMISSION_WRITE)}
variant="contained"
onClick={(e)=>{
setOpenDeviceMenu(true)
setAnchor(e.currentTarget)
}}
color="primary">
<Add />
</IconButton>
<Add />Control
</Button>
}
</ListSubheader>
{devices.map(device => (
<React.Fragment key={device.id()}>

View file

@ -996,6 +996,8 @@ export default function NewObjectInteraction(props: Props){
})
.catch(_err => {
openSnack("there was a problem adding the interaction to the selected components");
}).finally(() => {
close(true)
});
};

View file

@ -79,7 +79,11 @@ export default function Notifications(props: Props){
return (
<List style={{ margin: 5 }}>
<ListSubheader>Notifications</ListSubheader>
<ListSubheader>
<Typography sx={{paddingY: 2}}>
Notifications
</Typography>
</ListSubheader>
{recentNotifications && (
<React.Fragment>
{recentNotifications.map((notification, i) => (

View file

@ -1,5 +1,5 @@
import { Box, Button, Card, Typography } from "@mui/material";
import { useEffect, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import Alerts, { Alert } from "./Alerts";
import Controls, { Control } from "./Controls";
import { Component, Device, Interaction } from "models";
@ -21,11 +21,10 @@ interface Props {
objectType: pond.ObjectType
devices: Device[];
permissions: pond.Permission[]
refreshCallback?: () => {}
}
export default function ObjectInteractions(props: Props) {
const { linkedComponents, componentDevices, devices, objectKey, objectType, refreshCallback, permissions } = props
const { linkedComponents, componentDevices, devices, objectKey, objectType, permissions } = props
const [{as}] = useGlobalState()
//list of alerts, each alert contains a list of interactions with matching conditions
const [alerts, setAlerts] = useState<Alert[]>([]);
@ -86,7 +85,7 @@ export default function ObjectInteractions(props: Props) {
}
}
useEffect(() => {
const load = useCallback(()=>{
const newInteractions: Interaction[] = [];
const interactionSourceMap = new Map<string, string>();
const sinkOptions: Component[] = [];
@ -183,7 +182,11 @@ export default function ObjectInteractions(props: Props) {
}).catch(err => {
console.error("Interaction fetch error:", err)
})
}, [linkedComponents, interactionsAPI, componentDevices, as]);
},[linkedComponents, interactionsAPI, componentDevices, as])
useEffect(() => {
load()
}, [load]);
@ -193,8 +196,8 @@ export default function ObjectInteractions(props: Props) {
open={openNewInteraction}
onClose={(refresh) => {
setOpenNewInteraction(false)
if(refresh && refreshCallback){
refreshCallback()
if(refresh){
load()
}
}}
linkedComponents={linkedComponents}