Merge branch 'local_server' into dev_environment
This commit is contained in:
commit
73ad9c6816
5 changed files with 66 additions and 19 deletions
|
|
@ -8,7 +8,6 @@ import {
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
Theme,
|
|
||||||
Typography
|
Typography
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
|
|
@ -19,10 +18,10 @@ 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, useRef, useState } from "react";
|
import React, { useCallback, 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(() => {
|
||||||
return ({
|
return ({
|
||||||
addIcon: {
|
addIcon: {
|
||||||
color: "var(--status-ok)"
|
color: "var(--status-ok)"
|
||||||
|
|
@ -45,19 +44,19 @@ function AddDeviceTag(props: AddDeviceTagProps) {
|
||||||
const [searchValue, setSearchValue] = useState("");
|
const [searchValue, setSearchValue] = useState("");
|
||||||
const [tags, setTags] = useState<Tag[]>([])
|
const [tags, setTags] = useState<Tag[]>([])
|
||||||
|
|
||||||
const loadTags = () => {
|
const loadTags = useCallback(() => {
|
||||||
tagAPI.listTags().then(resp => {
|
tagAPI.listTags().then(resp => {
|
||||||
let newTags: Tag[] = [];
|
const newTags: Tag[] = [];
|
||||||
resp.data.tags.forEach((tag: pond.Tag) => {
|
resp.data.tags.forEach((tag: pond.Tag) => {
|
||||||
newTags.push(Tag.create(tag))
|
newTags.push(Tag.create(tag))
|
||||||
})
|
})
|
||||||
setTags(newTags)
|
setTags(newTags)
|
||||||
})
|
})
|
||||||
}
|
}, [tagAPI])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadTags()
|
loadTags()
|
||||||
}, [])
|
}, [loadTags])
|
||||||
|
|
||||||
const tagItems = tags
|
const tagItems = tags
|
||||||
.filter(tag => !deviceTags.some(tagSettings => tagSettings.key === tag.settings.key))
|
.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 [deviceTags, setDeviceTags] = useState<pond.TagSettings[]>(device.status.tags);
|
||||||
const previousDeviceRef = useRef(device);
|
const previousDeviceRef = useRef(device);
|
||||||
|
|
||||||
const loadTags = () => {
|
const loadTags = useCallback(() => {
|
||||||
// setLoading(true)
|
// setLoading(true)
|
||||||
tagAPI.listTags().then(resp => {
|
tagAPI.listTags().then(resp => {
|
||||||
let newTags: Tag[] = [];
|
const newTags: Tag[] = [];
|
||||||
resp.data.tags.forEach((tag: pond.Tag) => {
|
resp.data.tags.forEach((tag: pond.Tag) => {
|
||||||
newTags.push(Tag.create(tag))
|
newTags.push(Tag.create(tag))
|
||||||
})
|
})
|
||||||
|
|
@ -155,7 +154,7 @@ export default function DeviceTags(props: DeviceTagsProps) {
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
// setLoading(false)
|
// setLoading(false)
|
||||||
})
|
})
|
||||||
}
|
}, [tagAPI])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (previousDeviceRef.current !== device) {
|
if (previousDeviceRef.current !== device) {
|
||||||
|
|
@ -166,14 +165,16 @@ export default function DeviceTags(props: DeviceTagsProps) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadTags()
|
loadTags()
|
||||||
}, [])
|
}, [loadTags])
|
||||||
|
|
||||||
const addTag = (tag: Tag) => {
|
const addTag = (tag: Tag) => {
|
||||||
if (!deviceTags.some(dt => dt.key === tag.settings.key)) {
|
if (!deviceTags.some(dt => dt.key === tag.settings.key)) {
|
||||||
deviceAPI
|
deviceAPI
|
||||||
.tag(device.id(), tag.settings.key)
|
.tag(device.id(), tag.settings.key)
|
||||||
.then(() => {
|
.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));
|
.catch(() => error("Failed to tag device as " + tag.name));
|
||||||
}
|
}
|
||||||
|
|
@ -184,7 +185,7 @@ export default function DeviceTags(props: DeviceTagsProps) {
|
||||||
deviceAPI
|
deviceAPI
|
||||||
.untag(device.id(), tag.key())
|
.untag(device.id(), tag.key())
|
||||||
.then(() => {
|
.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"));
|
.catch(() => error("Failed to remove tag " + tag.name + " from device"));
|
||||||
}
|
}
|
||||||
|
|
@ -197,7 +198,7 @@ export default function DeviceTags(props: DeviceTagsProps) {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{deviceTags?.map(tagSettings => {
|
{deviceTags?.map(tagSettings => {
|
||||||
let pondTag = pond.Tag.create({ settings: tagSettings })
|
const pondTag = pond.Tag.create({ settings: tagSettings })
|
||||||
return (
|
return (
|
||||||
<Grid key={"device-tags-"+tagSettings.key}>
|
<Grid key={"device-tags-"+tagSettings.key}>
|
||||||
<DeviceTag tag={Tag.create(pondTag)} removeTagFromDevice={removeTagFromDevice} />
|
<DeviceTag tag={Tag.create(pondTag)} removeTagFromDevice={removeTagFromDevice} />
|
||||||
|
|
|
||||||
|
|
@ -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 { makeStyles } from "@mui/styles";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
// import { useMobile } from "hooks";
|
// import { useMobile } from "hooks";
|
||||||
|
|
@ -47,6 +47,23 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "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
|
disableGutters
|
||||||
maxWidth={false}
|
maxWidth={false}
|
||||||
children={<>{children}</>}
|
>
|
||||||
/>
|
{children}
|
||||||
|
<Typography component="div" className={classes.buildInfo}>
|
||||||
|
{new Date(__BUILD_DATE__).toLocaleDateString()}<br />
|
||||||
|
{__GIT_HASH__}
|
||||||
|
</Typography>
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -700,8 +700,12 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const setTags = (id: number, tags: string[]) => {
|
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) => {
|
return new Promise<AxiosResponse>((resolve, reject) => {
|
||||||
put(pondURL("/devices/" + id + "/tags"), { tags }).then(resp => {
|
put(pondURL(url), { tags }).then(resp => {
|
||||||
return resolve(resp)
|
return resolve(resp)
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
return reject(err)
|
return reject(err)
|
||||||
|
|
@ -746,8 +750,12 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const untag = (id: number, tag: string) => {
|
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) => {
|
return new Promise<AxiosResponse>((resolve, reject) => {
|
||||||
del(pondURL("/devices/" + id + "/tags/" + tag)).then(resp => {
|
del(pondURL(url)).then(resp => {
|
||||||
return resolve(resp)
|
return resolve(resp)
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
return reject(err)
|
return reject(err)
|
||||||
|
|
|
||||||
3
src/vite-env.d.ts
vendored
3
src/vite-env.d.ts
vendored
|
|
@ -1 +1,4 @@
|
||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare const __BUILD_DATE__: string
|
||||||
|
declare const __GIT_HASH__: string
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { VitePWA } from 'vite-plugin-pwa';
|
||||||
import * as path from 'path' // ✅ Import path module
|
import * as path from 'path' // ✅ Import path module
|
||||||
import { readFileSync, renameSync, existsSync, unlinkSync } from 'node:fs'
|
import { readFileSync, renameSync, existsSync, unlinkSync } from 'node:fs'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { execSync } from 'node:child_process'
|
||||||
|
|
||||||
const rootDir = path.dirname(fileURLToPath(import.meta.url))
|
const rootDir = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
|
@ -49,7 +50,19 @@ function emitLocalnetShellAsIndexHtml (mode: string): Plugin {
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig(({ command, mode }): UserConfig => {
|
export default defineConfig(({ command, mode }): UserConfig => {
|
||||||
const useLocalnetShell = mode === LOCALNET_MODE
|
const useLocalnetShell = mode === LOCALNET_MODE
|
||||||
|
|
||||||
|
let gitHash = 'unknown'
|
||||||
|
try {
|
||||||
|
gitHash = execSync('git rev-parse --short HEAD').toString().trim()
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
const buildDate = new Date().toISOString()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
define: {
|
||||||
|
__BUILD_DATE__: JSON.stringify(buildDate),
|
||||||
|
__GIT_HASH__: JSON.stringify(gitHash),
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
useLocalnetShellHtml(mode, command),
|
useLocalnetShellHtml(mode, command),
|
||||||
emitLocalnetShellAsIndexHtml(mode),
|
emitLocalnetShellAsIndexHtml(mode),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue