Merge branch 'local_server' into dev_environment

This commit is contained in:
Carter 2026-05-25 12:25:35 -06:00
commit 73ad9c6816
5 changed files with 66 additions and 19 deletions

View file

@ -8,7 +8,6 @@ import {
ListItem,
ListItemIcon,
ListItemText,
Theme,
Typography
} from "@mui/material";
import { makeStyles } from "@mui/styles";
@ -19,10 +18,10 @@ import TagSettings from "common/TagSettings";
import { Device, Tag } from "models";
import { filterByTag } from "pbHelpers/Tag";
import { useDeviceAPI, useSnackbar, useTagAPI } from "providers";
import React, { useEffect, useRef, useState } from "react";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { pond } from "protobuf-ts/pond";
const useStyles = makeStyles((_theme: Theme) => {
const useStyles = makeStyles(() => {
return ({
addIcon: {
color: "var(--status-ok)"
@ -45,19 +44,19 @@ function AddDeviceTag(props: AddDeviceTagProps) {
const [searchValue, setSearchValue] = useState("");
const [tags, setTags] = useState<Tag[]>([])
const loadTags = () => {
const loadTags = useCallback(() => {
tagAPI.listTags().then(resp => {
let newTags: Tag[] = [];
const newTags: Tag[] = [];
resp.data.tags.forEach((tag: pond.Tag) => {
newTags.push(Tag.create(tag))
})
setTags(newTags)
})
}
}, [tagAPI])
useEffect(() => {
loadTags()
}, [])
}, [loadTags])
const tagItems = tags
.filter(tag => !deviceTags.some(tagSettings => tagSettings.key === tag.settings.key))
@ -144,10 +143,10 @@ export default function DeviceTags(props: DeviceTagsProps) {
const [deviceTags, setDeviceTags] = useState<pond.TagSettings[]>(device.status.tags);
const previousDeviceRef = useRef(device);
const loadTags = () => {
const loadTags = useCallback(() => {
// setLoading(true)
tagAPI.listTags().then(resp => {
let newTags: Tag[] = [];
const newTags: Tag[] = [];
resp.data.tags.forEach((tag: pond.Tag) => {
newTags.push(Tag.create(tag))
})
@ -155,7 +154,7 @@ export default function DeviceTags(props: DeviceTagsProps) {
}).finally(() => {
// setLoading(false)
})
}
}, [tagAPI])
useEffect(() => {
if (previousDeviceRef.current !== device) {
@ -166,14 +165,16 @@ export default function DeviceTags(props: DeviceTagsProps) {
useEffect(() => {
loadTags()
}, [])
}, [loadTags])
const addTag = (tag: Tag) => {
if (!deviceTags.some(dt => dt.key === tag.settings.key)) {
deviceAPI
.tag(device.id(), tag.settings.key)
.then(() => {
setDeviceTags([...deviceTags, tag.settings]);
setDeviceTags(current =>
current.some(dt => dt.key === tag.settings.key) ? current : [...current, tag.settings]
);
})
.catch(() => error("Failed to tag device as " + tag.name));
}
@ -184,7 +185,7 @@ export default function DeviceTags(props: DeviceTagsProps) {
deviceAPI
.untag(device.id(), tag.key())
.then(() => {
setDeviceTags(deviceTags.filter(t => tag.key() !== t.key));
setDeviceTags(current => current.filter(t => tag.key() !== t.key));
})
.catch(() => error("Failed to remove tag " + tag.name + " from device"));
}
@ -197,7 +198,7 @@ export default function DeviceTags(props: DeviceTagsProps) {
return (
<React.Fragment>
{deviceTags?.map(tagSettings => {
let pondTag = pond.Tag.create({ settings: tagSettings })
const pondTag = pond.Tag.create({ settings: tagSettings })
return (
<Grid key={"device-tags-"+tagSettings.key}>
<DeviceTag tag={Tag.create(pondTag)} removeTagFromDevice={removeTagFromDevice} />

View file

@ -1,4 +1,4 @@
import { Container, SxProps, Theme } from "@mui/material";
import { Container, SxProps, Theme, Typography } from "@mui/material";
import { makeStyles } from "@mui/styles";
import classNames from "classnames";
// import { useMobile } from "hooks";
@ -47,6 +47,23 @@ const useStyles = makeStyles((theme: Theme) => ({
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
},
buildInfo: {
position: "fixed" as const,
bottom: 56,
left: 8,
fontSize: 10,
lineHeight: 1.3,
opacity: 0.35,
color: theme.palette.text.secondary,
pointerEvents: "none" as const,
zIndex: 1,
[theme.breakpoints.up("sm")]: {
bottom: 8,
},
[theme.breakpoints.up("md")]: {
left: `calc(${theme.spacing(9)} + 8px)`,
}
}
}))
@ -78,8 +95,13 @@ export const PageContainer: React.FunctionComponent<Props> = (props: Props) => {
}}
disableGutters
maxWidth={false}
children={<>{children}</>}
/>
>
{children}
<Typography component="div" className={classes.buildInfo}>
{new Date(__BUILD_DATE__).toLocaleDateString()}<br />
{__GIT_HASH__}
</Typography>
</Container>
);
};

View file

@ -700,8 +700,12 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
};
const setTags = (id: number, tags: string[]) => {
const keys = getContextKeys()
const types = getContextTypes()
let url = "/devices/" + id + "/tags" + "?keys=" + keys + "&types=" + types
if (as && !keys.includes(as)) url += "&as=" + as
return new Promise<AxiosResponse>((resolve, reject) => {
put(pondURL("/devices/" + id + "/tags"), { tags }).then(resp => {
put(pondURL(url), { tags }).then(resp => {
return resolve(resp)
}).catch(err => {
return reject(err)
@ -746,8 +750,12 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
};
const untag = (id: number, tag: string) => {
const keys = getContextKeys()
const types = getContextTypes()
let url = "/devices/" + id + "/tags/" + tag + "?keys=" + keys + "&types=" + types
if (as && !keys.includes(as)) url += "&as=" + as
return new Promise<AxiosResponse>((resolve, reject) => {
del(pondURL("/devices/" + id + "/tags/" + tag)).then(resp => {
del(pondURL(url)).then(resp => {
return resolve(resp)
}).catch(err => {
return reject(err)

3
src/vite-env.d.ts vendored
View file

@ -1 +1,4 @@
/// <reference types="vite/client" />
declare const __BUILD_DATE__: string
declare const __GIT_HASH__: string