implemented new tag method

This commit is contained in:
Carter 2025-03-04 14:40:51 -06:00
parent eb5fd127e3
commit 6439d217e9
6 changed files with 48 additions and 48 deletions

2
package-lock.json generated
View file

@ -7256,7 +7256,7 @@
}, },
"node_modules/protobuf-ts": { "node_modules/protobuf-ts": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#86c8ea9c667b2d01c56e0af05f77c1326f8e08fa", "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#cf0774d8aab2663916bbb0b2c44f4bb3352487b5",
"dependencies": { "dependencies": {
"protobufjs": "^6.8.8" "protobufjs": "^6.8.8"
} }

View file

@ -16,7 +16,7 @@ import { notNull, or } from "utils/types";
// import { MatchParams } from "navigation/Routes"; // import { MatchParams } from "navigation/Routes";
// import DeviceHologram from "./DeviceHologram"; // import DeviceHologram from "./DeviceHologram";
import { makeStyles } from "@mui/styles"; import { makeStyles } from "@mui/styles";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate } from "react-router-dom";
const useStyles = makeStyles((_theme: Theme) => { const useStyles = makeStyles((_theme: Theme) => {
return ({ return ({
@ -39,13 +39,12 @@ interface Props {
usage?: Usage; usage?: Usage;
loading?: boolean; loading?: boolean;
disableAddTag?: boolean; disableAddTag?: boolean;
tags: pond.Tag[];
groupID?: number; groupID?: number;
} }
export default function DeviceOverview(props: Props) { export default function DeviceOverview(props: Props) {
const [{ user, firmware }] = useGlobalState(); const [{ user, firmware }] = useGlobalState();
const { device, components, groupID, usage, loading, disableAddTag, tags } = props; const { device, components, groupID, usage, loading, disableAddTag } = props;
const prevComponents = usePrevious(components); const prevComponents = usePrevious(components);
const { info } = useSnackbar(); const { info } = useSnackbar();
const classes = useStyles(); const classes = useStyles();
@ -156,8 +155,8 @@ export default function DeviceOverview(props: Props) {
variant={modemComponent ? "filled" : "outlined"} variant={modemComponent ? "filled" : "outlined"}
clickable={modemComponent !== null} clickable={modemComponent !== null}
onClick={() => { onClick={() => {
console.log(modemComponent) // console.log(modemComponent)
console.log(modemComponent?.key()) // console.log(modemComponent?.key())
if (modemComponent && modemComponent.key()) { if (modemComponent && modemComponent.key()) {
navigate(pathToDevice() + "/components/" + modemComponent.key()); navigate(pathToDevice() + "/components/" + modemComponent.key());
} }
@ -234,7 +233,7 @@ export default function DeviceOverview(props: Props) {
<StatusChip status="pending" /> <StatusChip status="pending" />
</Grid> </Grid>
)} )}
{user.allowedTo("provision") && <DeviceTags tags={tags} device={device} disableAdd={disableAddTag} />} {user.allowedTo("provision") && <DeviceTags device={device} disableAdd={disableAddTag} />}
</Grid> </Grid>
); );
}; };

View file

@ -17,7 +17,6 @@ import {
Theme, Theme,
Typography Typography
} from "@mui/material"; } from "@mui/material";
// import { Theme } from "@material-ui/core/styles/createMuiTheme";
import DeleteButton from "common/DeleteButton"; import DeleteButton from "common/DeleteButton";
import PeriodSelect from "common/time/PeriodSelect"; import PeriodSelect from "common/time/PeriodSelect";
import ResponsiveDialog from "common/ResponsiveDialog"; import ResponsiveDialog from "common/ResponsiveDialog";
@ -27,7 +26,6 @@ import { IsExtended, ListDeviceProductDescribers } from "products/DeviceProduct"
import { pond } from "protobuf-ts/pond"; import { pond } from "protobuf-ts/pond";
import { useGlobalState } from "providers"; import { useGlobalState } from "providers";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
// import { useHistory } from "react-router";
import { quack } from "protobuf-ts/quack"; import { quack } from "protobuf-ts/quack";
import LinearMutationBuilder from "common/LinearMutationBuilder"; import LinearMutationBuilder from "common/LinearMutationBuilder";
import { makeStyles } from "@mui/styles"; import { makeStyles } from "@mui/styles";

View file

@ -19,7 +19,7 @@ import TagSettings from "common/TagSettings";
import { Device, Tag } from "models"; import { Device, Tag } from "models";
import { filterByTag } from "pbHelpers/Tag"; import { filterByTag } from "pbHelpers/Tag";
import { useDeviceAPI, useSnackbar, useTagAPI } from "providers"; import { useDeviceAPI, useSnackbar, useTagAPI } from "providers";
import React, { useEffect, useState } from "react"; import React, { useEffect, useRef, useState } from "react";
import { pond } from "protobuf-ts/pond"; import { pond } from "protobuf-ts/pond";
const useStyles = makeStyles((_theme: Theme) => { const useStyles = makeStyles((_theme: Theme) => {
@ -32,7 +32,7 @@ const useStyles = makeStyles((_theme: Theme) => {
interface AddDeviceTagProps { interface AddDeviceTagProps {
device: Device; device: Device;
deviceTags: string[]; deviceTags: pond.TagSettings[];
addTagToDevice: (tag: Tag) => void; addTagToDevice: (tag: Tag) => void;
} }
@ -60,7 +60,7 @@ function AddDeviceTag(props: AddDeviceTagProps) {
}, []) }, [])
const tagItems = tags const tagItems = tags
.filter(tag => !deviceTags.some(key => key === tag.settings.key)) .filter(tag => !deviceTags.some(tagSettings => tagSettings.key === tag.settings.key))
.filter(tag => filterByTag(searchValue, tag)) .filter(tag => filterByTag(searchValue, tag))
.sort((a, b) => (a.name().toLowerCase() > b.name().toLowerCase() ? 1 : -1)) .sort((a, b) => (a.name().toLowerCase() > b.name().toLowerCase() ? 1 : -1))
.map((tag, index, array) => ( .map((tag, index, array) => (
@ -102,7 +102,10 @@ function AddDeviceTag(props: AddDeviceTagProps) {
</List> </List>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
<TagSettings open={createNewOpen} mode="add" onClose={() => setCreateNewOpen(false)} /> <TagSettings open={createNewOpen} mode="add" onClose={() => {
setCreateNewOpen(false)
loadTags()
}} />
</React.Fragment> </React.Fragment>
); );
} }
@ -127,7 +130,6 @@ function DeviceTag(props: DeviceTagProps) {
interface DeviceTagsProps { interface DeviceTagsProps {
device: Device; device: Device;
disableAdd?: boolean; disableAdd?: boolean;
tags: pond.Tag[];
} }
export default function DeviceTags(props: DeviceTagsProps) { export default function DeviceTags(props: DeviceTagsProps) {
@ -138,8 +140,8 @@ export default function DeviceTags(props: DeviceTagsProps) {
// const [loading, setLoading] = useState(false) // const [loading, setLoading] = useState(false)
const deviceAPI = useDeviceAPI(); const deviceAPI = useDeviceAPI();
const { error } = useSnackbar(); const { error } = useSnackbar();
const [deviceTags, setDeviceTags] = useState<string[]>(device.status.tagKeys); const [deviceTags, setDeviceTags] = useState<pond.TagSettings[]>(device.status.tags);
// const previousDeviceRef = useRef(device); const previousDeviceRef = useRef(device);
const loadTags = () => { const loadTags = () => {
// setLoading(true) // setLoading(true)
@ -155,36 +157,35 @@ export default function DeviceTags(props: DeviceTagsProps) {
} }
useEffect(() => { useEffect(() => {
let newTags: string[] = [] if (previousDeviceRef.current !== device) {
props.tags.forEach(tag => { setDeviceTags(device.status.tags);
if (tag.settings) newTags.push(tag.settings?.key) previousDeviceRef.current = device;
}) }
setDeviceTags(newTags) }, [device]);
}, [props.tags])
useEffect(() => { useEffect(() => {
loadTags() loadTags()
}, []) }, [])
const addTag = (tag: Tag) => { const addTag = (tag: Tag) => {
if (!deviceTags.some(dt => dt === tag.key())) { if (!deviceTags.some(dt => dt.key === tag.settings.key)) {
deviceAPI deviceAPI
.tag(device.id(), tag.key()) .tag(device.id(), tag.settings.key)
.then(() => { .then(() => {
setDeviceTags([...deviceTags, tag.key()]); setDeviceTags([...deviceTags, tag.settings]);
}) })
.catch(() => error("Failed to tag device as " + tag.name())); .catch(() => error("Failed to tag device as " + tag.name));
} }
}; };
const removeTagFromDevice = (tag: Tag) => { const removeTagFromDevice = (tag: Tag) => {
if (deviceTags.some(dt => dt === tag.key())) { if (deviceTags.some(dt => dt.key === tag.settings.key)) {
deviceAPI deviceAPI
.untag(device.id(), tag.key()) .untag(device.id(), tag.key())
.then(() => { .then(() => {
setDeviceTags(deviceTags.filter(t => tag.key() !== t)); setDeviceTags(deviceTags.filter(t => tag.key() !== t.key));
}) })
.catch(() => error("Failed to remove tag " + tag.name() + " from device")); .catch(() => error("Failed to remove tag " + tag.name + " from device"));
} }
}; };
@ -194,14 +195,11 @@ export default function DeviceTags(props: DeviceTagsProps) {
return ( return (
<React.Fragment> <React.Fragment>
{deviceTags.map(key => { {deviceTags?.map(tagSettings => {
let tag = tags.find(t => t.settings.key === key); let pondTag = pond.Tag.create({ settings: tagSettings })
if (!tag) {
return null;
}
return ( return (
<Grid key={tag.settings.key}> <Grid key={"device-tags-"+tagSettings.key}>
<DeviceTag tag={tag} removeTagFromDevice={removeTagFromDevice} /> <DeviceTag tag={Tag.create(pondTag)} removeTagFromDevice={removeTagFromDevice} />
</Grid> </Grid>
); );
})} })}

View file

@ -1,4 +1,4 @@
import { Button, Checkbox, Divider, FormControlLabel, List, ListItem, Typography } from "@mui/material"; import { Button, Divider, List, ListItem, Typography } from "@mui/material";
import Grid from '@mui/material/Grid2'; import Grid from '@mui/material/Grid2';
import { Component, Device, Interaction, User } from "models"; import { Component, Device, Interaction, User } from "models";
import { getContextKeys, getContextTypes } from "pbHelpers/Context"; import { getContextKeys, getContextTypes } from "pbHelpers/Context";
@ -33,7 +33,7 @@ export default function DevicePage() {
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [permissions, setPermissions] = useState<pond.Permission[]>([]) const [permissions, setPermissions] = useState<pond.Permission[]>([])
const [preferences, setPreferences] = useState<pond.DevicePreferences>(pond.DevicePreferences.create()) const [preferences, setPreferences] = useState<pond.DevicePreferences>(pond.DevicePreferences.create())
const [tags, setTags] = useState<pond.Tag[]>([]); // const [tags, setTags] = useState<pond.Tag[]>([]);
const [interactions, setInteractions] = useState<Interaction[]>([]); const [interactions, setInteractions] = useState<Interaction[]>([]);
const [prefsMap, setPrefsMap] = useState<Map<string, pond.DeviceComponentPreferences>>(new Map()); const [prefsMap, setPrefsMap] = useState<Map<string, pond.DeviceComponentPreferences>>(new Map());
@ -52,10 +52,10 @@ export default function DevicePage() {
const loadDevice = () => { const loadDevice = () => {
if (loading) return if (loading) return
setLoading(true) setLoading(true)
deviceAPI.getPageData(deviceID, getContextKeys(), getContextTypes()).then(resp => { deviceAPI.getPageData(deviceID, getContextKeys(), getContextTypes()).then(resp => {
let device = Device.any(resp.data.device) let device = Device.any(resp.data.device)
// console.log(resp.data.device)
setDevice(device) setDevice(device)
let newPermissions: pond.Permission[] = [] let newPermissions: pond.Permission[] = []
resp.data.permissions.forEach(perm => { resp.data.permissions.forEach(perm => {
@ -69,7 +69,8 @@ export default function DevicePage() {
setPermissions(newPermissions) setPermissions(newPermissions)
let u = User.any(resp.data.user); let u = User.any(resp.data.user);
setPreferences(u.preferences) setPreferences(u.preferences)
setTags(resp.data.tags) // setTags(resp.data.tags)
resp.data.device?.status?.tagNames
let newComps: Component[] = [] let newComps: Component[] = []
resp.data.components.forEach(comp => { resp.data.components.forEach(comp => {
newComps.push(Component.create(comp)) newComps.push(Component.create(comp))
@ -290,8 +291,8 @@ export default function DevicePage() {
<Divider component="li" /> <Divider component="li" />
<ListItem> <ListItem>
<Grid width={"100%"} container justifyContent="flex-start" direction="row" spacing={2}> <Grid width={"100%"} container justifyContent="flex-start" direction="row" spacing={2}>
{sensorComponents.map(card => ( {sensorComponents.map((card, index) => (
<Grid size={{ <Grid key={"sensor-components-"+index} size={{
xs: 12, xs: 12,
sm: isMobile ? 12 : 6, sm: isMobile ? 12 : 6,
md: isMobile ? 12 : 6, md: isMobile ? 12 : 6,
@ -311,8 +312,8 @@ export default function DevicePage() {
<Divider component="li" /> <Divider component="li" />
<ListItem> <ListItem>
<Grid width={"100%"} container justifyContent="flex-start" direction="row" spacing={2}> <Grid width={"100%"} container justifyContent="flex-start" direction="row" spacing={2}>
{controllerComponents.map(card => ( {controllerComponents.map((card, index) => (
<Grid size={{ <Grid key={"sensor-components-"+index} size={{
xs: 12, xs: 12,
sm: isMobile ? 12 : 6, sm: isMobile ? 12 : 6,
md: isMobile ? 12 : 6, md: isMobile ? 12 : 6,
@ -422,9 +423,11 @@ export default function DevicePage() {
open={addComponentManualDialogOpen} open={addComponentManualDialogOpen}
onClose={() => setAddComponentManualDialogOpen(false)} onClose={() => setAddComponentManualDialogOpen(false)}
/> />
{user.hasFeature("developer") === true &&
<Button onClick={() => setAddComponentManualDialogOpen(true)}> <Button onClick={() => setAddComponentManualDialogOpen(true)}>
Manual Comp Manual Comp
</Button> </Button>
}
<DeviceActions <DeviceActions
device={device} device={device}
isPaused={false} isPaused={false}
@ -445,7 +448,6 @@ export default function DevicePage() {
// components={components} // components={components}
usage={getUsage()} usage={getUsage()}
loading={loading} loading={loading}
tags={tags}
groupID={parseInt(groupID)} groupID={parseInt(groupID)}
/> />
{componentCards()} {componentCards()}

View file

@ -4,6 +4,7 @@ import { createContext, PropsWithChildren, useContext } from "react";
import { pondURL } from "./pond"; import { pondURL } from "./pond";
import { useGlobalState } from "providers/StateContainer"; import { useGlobalState } from "providers/StateContainer";
import { AxiosResponse } from "axios"; import { AxiosResponse } from "axios";
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
export interface ITagAPIContext { export interface ITagAPIContext {
addTag: (tag: pond.TagSettings) => Promise<any>; addTag: (tag: pond.TagSettings) => Promise<any>;
@ -87,6 +88,8 @@ export default function TagProvider(props: PropsWithChildren<Props>) {
types?: string[] types?: string[]
) => { ) => {
limit = limit ? limit : 100 limit = limit ? limit : 100
// if (!keys) keys = getContextKeys()
// if (!types) types = getContextTypes()
const url = pondURL( const url = pondURL(
"/tags" + "/tags" +
"?limit=" + "?limit=" +